Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Implements stateful agent graphs using LangGraph. Use when building graphs, adding nodes/edges, defining state schemas, implementing checkpointing, handling interrupts, or creating multi-agent systems with LangGraph.
.claude/skills/majiayu000-langgraph-implementation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 45% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 108% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 80% | 0% |
| case-17 | ✗→✓ | ▲ Improved | -9% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 133% | 0% |
LangGraph builds stateful, multi-actor agent applications using a graph-based architecture:
pythonfrom langgraph.graph import StateGraph, START, END from langgraph.graph.message import MessagesState, add_messages from langgraph.checkpoint.memory import InMemorySaver from langgraph.types import Command, Send, interrupt, RetryPolicy from typing import Annotated from typing_extensions import TypedDict
pythonclass State(TypedDict): counter: int # LastValue - stores last value messages: Annotated[list, operator.add] # Reducer - appends lists items: Annotated[list, lambda a, b: a + [b] if b else a] # Custom reducer
pythonfrom langgraph.graph.message import MessagesState class State(MessagesState): # Inherits: messages: Annotated[list[AnyMessage], add_messages] user_id: str context: dict
pythonfrom pydantic import BaseModel class State(BaseModel): messages: Annotated[list, add_messages] validated_field: str # Pydantic validates on assignment
pythonbuilder = StateGraph(State) # Add nodes - functions that take state, return partial updates builder.add_node("process", process_fn) builder.add_node("decide", decide_fn) # Add edges builder.add_edge(START, "process") builder.add_edge("process", "decide") builder.add_edge("decide", END) # Compile graph = builder.compile()
pythondef my_node(state: State) -> dict: """Node receives full state, returns partial update.""" return {"counter": state["counter"] + 1} # With config access def my_node(state: State, config: RunnableConfig) -> dict: thread_id = config["configurable"]["thread_id"] return {"result": process(state, thread_id)} # With Runtime context (v0.6+) def my_node(state: State, runtime: Runtime[Context]) -> dict: user_id = runtime.context.get("user_id") return {"result": user_id}
pythonfrom typing import Literal def router(state: State) -> Literal["agent", "tools", "__end__"]: last_msg = state["messages"][-1] if hasattr(last_msg, "tool_calls") and last_msg.tool_calls: return "tools" return END # or "__end__" builder.add_conditional_edges("agent", router) # With path_map for visualization builder.add_conditional_edges( "agent", router, path_map={"agent": "agent", "tools": "tools", "__end__": END} )
pythonfrom langgraph.types import Command def dynamic_node(state: State) -> Command[Literal["next", "__end__"]]: if state["should_continue"]: return Command(goto="next", update={"step": state["step"] + 1}) return Command(goto=END) # Must declare destinations for visualization builder.add_node("dynamic", dynamic_node, destinations=["next", END])
pythonfrom langgraph.types import Send def fan_out(state: State) -> list[Send]: """Route to multiple node instances with different inputs.""" return [Send("worker", {"item": item}) for item in state["items"]] builder.add_conditional_edges(START, fan_out) builder.add_edge("worker", "aggregate") # Workers converge
pythonfrom langgraph.checkpoint.memory import InMemorySaver from langgraph.checkpoint.sqlite import SqliteSaver # Development from langgraph.checkpoint.postgres import PostgresSaver # Production # In-memory (testing only) graph = builder.compile(checkpointer=InMemorySaver()) # SQLite (development) with SqliteSaver.from_conn_string("checkpoints.db") as checkpointer: graph = builder.compile(checkpointer=checkpointer) # Thread-based invocation config = {"configurable": {"thread_id": "user-123"}} result = graph.invoke({"messages": [...]}, config)
python# Get current state state = graph.get_state(config) # Get state history for state in graph.get_state_history(config): print(state.values, state.next) # Update state manually graph.update_state(config, {"key": "new_value"}, as_node="node_name")
pythonfrom langgraph.types import interrupt, Command def review_node(state: State) -> dict: # Pause and surface value to client human_input = interrupt({"question": "Please review", "data": state["draft"]}) return {"approved": human_input["approved"]} # Resume with Command graph.invoke(Command(resume={"approved": True}), config)
pythongraph = builder.compile( checkpointer=checkpointer, interrupt_before=["human_review"], # Pause before node interrupt_after=["agent"], # Pause after node ) # Check pending interrupts state = graph.get_state(config) if state.next: # Has pending nodes # Resume graph.invoke(None, config)
python# Stream modes: "values", "updates", "custom", "messages", "debug" # Updates only (node outputs) for chunk in graph.stream(input, stream_mode="updates"): print(chunk) # {"node_name": {"key": "value"}} # Full state after each step for chunk in graph.stream(input, stream_mode="values"): print(chunk) # Multiple modes for mode, chunk in graph.stream(input, stream_mode=["updates", "messages"]): if mode == "messages": print("Token:", chunk) # Custom streaming from within nodes from langgraph.config import get_stream_writer def my_node(state): writer = get_stream_writer() writer({"progress": 0.5}) # Custom event return {"result": "done"}
python# Define subgraph sub_builder = StateGraph(SubState) sub_builder.add_node("step", step_fn) sub_builder.add_edge(START, "step") subgraph = sub_builder.compile() # Use as node in parent parent_builder = StateGraph(ParentState) parent_builder.add_node("subprocess", subgraph) parent_builder.add_edge(START, "subprocess") # Subgraph checkpointing subgraph = sub_builder.compile( checkpointer=None, # Inherit from parent (default) # checkpointer=True, # Use persistent checkpointing # checkpointer=False, # Disable checkpointing )
pythonfrom langgraph.types import RetryPolicy, CachePolicy retry = RetryPolicy( initial_interval=0.5, backoff_factor=2.0, max_attempts=3, retry_on=ValueError, # Or callable: lambda e: isinstance(e, ValueError) ) cache = CachePolicy(ttl=3600) # Cache for 1 hour builder.add_node("risky", risky_fn, retry_policy=retry, cache_policy=cache)
pythonfrom langgraph.prebuilt import create_react_agent, ToolNode # Simple agent graph = create_react_agent( model="anthropic:claude-3-5-sonnet", tools=[my_tool], prompt="You are a helpful assistant", checkpointer=InMemorySaver(), ) # Custom tool node tool_node = ToolNode([tool1, tool2]) builder.add_node("tools", tool_node)
pythondef should_continue(state) -> Literal["tools", "__end__"]: if state["messages"][-1].tool_calls: return "tools" return END builder.add_node("agent", call_model) builder.add_node("tools", ToolNode(tools)) builder.add_edge(START, "agent") builder.add_conditional_edges("agent", should_continue) builder.add_edge("tools", "agent")
python# Multiple nodes execute in parallel when they share the same trigger builder.add_edge(START, "node_a") builder.add_edge(START, "node_b") # Runs parallel with node_a builder.add_edge(["node_a", "node_b"], "join") # Wait for both
See PATTERNS.md for advanced patterns including multi-agent systems, hierarchical graphs, and complex workflows.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 23,722 | 25,103 | +6% | 1 | 1 | 0% | 3,859 | 5,578 | +45% | 0 | 0 | — |
case-02 | pass→pass | 7,638 | 14,363 | +88% | 1 | 1 | 0% | 1,432 | 3,331 | +133% | 0 | 0 | — |
case-03 | fail→pass | 26,181 | 13,686 | -48% | 1 | 1 | 0% | 1,954 | 4,057 | +108% | 0 | 0 | — |
case-04 | pass→pass | 13,313 | 11,757 | -12% | 1 | 1 | 0% | 2,021 | 4,150 | +105% | 0 | 0 | — |
case-05 | pass→pass | 10,957 | 15,578 | +42% | 1 | 1 | 0% | 2,054 | 4,722 | +130% | 0 | 0 | — |
case-06 | pass→pass | 11,464 | 9,553 | -17% | 1 | 1 | 0% | 1,808 | 4,103 | +127% | 0 | 0 | — |
case-07 | pass→pass | 7,248 | 3,379 | -53% | 1 | 1 | 0% | 990 | 2,945 | +197% | 0 | 0 | — |
case-08 | pass→pass | 10,867 | 5,943 | -45% | 1 | 1 | 0% | 1,668 | 3,460 | +107% | 0 | 0 | — |
case-09 | pass→pass | 8,400 | 5,004 | -40% | 1 | 1 | 0% | 1,556 | 3,143 | +102% | 0 | 0 | — |
case-10 | pass→pass | 7,828 | 4,948 | -37% | 1 | 1 | 0% | 1,492 | 3,150 | +111% | 0 | 0 | — |
case-11 | pass→pass | 11,627 | 3,996 | -66% | 1 | 1 | 0% | 1,700 | 3,085 | +81% | 0 | 0 | — |
case-12 | fail→fail | 4,420 | 2,835 | -36% | 1 | 1 | 0% | 847 | 2,830 | +234% | 0 | 0 | — |
case-13 | fail→pass | 9,962 | 5,747 | -42% | 1 | 1 | 0% | 1,966 | 3,543 | +80% | 0 | 0 | — |
case-14 | pass→pass | 3,664 | 4,414 | +20% | 1 | 1 | 0% | 679 | 3,205 | +372% | 0 | 0 | — |
case-15 | pass→pass | 10,046 | 4,893 | -51% | 1 | 1 | 0% | 1,915 | 3,248 | +70% | 0 | 0 | — |
case-16 | pass→pass | 5,627 | 5,884 | +5% | 1 | 1 | 0% | 1,016 | 3,407 | +235% | 0 | 0 | — |
case-17 | fail→pass | 20,640 | 5,985 | -71% | 1 | 1 | 0% | 3,823 | 3,486 | -9% | 0 | 0 | — |
case-18 | pass→pass | 9,845 | 6,292 | -36% | 1 | 1 | 0% | 1,792 | 3,492 | +95% | 0 | 0 | — |
case-19 | pass→pass | 7,792 | 5,331 | -32% | 1 | 1 | 0% | 1,648 | 3,402 | +106% | 0 | 0 | — |
case-20 | pass→pass | 5,155 | 3,700 | -28% | 1 | 1 | 0% | 1,044 | 3,065 | +194% | 0 | 0 | — |
case-21 | pass→pass | 8,231 | 5,716 | -31% | 1 | 1 | 0% | 1,588 | 3,425 | +116% | 0 | 0 | — |
case-22 | pass→pass | 7,489 | 5,336 | -29% | 1 | 1 | 0% | 1,533 | 3,346 | +118% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.