mcpField
L
LangGraph Fundamentals

LangGraph Fundamentals

github.com/jackjin1997Agent 工作流1339 次安装19 周前更新
代码仓库

关于 LangGraph Fundamentals

LangGraph Fundamentals 是介绍 LangGraph 的技能,面向构建有状态 Agent 工作流的开发者。内容涵盖图结构、状态合并、分支与循环,并强调编译后执行。

主要功能

  1. 01以有向图编排 Agent 工作流
  2. 02定义状态及合并 Reducer
  3. 03配置固定边与条件边
  4. 04使用 Command 更新状态并路由
  5. 05通过 Send 扇出并行任务
  6. 06编译图后执行

适用场景

  1. 01构建带分支或循环的复杂流程
  2. 02累积消息历史或日志状态
  3. 03在节点中实现状态驱动路由

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jackjin1997/clawforge --skill langgraph-fundamentals

安装后在 Claude Code 中输入/langgraph-fundamentals确认已加载。

详细介绍

LangGraph 将 Agent 工作流建模为**有向图**:
  • StateGraph:用于构建有状态图的主类
  • 节点:执行工作并更新状态的函数
  • 边:定义执行顺序(静态或条件式)
  • START/END:标记入口和出口的特殊节点
  • 带 Reducer 的状态:控制状态更新的合并方式

执行前必须对图调用 compile()。

使用 LangGraph 的场景使用替代方案的场景
需要对 Agent 编排进行精细控制快速原型开发 → LangChain agents
构建包含分支/循环的复杂工作流简单的无状态工作流 → LangChain direct
需要人在回路中参与、持久化需要开箱即用的功能 → Deep Agents

状态管理

需求解决方案示例
覆盖值不使用 reducer(默认)计数器等简单字段
追加到列表Reducer(operator.add / concat)消息历史记录、日志
自定义逻辑自定义 reducer 函数复杂合并
定义带有 reducer 的状态架构,以累积列表并对整数求和。 ```python from typing_extensions import TypedDict, Annotated import operator

class State(TypedDict): name: str # Default: overwrites on update messages: Annotated[list, operator.add] # Appends to list total: Annotated[int, operator.add] # Sums integers

</python>
<typescript>
使用 StateSchema 和 ReducedValue 累积数组。
```typescript
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) }
  ),
});
如果没有 reducer,返回列表会覆盖之前的值。 ```python # WRONG: List will be OVERWRITTEN class State(TypedDict): messages: list # No reducer!

Node 1 returns: {"messages": ["A"]}

Node 2 returns: {"messages": ["B"]}

Final: {"messages": ["B"]} # "A" is LOST!

CORRECT: Use Annotated with operator.add

from typing import Annotated import operator

class State(TypedDict): messages: Annotated[list, operator.add]

Final: {"messages": ["A", "B"]}

</python>
<typescript>
如果没有 ReducedValue,数组会被覆盖,而不是追加。
```typescript
// 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"] }
节点必须返回部分更新,而不是修改并返回完整状态。 ```python # WRONG: Returning entire state object def my_node(state: State) -> State: state["field"] = "updated" return state # Don't mutate and return!

CORRECT: Return dict with only the updates

def my_node(state: State) -> dict: return {"field": "updated"}

</python>
<typescript>
仅返回部分更新,不要返回完整状态对象。
```typescript
// 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" };
};

构建图

