LangGraph 执行流程深度解析
StateGraph.compile() → CompiledStateGraph.stream() 完整执行链路
📋 概述
核心概念
LangGraph 是基于 LangChain 的状态图框架,用于构建有状态的多智能体应用。
- StateGraph:构建器模式,负责定义图的结构
- CompiledStateGraph:编译后的可执行图,支持 invoke、stream 等方法
graph TD
A[StateGraph 定义] --> B[添加节点和边]
B --> C[compile 编译]
C --> D[CompiledStateGraph]
D --> E[stream 流式执行]
D --> F[invoke 同步执行]
E --> G[迭代器输出]
F --> H[一次性返回]
🔧 第一部分:StateGraph.compile() 执行流程
步骤解析
1. 初始化 StateGraph 构建器
创建 StateGraph 实例,定义状态类型(StateT)、上下文类型(ContextT)、输入类型(InputT)和输出类型(OutputT)
2. 添加节点(add_node)
注册图中的处理节点,每个节点是一个 Runnable 或函数。节点会被包装成 PregelNode,包含输入映射、输出映射、重试策略等
3. 添加边(add_edge / add_conditional_edges)
定义节点之间的连接关系。边会被转换为 Channel 和 PregelNode 的组合,实现状态传递和条件分支
4. 编译(compile)核心逻辑
调用 compile() 方法,将 StateGraph 转换为 CompiledStateGraph。这个过程包括:
- 验证图的完整性(检查孤立节点、循环依赖等)
- 构建 channels 字典,包含所有状态通道
- 创建 Pregel 实例,配置 stream_mode、input_channels、output_channels
- 注入 checkpointer(可选,用于状态持久化)
5. 返回 CompiledStateGraph 实例
编译完成后返回 CompiledStateGraph 对象,该对象继承自 Pregel,包含完整的执行逻辑
compile() 源码关键流程
python
def compile(
self,
checkpointer: Checkpointer | None = None,
*,
cache: Cache | None = None,
store: Store | None = None,
interrupt_before: All | Sequence[str] | None = None,
interrupt_after: All | Sequence[str] | None = None,
debug: bool = False,
) -> CompiledStateGraph:
# 1. 验证图结构
self.validate()
# 2. 构建 channels 字典
channels = {
**self.channels,
**self.managed,
START: EphemeralValue(self.input_schema),
}
# 3. 创建 CompiledStateGraph 实例
compiled = CompiledStateGraph[StateT, ContextT, InputT, OutputT](
builder=self,
schema_to_mapper={},
context_schema=self.context_schema,
nodes={}, # 节点会在内部构建
channels=channels,
input_channels=START,
stream_mode="updates",
output_channels=output_channels,
stream_channels=stream_channels,
checkpointer=checkpointer,
interrupt_before_nodes=interrupt_before,
interrupt_after_nodes=interrupt_after,
debug=debug,
)
return compiled
编译时序图
sequenceDiagram
participant User as 用户代码
participant Builder as StateGraph
participant Compile as compile()
participant Graph as CompiledStateGraph
User->>Builder: 创建 StateGraph(State)
User->>Builder: add_node("agent", call_model)
User->>Builder: add_edge(START, "agent")
User->>Builder: add_edge("agent", END)
User->>Compile: graph = builder.compile()
Note over Compile: 1. 验证图结构
Note over Compile: 2. 构建 channels
Note over Compile: 3. 创建 Pregel 实例
Note over Compile: 4. 配置 stream_mode
Compile->>Graph: 返回 CompiledStateGraph
Graph-->>User: 可执行的图实例
🌊 第二部分:CompiledStateGraph.stream() 执行流程
stream() vs invoke()
| 特性 | stream() | invoke() |
|---|---|---|
| 返回类型 | Iterator(迭代器) | Any(一次性返回) |
| 执行方式 | 逐步执行,实时输出 | 等待完成,批量返回 |
| 适用场景 | 需要实时反馈(如 LLM 流式输出) | 简单调用,不需要中间状态 |
| 内存占用 | 低(逐步处理) | 高(需要缓存所有状态) |
步骤解析
1. 参数处理与默认值设置
处理 stream_mode、output_keys、interrupt_before/after 等参数。如果未指定 stream_mode,默认使用 "updates" 或 "values"
2. 创建同步队列(SyncQueue)
创建用于接收流式输出的队列,这是 stream() 方法的核心数据结构
3. 配置回调管理器
设置 LangChain 的回调系统,用于追踪执行过程、记录日志、发送遥测数据等
4. 启动 Pregel 执行循环
调用 PregelLoop,这是 LangGraph 的核心执行引擎,负责:
- 从输入通道读取数据
- 触发满足条件的节点执行
- 将节点输出写入输出通道
- 检查中断条件
- 持久化检查点(如果启用)
5. 迭代输出
根据 stream_mode 的不同,逐步 yield 输出:
- "values":输出每个步骤后的完整状态
- "updates":只输出节点返回的更新
- "messages":输出 LLM 的 token 流和元数据
- "debug":输出详细的调试信息
6. 执行完成或中断
当图执行完成(到达 END 节点)或遇到中断条件时,停止迭代
stream() 源码关键流程
python
def stream(
self,
input: InputT | Command | None,
config: RunnableConfig | None = None,
*,
stream_mode: StreamMode | Sequence[StreamMode] | None = None,
output_keys: str | Sequence[str] | None = None,
interrupt_before: All | Sequence[str] | None = None,
interrupt_after: All | Sequence[str] | None = None,
durability: Durability | None = None,
**kwargs,
) -> Iterator[dict[str, Any] | Any]:
# 1. 参数处理
if stream_mode is None:
stream_mode = "updates" # 默认模式
# 2. 创建同步队列
stream = SyncQueue()
# 3. 配置回调管理器
callback_manager = get_callback_manager_for_config(config)
run_manager = callback_manager.on_chain_start(None, input, ...)
# 4. 启动 Pregel 执行循环(在后台线程中)
with get_executor_for_config(config) as executor:
future = executor.submit(
_run_stream, # 核心执行函数
input,
config,
stream,
stream_mode,
output_keys,
interrupt_before,
interrupt_after,
durability,
)
# 5. 从队列中读取输出
while True:
try:
chunk = stream.get(block=True, timeout=0.1)
if chunk is None: # 结束标志
break
yield chunk # 逐步输出
except queue.Empty:
if future.done():
break
continue
📊 关键概念对比
StateGraph vs CompiledStateGraph
| 特性 | StateGraph | CompiledStateGraph |
|---|---|---|
| 角色 | 构建器(Builder) | 可执行实例 |
| 用途 | 定义图结构(节点、边) | 执行图(invoke/stream) |
| 生命周期 | 编译前 | 编译后 |
| 核心方法 | add_node, add_edge, compile | invoke, stream, get_state |
| 是否可执行 | ❌ | ✅ |
| 继承关系 | 独立类 | 继承自 Pregel |
stream() vs invoke()
| 特性 | stream() | invoke() |
|---|---|---|
| 返回类型 | Iterator(迭代器) | Any(一次性返回) |
| 执行方式 | 逐步执行,实时输出 | 等待完成,批量返回 |
| 适用场景 | 需要实时反馈(如 LLM 流式输出) | 简单调用,不需要中间状态 |
| 内存占用 | 低(逐步处理) | 高(需要缓存所有状态) |
| stream_mode | 支持多种模式 | 不支持 |
💡 关键要点总结
compile() 的关键作用
- 将声明式的 StateGraph 转换为可执行的 Pregel 实例
- 构建 channels 字典,管理状态传递
- 配置 stream_mode、checkpointer 等运行时参数
- 验证图的完整性(无孤立节点、无死循环)
stream() 的执行机制
- 使用 SyncQueue 在生产者(执行线程)和消费者(主线程)之间传递数据
- 在 ThreadPoolExecutor 中运行 PregelLoop,避免阻塞主线程
- 通过 yield 逐步输出结果,支持实时反馈
- 支持多种 stream_mode,满足不同场景需求
PregelLoop 的核心职责
- 从输入通道读取数据
- 触发满足条件的节点执行(基于边的连接关系)
- 将节点输出写入输出通道
- 检查中断条件(interrupt_before/after)
- 持久化检查点(如果启用 checkpointer)
- 判断是否到达终止条件(END 节点或无更多可执行节点)
stream_mode 详解
- "values":输出每个步骤后的完整状态,适合调试
- "updates":只输出节点返回的更新,最常用
- "messages":输出 LLM 的 token 流和元数据,适合实时显示 AI 回复
- "debug":输出详细的调试信息,包括任务调度、通道状态等
- "checkpoints":输出检查点事件,用于状态恢复
🎯 实战示例
示例 1:基本编译与执行
python
from langgraph.graph import StateGraph, START, END
from langgraph.graph.state import CompiledStateGraph
from typing_extensions import TypedDict
# 1. 定义状态
class State(TypedDict):
messages: list
summary: str | None
# 2. 构建图
builder = StateGraph(State)
builder.add_node("agent", call_model)
builder.add_edge(START, "agent")
builder.add_edge("agent", END)
# 3. 编译
graph: CompiledStateGraph = builder.compile()
# 4. 执行
result = graph.invoke({"messages": ["你好"]})
print(result)
# 5. 流式执行
for chunk in graph.stream({"messages": ["你好"]}, stream_mode="messages"):
message_chunk, metadata = chunk
print(message_chunk.content, end="", flush=True)
示例 2:带检查点的流式执行
python
from langgraph.checkpoint.memory import MemorySaver
# 1. 创建检查点保存器
memory = MemorySaver()
# 2. 编译时注入 checkpointer
graph = builder.compile(checkpointer=memory)
# 3. 流式执行,使用 config 指定 thread_id
config = {"configurable": {"thread_id": "conversation_1"}}
for event in graph.stream(
{"messages": ["你好,请介绍一下自己"]},
config,
stream_mode="messages"
):
message_chunk, _metadata = event
if hasattr(message_chunk, 'content') and message_chunk.content:
print(message_chunk.content, end="", flush=True)