Back to Blog

Multi-Agent Collaboration: Orchestrating Complex Workflows with LangGraph

3 min read
Bishwambhar SenBy Bishwambhar Sen

As LLM agent architectures mature, single-agent patterns are being replaced by multi-agent systems. A single agent tasked with planning, research, coding, and review often struggles to maintain context and manage task complexity.

By breaking tasks down and allocating them to a network of specialized agents, you can achieve higher overall reliability. LangGraph is a framework designed specifically to build these multi-agent systems using cyclic state graphs.

Multi-Agent Collaboration with LangGraphMulti-Agent Collaboration with LangGraph

The Mechanics of LangGraph

LangGraph models agent workflows as graphs:

  • State: A shared, thread-safe data structure that tracks the state of the workflow.
  • Nodes: Standard Python functions or LLM tasks that read from the state, execute logic, and write updates back to the state.
  • Edges: Connections defining the execution path.
  • Conditional Edges: Logic checks that evaluate current state values to determine which node to execute next.

Mathematically, a LangGraph is a directed graph where state transitions are governed by transition functions:

\delta: S \times V \rightarrow S

where S is the state space, V is the current node, and \delta returns the updated state.

Python Code: A Writer-Reviewer Loop in LangGraph

Let's build a basic developer-critic workflow in LangGraph:

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
import operator

# 1. Define the shared state
class AgentState(TypedDict):
    content: str
    feedback: str
    revision_count: int

# 2. Define the nodes
def writer_node(state: AgentState):
    print("-> Writer updating content")
    rev = state.get("revision_count", 0) + 1
    # Simple simulation of content improvement
    return {
        "content": f"Draft version {rev} (improved based on: {state.get('feedback', 'none')})",
        "revision_count": rev
    }

def critic_node(state: AgentState):
    print("-> Critic evaluating content")
    content = state["content"]
    if "version 3" in content:
        return {"feedback": "approved"}
    return {"feedback": "Needs more detail."}

# 3. Define the routing logic
def router(state: AgentState):
    if state["feedback"] == "approved":
        return "end"
    return "write"

# 4. Build the graph
workflow = StateGraph(AgentState)

workflow.add_node("writer", writer_node)
workflow.add_node("critic", critic_node)

workflow.set_entry_point("writer")
workflow.add_edge("writer", "critic")

workflow.add_conditional_edges(
    "critic",
    router,
    {
        "write": "writer",
        "end": END
    }
)

app = workflow.compile()

# Run the graph
final_state = app.invoke({
    "content": "Initial prompt.",
    "feedback": "",
    "revision_count": 0
})

print(f"\nFinal content: {final_state['content']}")
print(f"Revisions taken: {final_state['revision_count']}")
# -> Writer updating content / -> Critic evaluating content  (x3)
# Final content: Draft version 3 (improved based on: Needs more detail.)
# Revisions taken: 3

Why Cyclic State Graphs Matter

Standard DAG (Directed Acyclic Graph) tools like LangChain Expression Language (LCEL) or traditional orchestrators are linear. However, complex human workflows are naturally cyclic—requiring iteration, refinement, and testing. LangGraph's support for loops enables developers to implement these native cyclic patterns.

Conclusion

Multi-agent setups are frequently oversold. Every agent boundary you add is another LLM call, another serialization of state into a prompt, and another place where context silently drops. A writer-critic pair that takes three revisions costs roughly six model calls to produce what a single well-prompted call often produces in one. Before splitting a task across agents, check whether the single-agent version actually failed, or whether it just had a vague prompt.

The specific failure mode to watch for in the loop above is critic collapse: once a critic model has seen its own prior feedback in state, it tends to converge on approving whatever it said last time, so revision three gets rubber-stamped regardless of quality. Two mitigations work in practice — hide prior feedback from the critic so each evaluation is independent, and hard-cap revision_count (the example caps at three for exactly this reason). Never rely on the critic alone to decide when to stop; a runaway loop against a paid API is an expensive way to learn this.

Where multi-agent genuinely earns its cost is when the sub-tasks need different tools or different models — a cheap model doing retrieval, an expensive one doing synthesis. That is a real architectural win. "Specialization improves reasoning" on its own, without a tool or cost difference behind it, usually is not.