LangGraph astream 七种流式模式完全指南

35 阅读6分钟

LangGraph astream 七种流式模式完全指南

概述

LangGraph 图公开了 .stream()(同步)和 .astream()(异步)方法,以迭代器形式生成流式输出。这些方法支持七种不同的流式模式(StreamMode),每种模式适用于不同的场景。

StreamMode = Literal[
    "values",       # 完整状态值
    "updates",      # 状态更新
    "checkpoints",  # 检查点事件
    "tasks",        # 任务事件
    "debug",        # 调试信息
    "messages",     # LLM 消息流
    "custom"        # 自定义数据
]

一、values 模式

描述

在图的每个步骤之后,流式传输状态的完整值

使用场景

  • 需要查看每个步骤后的完整状态
  • 调试状态变化
  • 了解状态累积过程

示例

from typing import TypedDict
from langgraph.graph import StateGraph, START, END

class State(TypedDict):
    topic: str
    joke: str

def refine_topic(state: State):
    return {"topic": state["topic"] + " and cats"}

def generate_joke(state: State):
    return {"joke": f"This is a joke about {state['topic']}"}

graph = (
    StateGraph(State)
    .add_node(refine_topic)
    .add_node(generate_joke)
    .add_edge(START, "refine_topic")
    .add_edge("refine_topic", "generate_joke")
    .add_edge("generate_joke", END)
    .compile()
)

# 使用 values 模式
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode="values",
):
    print(chunk)

输出:

{'topic': 'ice cream and cats', 'joke': ''}
{'topic': 'ice cream and cats', 'joke': 'This is a joke about ice cream and cats'}

二、updates 模式

描述

在图的每个步骤之后,流式传输状态的更新。如果同一步骤中有多次更新(例如运行了多个节点),这些更新将单独流式传输。

使用场景

  • 仅关注节点返回的变化
  • 需要知道哪个节点产生了什么变化
  • 最常用的流式模式

示例

# 使用 updates 模式
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode="updates",
):
    print(chunk)

输出:

{'refine_topic': {'topic': 'ice cream and cats'}}
{'generate_joke': {'joke': 'This is a joke about ice cream and cats'}}

三、checkpoints 模式

描述

当创建检查点时发出事件,格式与 get_state() 返回的格式相同。

使用场景

  • 需要监控检查点创建
  • 实现持久化状态的监听
  • 调试检查点行为

示例

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)

config = {"configurable": {"thread_id": "my-thread"}}

for chunk in graph.stream(
    inputs,
    config,
    stream_mode="checkpoints",
):
    print(chunk)

四、tasks 模式

描述

当任务开始和完成时发出事件,包括其结果和错误。

使用场景

  • 监控任务执行状态
  • 追踪任务开始/结束时间
  • 处理任务级别的错误

示例

for chunk in graph.stream(
    inputs,
    stream_mode="tasks",
):
    print(chunk)

典型输出结构:

{
    'event': 'task',           # 事件类型
    'name': 'node_name',       # 任务名称
    'status': 'start',         # start/complete/error
    'result': {...},           # 任务结果
    'error': None              # 错误信息
}

五、debug 模式

描述

发出 checkpointstasks 事件用于调试目的。提供最详细的执行信息。

使用场景

  • 深度调试图执行
  • 分析性能瓶颈
  • 理解完整的执行流程

示例

for chunk in graph.stream(
    inputs,
    stream_mode="debug",
):
    print(chunk)

组合使用:

# debug 模式等价于组合使用 checkpoints 和 tasks
for mode, chunk in graph.stream(
    inputs,
    stream_mode=["debug", "updates", "values"],
):
    print(f"[{mode}]", chunk)

六、messages 模式

描述

从调用 LLM 的任何图节点流式传输 2 元组(消息块,元数据)

使用场景

  • 实时显示 LLM 输出
  • 逐令牌流式传输响应
  • 过滤特定 LLM 调用的输出

示例

from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, START

llm = init_chat_model(model="openai:gpt-4o-mini")

def call_model(state):
    llm_response = llm.invoke([
        {"role": "user", "content": f"Generate a joke about {state['topic']}"}
    ])
    return {"joke": llm_response.content}

graph = (
    StateGraph(State)
    .add_node(call_model)
    .add_edge(START, "call_model")
    .compile()
)

# 流式传输 LLM 令牌
for message_chunk, metadata in graph.stream(
    {"topic": "ice cream"},
    stream_mode="messages",
):
    if message_chunk.content:
        print(message_chunk.content, end="|", flush=True)

元数据结构:

{
    "langgraph_node": "node_name",     # 调用 LLM 的节点
    "tags": ["tag1", "tag2"],           # LLM 的标签
    "run_id": "...",                    # 运行 ID
    # ... 其他元数据
}

按 LLM 调用过滤

llm_1 = init_chat_model(model="openai:gpt-4o-mini", tags=['joke'])
llm_2 = init_chat_model(model="openai:gpt-4o-mini", tags=['poem'])

async for msg, metadata in graph.astream(
    {"topic": "cats"},
    stream_mode="messages",
):
    if metadata["tags"] == ["joke"]:
        print(msg.content, end="|", flush=True)

