J'Blog

2 课 · 核心

语义检索

基于 Chroma 向量库,与 Workspace 知识库分离。用自然语言描述你想查的内容。 非管理员每人每天可检索 5 次(消耗站点 AI 配额)。

图 API

原文:Graph API

一句话

LangGraph 将代理工作流建模为图,通过定义状态(State)、节点(Nodes)和边(Edges)来构建复杂、循环的工作流。

什么时候翻这页

当你需要了解 LangGraph 的核心 API 结构,学习如何定义状态、创建节点和连接它们时,可以参考本页内容。

核心概念

图(Graphs)

LangGraph 将代理工作流建模为图,使用三个关键组件定义代理行为:

  1. 状态(State):表示应用程序当前快照的共享数据结构,可以是任何数据类型,但通常使用共享状态模式定义。
  2. 节点(Nodes):编码代理逻辑的函数,接收当前状态作为输入,执行一些计算或副作用,并返回更新后的状态。
  3. 边(Edges):根据当前状态确定下一个要执行节点的函数,可以是条件分支或固定转换。

通过组合节点和边,可以创建随时间演化的复杂循环工作流。

StateGraph

StateGraph 类是使用的主要图类,由用户定义的 State 对象参数化。

编译图

构建图时,首先定义状态,然后添加节点和边,最后编译它。编译是一个简单的步骤,它提供对图结构的基本检查(没有孤立的节点等),也是你可以指定运行时参数(如 checkpointers 和断点)的地方。

怎么做

定义状态

定义图时首先要定义图的 StateState 包括图的 schema 和指定如何应用更新的 reducer 函数。schema 将是图中所有节点和边的输入模式,可以是 TypedDictPydantic 模型。

Schema

指定图模式的主要方法是使用 TypedDict。如果要在状态中提供默认值,请使用 dataclass。如果需要递归数据验证,也可以使用 Pydantic BaseModel 作为图状态(注意 Pydantic 的性能低于 TypedDictdataclass)。

Reducers

Reducers 是理解节点更新如何应用于 State 的关键。State 中的每个键都有自己的独立归约函数。如果没有明确指定归约函数,则假定对该键的所有更新都应该覆盖它。

在图状态中使用消息

在许多情况下,将先前的对话历史存储为图状态中的消息列表很有帮助。为此,我们可以向图状态添加一个存储 Message 对象列表的键(通道),并使用归约函数(如 operator.addadd_messages)来更新它。

创建节点

节点是接受以下参数的 Python 函数(同步或异步):

  1. state—图的状态
  2. config—包含配置信息(如 thread_id)和跟踪信息(如 tags)的 RunnableConfig 对象
  3. runtime—包含运行时上下文和其他信息的 Runtime 对象

使用 add_node 方法将节点添加到图中。

连接节点

边定义了逻辑如何路由以及图如何决定停止。主要有几种类型的边:

  • 正常边:直接从一个节点到下一个节点。
  • 条件边:调用函数确定下一个要去的节点。
  • 入口点:用户输入到达时首先调用的节点。
  • 条件入口点:调用函数确定用户输入到达时首先调用的节点。

使用 add_edge 方法添加正常边,使用 add_conditional_edges 方法添加条件边。

使用 Send 和 Command

  • Send:用于支持 map-reduce 等设计模式,允许在运行时动态创建边。
  • Command:用于控制图执行的多功能原语,可以结合状态更新和路由。

处理递归限制

递归限制设置图在单次执行期间可以执行的最大超级步骤数。可以通过 config 字典中的 recursion_limit 键在运行时设置递归限制。

命令 / API 速查

创建和编译图

from langgraph.graph import StateGraph, START, END

# 定义状态
class State(TypedDict):
    messages: list[dict]

# 创建图构建器
builder = StateGraph(State)

# 添加节点
builder.add_node("node_name", node_function)

# 添加边
builder.add_edge(START, "node_name")
builder.add_edge("node_name", END)

# 编译图
graph = builder.compile(checkpointer=checkpointer)

处理状态

# 使用 TypedDict 定义状态
class State(TypedDict):
    messages: list[dict]
    counter: int

# 使用带归约器的状态
from typing import Annotated, List
from operator import add

class State(TypedDict):
    messages: Annotated[List[dict], add]
    counter: int

创建节点

# 基本节点
def my_node(state: State) -> dict:
    # 处理逻辑
    return {"key": "value"}

# 使用运行时上下文的节点
def my_node_with_runtime(state: State, runtime: Runtime) -> dict:
    # 访问运行时信息
    print(f"Thread ID: {runtime.execution_info.thread_id}")
    return {"key": "value"}

# 添加节点到图
builder.add_node("my_node", my_node)

添加边

# 添加正常边
builder.add_edge("node_a", "node_b")

# 添加条件边
def routing_function(state: State) -> str:
    # 返回下一个节点名称
    return "node_b" if state["condition"] else "node_c"

builder.add_conditional_edges("node_a", routing_function)

# 添加条件边带映射
builder.add_conditional_edges(
    "node_a", 
    routing_function, 
    {"condition_a": "node_b", "condition_b": "node_c"}
)

使用 Send

from langgraph.types import Send

def continue_to_jokes(state: OverallState):
    return [Send("generate_joke", {"subject": s}) for s in state['subjects']]

builder.add_conditional_edges("node_a", continue_to_jokes)

使用 Command

from langgraph.types import Command

# 从节点返回 Command
def my_node(state: State) -> Command[Literal["other_node"]]:
    return Command(
        update={"key": "value"},
        goto="other_node"
    )

# 使用 resume 继续
graph.invoke(Command(resume="value"), config)

处理消息

from langgraph.graph import MessagesState
from langgraph.graph.message import add_messages

# 使用 MessagesState
class State(MessagesState):
    additional_field: str

# 或自定义消息处理
class State(TypedDict):
    messages: Annotated[list[AnyMessage], add_messages]

与 Hello-Agents / Claude Code 的联系

在 Hello-Agents 中,我们学习了 LangGraph 的基本概念和简单用法。本页内容提供了更深入的 API 细节,特别是关于状态管理、节点和边的更高级用法。Hello-Agents 中的示例使用了 StateGraph 和基本节点/边概念,而本页内容扩展了这些概念,包括归约器、消息处理、Send 和 Command 等高级特性。

初学者易错点

  1. 忘记编译图:在使用图之前必须调用 .compile() 方法。
  2. 混合路由机制:不要在同一节点混合使用正常边和动态路由(Command),因为两条路径都可能执行,使图行为难以理解。
  3. 幂等性设计:当使用 checkpointer 时,节点可能会重新执行,确保节点逻辑是幂等的。
  4. 状态更新误解:节点返回的是状态更新,而不是整个状态对象。
  5. 递归限制处理:不正确处理递归限制可能导致图执行失败,应考虑使用 RemainingSteps 进行主动处理。

相关词条

官方原文:https://docs.langchain.com/oss/python/langgraph/graph-api