--- description: best pract globs: alwaysApply: false --- The most robust pattern is to treat every agent node as a pure function AgentState → Command, where AgentState is an explicit, typed snapshot of everything the rest of the graph must know. My overall confidence that the practices below will remain valid for ≥ 12 months is 85 % (expert opinion). 1 Design a single source of truth for state Guideline Why it matters Key LangGraph API Define a typed schema (TypedDict or pydantic.BaseModel) for the whole graph. Static typing catches missing keys early and docs double as living design specs. langchain-ai.github.io StateGraph(YourState) Use channel annotations such as Annotated[list[BaseMessage], operator.add] on mutable fields. Makes accumulation (+) vs. overwrite clear and prevents accidental loss of history. langchain-ai.github.io messages: Annotated[list[BaseMessage], operator.add] Keep routing out of business data—store the next hop in a dedicated field (next: Literal[...]). Separates control-flow from payload; easier to debug and replay. langchain-ai.github.io next: Literal["planner", "researcher", "__end__"] 2 Pass information with Command objects Pattern python Copy Edit def planner(state: AgentState) -> Command[Literal["researcher", "executor", END]]: decision = model.invoke(...state.messages) return Command( goto = decision["next"], update = { "messages": [decision["content"]], "plan": decision["plan"] } ) Best-practice notes Always update via update=… rather than mutating the state in-place. This guarantees immutability between nodes and makes time-travel/debugging deterministic. langchain-ai.github.io When handing off between sub-graphs, set graph=Command.PARENT or the target sub-graph’s name so orchestration stays explicit. langchain-ai.github.io 3 Choose a message-sharing strategy early Strategy Pros Cons When to use Shared scratch-pad (every intermediate LLM thought stored in messages) langchain-ai.github.io Maximum transparency; great for debugging & reflection. Context window bloat, higher cost/time. ≤ 3 specialist agents or short tasks. Final-result only (each agent keeps private scratch-pad, shares only its final answer) langchain-ai.github.io Scales to 10 + agents; small token footprint. Harder to post-mortem; agents need local memory. Large graphs; production workloads. Tip: If you hide scratch-pads, store them in a per-agent key (e.g. researcher_messages) for replay or fine-tuning even if they’re not sent downstream. langchain-ai.github.io 4 Inject only what a tool needs When exposing sub-agents as tools under a supervisor: python Copy Edit from langgraph.prebuilt import InjectedState def researcher(state: Annotated[AgentState, InjectedState]): ... Why: keeps tool signatures clean and prevents leaking confidential state. Extra: If the tool must update global state, let it return a Command so the supervisor doesn’t have to guess what changed. langchain-ai.github.io 5 Structure the graph for clarity & safety Network ➜ every agent connects to every other (exploration, research prototypes). Supervisor ➜ one LLM decides routing (good default for 3-7 agents). Hierarchical ➜ teams of agents with team-level supervisors (scales past ~7 agents). langchain-ai.github.io Pick the simplest architecture that meets today’s needs; refactor to sub-graphs as complexity grows. 6 Operational best practices Concern Best practice Tracing & observability Attach a LangFuse run-ID to every AgentState at graph entry; emit state snapshots on node enter/exit so traces line up with LangFuse v3 spans. Memory & persistence Use Checkpointer for cheap disk-based snapshots or a Redis backend for high-QPS, then time-travel when an LLM stalls. Parallel branches Use map edges (built-in) to fan-out calls, but cap parallelism with an asyncio semaphore to avoid API rate-limits. Vector lookup Put retrieval results in a dedicated key (docs) so they don’t clutter messages; store only document IDs if you need to replay cheaply. 7 Evidence from the literature (why graphs work) Peer-reviewed source Key takeaway Credibility (0-10) AAAI 2024 Graph of Thoughts‎ shows arbitrary-graph reasoning beats tree/chain structures by up to 62 % on sorting tasks. arxiv.org Graph topology yields better exploration & feedback loops—mirrors LangGraph’s StateGraph. 9 EMNLP 2024 EPO Hierarchical LLM Agents demonstrates hierarchical agents outperform flat agents on ALFRED by >12 % and scales with preference-based training. aclanthology.org Validates splitting planning vs. execution agents (Supervisor + workers). 9 Non-peer-reviewed source Why included Credibility Official LangGraph docs (June 2025). langchain-ai.github.io Primary specification of the library’s APIs and guarantees. 8 8 Minimal starter template (v 0.6.*) python Copy Edit from typing import Annotated, Literal, Sequence, TypedDict from langgraph.graph import StateGraph, START, END from langgraph.types import Command from langchain_openai import ChatOpenAI import operator class AgentState(TypedDict): messages: Annotated[Sequence[str], operator.add] next: Literal["planner", "researcher", "__end__"] plan: str | None llm = ChatOpenAI() def planner(state: AgentState) -> Command[Literal["researcher", END]]: resp = llm.invoke(...) return Command( goto = resp["next"], update = {"messages": [resp["content"]], "plan": resp["plan"]} ) def researcher(state: AgentState) -> Command[Literal["planner"]]: resp = llm.invoke(...) return Command(goto="planner", update={"messages": [resp["content"]]}) g = StateGraph(AgentState) g.add_node(planner) g.add_node(researcher) g.add_edge(START, planner) g.add_edge(planner, researcher) g.add_edge(researcher, planner) g.add_conditional_edges(planner) graph = g.compile() Bottom line Use typed immutable state, route with Command, and keep private scratch-pads separate from shared context. These patterns align with both the latest LangGraph APIs and empirical results from hierarchical, graph-based agent research.