需求边类型使用时机
始终转到同一节点add_edge()固定的确定性流程
根据状态路由add_conditional_edges()动态分支
更新状态并路由Command在单个节点中组合逻辑
扇出到多个节点Send使用动态输入进行并行处理
由线性边连接的简单双节点图。 ```python from langgraph.graph import StateGraph, START, END from typing_extensions import TypedDict

class State(TypedDict): input: str output: str

def process_input(state: State) -> dict: return {"output": f"Processed: {state['input']}"}

def finalize(state: State) -> dict: return {"output": state["output"].upper()}

graph = ( StateGraph(State) .add_node("process", process_input) .add_node("finalize", finalize) .add_edge(START, "process") .add_edge("process", "finalize") .add_edge("finalize", END) .compile() )

result = graph.invoke({"input": "hello"}) print(result["output"]) # "PROCESSED: HELLO"

</python>
<typescript>
使用 addEdge 连接节点,并在调用前编译。
```typescript
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"
使用条件边,根据状态路由到不同节点。 ```python from typing import Literal from langgraph.graph import StateGraph, START, END

class State(TypedDict): query: str route: str result: str

def classify(state: State) -> dict: if "weather" in state["query"].lower(): return {"route": "weather"} return {"route": "general"}

def route_query(state: State) -> Literal["weather", "general"]: return state["route"]

graph = ( StateGraph(State) .add_node("classify", classify) .add_node("weather", lambda s: {"result": "Sunny, 72F"}) .add_node("general", lambda s: {"result": "General response"}) .add_edge(START, "classify") .add_conditional_edges("classify", route_query, ["weather", "general"]) .add_edge("weather", END) .add_edge("general", END) .compile() )

</python>
<typescript>
addConditionalEdges 根据函数返回值进行路由。
```typescript
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 允许你在一次返回中更新状态并选择下一个节点。 ```python from langgraph.types import Command from typing import Literal

class State(TypedDict): count: int result: str

def node_a(state: State) -> Command[Literal["node_b", "node_c"]]: """Update state AND decide next node in one return.""" new_count = state["count"] + 1 if new_count > 5: return Command(update={"count": new_count}, goto="node_c") return Command(update={"count": new_count}, goto="node_b")

graph = ( StateGraph(State) .add_node("node_a", node_a) .add_node("node_b", lambda s: {"result": "B"}) .add_node("node_c", lambda s: {"result": "C"}) .add_edge(START, "node_a") .add_edge("node_b", END) .add_edge("node_c", END) .compile() )

</python>
<typescript>
返回带有 update 和 goto 的 Command,以组合状态更改与路由。
```typescript
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 扇出:从条件边返回 `[Send("worker", {...})]`,以启动并行工作节点。结果字段需要使用 reducer。要构建复杂工作流,必须深入理解 Send 和执行模式。 使用指向 END 的条件边来避免无限循环。 ```python from langgraph.graph import StateGraph, START, END

class State(TypedDict): count: int max_iterations: int

def increment(state: State) -> dict: return {"count": state["count"] + 1}

def should_continue(state: State) -> str: if state["count"] >= state["max_iterations"]: return END return "increment"

graph = ( StateGraph(State) .add_node("increment", increment) .add_edge(START, "increment") .add_conditional_edges("increment", should_continue) .compile() )

result = graph.invoke({"count": 0, "max_iterations": 5}) print(result["count"]) # 5

</python>
<typescript>
从条件函数返回 END,以终止循环。
```typescript
import { StateGraph, StateSchema, START, END } from "@langchain/langgraph";
import { z } from "zod";

const State = new StateSchema({
  count: z.number().default(0),
  maxIterations: z.number(),
});

const increment = async (state: typeof State.State) => {
  return { count: state.count + 1 };
};

const shouldContinue = (state: typeof State.State) => {
  if (state.count >= state.maxIterations) return END;
  return "increment";
};

const graph = new StateGraph(State)
  .addNode("increment", increment)
  .addEdge(START, "increment")
  .addConditionalEdges("increment", shouldContinue)
  .compile();

const result = await graph.invoke({ count: 0, maxIterations: 5 });
console.log(result.count);  // 5
必须调用 compile() 才能获得可执行图。 ```python # WRONG builder.invoke({"input": "test"}) # AttributeError!

CORRECT

graph = builder.compile() graph.invoke({"input": "test"})

</python>
<typescript>
必须调用 compile() 才能获得可执行图。
```typescript
// WRONG
await builder.invoke({ input: "test" });

// CORRECT
const graph = builder.compile();
await graph.invoke({ input: "test" });
提供指向 END 的条件路径,以避免无限循环。 ```python # WRONG: Loops forever builder.add_edge("node_a", "node_b") builder.add_edge("node_b", "node_a")

CORRECT

def should_continue(state): return END if state["count"] > 10 else "node_b" builder.add_conditional_edges("node_a", should_continue)

</python>
<typescript>
使用带有 END 返回值的条件边来跳出循环。
```typescript
// 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");
其他常见错误: ```python # Router must return names of nodes that exist in the graph builder.add_node("my_node", func) # Add node BEFORE referencing in edges builder.add_conditional_edges("node_a", router, ["my_node"])

Command return type needs Literal for routing destinations (Python)

def node_a(state) -> Command[Literal["node_b", "node_c"]]: return Command(goto="node_b")

START is entry-only - cannot route back to it

builder.add_edge("node_a", START) # WRONG! builder.add_edge("node_a", "entry") # Use a named entry node instead

Reducer expects matching types

return {"items": ["item"]} # List for list reducer, not a string

```typescript
// 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"] });
### 可以配置的内容
  • 使用 TypedDict/StateSchema 定义自定义状态架构
  • 添加 reducer,以控制状态更新的合并方式
  • 创建节点(任何函数),并添加静态边或条件边
  • 使用 Command 同时更新状态并进行路由
  • 创建带条件终止的循环
无法配置的内容
  • 修改 START/END 的行为
  • 在节点函数外访问状态
  • 直接修改状态(必须返回部分更新字典)

评论

支持 Markdown