> ## Documentation Index
> Fetch the complete documentation index at: https://cndoc-langchain.site/llms.txt
> Use this file to discover all available pages before exploring further.

# 流式处理

LangGraph 实现了一个流式系统，用于呈现实时更新。流式处理对于提升基于大语言模型构建的应用程序的响应速度至关重要。通过渐进式地显示输出，甚至在完整响应准备好之前，流式处理显著改善了用户体验（UX），尤其是在处理大语言模型的延迟时。

## 入门

### 基本用法

LangGraph 图暴露了 [`stream`](https://reference.langchain.com/python/langgraph/pregel/#langgraph.pregel.Pregel.stream)（同步）和 [`astream`](https://reference.langchain.com/python/langgraph/pregel/#langgraph.pregel.Pregel.astream)（异步）方法，以迭代器的形式产生流式输出。传入一个或多个[流模式](#stream-modes)来控制接收的数据。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode=["updates", "custom"],  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "updates":
        for node_name, state in chunk["data"].items():
            print(f"Node {node_name} updated: {state}")
    elif chunk["type"] == "custom":
        print(f"Status: {chunk['data']['status']}")
```

```shell title="Output" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
Status: thinking of a joke...
Node generate_joke updated: {'joke': 'Why did the ice cream go to school? To get a sundae education!'}
```

<Accordion title="完整示例">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from typing import TypedDict
  from langgraph.graph import StateGraph, START, END
  from langgraph.config import get_stream_writer


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


  def generate_joke(state: State):
      writer = get_stream_writer()
      writer({"status": "thinking of a joke..."})
      return {"joke": f"Why did the {state['topic']} go to school? To get a sundae education!"}

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

  for chunk in graph.stream(
      {"topic": "ice cream"},
      stream_mode=["updates", "custom"],
      version="v2",
  ):
      if chunk["type"] == "updates":
          for node_name, state in chunk["data"].items():
              print(f"Node {node_name} updated: {state}")
      elif chunk["type"] == "custom":
          print(f"Status: {chunk['data']['status']}")
  ```

  ```shell title="Output" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  Status: thinking of a joke...
  Node generate_joke updated: {'joke': 'Why did the ice cream go to school? To get a sundae education!'}
  ```
</Accordion>

### 流输出格式 (v2)

<Note>
  需要 LangGraph >= 1.1。本页所有示例均使用 `version="v2"`。
</Note>

向 `stream()` 或 `astream()` 传入 `version="v2"` 以获得统一的输出格式。每个数据块都是一个 `StreamPart` 字典，具有统一的结构——无论流模式、模式数量或子图设置如何：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
{
    "type": "values" | "updates" | "messages" | "custom" | "checkpoints" | "tasks" | "debug",
    "ns": (),           # 命名空间元组，用于子图事件
    "data": ...,        # 实际负载（类型因流模式而异）
}
```

每种流模式都有一个对应的 `TypedDict`，包含 [`ValuesStreamPart`](https://reference.langchain.com/python/langgraph/types/ValuesStreamPart)、[`UpdatesStreamPart`](https://reference.langchain.com/python/langgraph/types/UpdatesStreamPart)、[`MessagesStreamPart`](https://reference.langchain.com/python/langgraph/types/MessagesStreamPart)、[`CustomStreamPart`](https://reference.langchain.com/python/langgraph/types/CustomStreamPart)、[`CheckpointStreamPart`](https://reference.langchain.com/python/langgraph/types/CheckpointStreamPart)、[`TasksStreamPart`](https://reference.langchain.com/python/langgraph/types/TasksStreamPart)、[`DebugStreamPart`](https://reference.langchain.com/python/langgraph/types/DebugStreamPart)。你可以从 `langgraph.types` 导入这些类型。联合类型 [`StreamPart`](https://reference.langchain.com/python/langgraph/types/StreamPart) 是基于 `part["type"]` 的可区分联合，可在编辑器和类型检查器中实现完整的类型缩窄。

使用 v1（默认）时，输出格式会根据你的流选项而变化（单模式返回原始数据，多模式返回 `(mode, data)` 元组，子图返回 `(namespace, data)` 元组）。使用 v2 时，格式始终相同：

<CodeGroup>
  ```python v2 (new) theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  for chunk in graph.stream(inputs, stream_mode="updates", version="v2"):
      print(chunk["type"])  # "updates"
      print(chunk["ns"])    # ()
      print(chunk["data"])  # {"node_name": {"key": "value"}}
  ```

  ```python v1 (current default) theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  for chunk in graph.stream(inputs, stream_mode="updates"):
      print(chunk)  # {"node_name": {"key": "value"}}
  ```
</CodeGroup>

v2 格式还支持类型缩窄，这意味着你可以按 `chunk["type"]` 过滤数据块，并获得正确的负载类型。每个分支将 `part["data"]` 缩窄为该模式的特定类型：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
for part in graph.stream(
    {"topic": "ice cream"},
    stream_mode=["values", "updates", "messages", "custom"],
    version="v2",
):
    if part["type"] == "values":
        # ValuesStreamPart — 每个步骤后的完整状态快照
        print(f"State: topic={part['data']['topic']}")
    elif part["type"] == "updates":
        # UpdatesStreamPart — 每个节点的更改键
        for node_name, state in part["data"].items():
            print(f"Node `{node_name}` updated: {state}")
    elif part["type"] == "messages":
        # MessagesStreamPart — 来自 LLM 调用的 (message_chunk, metadata)
        msg, metadata = part["data"]
        print(msg.content, end="", flush=True)
    elif part["type"] == "custom":
        # CustomStreamPart — 来自 get_stream_writer() 的任意数据
        print(f"Progress: {part['data']['progress']}%")
```

## 流模式

将以下一种或多种流模式作为列表传递给 [`stream`](https://reference.langchain.com/python/langgraph/graphs/#langgraph.graph.state.CompiledStateGraph.stream) 或 [`astream`](https://reference.langchain.com/python/langgraph/graphs/#langgraph.graph.state.CompiledStateGraph.astream) 方法：

| 模式                          | 类型                                                                                                    | 描述                                                                                                               |
| :-------------------------- | :---------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------- |
| [values](#graph-state)      | [`ValuesStreamPart`](https://reference.langchain.com/python/langgraph/types/ValuesStreamPart)         | 每个步骤后的完整状态。                                                                                                      |
| [updates](#graph-state)     | [`UpdatesStreamPart`](https://reference.langchain.com/python/langgraph/types/UpdatesStreamPart)       | 每个步骤后的状态更新。同一步骤中的多个更新会分别流式传输。                                                                                    |
| [messages](#llm-tokens)     | [`MessagesStreamPart`](https://reference.langchain.com/python/langgraph/types/MessagesStreamPart)     | 来自 LLM 调用的 (LLM token, metadata) 二元组。                                                                            |
| [custom](#custom-data)      | [`CustomStreamPart`](https://reference.langchain.com/python/langgraph/types/CustomStreamPart)         | 通过 [`get_stream_writer`](https://reference.langchain.com/python/langgraph/config/get_stream_writer) 从节点发出的自定义数据。 |
| [checkpoints](#checkpoints) | [`CheckpointStreamPart`](https://reference.langchain.com/python/langgraph/types/CheckpointStreamPart) | 检查点事件（与 `get_state()` 格式相同）。需要检查点保存器。                                                                            |
| [tasks](#tasks)             | [`TasksStreamPart`](https://reference.langchain.com/python/langgraph/types/TasksStreamPart)           | 任务开始/完成事件，包含结果和错误。需要检查点保存器。                                                                                      |
| [debug](#debug)             | [`DebugStreamPart`](https://reference.langchain.com/python/langgraph/types/DebugStreamPart)           | 所有可用信息——结合了 `checkpoints` 和 `tasks` 以及额外的元数据。                                                                    |

<a id="messages" />

### 图状态

使用流模式 `updates` 和 `values` 在图执行时流式传输图的状态。

* `updates` 在图的每个步骤后流式传输状态的**更新**。
* `values` 在图的每个步骤后流式传输状态的**完整值**。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
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()
)
```

<Tabs>
  <Tab title="updates">
    使用此模式仅流式传输节点在每个步骤后返回的**状态更新**。流式输出包括节点的名称以及更新内容。

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    for chunk in graph.stream(
        {"topic": "ice cream"},
        stream_mode="updates",  # [!code highlight]
        version="v2",  # [!code highlight]
    ):
        if chunk["type"] == "updates":
            for node_name, state in chunk["data"].items():
                print(f"Node `{node_name}` updated: {state}")
    ```

    ```shell title="Output" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    Node `refine_topic` updated: {'topic': 'ice cream and cats'}
    Node `generate_joke` updated: {'joke': 'This is a joke about ice cream and cats'}
    ```
  </Tab>

  <Tab title="values">
    使用此模式在每个步骤后流式传输图的**完整状态**。

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    for chunk in graph.stream(
        {"topic": "ice cream"},
        stream_mode="values",  # [!code highlight]
        version="v2",  # [!code highlight]
    ):
        if chunk["type"] == "values":
            print(f"topic: {chunk['data']['topic']}, joke: {chunk['data']['joke']}")
    ```

    ```shell title="Output" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    topic: ice cream, joke:
    topic: ice cream and cats, joke:
    topic: ice cream and cats, joke: This is a joke about ice cream and cats
    ```
  </Tab>
</Tabs>

### LLM tokens

使用 `messages` 流模式，从图的任何部分（包括节点、工具、子图或任务）**逐 token** 流式传输大语言模型（LLM）输出。

[`messages` 模式](#stream-modes)的流式输出是一个元组 `(message_chunk, metadata)`，其中：

* `message_chunk`：来自 LLM 的 token 或消息片段。
* `metadata`：一个字典，包含有关图节点和 LLM 调用的详细信息。

> 如果你的 LLM 不是作为 LangChain 集成提供的，你可以改用 `custom` 模式来流式传输其输出。详情请参阅[与任何 LLM 一起使用](#use-with-any-llm)。

<Warning>
  **Python \< 3.11 中异步需要手动配置**
  在 Python \< 3.11 中使用异步代码时，你必须显式地将 [`RunnableConfig`](https://reference.langchain.com/python/langchain-core/runnables/config/RunnableConfig) 传递给 `ainvoke()` 以启用正确的流式传输。详情请参阅[Python \< 3.11 中的异步](#async)，或升级到 Python 3.11+。
</Warning>

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from dataclasses import dataclass

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


@dataclass
class MyState:
    topic: str
    joke: str = ""


model = init_chat_model(model="gpt-5.4-mini")

def call_model(state: MyState):
    """调用 LLM 生成关于某个主题的笑话"""
    # 注意，即使使用 .invoke 而不是 .stream 运行 LLM，也会发出消息事件
    model_response = model.invoke(  # [!code highlight]
        [
            {"role": "user", "content": f"Generate a joke about {state.topic}"}
        ]
    )
    return {"joke": model_response.content}

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

# "messages" 流模式流式传输带有元数据的 LLM tokens
# 使用 version="v2" 获得统一的 StreamPart 格式
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode="messages",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "messages":
        message_chunk, metadata = chunk["data"]
        if message_chunk.content:
            print(message_chunk.content, end="|", flush=True)
```

#### 按 LLM 调用过滤

你可以将 `tags` 与 LLM 调用关联，以按 LLM 调用过滤流式 token。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langchain.chat_models import init_chat_model

# model_1 标记为 "joke"
model_1 = init_chat_model(model="gpt-5.4-mini", tags=['joke'])
# model_2 标记为 "poem"
model_2 = init_chat_model(model="gpt-5.4-mini", tags=['poem'])

graph = ... # 定义一个使用这些 LLM 的图

# stream_mode 设置为 "messages" 以流式传输 LLM tokens
# 元数据包含有关 LLM 调用的信息，包括标签
async for chunk in graph.astream(
    {"topic": "cats"},
    stream_mode="messages",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "messages":
        msg, metadata = chunk["data"]
        # 通过元数据中的 tags 字段过滤流式 token，仅包含
        # 带有 "joke" 标签的 LLM 调用的 token
        if metadata["tags"] == ["joke"]:
            print(msg.content, end="|", flush=True)
```

<Accordion title="扩展示例：按标签过滤">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from typing import TypedDict

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

  # joke_model 标记为 "joke"
  joke_model = init_chat_model(model="gpt-5.4-mini", tags=["joke"])
  # poem_model 标记为 "poem"
  poem_model = init_chat_model(model="gpt-5.4-mini", tags=["poem"])


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


  async def call_model(state, config):
        topic = state["topic"]
        print("Writing joke...")
        # 注意：对于 python < 3.11，需要显式传递 config
        # 因为在那之前没有添加上下文变量支持：https://docs.python.org/3/library/asyncio-task.html#creating-tasks
        # 显式传递 config 以确保上下文变量正确传播
        # 这在 Python < 3.11 中使用异步代码时是必需的。请参阅异步部分了解更多详情
        joke_response = await joke_model.ainvoke(
              [{"role": "user", "content": f"Write a joke about {topic}"}],
              config,
        )
        print("\n\nWriting poem...")
        poem_response = await poem_model.ainvoke(
              [{"role": "user", "content": f"Write a short poem about {topic}"}],
              config,
        )
        return {"joke": joke_response.content, "poem": poem_response.content}


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

  # stream_mode 设置为 "messages" 以流式传输 LLM tokens
  # 元数据包含有关 LLM 调用的信息，包括标签
  async for chunk in graph.astream(
        {"topic": "cats"},
        stream_mode="messages",
        version="v2",
  ):
      if chunk["type"] == "messages":
          msg, metadata = chunk["data"]
          if metadata["tags"] == ["joke"]:
              print(msg.content, end="|", flush=True)
  ```
</Accordion>

#### 从流中省略消息

使用 `nostream` 标签完全排除 LLM 输出。标记为 `nostream` 的调用仍然运行并产生输出；它们的 token 只是在 `messages` 模式下不会被发出。

这在以下情况下很有用：

* 你需要 LLM 输出用于内部处理（例如结构化输出），但不想将其流式传输给客户端
* 你通过不同的通道（例如自定义 UI 消息）流式传输相同的内容，并希望避免在 `messages` 流中重复输出

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from typing import Any, TypedDict

from langchain_anthropic import ChatAnthropic
from langgraph.graph import START, StateGraph

stream_model = ChatAnthropic(model_name="claude-haiku-4-5-20251001")
internal_model = ChatAnthropic(model_name="claude-haiku-4-5-20251001").with_config(
    {"tags": ["nostream"]}
)


class State(TypedDict):
    topic: str
    answer: str
    notes: str


def answer(state: State) -> dict[str, Any]:
    r = stream_model.invoke(
        [{"role": "user", "content": f"简要回复关于 {state['topic']} 的内容"}]
    )
    return {"answer": r.content}


def internal_notes(state: State) -> dict[str, Any]:
    # 由于 nostream 标签，此模型的令牌在 stream_mode="messages" 中被省略
    r = internal_model.invoke(
        [{"role": "user", "content": f"关于 {state['topic']} 的私有笔记"}]
    )
    return {"notes": r.content}


graph = (
    StateGraph(State)
    .add_node("write_answer", answer)
    .add_node("internal_notes", internal_notes)
    .add_edge(START, "write_answer")
    .add_edge("write_answer", "internal_notes")
    .compile()
)

initial_state: State = {"topic": "AI", "answer": "", "notes": ""}
stream = graph.stream(initial_state, stream_mode="messages")
```

#### 按节点过滤

要仅从特定节点流式传输 token，请使用 `stream_mode="messages"` 并通过流式元数据中的 `langgraph_node` 字段过滤输出：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
# "messages" 流模式流式传输带有元数据的 LLM tokens
# 使用 version="v2" 获得统一的 StreamPart 格式
for chunk in graph.stream(
    inputs,
    stream_mode="messages",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "messages":
        msg, metadata = chunk["data"]
        # 通过元数据中的 langgraph_node 字段过滤流式 token
        # 仅包含来自指定节点的 token
        if msg.content and metadata["langgraph_node"] == "some_node_name":
            ...
```

<Accordion title="扩展示例：从特定节点流式传输 LLM tokens">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from typing import TypedDict
  from langgraph.graph import START, StateGraph
  from langchain_openai import ChatOpenAI

  model = ChatOpenAI(model="gpt-5.4-mini")


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


  def write_joke(state: State):
        topic = state["topic"]
        joke_response = model.invoke(
              [{"role": "user", "content": f"Write a joke about {topic}"}]
        )
        return {"joke": joke_response.content}


  def write_poem(state: State):
        topic = state["topic"]
        poem_response = model.invoke(
              [{"role": "user", "content": f"Write a short poem about {topic}"}]
        )
        return {"poem": poem_response.content}


  graph = (
        StateGraph(State)
        .add_node(write_joke)
        .add_node(write_poem)
        # 并发写笑话和诗
        .add_edge(START, "write_joke")
        .add_edge(START, "write_poem")
        .compile()
  )

  # "messages" 流模式流式传输带有元数据的 LLM tokens
  # 使用 version="v2" 获得统一的 StreamPart 格式
  for chunk in graph.stream(
      {"topic": "cats"},
      stream_mode="messages",  # [!code highlight]
      version="v2",  # [!code highlight]
  ):
      if chunk["type"] == "messages":
          msg, metadata = chunk["data"]
          # 通过元数据中的 langgraph_node 字段过滤流式 token
          # 仅包含来自 write_poem 节点的 token
          if msg.content and metadata["langgraph_node"] == "write_poem":
              print(msg.content, end="|", flush=True)
  ```
</Accordion>

### 自定义数据

要从 LangGraph 节点或工具内部发送**自定义用户定义数据**，请按照以下步骤操作：

1. 使用 [`get_stream_writer`](https://reference.langchain.com/python/langgraph/config/get_stream_writer) 访问流写入器并发出自定义数据。
2. 在调用 `.stream()` 或 `.astream()` 时设置 `stream_mode="custom"` 以在流中获取自定义数据。你可以组合多种模式（例如 `["updates", "custom"]`），但至少一种必须是 `"custom"`。

<Warning>
  **Python \< 3.11 中异步没有 [`get_stream_writer`](https://reference.langchain.com/python/langgraph/config/get_stream_writer)**
  在 Python \< 3.11 上运行的异步代码中，[`get_stream_writer`](https://reference.langchain.com/python/langgraph/config/get_stream_writer) 将不起作用。
  相反，请向你的节点或工具添加一个 `writer` 参数并手动传递它。
  用法示例请参阅[Python \< 3.11 中的异步](#async)。
</Warning>

<Tabs>
  <Tab title="节点">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from typing import TypedDict
    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()
    )

    inputs = {"query": "example"}

    # 设置 stream_mode="custom" 以在流中接收自定义数据
    for chunk in graph.stream(inputs, stream_mode="custom", version="v2"):
        if chunk["type"] == "custom":
            print(f"Custom event: {chunk['data']['custom_key']}")
    ```
  </Tab>

  <Tab title="工具">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from langchain.tools import tool
    from langgraph.config import get_stream_writer

    @tool
    def query_database(query: str) -> str:
        """查询数据库。"""
        # 访问流写入器以发送自定义数据
        writer = get_stream_writer()  # [!code highlight]
        # 发出一个自定义键值对（例如进度更新）
        writer({"data": "Retrieved 0/100 records", "type": "progress"})  # [!code highlight]
        # 执行查询
        # 发出另一个自定义键值对
        writer({"data": "Retrieved 100/100 records", "type": "progress"})
        return "some-answer"


    graph = ... # 定义一个使用此工具的图

    # 设置 stream_mode="custom" 以在流中接收自定义数据
    for chunk in graph.stream(inputs, stream_mode="custom", version="v2"):
        if chunk["type"] == "custom":
            print(f"{chunk['data']['type']}: {chunk['data']['data']}")
    ```
  </Tab>
</Tabs>

### 子图输出

要将[子图](/oss/python/langgraph/use-subgraphs)的输出包含在流式输出中，你可以在父图的 `.stream()` 方法中设置 `subgraphs=True`。这将流式传输父图和任何子图的输出。

输出将以元组 `(namespace, data)` 的形式流式传输，其中 `namespace` 是一个元组，包含调用子图的节点路径，例如 `("parent_node:<task_id>", "child_node:<task_id>")`。

<Tabs>
  <Tab title="v2 (LangGraph >= 1.1)">
    使用 `version="v2"` 时，子图事件使用相同的 `StreamPart` 格式。`ns` 字段标识来源：

    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    for chunk in graph.stream(
        {"foo": "foo"},
        subgraphs=True,  # [!code highlight]
        stream_mode="updates",
        version="v2", # [!code highlight]
    ):
        print(chunk["type"])  # "updates"
        print(chunk["ns"])    # 根图为 ()，子图为 ("node_name:<task_id>",)
        print(chunk["data"])  # {"node_name": {"key": "value"}}
    ```
  </Tab>

  <Tab title="v1 (default)">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    for chunk in graph.stream(
        {"foo": "foo"},
        # 设置 subgraphs=True 以流式传输子图的输出
        subgraphs=True,  # [!code highlight]
        stream_mode="updates",
    ):
        print(chunk)
    ```
  </Tab>
</Tabs>

<Accordion title="扩展示例：从子图流式传输">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from langgraph.graph import START, StateGraph
  from typing import TypedDict

  # 定义子图
  class SubgraphState(TypedDict):
      foo: str  # 注意此键与父图状态共享
      bar: str

  def subgraph_node_1(state: SubgraphState):
      return {"bar": "bar"}

  def subgraph_node_2(state: SubgraphState):
      return {"foo": state["foo"] + state["bar"]}

  subgraph_builder = StateGraph(SubgraphState)
  subgraph_builder.add_node(subgraph_node_1)
  subgraph_builder.add_node(subgraph_node_2)
  subgraph_builder.add_edge(START, "subgraph_node_1")
  subgraph_builder.add_edge("subgraph_node_1", "subgraph_node_2")
  subgraph = subgraph_builder.compile()

  # 定义父图
  class ParentState(TypedDict):
      foo: str

  def node_1(state: ParentState):
      return {"foo": "hi! " + state["foo"]}

  builder = StateGraph(ParentState)
  builder.add_node("node_1", node_1)
  builder.add_node("node_2", subgraph)
  builder.add_edge(START, "node_1")
  builder.add_edge("node_1", "node_2")
  graph = builder.compile()

  for chunk in graph.stream(
      {"foo": "foo"},
      stream_mode="updates",
      # 设置 subgraphs=True 以流式传输子图的输出
      subgraphs=True,  # [!code highlight]
      version="v2",  # [!code highlight]
  ):
      if chunk["type"] == "updates":
          if chunk["ns"]:
              print(f"Subgraph {chunk['ns']}: {chunk['data']}")
          else:
              print(f"Root: {chunk['data']}")
  ```

  ```
  Root: {'node_1': {'foo': 'hi! foo'}}
  Subgraph ('node_2:dfddc4ba-c3c5-6887-5012-a243b5b377c2',): {'subgraph_node_1': {'bar': 'bar'}}
  Subgraph ('node_2:dfddc4ba-c3c5-6887-5012-a243b5b377c2',): {'subgraph_node_2': {'foo': 'hi! foobar'}}
  Root: {'node_2': {'foo': 'hi! foobar'}}
  ```

  **注意**，我们不仅接收节点更新，还接收命名空间，这些命名空间告诉我们正在从哪个图（或子图）流式传输。
</Accordion>

### 检查点

使用 `checkpoints` 流模式在图执行时接收检查点事件。每个检查点事件与 `get_state()` 的输出格式相同。需要[检查点保存器](/oss/python/langgraph/persistence)。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langgraph.checkpoint.memory import MemorySaver

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(checkpointer=MemorySaver())
)

config = {"configurable": {"thread_id": "1"}}

for chunk in graph.stream(
    {"topic": "ice cream"},
    config=config,
    stream_mode="checkpoints",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "checkpoints":
        print(chunk["data"])
```

### 任务

使用 `tasks` 流模式在图执行时接收任务开始和完成事件。任务事件包含有关正在运行的节点、其结果和任何错误的信息。需要[检查点保存器](/oss/python/langgraph/persistence)。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langgraph.checkpoint.memory import MemorySaver

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(checkpointer=MemorySaver())
)

config = {"configurable": {"thread_id": "1"}}

for chunk in graph.stream(
    {"topic": "ice cream"},
    config=config,
    stream_mode="tasks",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "tasks":
        print(chunk["data"])
```

<a id="debug" />

### 调试

使用 `debug` 流模式在图执行期间流式传输尽可能多的信息。流式输出包括节点的名称以及完整状态。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
for chunk in graph.stream(
    {"topic": "ice cream"},
    stream_mode="debug",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "debug":
        print(chunk["data"])
```

<Note>
  `debug` 模式结合了 `checkpoints` 和 `tasks` 事件以及额外的元数据。如果你只需要调试信息的子集，请直接使用 `checkpoints` 或 `tasks`。
</Note>

### 同时使用多种模式

你可以将列表作为 `stream_mode` 参数传递，以同时流式传输多种模式。

使用 `version="v2"` 时，每个数据块都是一个 `StreamPart` 字典。使用 `chunk["type"]` 区分模式：

<CodeGroup>
  ```python v2 theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  for chunk in graph.stream(inputs, stream_mode=["updates", "custom"], version="v2"):
      if chunk["type"] == "updates":
          for node_name, state in chunk["data"].items():
              print(f"Node `{node_name}` updated: {state}")
      elif chunk["type"] == "custom":
          print(f"Custom event: {chunk['data']}")
  ```

  ```python v1 theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  for mode, chunk in graph.stream(inputs, stream_mode=["updates", "custom"]):
      print(chunk)
  ```
</CodeGroup>

## 高级

### 与任何 LLM 一起使用

你可以使用 `stream_mode="custom"` 从**任何 LLM API** 流式传输数据——即使该 API **没有**实现 LangChain 聊天模型接口。

这让你可以集成原始 LLM 客户端或提供自己流式接口的外部服务，使 LangGraph 在自定义设置中高度灵活。

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langgraph.config import get_stream_writer

def call_arbitrary_model(state):
    """调用任意模型并流式传输输出的示例节点"""
    # 获取流写入器以发送自定义数据
    writer = get_stream_writer()  # [!code highlight]
    # 假设你有一个产生数据块的流式客户端
    # 使用你的自定义流式客户端生成 LLM tokens
    for chunk in your_custom_streaming_client(state["topic"]):
        # 使用写入器将自定义数据发送到流
        writer({"custom_llm_chunk": chunk})  # [!code highlight]
    return {"result": "completed"}

graph = (
    StateGraph(State)
    .add_node(call_arbitrary_model)
    # 根据需要添加其他节点和边
    .compile()
)
# 设置 stream_mode="custom" 以在流中接收自定义数据
for chunk in graph.stream(
    {"topic": "cats"},
    stream_mode="custom",  # [!code highlight]
    version="v2",  # [!code highlight]
):
    if chunk["type"] == "custom":
        # 数据块数据将包含从 LLM 流式传输的自定义数据
        print(chunk["data"])
```

<Accordion title="扩展示例：流式传输任意聊天模型">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import operator
  import json

  from typing import TypedDict
  from typing_extensions import Annotated
  from langgraph.graph import StateGraph, START

  from openai import AsyncOpenAI

  openai_client = AsyncOpenAI()
  model_name = "gpt-5.4-mini"


  async def stream_tokens(model_name: str, messages: list[dict]):
      response = await openai_client.chat.completions.create(
          messages=messages, model=model_name, stream=True
      )
      role = None
      async for chunk in response:
          delta = chunk.choices[0].delta

          if delta.role is not None:
              role = delta.role

          if delta.content:
              yield {"role": role, "content": delta.content}


  # 这是我们的工具
  async def get_items(place: str) -> str:
      """使用此工具列出你可能在被问及的地方找到的物品。"""
      writer = get_stream_writer()
      response = ""
      async for msg_chunk in stream_tokens(
          model_name,
          [
              {
                  "role": "user",
                  "content": (
                      "Can you tell me what kind of items "
                      f"i might find in the following place: '{place}'. "
                      "List at least 3 such items separating them by a comma. "
                      "And include a brief description of each item."
                  ),
              }
          ],
      ):
          response += msg_chunk["content"]
          writer(msg_chunk)

      return response


  class State(TypedDict):
      messages: Annotated[list[dict], operator.add]


  # 这是工具调用图节点
  async def call_tool(state: State):
      ai_message = state["messages"][-1]
      tool_call = ai_message["tool_calls"][-1]

      function_name = tool_call["function"]["name"]
      if function_name != "get_items":
          raise ValueError(f"Tool {function_name} not supported")

      function_arguments = tool_call["function"]["arguments"]
      arguments = json.loads(function_arguments)

      function_response = await get_items(**arguments)
      tool_message = {
          "tool_call_id": tool_call["id"],
          "role": "tool",
          "name": function_name,
          "content": function_response,
      }
      return {"messages": [tool_message]}


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

  让我们使用包含工具调用的 [`AIMessage`](https://reference.langchain.com/python/langchain-core/messages/ai/AIMessage) 调用图：

  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  inputs = {
      "messages": [
          {
              "content": None,
              "role": "assistant",
              "tool_calls": [
                  {
                      "id": "1",
                      "function": {
                          "arguments": '{"place":"bedroom"}',
                          "name": "get_items",
                      },
                      "type": "function",
                  }
              ],
          }
      ]
  }

  async for chunk in graph.astream(
      inputs,
      stream_mode="custom",
      version="v2",
  ):
      if chunk["type"] == "custom":
          print(chunk["data"]["content"], end="|", flush=True)
  ```
</Accordion>

### 为特定聊天模型禁用流式传输

如果你的应用程序混合了支持流式传输和不支持流式传输的模型，你可能需要为不支持流式传输的模型显式禁用流式传输。

在初始化模型时设置 `streaming=False`。

<Tabs>
  <Tab title="init_chat_model">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from langchain.chat_models import init_chat_model

    model = init_chat_model(
        "claude-sonnet-4-6",
        # 设置 streaming=False 以禁用聊天模型的流式传输
        streaming=False  # [!code highlight]
    )
    ```
  </Tab>

  <Tab title="聊天模型接口">
    ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    from langchain_openai import ChatOpenAI

    # 设置 streaming=False 以禁用聊天模型的流式传输
    model = ChatOpenAI(model="o1-preview", streaming=False)
    ```
  </Tab>
</Tabs>

<Note>
  并非所有聊天模型集成都支持 `streaming` 参数。如果你的模型不支持它，请改用 `disable_streaming=True`。此参数可通过基类在所有聊天模型上使用。
</Note>

### 迁移到 v2

v2 流式格式（本页使用）提供了统一的输出格式。以下是主要差异和迁移方法的摘要：

| 场景                    | v1 (默认)                       | v2 (`version="v2"`)                         |
| --------------------- | ----------------------------- | ------------------------------------------- |
| 单流模式                  | 原始数据（字典）                      | 带有 `type`、`ns`、`data` 的 `StreamPart` 字典     |
| 多流模式                  | `(mode, data)` 元组             | 相同的 `StreamPart` 字典，按 `chunk["type"]` 过滤    |
| 子图流式传输                | `(namespace, data)` 元组        | 相同的 `StreamPart` 字典，检查 `chunk["ns"]`        |
| 多模式 + 子图              | `(namespace, mode, data)` 三元组 | 相同的 `StreamPart` 字典                         |
| `invoke()` 返回类型       | 普通字典（状态）                      | 带有 `.value` 和 `.interrupts` 的 `GraphOutput` |
| 中断位置（流）               | 状态字典中的 `__interrupt__` 键      | `values` 流部分上的 `interrupts` 字段              |
| 中断位置（invoke）          | 结果字典中的 `__interrupt__` 键      | `GraphOutput` 上的 `.interrupts` 属性           |
| Pydantic/dataclass 输出 | 返回普通字典                        | 强制转换为模型/数据类实例                               |

#### v2 invoke 格式

当你向 `invoke()` 或 `ainvoke()` 传递 `version="v2"` 时，它返回一个 [`GraphOutput`](https://reference.langchain.com/python/langgraph/types/GraphOutput) 对象，具有 `.value` 和 `.interrupts` 属性：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from langgraph.types import GraphOutput

result = graph.invoke(inputs, version="v2")

assert isinstance(result, GraphOutput)
result.value       # 你的输出 — 字典、Pydantic 模型或数据类
result.interrupts  # tuple[Interrupt, ...]，如果没有发生中断则为空
```

使用除默认 `"values"` 之外的任何流模式，`invoke(..., stream_mode="updates", version="v2")` 返回 `list[StreamPart]` 而不是 `list[tuple]`。

<Warning>
  `GraphOutput` 上的字典式访问（`result["key"]`、`"key" in result`、`result["__interrupt__"]`）仍然有效以保持向后兼容性，但已**弃用**，将在未来版本中移除。请迁移到 `result.value` 和 `result.interrupts`。
</Warning>

这将状态与中断元数据分离。使用 v1 时，中断嵌入在返回的字典中，位于 `__interrupt__` 下：

<CodeGroup>
  ```python v2 (new) theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  config = {"configurable": {"thread_id": "thread-1"}}
  result = graph.invoke(inputs, config=config, version="v2")

  if result.interrupts:
      print(result.interrupts[0].value)
      graph.invoke(Command(resume=True), config=config, version="v2")
  ```

  ```python v1 (current default) theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  config = {"configurable": {"thread_id": "thread-1"}}
  result = graph.invoke(inputs, config=config)

  if "__interrupt__" in result:
      print(result["__interrupt__"][0].value)
      graph.invoke(Command(resume=True), config=config)
  ```
</CodeGroup>

#### Pydantic 和 dataclass 状态强制转换

当你的图状态是 Pydantic 模型或 dataclass 时，v2 `values` 模式会自动将输出强制转换为正确的类型：

```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
from pydantic import BaseModel
from typing import Annotated
import operator

class MyState(BaseModel):
    value: str
    items: Annotated[list[str], operator.add]

# 使用 version="v2"，chunk["data"] 是一个 MyState 实例
for chunk in graph.stream(
    {"value": "x", "items": []}, stream_mode="values", version="v2"
):
    print(type(chunk["data"]))  # <class 'MyState'>
```

<a id="async" />

### Python \< 3.11 中的异步

在 Python 版本 \< 3.11 中，[asyncio 任务](https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task)不支持 `context` 参数。
这限制了 LangGraph 自动传播上下文的能力，并以两种关键方式影响 LangGraph 的流式机制：

1. 你**必须**显式地将 [`RunnableConfig`](https://python.langchain.com/docs/concepts/runnables/#runnableconfig) 传递给异步 LLM 调用（例如 `ainvoke()`），因为回调不会自动传播。
2. 你**不能**在异步节点或工具中使用 [`get_stream_writer`](https://reference.langchain.com/python/langgraph/config/get_stream_writer)——你必须直接传递 `writer` 参数。

<Accordion title="扩展示例：带手动配置的异步 LLM 调用">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from typing import TypedDict
  from langgraph.graph import START, StateGraph
  from langchain.chat_models import init_chat_model

  model = init_chat_model(model="gpt-5.4-mini")

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

  # 在异步节点函数中接受 config 作为参数
  async def call_model(state, config):
      topic = state["topic"]
      print("Generating joke...")
      # 将 config 传递给 model.ainvoke() 以确保正确的上下文传播
      joke_response = await model.ainvoke(  # [!code highlight]
          [{"role": "user", "content": f"Write a joke about {topic}"}],
          config,
      )
      return {"joke": joke_response.content}

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

  # 设置 stream_mode="messages" 以流式传输 LLM tokens
  async for chunk in graph.astream(
      {"topic": "ice cream"},
      stream_mode="messages",  # [!code highlight]
      version="v2",  # [!code highlight]
  ):
      if chunk["type"] == "messages":
          message_chunk, metadata = chunk["data"]
          if message_chunk.content:
              print(message_chunk.content, end="|", flush=True)
  ```
</Accordion>

<Accordion title="扩展示例：使用流写入器的异步自定义流式传输">
  ```python theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  from typing import TypedDict
  from langgraph.types import StreamWriter

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

  # 在异步节点或工具的函数签名中添加 writer 作为参数
  # LangGraph 将自动将流写入器传递给函数
  async def generate_joke(state: State, writer: StreamWriter):  # [!code highlight]
        writer({"custom_key": "Streaming custom data while generating a joke"})
        return {"joke": f"This is a joke about {state['topic']}"}

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

  # 设置 stream_mode="custom" 以在流中接收自定义数据  # [!code highlight]
  async for chunk in graph.astream(
        {"topic": "ice cream"},
        stream_mode="custom",
        version="v2",
  ):
        if chunk["type"] == "custom":
            print(chunk["data"])
  ```
</Accordion>

***

<div className="source-links">
  <Callout icon="terminal-2">
    [将这些文档连接](/use-these-docs)到 Claude、VSCode 等，通过 MCP 获取实时答案。
  </Callout>

  <Callout icon="edit">
    [在 GitHub 上编辑此页面](https://github.com/langchain-ai/docs/edit/main/src/oss/langgraph/streaming.mdx) 或 [提交问题](https://github.com/langchain-ai/docs/issues/new/choose)。
  </Callout>
</div>
