agentsope/agentsop-langgraph
| Decision protocol for building, debugging, and operating LangGraph-based agent systems. Activates when a coder agent is asked to design a stateful LLM workflow, add human-in-the-loop, choose a multi-agent pattern (supervisor / swarm / hierarchical), pick a checkpoint backend, or migrate a fragile chain into a durable graph. LangGraph is positioned by its maintainers as a "low-level orchestration framework for building, managing, and deploying long-running, stateful agents" — this skill encodes the *when* and *why*, not the API.
npx skills add https://github.com/agentsope/SkillAlchemy --skill agentsop-langgraph
> Source posture: every non-trivial claim is cited inline. Citations use short
> tags like [lc-docs], [lc-blog/interrupt], [gh/6731], [zenml/uber] —
> resolve them against references/*.md for the full URL.
Activate this skill when any of the following triggers fire:
StateGraph, MessageGraph, create_react_agent,interrupt(, Command(resume=, add_messages, checkpointer, PostgresSaver,
Send(, or entrypoint / task decorators.
must survive a process crash) — LangGraph's stated sweet spot
[lc-docs/why-langgraph].
validation) — LangGraph offers a first-class interrupt() primitive that
competitors require "duct-taping" to achieve [bswen/hitl].
GRAPH_RECURSION_LIMIT errors, infinite loops, orInvalidUpdateError on parallel branches — these are LangGraph-specific failure
modes with known fixes [lc-docs/errors] [cheatsheet/gotchas].
raw LangChain — section *生态对照* gives the decision matrix.
agent to something durable and observable.
Do not activate if the task is a single LLM call, a one-shot RAG query, or
a stateless tool pipeline — Sec. 反模式 explains why graphs are overkill there.
LangGraph is a state machine, not a chain. The cleanest one-liner from the
2026 docs: "If chains were about passing outputs between steps, graphs are about
maintaining and evolving a shared state over time" [eastondev/2026]. Pre-LLM
analog: think BPMN / finite state machine / Pregel-style "supersteps", not a
Unix pipe. The official position is even more reductive: LangGraph is "a
deterministic execution engine for AI reasoning workflows" [eastondev/2026].
Three load-bearing concepts ride this model:
shared, typed object (TypedDict / Pydantic / dataclass). A node returns a
*partial update*, never a mutation. How updates merge into state is governed
by reducers, declared via Annotated[list[Msg], add_messages] etc.
Missing a reducer on a key that two parallel nodes both write to triggers
InvalidUpdateError — reducers are mandatory for parallel writes
[cheatsheet/gotchas]. The reducer system is what lets the graph be
composable, replayable, and crash-safe.
snapshotted into a checkpointer (SQLite for local, Postgres for production,
Redis for fast TTL'd swarms) [lc-docs/persistence] [redis/checkpoint].
This single property is what unlocks the headline features: durable execution
that "persists through failures and resumes from their exact stopping point",
time-travel debugging (replay or fork from any checkpoint), and
human-in-the-loop (a thread can sit interrupted for hours and resume cleanly)
[gh/langgraph-readme] [dragonforest/timetravel].
(always go to N), conditional (a function reads state and picks a next node),
or dynamic via the Send API (a routing function returns a list of Send
objects to spawn variable-count parallel workers) [deepwiki/mapreduce].
This is where LangGraph diverges from CrewAI's role-based crew and AutoGen's
conversational pattern — control flow is explicit, not emergent from
chat history.
The OS-level claim: "2026 is the year of Stateful Orchestration"
[eastondev/2026]. LangGraph bet that production agents need persistence,
explicit control flow, and observability more than they need elegance. That bet
is paying off (Klarna serves 85M users on it, Replit pushed it so hard
LangSmith had to be rewritten to ingest the traces) — but the cost is verbosity
that frustrates anyone trying it on a toy problem [lc-blog/production]
[duplocloud/compare].
A coder agent should walk this protocol top-down. Each step has a **decision
gate** — if the answer is "no" or "not yet", stop and reconsider before adding
graph complexity.
Gate questions:
If all four are no, use a plain RunnableSequence or raw API calls and
exit. Over-graphing simple flows is the #1 anti-pattern [swarnendu/best].
| Need | Choice | Why |
|---|---|---|
| Standard tool-calling ReAct loop | create_react_agent (prebuilt) | Syntactic sugar over StateGraph; ~3 lines of code [agentsindex/v1] |
| Imperative Python style, async tasks, no explicit graph | Functional API (@entrypoint, @task) | Shares the runtime with StateGraph; trades time-travel granularity for code brevity [lc-blog/functional] |
| Multi-agent, parallel, custom routing, supervisor | StateGraph (manual) | Required for non-trivial topology [agentsindex/v1] |
| Chat-only message history | MessageGraph *(legacy)* | Only for very basic chatbots; prefer StateGraph [cheatsheet/gotchas] |
Default to create_react_agent and graduate to StateGraph only when you
need parallel nodes, supervisor-worker patterns, custom retry logic, or
complex branching [agentsindex/v1].
The state schema is "the most critical design component" [bharatraj/state].
Discipline:
TypedDict for ergonomics, Pydantic only when validation matters.(add_messages, operator.add, or custom) — otherwise plan for it to be
overwritten last-write-wins.
checkpointer on every superstep [bharatraj/state].
do not mutate inputs [swarnendu/best].
Decision tree, sourced from LangChain's own benchmark [lc-blog/benchmark]:
Is there exactly one "user-facing" persona?
├─ YES → Supervisor pattern (single supervisor, sub-agents are tools)
│ - Highest token cost (supervisor "translates" sub-agent output)
│ - Safest with third-party agents
│ - LangChain's *current recommended default*
└─ NO → Do sub-agents know about each other?
├─ YES → Swarm pattern (dynamic handoff, last-active agent remembered)
│ - Lower tokens than supervisor (no translation step)
│ - Slightly higher accuracy in the τ-bench retest
│ - Bad fit for third-party agents
└─ NO → Hierarchical Teams (supervisor-of-supervisors)
- Use only when ≥6 specialists need grouping
Concrete bench finding: swarm "slightly outperformed supervisor across all
scenarios"; supervisor "consistently uses more tokens than swarm" because of
the telephone-game translation overhead [lc-blog/benchmark]. LangChain's
own response was to fix the supervisor (remove handoff messages, add a
forwarding-messages tool, tune tool names) for "a nearly 50% increase in
performance" [lc-blog/benchmark].
Use interrupt(value) at the node that would perform the high-blast-radius
operation; resume with Command(resume=...) [lc-blog/interrupt]. Four
canonical patterns [lc-blog/interrupt]:
Rule of thumb: "interrupt on irreversible, high-blast-radius actions only —
not on every step" [bswen/hitl]. Side effects (DB writes, API calls) must
go after the interrupt or in a downstream node — placing them before
causes unwanted re-execution on resume [cheatsheet/gotchas].
| Backend | Use when | Source |
|---|---|---|
| InMemorySaver | Tests / notebooks only | [lc-docs/persistence] |
| SqliteSaver / AsyncSqliteSaver | Single-machine local dev, low concurrency | [lc-docs/persistence] |
| PostgresSaver / AsyncPostgresSaver | Production default, multi-user, ACID needed | [lc-docs/persistence] |
| RedisSaver | High-throughput swarms, TTL-expiring sessions, sub-ms reads | [redis/checkpoint] |
Run checkpointer.setup() as a CI/CD migration, never inside app runtime
[bswen/hitl]. Implement a TTL sweep for interrupted-but-never-resumed
threads (e.g., abandon after 24 h) — otherwise state accumulates indefinitely
[bswen/hitl].
far; production needs the trace UI [swarnendu/best].
recursion_limit (default 25); raise it viagraph.invoke({...}, {"recursion_limit": 100}) only after confirming
the loop *can* terminate [lc-docs/errors].
recursion_limit as a safety net, not control flow. Hitting itmeans the conditional edge logic is wrong, not that the limit is too low
[cheatsheet/gotchas].
Each operation is a primitive a coder agent can invoke. Format:
Trigger → Action → Output → Evidence.
requirements.
from langgraph.prebuilt import create_react_agent; passmodel + tools list. Skip StateGraph entirely.
.invoke() / .stream() withbuilt-in message history.
[agentsindex/v1] "Start with create_react_agent for anystandard tool-calling agent."
custom retry, or a non-message state field.
StateGraph(MyTypedDict), manually add theLLM node, tool node, and conditional edge that routes on tool_calls.
[agentsindex/v1] "If you find yourself needing parallel nodeexecution, a supervisor-worker pattern, custom retry logic, or complex
branching, migrate to a manual StateGraph."
InvalidUpdateErrorraises InvalidUpdateError.
key: list[X] withkey: Annotated[list[X], operator.add] (or add_messages for chat).
[cheatsheet/gotchas] "Reducers are mandatory, not optional,for parallel execution."
Sendparallel tasks (e.g., one summarizer per retrieved doc).
[Send("worker", {"chunk": c}) for c in state["chunks"]]. The worker uses
its own state schema, and results are reduced back via operator.add.
[deepwiki/mapreduce] "Send allows a conditional edgefunction to schedule a node with a custom state…the primary mechanism for
dynamic fan-out."
send email, run SQL DELETE).
decision = interrupt({"proposed": payload}) *before* the side effect.
Compile the graph with a checkpointer. The caller resumes with
graph.invoke(Command(resume="approve"), config).
decision threaded back into the node.
[lc-blog/interrupt] four-pattern table + [bswen/hitl] side-effect ordering.
a reusable validation pipeline) deserves its own state and lifecycle.
parent.add_node("research", research_subgraph). If schemas differ, wrap
the call in a node function that translates between schemas.
unless keys overlap.
[lc-docs/subgraphs] [deepwiki/subgraphs] "subgraph stateis only accessible when the subgraph is interrupted" — accept the debug
cost.
state must survive deploys.
from langgraph.checkpoint.postgres.aio import AsyncPostgresSaver,pass to .compile(checkpointer=...). Run await saver.setup() in a
migration job, not at app boot.
guarantees for state transitions.
[lc-docs/persistence] PostgresSaver "ideal for using inproduction"; [bswen/hitl] "handle this as part of a CI/CD migration
script…not inside the primary application runtime."
graph.stream(input, stream_mode=["messages", "updates"])— messages yields LLM tokens, updates yields state diffs. For
intra-tool progress, emit via stream_mode="custom".
[lc-docs/streaming] five modes — values, updates, messages,custom, debug.
recursion_limitGRAPH_RECURSION_LIMIT (e.g., text-to-SQL retryingthe same broken query).
condition counting retries in state and routing to END after N attempts.
Bump recursion_limit only as a temporary diagnostic.
[lc-docs/errors] "Check your logic for infinite loops";[cheatsheet/gotchas] "Hitting the limit indicates an underlying design
flaw"; concrete bug case [gh/6731].
state the LLM saw at step 7 and try a different prompt.
graph.get_state_history(config), pick the checkpoint, invoke with
config={"configurable": {"thread_id": ..., "checkpoint_id": ...}}.
Modify state with graph.update_state(...) to fork.
on replayed nodes.
[dragonforest/timetravel] "replay…the agent knows that thischeckpoint has already been executed and will just display the historical
output instead of making new LLM calls."
Databricks query returned an error, the agent retried the same broken query
20 times until GRAPH_RECURSION_LIMIT fired. It had worked on 0.6.x
[gh/6731].
[gh/6731].
recursion_limit — that masksthe bug and burns Databricks quota [cheatsheet/gotchas].
retries: Annotated[int, operator.add].
edge, route to END (or a "give up and ask user" node) once
retries >= 3.
letting the LLM see it on the next loop — the *content* of the error
usually informs whether to retry or abandon.
graph terminates within 3 iterations.
community-acknowledged fact is that LangGraph's recursion limit is a safety
net, not control flow — exit conditions are the real fix [lc-docs/errors].
cyclic graph.** Trust nothing the LLM does to terminate itself.
and shipping. They started with the supervisor pattern (one router agent
→ three specialists). User-perceived latency was high and token cost was
double what they budgeted. They wondered if swarm would be better.
"transferring you to...").
supervisor"; supervisor uses more tokens because of the translation
step [lc-blog/benchmark].
funnel); UX favours swarm (last-active agent stays active across turns).
supervisor fixes — "removing handoff messages, forwarding messages tool,
tool naming optimization" — which yielded a "nearly 50% increase in
performance" in the bench [lc-blog/benchmark].
trail is tolerant, migrate to swarm.
swarm's efficiency while preserving the single-funnel audit log.
on (a) whether sub-agents can know each other, (b) whether one user-facing
voice is mandated. Then optimise the chosen pattern with LangChain's own
published fixes before switching paradigms.**
hundreds of steps" per run. Traces were so large that LangSmith — built by
the same team — couldn't ingest or render them initially
[alphabold/case].
tests → deploy → debug → fix).
observability layer** — Replit and LangChain iterated on LangSmith's
ingestion and rendering specifically for this trace shape
[alphabold/case].
teams (planner-team, codegen-team, test-team) — flat graphs of that
size are unreadable [deepwiki/subgraphs].
Send API for fan-out at known parallel points (e.g.,generate-then-test in parallel) so each branch is a distinct trace
segment.
interrupt at the deploy boundary — humans approve adeploy plan rather than letting the agent push autonomously.
to test prompt variants without re-paying for upstream LLM calls
[dragonforest/timetravel].
LangSmith improvements. The lesson: at scale, the observability tool is
part of the system design, not external to it.
observability story alongside the graph topology. Subgraphs + Send are
not optional optimisations — they are how you make the graph debuggable
at production scale.**
interrupt() re-executed on resume"calls interrupt() for a human to confirm the receipt. On resume, the
card was charged *twice* because resuming a thread "re-runs the entire
node function" [cheatsheet/gotchas].
silently; cannot rewrite payment SDK.
the top, not from the line of the interrupt(). Treat every node body
as potentially re-runnable.
*after* the interrupt-bearing node returns approval into state. Now the
interrupt-node only proposes; the next node executes.
interrupt, pass it to the payment SDK as idempotency key — re-run
becomes a no-op even if topology changes.
interrupt() in the same node; (b) every external side-effect uses an
idempotency key drawn from state. Sourced directly from the cheatsheet
pitfall list [cheatsheet/gotchas].
Concrete don'ts, each with the underlying reasoning.
with no cycles, no HITL, and no need to survive a crash does not need
LangGraph. The abstraction overhead "could be a disadvantage in more
straightforward scenarios" [duplocloud/compare]. Use a RunnableSequence.
recursion_limit as a termination strategy. It "is notintended to be a primary control flow mechanism"; hitting it "indicates an
underlying design flaw" [cheatsheet/gotchas]. Bake exit conditions into
state.
interrupt(). On resume, the node bodyre-runs from the top [cheatsheet/gotchas].
return a partial state update rather than mutating inputs" [swarnendu/best].
Mutation breaks checkpoint replayability.
reducers on a key two nodes both write to triggers InvalidUpdateError
[cheatsheet/gotchas].
checkpointer.setup() at app boot. Treat it as a DBmigration; run via CI/CD [bswen/hitl].
one, "state is held in the checkpointer indefinitely" [bswen/hitl].
MessageGraph for new code. It's only "for basic chatbots";every production case in this skill uses StateGraph [cheatsheet/gotchas].
superstep fails atomically" — successful branches are discarded. Rate
limits also hit faster [aipractitioner/scaling].
Send for fixed-cardinality work. Static paralleledges are simpler. Reserve Send for genuinely runtime-variable workloads
[aipractitioner/scaling].
bug [gh/6731] is the canonical proof — always bound retries explicitly.
Hard boundaries (LangGraph is the wrong tool when):
[bswen/compare].
Source: LangChain's own production page [lc-built-with], the Bswen
side-by-side comparison [bswen/compare], the OpenAgents comparison
[openagents/2026], and the v1.0 vs functional-API blog [lc-blog/functional].
| Dimension | LangGraph | CrewAI | AutoGen | OpenAI Swarm |
|---|---|---|---|---|
| Mental model | State machine / graph | Role-playing crew | Conversation between agents | Minimal handoff routine |
| Time-to-prototype | Hours-to-days | <1 hour | Moderate | <30 min |
| Production-ready | Yes (Klarna, Replit, Uber, LinkedIn, AppFolio, Elastic) | Limited (no built-in persistence) | Yes (maintenance mode 2026) | No (explicitly experimental) |
| State management | First-class, typed, reducer-merged | Implicit in task chain | In conversation history | Minimal |
| Persistence / durability | First-class (Sqlite/Postgres/Redis) | Bolt-on | Bolt-on | None |
| Human-in-the-loop | First-class (interrupt()) | Limited | Limited | None |
| Observability | LangSmith integration | Basic | Basic | Minimal |
| Steepness | Steep | Gentle | Moderate | Gentle |
Decision heuristics:
long-running threads, multiple users share threads, you already use the
LangChain ecosystem, or you need to insert humans into the loop without
duct-tape [bswen/compare].
fits the domain, no persistence needed. Many teams "use CrewAI for rapid
prototyping to validate workflow logic, then port critical pipelines to
LangGraph for production" [bswen/compare].
iterative refinement is the point — but note Microsoft has shifted focus
to the broader Agent Framework, so AutoGen is effectively in maintenance
mode [bswen/compare].
code. OpenAI itself labels it experimental [bswen/compare].
LLM call or simple RAG. LangGraph is overkill [duplocloud/compare].
Internal LangGraph subdivision — also a choice point:
create_react_agent (prebuilt): default for one tool-calling agent.@entrypoint, @task): imperative Python style, sharesthe runtime, trades fine-grained time-travel for code brevity
[lc-blog/functional].
StateGraph: full control, required for multi-agent / parallel / customrouting.
Pick the smallest one that fits the requirements; promote upward as needed.
Short tags used inline → full sources in references/:
[lc-docs] = https://docs.langchain.com/oss/python/langgraph/*[lc-docs/why-langgraph] / [lc-docs/persistence] /[lc-docs/errors] / [lc-docs/streaming] / [lc-docs/subgraphs]
[lc-blog/interrupt] = www.langchain.com/blog/making-it-easier-to-build-human-in-the-loop-agents-with-interrupt[lc-blog/benchmark] = www.langchain.com/blog/benchmarking-multi-agent-architectures[lc-blog/production] = www.langchain.com/blog/is-langgraph-used-in-production[lc-blog/functional] = www.langchain.com/blog/introducing-the-langgraph-functional-api[lc-built-with] = www.langchain.com/built-with-langgraph[gh/langgraph-readme] = github.com/langchain-ai/langgraph[gh/6731] = github.com/langchain-ai/langgraph/issues/6731[zenml/uber] = www.zenml.io/llmops-database/building-ai-developer-tools-using-langgraph-for-large-scale-software-development[alphabold/case] = www.alphabold.com/langgraph-agents-in-production/[bswen/hitl] = docs.bswen.com/blog/2026-04-16-langgraph-human-in-the-loop/[bswen/compare] = docs.bswen.com/blog/2026-04-29-agent-framework-production-comparison/[openagents/2026] = openagents.org/blog/posts/2026-02-23-open-source-ai-agent-frameworks-compared[eastondev/2026] = eastondev.com/blog/en/posts/ai/20260424-langgraph-agent-architecture[deepwiki/mapreduce] = deepwiki.com/langchain-ai/langchain-academy/7.1-map-reduce-pattern[deepwiki/subgraphs] = deepwiki.com/langchain-ai/langgraph/3.5-control-flow-primitives[swarnendu/best] = www.swarnendu.de/blog/langgraph-best-practices/[cheatsheet/gotchas] = sumanmichael.github.io/langgraph-cheatsheet/cheatsheet/faqs-gotchas/[bharatraj/state] = medium.com/@bharatraj1918/langgraph-state-management-part-1[duplocloud/compare] = duplocloud.com/blog/langchain-vs-langgraph/[dragonforest/timetravel] = dragonforest.in/time-travel-in-langgraph/[redis/checkpoint] = redis.io/blog/langgraph-redis-checkpoint-010/[aipractitioner/scaling] = aipractitioner.substack.com/p/scaling-langgraph-agents-parallelization[agentsindex/v1] = agentsindex.ai/blog/langgraph-tutorialTake agentsope/agentsop-langgraph from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.