Langgraph Fundamentals
LangChainOfficialPublished by LangChain
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
LangGraph is a framework for building stateful, multi-actor applications with LLMs by modeling workflows as directed graphs. This skill provides the core concepts for implementing StateGraphs, including node definitions, edge routing, state management with reducers, and execution patterns like invoke and stream. It covers both Python and TypeScript implementations, ensuring developers can build complex, branching, or looping agentic workflows with persistence and human-in-the-loop capabilities.
What it does
- Defines state schemas with reducers for data accumulation.
- Structures agent workflows as directed graphs with nodes and edges.
- Implements conditional routing and parallel fan-out using the Send API.
- Provides patterns for stateful execution and streaming results.
- Integrates error handling strategies for transient and logic-based failures.
- Supports both Python and TypeScript development environments.
When to use it
- When you need fine-grained control over agent orchestration.
- When building complex workflows that require branching or loops.
- When your application requires persistence or human-in-the-loop interactions.
- When you need to manage shared state across multiple agent steps.
Advantages
- Enables complex, non-linear agentic workflows.
- Provides robust state management through custom reducers.
- Supports advanced patterns like parallel execution and dynamic routing.
- Offers clear separation between state logic and execution flow.
Usage examples
- “How do I define a state schema in Python that appends messages instead of overwriting them?”
- The agent explains using Annotated with operator.add to ensure the list is updated via a reducer rather than replaced.
- “Show me how to route between nodes conditionally in a TypeScript LangGraph project.”
- The agent provides a code example using addConditionalEdges and a routing function that returns the next node name based on state.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/langchain-ai/langgraph-fundamentals/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip langgraph-fundamentals.zip -d .agents/skills
Documentation by LangChain
<overview> LangGraph models agent workflows as **directed graphs**:
StateGraph: Main class for building stateful graphs
Nodes: Functions that perform work and update state
Edges: Define execution order (static or conditional)
START/END: Special nodes marking entry and exit points
State with Reducers: Control how state updates are merged
Graphs must be compile()d before execution.
</overview>
<design-methodology>
Designing a LangGraph application
Follow these 5 steps when building a new graph:
Map out discrete steps — sketch a flowchart of your workflow. Each step becomes a node.
Identify what each step does — categorize nodes: LLM step, data step, action step, or user input step. For each, determine static context (prompt), dynamic context (from state), retry strategy, and desired outcome.
Design your state — state is shared memory for all nodes. Store raw data, format prompts on-demand inside nodes.
Build your nodes — implement each step as a function that takes state and returns partial updates.
Wire it together — connect nodes with edges, add conditional routing, compile with a checkpointer if needed.
</design-methodology>
<when-to-use-langgraph>
| Use LangGraph When | Use Alternatives When |
|---|---|
| Need fine-grained control over agent orchestration | Quick prototyping → LangChain agents |
| Building complex workflows with branching/loops | Simple stateless workflows → LangChain direct |
| Require human-in-the-loop, persistence | Batteries-included features → Deep Agents |
</when-to-use-langgraph>
State Management
<state-update-strategies>
| Need | Solution | Example |
|---|---|---|
| Overwrite value | No reducer (default) | Simple fields like counters |
| Append to list | Reducer (operator.add / concat) | Message history, logs |
| Custom logic | Custom reducer function | Complex merging |
</state-update-strategies>
Nodes
<node-function-signatures>
Node functions return partial state updates. Signatures for configuration and runtime access differ by language; use the applicable implementation reference.
</node-function-signatures>
Edges
<edge-type-selection>
| Need | Edge Type | When to Use |
|---|---|---|
| Always go to same node | add_edge() | Fixed, deterministic flow |
| Route based on state | add_conditional_edges() | Dynamic branching |
| Update state AND route | Command | Combine logic in single node |
| Fan-out to multiple nodes | Send | Parallel processing with dynamic inputs |
</edge-type-selection>
Command
Command combines state updates and routing in a single return value. Fields:
update: State updates to apply (like returning a dict from a node)goto: Node name(s) to navigate to nextresume: Value to resume afterinterrupt()— see human-in-the-loop skill
<command-return-type-annotations>
Python: Use Command[Literal["node_a", "node_b"]] as the return type annotation to declare valid goto destinations.
TypeScript: Pass { ends: ["node_a", "node_b"] } as the third argument to addNode to declare valid goto destinations.
</command-return-type-annotations>
<warning-command-static-edges>
Warning: Command only adds dynamic edges — static edges defined with add_edge / addEdge still execute. If node_a returns Command(goto="node_c") and you also have graph.add_edge("node_a", "node_b"), both node_b and node_c will run.
</warning-command-static-edges>
Send API
Fan-out with Send: return [Send("worker", {...})] from a conditional edge to spawn parallel workers. Requires a reducer on the results field.
Running Graphs: Invoke and Stream
<invoke-basics>
Call graph.invoke(input, config) to run a graph to completion and return the final state.
</invoke-basics>
<stream-mode-selection>
| Mode | What it Streams | Use Case |
|---|---|---|
values | Full state after each step | Monitor complete state |
updates | State deltas | Track incremental updates |
messages | LLM tokens + metadata | Chat UIs |
custom | User-defined data | Progress indicators |
</stream-mode-selection>
Error Handling
Match the error type to the right handler:
<error-handling-table>
| Error Type | Who Fixes | Strategy | Example |
|---|---|---|---|
| Transient (network, rate limits) | System | RetryPolicy(max_attempts=3) | add_node(..., retry_policy=...) |
| LLM-recoverable (tool failures) | LLM | ToolNode(tools, handle_tool_errors=True) | Error returned as ToolMessage |
| User-fixable (missing info) | Human | interrupt({"message": ...}) | Collect missing data (see HITL skill) |
| Unexpected | Developer | Let bubble up | raise |
</error-handling-table>
Core boundaries
Return partial state updates from nodes instead of mutating state directly.
Route loops through a named node;
STARTis entry-only.Define reducers for accumulated list fields; otherwise, the last write wins.
Account for static edges when using
Commandwithgoto, because both routes execute.
Implementation references
If writing, modifying, or debugging LangGraph code, determine the project's language from its existing files, then read the applicable reference before implementing:
For Python, read references/python.md.
For TypeScript, read references/typescript.md.
Read both only when the task covers both languages. For conceptual questions that require no code, do not load either reference.
Files
- ATTRIBUTION.mdAdded by AutoGineer906 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md5.8 KiB
- references/python.md9.0 KiB
- references/typescript.md8.8 KiB
references/typescript.md
TypeScript implementation reference
Use this reference only for LangGraph projects written in TypeScript. The shared concepts, decisions, and invariants remain in ../SKILL.md.
Contents
State Management
Nodes
Edges
Command
Send API
Running Graphs: Invoke and Stream
Error Handling
Common Fixes
State Management
State with reducer
Use StateSchema with ReducedValue for accumulating arrays.
import { StateSchema, ReducedValue, MessagesValue } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
name: z.string(), // Default: overwrites
messages: MessagesValue, // Built-in for messages
items: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (current, update) => current.concat(update) }
),
});Forgot reducer for list
Without ReducedValue, arrays are overwritten not appended.
// WRONG: Array will be overwritten
const State = new StateSchema({
items: z.array(z.string()), // No reducer!
});
// Node 1: { items: ["A"] }, Node 2: { items: ["B"] }
// Final: { items: ["B"] } // A is lost!
// CORRECT: Use ReducedValue
const State = new StateSchema({
items: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (current, update) => current.concat(update) }
),
});
// Final: { items: ["A", "B"] }Return partial state updates
Return partial updates only, not the full state object.
// WRONG: Returning entire state
const myNode = async (state: typeof State.State) => {
state.field = "updated";
return state; // Don't do this!
};
// CORRECT: Return partial updates
const myNode = async (state: typeof State.State) => {
return { field: "updated" };
};Nodes
Node function signatures
| Signature | When to Use |
|---|---|
(state) => {...} | Simple nodes that only need state |
(state, config) => {...} | Need thread_id, tags, or configurable values |
import { GraphNode, StateSchema } from "@langchain/langgraph";
const plainNode: GraphNode<typeof State> = (state) => {
return { results: "done" };
};
const nodeWithConfig: GraphNode<typeof State> = (state, config) => {
const threadId = config?.configurable?.thread_id;
return { results: `Thread: ${threadId}` };
};Edges
Basic graph
Chain nodes with addEdge and compile before invoking.
import { StateGraph, StateSchema, START, END } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
input: z.string(),
output: z.string().default(""),
});
const processInput = async (state: typeof State.State) => {
return { output: `Processed: ${state.input}` };
};
const finalize = async (state: typeof State.State) => {
return { output: state.output.toUpperCase() };
};
const graph = new StateGraph(State)
.addNode("process", processInput)
.addNode("finalize", finalize)
.addEdge(START, "process")
.addEdge("process", "finalize")
.addEdge("finalize", END)
.compile();
const result = await graph.invoke({ input: "hello" });
console.log(result.output); // "PROCESSED: HELLO"Conditional edges
addConditionalEdges routes based on function return value.
import { StateGraph, StateSchema, START, END } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
query: z.string(),
route: z.string().default(""),
result: z.string().default(""),
});
const classify = async (state: typeof State.State) => {
if (state.query.toLowerCase().includes("weather")) {
return { route: "weather" };
}
return { route: "general" };
};
const routeQuery = (state: typeof State.State) => state.route;
const graph = new StateGraph(State)
.addNode("classify", classify)
.addNode("weather", async () => ({ result: "Sunny, 72F" }))
.addNode("general", async () => ({ result: "General response" }))
.addEdge(START, "classify")
.addConditionalEdges("classify", routeQuery, ["weather", "general"])
.addEdge("weather", END)
.addEdge("general", END)
.compile();Command
Command state and routing
Return Command with update and goto to combine state change with routing.
import { StateGraph, StateSchema, START, END, Command } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
count: z.number().default(0),
result: z.string().default(""),
});
const nodeA = async (state: typeof State.State) => {
const newCount = state.count + 1;
if (newCount > 5) {
return new Command({ update: { count: newCount }, goto: "node_c" });
}
return new Command({ update: { count: newCount }, goto: "node_b" });
};
const graph = new StateGraph(State)
.addNode("node_a", nodeA, { ends: ["node_b", "node_c"] })
.addNode("node_b", async () => ({ result: "B" }))
.addNode("node_c", async () => ({ result: "C" }))
.addEdge(START, "node_a")
.addEdge("node_b", END)
.addEdge("node_c", END)
.compile();Send API
Orchestrator worker
Fan out tasks to parallel workers using the Send API and aggregate results.
import { Send, StateGraph, StateSchema, ReducedValue, START, END } from "@langchain/langgraph";
import { z } from "zod";
const State = new StateSchema({
tasks: z.array(z.string()),
results: new ReducedValue(
z.array(z.string()).default(() => []),
{ reducer: (curr, upd) => curr.concat(upd) }
),
summary: z.string().default(""),
});
const orchestrator = (state: typeof State.State) => {
return state.tasks.map((task) => new Send("worker", { task }));
};
const worker = async (state: { task: string }) => {
return { results: [`Completed: ${state.task}`] };
};
const synthesize = async (state: typeof State.State) => {
return { summary: `Processed ${state.results.length} tasks` };
};
const graph = new StateGraph(State)
.addNode("worker", worker)
.addNode("synthesize", synthesize)
.addConditionalEdges(START, orchestrator, ["worker"])
.addEdge("worker", "synthesize")
.addEdge("synthesize", END)
.compile();Send accumulator
Use ReducedValue to accumulate parallel worker results.
// WRONG: No reducer
const State = new StateSchema({ results: z.array(z.string()) });
// CORRECT
const State = new StateSchema({
results: new ReducedValue(z.array(z.string()).default(() => []), { reducer: (curr, upd) => curr.concat(upd) }),
});Running Graphs: Invoke and Stream
Invoke basics
const result = await graph.invoke({ input: "hello" });
// With config
const result = await graph.invoke({ input: "hello" }, { configurable: { thread_id: "1" } });Stream llm tokens
Stream LLM tokens in real-time for chat UI display.
for await (const chunk of graph.stream(
{ messages: [new HumanMessage("Hello")] },
{ streamMode: "messages" }
)) {
const [token, metadata] = chunk;
if (token.content) {
process.stdout.write(token.content);
}
}Stream custom data
Emit custom progress updates from within nodes using the stream writer.
import { getWriter } from "@langchain/langgraph";
const myNode = async (state: typeof State.State) => {
const writer = getWriter();
writer("Processing step 1...");
// Do work
writer("Complete!");
return { result: "done" };
};
for await (const chunk of graph.stream({ data: "test" }, { streamMode: "custom" })) {
console.log(chunk);
}Error Handling
Retry policy
Use retryPolicy for transient errors.
workflow.addNode(
"searchDocumentation",
searchDocumentation,
{
retryPolicy: { maxAttempts: 3, initialInterval: 1.0 },
},
);Tool node error handling
Use ToolNode from @langchain/langgraph/prebuilt to handle tool execution and errors. When handleToolErrors is true, errors are returned as ToolMessages so the LLM can recover.
import { ToolNode } from "@langchain/langgraph/prebuilt";
const toolNode = new ToolNode(tools, { handleToolErrors: true });
workflow.addNode("tools", toolNode);Common Fixes
Compile before execution
Must compile() to get executable graph.
// WRONG
await builder.invoke({ input: "test" });
// CORRECT
const graph = builder.compile();
await graph.invoke({ input: "test" });Infinite loop needs exit
Use conditional edges with END return to break loops.
// WRONG: Loops forever
builder.addEdge("node_a", "node_b").addEdge("node_b", "node_a");
// CORRECT
builder.addConditionalEdges("node_a", (state) => state.count > 10 ? END : "node_b");Additional common mistakes
// Always await graph.invoke() - it returns a Promise
const result = await graph.invoke({ input: "test" });
// TS Command nodes need { ends } to declare routing destinations
builder.addNode("router", routerFn, { ends: ["node_b", "node_c"] });Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python, Needs Node.js
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- LangChain
- Publisher
- LangChain (Official)
- Source
- langchain-ai/langchain-skills · config/skills/langgraph-fundamentals · commit 126c384
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by LangChain or LangChain.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download langgraph-fundamentals.zip