高级特性
流式执行
基本介绍
流式执行(Streaming Execution)是指程序在任务尚未全部完成时,就将执行过程中已经产生的中间结果、状态变化或事件持续输出给调用方,而不是等待整个任务结束后一次性返回最终结果
LangGraph 的流式执行是指,状态图计算过程中,将节点输出、状态更新、消息增量、自定义事件或调试信息等写入流式队列,并在特定的时机将流式队列中的信息返还给调用者
在同步执行中,这些数据最终进入内部的同步流式队列;在异步执行中,则进入对应的异步队列
调用方通过迭代器逐条消费这些数据,因此不必等到整张图执行完毕后再获得反馈
| 维度 | stream() | astream() |
|---|---|---|
| 调用方式 | 同步迭代 for chunk in graph.stream(...) | 异步迭代 async for chunk in graph.astream(...) |
| 运行环境 | 普通 Python 脚本 | asyncio 事件循环(Jupyter、FastAPI 等) |
| 参数与输出 | 相同 stream_mode,语义一致 | 相同 stream_mode,语义一致 |
| 适用场景 | 同步批处理、CLI 工具 | async 服务端、结合其他异步 IO |
stream_mode
流式输出支持不同的输出模式 stream_mode,这规定了流式输出的内容(在 LangGraph 中称为 chunk),stream_mode 可传入单个字符串或模式列表
| 模式 | 输出内容 | 适用场景 |
|---|---|---|
| values | 每个超步后的完整状态 | 需要完整状态快照 |
| updates | 节点产生的状态更新(增量) | 只关心节点输出变化 |
| messages | messages 状态字段的增量更新 | LLM 对话流式输出 |
| checkpoints | 检查点更新事件(需检查点存储器) | 持久化监控、中断调试 |
| tasks | 任务开始/结果事件(含触发通道、异常) | 运行时观测、任务追踪 |
| debug | checkpoints + tasks 的统一封装,附加超步编号、时间戳 | 调试排错 |
| custom | 节点/工具通过 stream_writer 主动写出的自定义数据 | 进度通知、阶段说明 |
values 模式
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class OverAllState(TypedDict):
initial_state: str
node_a_output: str
node_b_output: str
def node_a(state: OverAllState) -> OverAllState:
return {
"node_a_output": "节点A的输出"
}
def node_b(state: OverAllState) -> OverAllState:
return {
"node_b_output": "节点B的输出"
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("node_a", node_a)
builder.add_node("node_b", node_b)
builder.add_edge(START, "node_a")
builder.add_edge("node_a", "node_b")
builder.add_edge("node_b", END)
graph = builder.compile()
for chunk in graph.stream(
{"initial_state": "初始状态"},
stream_mode=["values"],
):
print(chunk)updates 模式
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class OverAllState(TypedDict):
initial_state: str
node_a_output: str
node_b_output: str
def node_a(state: OverAllState) -> OverAllState:
return {
"node_a_output": "节点A的输出"
}
def node_b(state: OverAllState) -> OverAllState:
return {
"node_b_output": "节点B的输出"
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("node_a", node_a)
builder.add_node("node_b", node_b)
builder.add_edge(START, "node_a")
builder.add_edge("node_a", "node_b")
builder.add_edge("node_b", END)
graph = builder.compile()
for chunk in graph.stream(
{"initial_state": "初始状态"},
stream_mode=["updates"],
):
print(chunk)messages 模式
python
from langgraph.graph import StateGraph, START, END, MessagesState
from langchain.messages import HumanMessage
from langchain_deepseek import ChatDeepSeek
from dotenv import load_dotenv
load_dotenv(override=True)
model = ChatDeepSeek(
model="deepseek-v4-flash",
extra_body={
"thinking": {
"type": "disabled"
}
}
)
def llm_node(state: MessagesState) -> MessagesState:
messages = state["messages"]
response = model.invoke(messages)
return {
"messages": [response],
}
builder = StateGraph(state_schema=MessagesState)
builder.add_node("llm_node", llm_node)
builder.add_edge(START, "llm_node")
builder.add_edge("llm_node", END)
graph = builder.compile()
for chunk in graph.stream(
{
"messages":[HumanMessage(content="你好!")]
},
stream_mode=["values","messages"],
):
print(chunk)checkpoints 模式
checkpoints 模式依赖检查点存储器,它输出的是运行时构造的检查点事件,其结构接近 get_state 返回的状态快照
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
class OverAllState(TypedDict):
initial_state: str
parallel_node_a_1: str
parallel_node_a_2: str
node_b_output: str
def parallel_node_a_1(state: OverAllState) -> OverAllState:
return {
"parallel_node_a_1": "并行节点A-1的输出"
}
def parallel_node_a_2(state: OverAllState) -> OverAllState:
return {
"parallel_node_a_2": "并行节点A-2的输出"
}
def node_b(state: OverAllState) -> OverAllState:
interrupt("hello")
return {
"node_b_output": "节点B的输出"
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("parallel_node_a_1", parallel_node_a_1)
builder.add_node("parallel_node_a_2", parallel_node_a_2)
builder.add_node("node_b", node_b)
builder.add_edge(START, "parallel_node_a_1")
builder.add_edge(START, "parallel_node_a_2")
builder.add_edge(["parallel_node_a_1", "parallel_node_a_2"], "node_b")
builder.add_edge("node_b", END)
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "123"}}
for chunk in graph.stream(
{"initial_state": "初始状态"},
stream_mode=["checkpoints"],
config=config
):
print(chunk)
print('=' * 30, '-> 中断前后分界线 <-', '=' * 30)
for chunk in graph.stream(
Command(resume=""),
stream_mode=["checkpoints"],
config=config
):
print(chunk)tasks 模式
tasks 模式以任务为单位输出开始事件和结果事件,它比 updates 更偏向运行时观测:除了任务结果,还会暴露任务输入、触发通道、异常和中断信息
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class OverAllState(TypedDict):
initial_state: str
parallel_node_a_1: str
parallel_node_a_2: str
node_b_output: str
def parallel_node_a_1(state: OverAllState) -> OverAllState:
return {
"parallel_node_a_1": "并行节点A-1的输出"
}
def parallel_node_a_2(state: OverAllState) -> OverAllState:
return {
"parallel_node_a_2": "并行节点A-2的输出"
}
def node_b(state: OverAllState) -> OverAllState:
return {
"node_b_output": "节点B的输出"
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("parallel_node_a_1", parallel_node_a_1)
builder.add_node("parallel_node_a_2", parallel_node_a_2)
builder.add_node("node_b", node_b)
builder.add_edge(START, "parallel_node_a_1")
builder.add_edge(START, "parallel_node_a_2")
builder.add_edge(["parallel_node_a_1", "parallel_node_a_2"], "node_b")
builder.add_edge("node_b", END)
graph = builder.compile()
for chunk in graph.stream(
{"initial_state": "初始状态"},
stream_mode=["tasks"]
):
print(chunk)debug 模式
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
class OverAllState(TypedDict):
initial_state: str
parallel_node_a_1: str
parallel_node_a_2: str
node_b_output: str
def parallel_node_a_1(state: OverAllState) -> OverAllState:
return {
"parallel_node_a_1": "并行节点A-1的输出"
}
def parallel_node_a_2(state: OverAllState) -> OverAllState:
return {
"parallel_node_a_2": "并行节点A-2的输出"
}
def node_b(state: OverAllState) -> OverAllState:
interrupt("hello")
return {
"node_b_output": "节点B的输出"
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("parallel_node_a_1", parallel_node_a_1)
builder.add_node("parallel_node_a_2", parallel_node_a_2)
builder.add_node("node_b", node_b)
builder.add_edge(START, "parallel_node_a_1")
builder.add_edge(START, "parallel_node_a_2")
builder.add_edge(["parallel_node_a_1", "parallel_node_a_2"], "node_b")
builder.add_edge("node_b", END)
checkpointer = InMemorySaver()
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "123"}}
for chunk in graph.stream(
{"initial_state": "初始状态"},
stream_mode=["debug"],
config=config
):
print(chunk)
print('=' * 30, '-> 中断前后分界线 <-', '=' * 30)
for chunk in graph.stream(
Command(resume=""),
stream_mode=["debug"],
config=config
):
print(chunk)custom 模式
(1)节点中写出内容
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.runtime import Runtime
class OverAllState(TypedDict):
initial_state: str
node_a_output: str
node_b_output: str
def node_a(state: OverAllState, runtime: Runtime) -> OverAllState:
stream_writer = runtime.stream_writer
stream_writer("节点 A 正在执行...")
return {
"node_a_output": "节点A的输出"
}
def node_b(state: OverAllState, runtime: Runtime) -> OverAllState:
stream_writer = runtime.stream_writer
stream_writer("节点 B 正在执行...")
return {
"node_b_output": "节点B的输出"
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("node_a", node_a)
builder.add_node("node_b", node_b)
builder.add_edge(START, "node_a")
builder.add_edge("node_a", "node_b")
builder.add_edge("node_b", END)
graph = builder.compile()
for chunk in graph.stream(
{"initial_state": "初始状态"},
stream_mode=["custom"],
):
print(chunk)(2)工具中写出内容
工具中写出内容通过 ToolRuntime 实例的 stream_writer,用法与节点中一致
python
from typing import Literal
from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.prebuilt.tool_node import ToolNode, ToolRuntime
from langgraph.runtime import Runtime
from langchain.tools import tool
from langchain.messages import HumanMessage
from langchain_deepseek import ChatDeepSeek
from dotenv import load_dotenv
load_dotenv(override=True)
model = ChatDeepSeek(
model="deepseek-v4-flash",
extra_body={
"thinking": {
"type": "disabled"
}
}
)
@tool(parse_docstring=True)
def get_weather(city: str, runtime: ToolRuntime) -> str:
"""
根据城市查询当日天气
Args:
city: 城市名称
"""
stream_writer = runtime.stream_writer
stream_writer(f"正在查询 {city} 今天的天气...")
return f"{city} 今天天气不错"
tools = [get_weather]
model_with_tools = model.bind_tools(tools=tools)
def llm_node(state: MessagesState, runtime: Runtime) -> MessagesState:
messages = state["messages"]
response = model_with_tools.invoke(messages)
stream_writer = runtime.stream_writer
stream_writer("正在执行 llm_node...")
return {
"messages": [response]
}
def router(state: MessagesState) -> Literal["tool_node", END]:
last_msg = state["messages"][-1]
if last_msg.tool_calls:
return "tool_node"
return END
builder = StateGraph(state_schema=MessagesState)
builder.add_node("llm_node", llm_node)
builder.add_node("tool_node", ToolNode(tools=tools))
builder.add_edge(START, "llm_node")
builder.add_conditional_edges("llm_node", router, path_map=["tool_node", END])
builder.add_edge("tool_node", "llm_node")
graph = builder.compile()
for chunk in graph.stream(
{"messages": [HumanMessage("今天北京天气如何?")]},
stream_mode=["custom"]
):
print(chunk)子图
子图嵌入方式
| 方式 | 用法 | 适用场景 | 通信方式 |
|---|---|---|---|
| 节点函数中调用子图 | 在节点函数内 subgraph.invoke() | 父子图状态完全隔离 | 手动做输入输出映射 |
| 子图直接作为父图节点 | add_node("name", compiled_subgraph) | 父子图共享状态字段 | 通过共享字段自动通信 |
节点函数调用子图
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
# 构建子图
class SubgraphState(TypedDict):
raw_text: str # 未清洗文本
stripped_text: str # 去除收尾空格的文本
punctuated_text: str # 句尾添加句号的文本
def subgraph_strip_node(state: SubgraphState) -> SubgraphState:
raw_text = state["raw_text"]
stripped_text = raw_text.strip()
return {
"stripped_text": stripped_text
}
def subgraph_punctuate_node(state: SubgraphState) -> SubgraphState:
stripped_text = state["stripped_text"]
punctuated_text = stripped_text + "。"
return {
"punctuated_text": punctuated_text
}
builder = StateGraph(state_schema=SubgraphState)
builder.add_node("subgraph_strip_node", subgraph_strip_node)
builder.add_node("subgraph_punctuate_node", subgraph_punctuate_node)
builder.add_edge(START, "subgraph_strip_node")
builder.add_edge("subgraph_strip_node", "subgraph_punctuate_node")
builder.add_edge("subgraph_punctuate_node", END)
subgraph = builder.compile()
# 构建父图
class ParentState(TypedDict):
input_text: str # 输入的未清洗的文本
cleaned_text: str # 清洗后的文本
def call_subgraph(state: ParentState) -> ParentState:
input_text = state["input_text"]
res = subgraph.invoke({"raw_text": input_text})
cleaned_text = res["punctuated_text"]
return {
"cleaned_text": cleaned_text
}
builder = StateGraph(state_schema=ParentState)
builder.add_node("call_subgraph", call_subgraph)
builder.add_edge(START, "call_subgraph")
builder.add_edge("call_subgraph", END)
parent_graph = builder.compile()
input_text = " LangGraph 真有意思 "
res = parent_graph.invoke({"input_text": input_text})
cleaned_text = res["cleaned_text"]
print("=" * 30, "-> 原始文本 <-", "=" * 30)
print(input_text)
print("=" * 30, "-> 清洗后的文本 <-", "=" * 30)
print(cleaned_text)
from IPython.display import display, Image
display(
Image(
parent_graph
.get_graph(xray=True)
.draw_mermaid_png()
)
)子图作为父图节点
python
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
# 定义全局共享状态
class OverAllState(TypedDict):
raw_text: str # 未清洗文本
cleaned_text: str # 清洗后的文本
# 构建子图
def subgraph_strip_node(state: OverAllState) -> OverAllState:
raw_text = state["raw_text"]
stripped_text = raw_text.strip()
return {
"cleaned_text": stripped_text
}
def subgraph_punctuate_node(state: OverAllState) -> OverAllState:
cleaned_text = state["cleaned_text"]
punctuated_text = cleaned_text + "。"
return {
"cleaned_text": punctuated_text
}
builder = StateGraph(state_schema=OverAllState)
builder.add_node("subgraph_strip_node", subgraph_strip_node)
builder.add_node("subgraph_punctuate_node", subgraph_punctuate_node)
builder.add_edge(START, "subgraph_strip_node")
builder.add_edge("subgraph_strip_node", "subgraph_punctuate_node")
builder.add_edge("subgraph_punctuate_node", END)
subgraph = builder.compile()
# 构建父图
builder = StateGraph(state_schema=OverAllState)
builder.add_node("subgraph_node", subgraph)
builder.add_edge(START, "subgraph_node")
builder.add_edge("subgraph_node", END)
parent_graph = builder.compile()
raw_text = " LangGraph 真有意思 "
res = parent_graph.invoke({"raw_text": raw_text})
cleaned_text = res["cleaned_text"]
print("=" * 30, "-> 原始文本 <-", "=" * 30)
print(raw_text)
print("=" * 30, "-> 清洗后的文本 <-", "=" * 30)
print(cleaned_text)
from IPython.display import display, Image
display(
Image(
parent_graph
.get_graph(xray=True)
.draw_mermaid_png()
)
)持久化策略
| 策略 | 编译参数 | 检查点保存 | 中断恢复 | 多轮记忆 |
|---|---|---|---|---|
| Per-invocation(默认) | checkpointer=None 或省略 | ✓ | ✓ | ✗(同 thread_id 再次调用不加载历史) |
| Per-thread | checkpointer=True | ✓ | ✓ | ✓(同 thread_id 调用加载历史) |
| Stateless | checkpointer=False | ✗ | ✗ | ✗ |
动态路由
python
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
from langgraph.types import Command
from loguru import logger
# ==================== 子图 ====================
class SubState(TypedDict):
data: str
def sub_node(state: SubState) -> Command:
"""子图节点:执行后动态路由回父图的 parent_router"""
logger.info("[子图] sub_node 执行")
return Command(
update={"data": state["data"] + " → 子图"},
goto="node_b", # 路由到父图节点
graph=Command.PARENT, # 指定目标为父图
)
sub_builder = StateGraph(state_schema=SubState)
sub_builder.add_node("sub_node", sub_node)
sub_builder.add_edge(START, "sub_node")
sub_graph = sub_builder.compile()
# ==================== 父图 ====================
class ParentState(TypedDict):
data: str
def node_a(state: ParentState) -> ParentState:
print("[父图] node_a 执行")
return {"visited_a": True}
def node_b(state: ParentState) -> ParentState:
print("[父图] node_b 执行")
return {"visited_b": True}
parent_builder = StateGraph(state_schema=ParentState)
parent_builder.add_node(
"sub_graph", sub_graph
)
parent_builder.add_node("node_a", node_a)
parent_builder.add_node("node_b", node_b)
parent_builder.add_edge(START, "sub_graph")
parent_builder.add_edge("node_a", "node_b")
parent_builder.add_edge("node_b", END)
parent_graph = parent_builder.compile()
result = parent_graph.invoke({"data": "初始"})
print(f"\n最终结果: {result}")
from IPython.display import display
display(parent_graph)运行时设计模式
| 模式 | 图结构 | 运行时动态性 | 核心 LangGraph 能力 |
|---|---|---|---|
| Prompt Chaining | 顺序链 | 低 | 静态边、条件边 |
| Parallelization | 固定 Fan-out/Fan-in | 低 | 并行超步、汇聚 |
| Routing | 条件分支 | 中 | 结构化输出、条件边 |
| Orchestrator-worker | 动态 Fan-out/Fan-in | 高 | Send、WorkerState、Reducer |
| Evaluator-optimizer | 反馈循环 | 中 | 条件边、循环、反馈状态 |
| Agent | 自主决策循环 | 最高 | MessagesState、工具调用、ToolNode |
