langgraph.stream中不同模式的区别

langgraph.stream中不同模式的区别,给我写一个mock代码,举例说明

下面按 LangGraph graph.stream(..., stream_mode=...) 的常见模式来讲。官方文档里当前列出的 stream modes 包括:valuesupdatesmessagescustomcheckpointstasksdebug;并且推荐在新代码里加 version="v2",因为 v2 输出统一为 {"type": ..., "ns": ..., "data": ...} 的结构。(LangChain 文档)

1. 各模式区别

模式 输出什么 适合场景
values 每一步后的 完整 state 想看当前全量状态,比如调试状态流转
updates 每个节点返回的 增量更新 UI 只关心“刚刚哪个节点改了什么”
messages LLM token/message chunk + metadata 做聊天打字机效果、流式输出模型回答
custom 节点里手动 writer(...) 发出的自定义事件 进度条、日志、阶段提示
checkpoints checkpoint 事件,格式类似 get_state() 需要持久化状态检查点;需要 checkpointer
tasks task start/finish、结果、错误 看任务生命周期;需要 checkpointer
debug 尽可能多的运行时信息 深度调试,不建议直接给生产 UI 用

官方说明里:updates 是每步后的 state updates;values 是每步后的完整 state;messages 是 LLM 调用产生的 (LLM token, metadata)custom 是通过 get_stream_writer() 从节点里发出的自定义数据。(LangChain 文档)


2. 一个最小 mock graph

这个例子不依赖真实 LLM,方便你直接理解 values / updates / custom / debug 的差异。

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


class State(TypedDict):
    user_input: str
    cleaned_input: str
    result: str


def preprocess(state: State):
    writer = get_stream_writer()
    writer({"stage": "preprocess", "msg": "开始清洗输入"})

    return {
        "cleaned_input": state["user_input"].strip().lower()
    }


def generate_answer(state: State):
    writer = get_stream_writer()
    writer({"stage": "generate_answer", "msg": "开始生成答案"})

    return {
        "result": f"mock answer for: {state['cleaned_input']}"
    }


builder = StateGraph(State)

builder.add_node("preprocess", preprocess)
builder.add_node("generate_answer", generate_answer)

builder.add_edge(START, "preprocess")
builder.add_edge("preprocess", "generate_answer")
builder.add_edge("generate_answer", END)

graph = builder.compile()

3. stream_mode="updates":只看每个节点改了什么

for chunk in graph.stream(
    {"user_input": "  Hello LangGraph  "},
    stream_mode="updates",
    version="v2",
):
    print(chunk)

可能输出:

{
    "type": "updates",
    "ns": (),
    "data": {
        "preprocess": {
            "cleaned_input": "hello langgraph"
        }
    }
}

{
    "type": "updates",
    "ns": (),
    "data": {
        "generate_answer": {
            "result": "mock answer for: hello langgraph"
        }
    }
}

可以理解为:节点返回什么,就流出什么
适合前端显示:“preprocess 完成了”、“generate_answer 完成了”。


4. stream_mode="values":每一步都给完整 state

for chunk in graph.stream(
    {"user_input": "  Hello LangGraph  "},
    stream_mode="values",
    version="v2",
):
    print(chunk)

可能输出:

{
    "type": "values",
    "ns": (),
    "data": {
        "user_input": "  Hello LangGraph  "
    }
}

{
    "type": "values",
    "ns": (),
    "data": {
        "user_input": "  Hello LangGraph  ",
        "cleaned_input": "hello langgraph"
    }
}

{
    "type": "values",
    "ns": (),
    "data": {
        "user_input": "  Hello LangGraph  ",
        "cleaned_input": "hello langgraph",
        "result": "mock answer for: hello langgraph"
    }
}

可以理解为:每次都给你一张完整快照
缺点是数据量更大;优点是消费端不用自己拼 state。


5. stream_mode="custom":节点里主动发事件

前面节点里有:

writer = get_stream_writer()
writer({"stage": "preprocess", "msg": "开始清洗输入"})

所以可以这样消费:

for chunk in graph.stream(
    {"user_input": "  Hello LangGraph  "},
    stream_mode="custom",
    version="v2",
):
    print(chunk)

可能输出:

