startup-validator / src /graph.py
vaibhavbasidoni's picture
Initial commit: Startup Validator multi-agent system
65ade69
Raw History Blame Contribute Delete
3.62 kB
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from pydantic import BaseModel, Field
from src.state import AgentState
from src.agents import competitor_agent, market_sizing_agent, sentiment_agent, os_risk_agent, get_llm
# --- Structured Output Schema ---
# We use Pydantic to tell the LLM exactly what JSON structure we want back.
class FinalReport(BaseModel):
score: int = Field(description="A confidence score from 0 to 100 based on the viability of the startup idea.")
verdict: str = Field(description="A categorical verdict: 'Go', 'Caution', or 'No-Go'.")
summary: str = Field(description="A brief 2-3 sentence summary explaining the final verdict and score.")
# --- The Scoring Engine (Node) ---
# This node runs AFTER all 4 parallel agents have finished.
def scoring_node(state: AgentState) -> dict:
idea = state["idea"]
results = state["results"]
print(f"\n[Scoring Engine] All 4 agents finished. Analyzing results for: {idea[:30]}...")
# Format the findings from all 4 agents into a readable string for the LLM
compiled_research = ""
for result in results:
compiled_research += f"\n### {result['agent'].replace('_', ' ').title()} ###\n"
compiled_research += f"{result['findings']}\n"
prompt = f"""You are a harsh but fair Y Combinator partner evaluating a startup idea.
Idea: '{idea}'
Here is the research from your 4 analysts:
{compiled_research}
Based on this research, provide a final evaluation.
- Score (0-100): How viable is this? (100 = Guaranteed unicorn, 0 = Terrible idea)
- Verdict: 'Go', 'Caution', or 'No-Go'
- Summary: Justify your decision in 2-3 sentences based on the research.
"""
llm = get_llm()
# We use `.with_structured_output` to force the LLM to return a JSON object matching our Pydantic schema
structured_llm = llm.with_structured_output(FinalReport)
response = structured_llm.invoke([{"role": "user", "content": prompt}])
# Return the updates to the state. We are updating the 3 final fields.
return {
"score": response.score,
"verdict": response.verdict,
"summary": response.summary
}
# --- Construct the Graph ---
def build_graph():
# 1. Initialize the graph with our state schema
workflow = StateGraph(AgentState)
# 2. Add all the nodes (functions)
workflow.add_node("competitor_agent", competitor_agent)
workflow.add_node("market_sizing_agent", market_sizing_agent)
workflow.add_node("sentiment_agent", sentiment_agent)
workflow.add_node("os_risk_agent", os_risk_agent)
workflow.add_node("scoring_node", scoring_node)
# 3. Define the routing (Edges)
# Fan-out: START branches to all 4 research agents simultaneously
workflow.add_edge(START, "competitor_agent")
workflow.add_edge(START, "market_sizing_agent")
workflow.add_edge(START, "sentiment_agent")
workflow.add_edge(START, "os_risk_agent")
# Fan-in: All 4 agents must finish and point to the scoring node
# LangGraph automatically waits for all incoming edges to a node to complete
workflow.add_edge("competitor_agent", "scoring_node")
workflow.add_edge("market_sizing_agent", "scoring_node")
workflow.add_edge("sentiment_agent", "scoring_node")
workflow.add_edge("os_risk_agent", "scoring_node")
# Finish the graph
workflow.add_edge("scoring_node", END)
# Compile the graph into an executable format
return workflow.compile()
# Export the compiled graph
app = build_graph()