按节点过滤

for msg, metadata in graph.stream(
    inputs,
    stream_mode="messages",
):
    if msg.content and metadata["langgraph_node"] == "write_poem":
        print(msg.content, end="|", flush=True)

七、custom 模式

描述

使用 StreamWriter 从节点或任务内部流式传输自定义用户定义数据

使用场景

  • 发送进度更新
  • 流式传输任意 LLM API 的输出
  • 自定义实时数据推送

基本示例

from langgraph.config import get_stream_writer
from langgraph.graph import StateGraph, START

class State(TypedDict):
    query: str
    answer: str

def node(state: State):
    writer = get_stream_writer()  # 获取流写入器
    writer({"custom_key": "Generating custom data inside node"})
    return {"answer": "some data"}

graph = (
    StateGraph(State)
    .add_node(node)
    .add_edge(START, "node")
    .compile()
)

for chunk in graph.stream({"query": "example"}, stream_mode="custom"):
    print(chunk)

工具中使用自定义流

from langchain_core.tools import tool
from langgraph.config import get_stream_writer

@tool
def query_database(query: str) -> str:
    """Query the database."""
    writer = get_stream_writer()
    writer({"data": "Retrieved 0/100 records", "type": "progress"})
    # perform query
    writer({"data": "Retrieved 100/100 records", "type": "progress"})
    return "some-answer"

for chunk in graph.stream(inputs, stream_mode="custom"):
    print(chunk)

流式传输任意 LLM

from langgraph.config import get_stream_writer
from openai import AsyncOpenAI

openai_client = AsyncOpenAI()

async def stream_tokens(model_name: str, messages: list[dict]):
    response = await openai_client.chat.completions.create(
        messages=messages, model=model_name, stream=True
    )
    async for chunk in response:
        delta = chunk.choices[0].delta
        if delta.content:
            yield {"content": delta.content}

async def call_arbitrary_model(state):
    writer = get_stream_writer()
    async for chunk in stream_tokens("gpt-4o-mini", [
        {"role": "user", "content": f"Tell me about {state['topic']}"}
    ]):
        writer({"custom_llm_chunk": chunk})
    return {"result": "completed"}

async for chunk in graph.astream(
    {"topic": "cats"},
    stream_mode="custom",
):
    print(chunk.get("content", ""), end="|", flush=True)

组合多种模式

你可以将列表作为 stream_mode 参数传递,以同时流式处理多种模式。

async for mode, chunk in graph.astream(
    inputs,
    stream_mode=["updates", "custom"],
):
    print(f"[{mode}]", chunk)

输出示例:

[custom] {'progress': 'Starting...'}
[updates] {'node1': {'foo': 'bar'}}
[custom] {'progress': 'Processing...'}
[updates] {'node2': {'baz': 'qux'}}
[custom] {'progress': 'Done!'}

Python < 3.11 的注意事项

在 Python < 3.11 版本中,asyncio 任务不支持 context 参数,这影响流式传输:

1. 必须显式传递 RunnableConfig

async def call_model(state, config):  # 接受 config 参数
    response = await llm.ainvoke(
        [{"role": "user", "content": f"Write about {state['topic']}"}],
        config,  # 必须传递
    )
    return {"result": response.content}

2. 不能使用 get_stream_writer()

必须直接传递 writer 参数:

from langgraph.types import StreamWriter

async def my_node(state: State, writer: StreamWriter):  # 接受 writer 参数
    writer({"custom_key": "data"})
    return {"answer": "done"}

子图流式传输

要在流式输出中包含来自子图的输出,设置 subgraphs=True

for chunk in graph.stream(
    {"foo": "foo"},
    subgraphs=True,
    stream_mode="updates",
):
    print(chunk)

输出格式: (命名空间, 数据) 元组

((), {'node_1': {'foo': 'hi! foo'}})
(('node_2:task-id',), {'subgraph_node_1': {'bar': 'bar'}})
(('node_2:task-id',), {'subgraph_node_2': {'foo': 'hi! foobar'}})
((), {'node_2': {'foo': 'hi! foobar'}})

禁用特定模型的流式处理

对于不支持流式处理的模型,可以显式禁用:

from langchain.chat_models import init_chat_model

model = init_chat_model(
    "anthropic:claude-3-7-sonnet-latest",
    disable_streaming=True
)

或使用 ChatOpenAI:

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="o1-preview", disable_streaming=True)

快速参考表

模式输出内容使用场景
values完整状态值调试状态变化
updates节点返回的更新最常用,关注变化
checkpoints检查点事件监控持久化
tasks任务开始/完成/错误任务级别监控
debugcheckpoints + tasks深度调试
messages(消息块, 元数据)LLM 令牌流
custom自定义数据进度更新/任意数据

最佳实践

  1. 开发阶段:使用 debug 模式获取完整信息
  2. 生产环境:使用 updates 模式减少数据传输
  3. 用户界面:使用 messages 模式实时显示 LLM 输出
  4. 进度显示:使用 custom 模式发送自定义进度
  5. 状态监控:组合使用 ["values", "custom"] 获取完整状态和自定义数据