{
    "type": "custom",
    "ns": (),
    "data": {
        "stage": "preprocess",
        "msg": "开始清洗输入"
    }
}

{
    "type": "custom",
    "ns": (),
    "data": {
        "stage": "generate_answer",
        "msg": "开始生成答案"
    }
}

这个模式很适合做:

正在检索文档...
正在调用工具...
正在生成摘要...
已完成 80%

6. 多模式一起用:["custom", "updates"]

这是实际项目里很常见的写法:
custom 给用户看进度,updates 给系统更新状态。

for chunk in graph.stream(
    {"user_input": "  Hello LangGraph  "},
    stream_mode=["custom", "updates"],
    version="v2",
):
    if chunk["type"] == "custom":
        print("[progress]", chunk["data"])

    elif chunk["type"] == "updates":
        print("[state update]", chunk["data"])

可能输出:

[progress] {'stage': 'preprocess', 'msg': '开始清洗输入'}
[state update] {'preprocess': {'cleaned_input': 'hello langgraph'}}

[progress] {'stage': 'generate_answer', 'msg': '开始生成答案'}
[state update] {'generate_answer': {'result': 'mock answer for: hello langgraph'}}

在 v2 格式里,无论单模式还是多模式,都是通过 chunk["type"] 区分类型;旧 v1 默认格式在单模式、多模式、subgraph 情况下返回结构会变化。(LangChain 文档)


7. messages 模式:真实 LLM token 流

messages 需要节点里调用真实 LangChain ChatModel。它流出来的是:

(message_chunk, metadata)

官方文档说明 messages 会从图里的 LLM 调用中流式产出 token/message segment 和 metadata。(LangChain 文档)

mock 写法大概是:

from typing import TypedDict
from langchain.chat_models import init_chat_model
from langgraph.graph import StateGraph, START, END


class ChatState(TypedDict):
    topic: str
    answer: str


model = init_chat_model("gpt-4o-mini")  # 示例,按你的 provider 改


def call_llm(state: ChatState):
    response = model.invoke([
        {
            "role": "user",
            "content": f"用一句话解释:{state['topic']}"
        }
    ])
    return {"answer": response.content}


builder = StateGraph(ChatState)
builder.add_node("call_llm", call_llm)
builder.add_edge(START, "call_llm")
builder.add_edge("call_llm", END)

graph = builder.compile()


for chunk in graph.stream(
    {"topic": "LangGraph stream mode"},
    stream_mode="messages",
    version="v2",
):
    if chunk["type"] == "messages":
        message_chunk, metadata = chunk["data"]

        # message_chunk.content 通常就是 token / partial text
        if message_chunk.content:
            print(message_chunk.content, end="", flush=True)

你还可以通过 metadata 过滤节点,例如只显示某个节点的 token:

for chunk in graph.stream(
    {"topic": "LangGraph stream mode"},
    stream_mode="messages",
    version="v2",
):
    if chunk["type"] == "messages":
        message_chunk, metadata = chunk["data"]

        if metadata.get("langgraph_node") == "call_llm":
            print(message_chunk.content, end="", flush=True)

8. debug 模式:调试用

for chunk in graph.stream(
    {"user_input": "  Hello LangGraph  "},
    stream_mode="debug",
    version="v2",
):
    print(chunk)

debug 会包含非常详细的执行信息。官方文档描述它是 “all available info”,包含 checkpointstasks 以及额外 metadata。(LangChain 文档)

一般建议:

开发调试:debug
前端状态更新:updates
前端完整状态渲染:values
聊天 token 输出:messages
进度条/自定义日志:custom

9. 最实用的选择建议

日常开发里我会这样选:

# 做普通工作流 UI
stream_mode=["custom", "updates"]

# 做 ChatGPT 一样的打字机效果
stream_mode=["messages", "updates"]

# 调试 state 为什么不对
stream_mode="values"

# 深度排查 graph 执行过程
stream_mode="debug"

一句话总结:

values = 给我完整状态
updates = 只告诉我变了什么
messages = 给我 LLM token
custom = 给我业务自定义事件
debug = 全都给我,方便排查
posted @ 2026-05-27 10:15  X1OO  阅读(2)  评论(0)    收藏  举报