While simple sequential chains (like those in early LangChain) work for basic question-answering, production AI applications require loops, conditional branching, human-in-the-loop approvals, and multi-agent debate. In 2026, LangGraph has become the premier framework for developing complex, cyclic agent architectures.
In this tutorial, we will construct a complete multi-agent workflow using LangGraph’s StateGraph. We build a collaborative research pipeline where a Researcher Agent searches for information and a Synthesizer Agent reviews and formats the response, with automatic looping if details are missing.
1. Multi-Agent Graph Architecture
┌───────────────┐
│ START │
└───────┬───────┘
▼
┌───────────────┐
│ Researcher │ ◄───────┐
└───────┬───────┘ │
▼ │ (Needs more data)
┌───────────────┐ │
│ Evaluator ├─────────┘
└───────┬───────┘
│ (Approved)
▼
┌───────────────┐
│ Synthesizer │
└───────┬───────┘
▼
┌───────────────┐
│ END │
└───────────────┘
2. Environment Setup
pip install langgraph>=0.2.0 langchain-core>=0.3.0 pydantic>=2.7.0
3. Implementation: StateGraph with Cyclic Feedback
Create agent_graph.py:
from typing import TypedDict, Annotated, List
import operator
from langgraph.graph import StateGraph, START, END
# Define shared agent state
class AgentState(TypedDict):
query: str
research_notes: Annotated[List[str], operator.add]
evaluation_passed: bool
final_report: str
iteration_count: int
def researcher_node(state: AgentState) -> dict:
# Simulates gathering information from web and internal docs.
query = state["query"]
iteration = state.get("iteration_count", 0) + 1
new_note = f"[Iteration {iteration}] Gathered telemetry and benchmarks for query: {query}"
return {
"research_notes": [new_note],
"iteration_count": iteration
}
def evaluator_node(state: AgentState) -> dict:
# Checks whether sufficient evidence has been gathered.
passed = state["iteration_count"] >= 2
return {"evaluation_passed": passed}
def synthesizer_node(state: AgentState) -> dict:
# Compiles the final technical briefing.
notes = "\n".join(state["research_notes"])
report = (
f"# Research Briefing for '{state['query']}'\n\n"
f"## Summary of Findings:\n{notes}\n\n"
f"**Status:** Approved for production deployment."
)
return {"final_report": report}
def route_evaluator(state: AgentState) -> str:
# Conditional edge router: continue loop or finalize.
if state["evaluation_passed"]:
return "synthesizer"
return "researcher"
# Build the workflow graph
workflow = StateGraph(AgentState)
# Add nodes
workflow.add_node("researcher", researcher_node)
workflow.add_node("evaluator", evaluator_node)
workflow.add_node("synthesizer", synthesizer_node)
# Add edges
workflow.add_edge(START, "researcher")
workflow.add_edge("researcher", "evaluator")
workflow.add_conditional_edges("evaluator", route_evaluator, {
"researcher": "researcher",
"synthesizer": "synthesizer"
})
workflow.add_edge("synthesizer", END)
# Compile the graph
app = workflow.compile()
if __name__ == "__main__":
initial_state = {
"query": "Model Context Protocol vs Custom REST API Benchmarks",
"research_notes": [],
"evaluation_passed": False,
"final_report": "",
"iteration_count": 0
}
result = app.invoke(initial_state)
print(result["final_report"])
4. Key LangGraph Concepts Explained
TypedDictState: All nodes read from and write to a shared typed dictionary.Annotated[List[str], operator.add]: Tells LangGraph to append list items rather than overwriting previous entries.- Conditional Edges: Decides the next node at runtime based on the evaluation result.
5. Frequently Asked Questions (FAQ)
How do I prevent infinite loops in LangGraph?
LangGraph includes a built-in recursion limiter (recursion_limit=25). You should also increment an iteration_count in your state and force completion when a maximum count is reached.
Can I save conversation state to a database?
Yes! LangGraph supports persistent checkpointers like SqliteSaver and PostgresSaver.



Leave a Reply