LangGraph Agents

Course Content

LangGraph Agents

7 sections · 49 lessons

What is the Send API used for (map-reduce / fan-out fan-in patterns)?


One claim file, one extract worker per documentsplit lists 5to 40 documentsSend oneextractper documenteach worker seesonly its payloadoperator.addmerges allresultsmerge sorts,flags OCR failuresmax_concurrency 8: about 25 seconds instead of 3 minutes.
Input to each worker is private but its output is shared, which is why the results key needs an appending reducer.

What you need to know

Python
import operatorfrom typing import Annotated, TypedDictfrom langgraph.graph import StateGraph, START, ENDfrom langgraph.types import Sendclass State(TypedDict):    topics: list[str]    findings: Annotated[list[str], operator.add]    report: strclass WorkerInput(TypedDict):    topic: strdef plan(state: State) -> dict:    return {}def fan_out(state: State) -> list[Send]:    return [Send("research", {"topic": t}) for t in state["topics"]]def research(state: WorkerInput) -> dict:    return {"findings": [f"notes on {state['topic']}"]}def synthesise(state: State) -> dict:    return {"report": "\n".join(sorted(state["findings"]))}builder = StateGraph(State)builder.add_node("plan", plan)builder.add_node("research", research)builder.add_node("synthesise", synthesise)builder.add_edge(START, "plan")builder.add_conditional_edges("plan", fan_out, ["research"])builder.add_edge("research", "synthesise")builder.add_edge("synthesise", END)graph = builder.compile()print(graph.invoke({"topics": ["pricing", "competitors", "regulation"], "findings": []}))

The two rules

  1. Input is private. The dict in Send is the worker's whole input. It does not see topics or other workers.
  2. Output is shared. Every worker's update merges into global state, so findings needs operator.add or a similar reducer.

Fan-in

All Send workers here run in the same super-step, so synthesise runs once, after all of them. If workers have different path lengths (for example, a worker is itself a two-node path), mark the join defer=True.

Costs and limits

  • Every worker is a model call: 50 topics means 50 calls. Cap the list length.
  • Use max_concurrency in the run config to avoid provider rate limits.
  • Catch expected errors inside the worker and return a partial result, so one bad item does not fail the step.

A real-life example

An insurance company receives claim files with 5 to 40 attached documents. A split node lists the documents; Send starts one extract worker per document; each writes {"doc_id": ..., "fields": {...}} into extracted. With max_concurrency: 8, a 40-document file finishes in about 25 seconds instead of 3 minutes sequentially. A merge node sorts by doc_id, reconciles conflicting dates, and flags two documents where OCR failed for a human.

Follow-up questions to expect

  • "Static fan-out versus Send?" — Several fixed edges when the branches are known at build time (search news and filings); Send when the count depends on data.
  • "Can Send target a subgraph?" — Yes; the target can be any node, including a compiled subgraph.
  • "Can you use Send for self-consistency?" — Yes: send the same question five times with different seeds and vote in the join node.