本页面介绍了 LangGraph 的流式输出模式 API。它通过以下流式输出模式公开图执行: updates, values, messages, custom, checkpoints, tasks和 debug。当您需要直接访问图运行时事件或特定的流式输出模式输出时使用它。
快速开始
基本用法
LangGraph 图公开了 stream(同步)和 astream(异步)方法以迭代器形式产生流式输出。传递一个或多个 流式输出模式 来控制您接收的数据。
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']}")
Status: thinking of a joke...
Node generate_joke updated: {'joke': 'Why did the ice cream go to school? To get a sundae education!'}
Full example
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']}")
Status: thinking of a joke...
Node generate_joke updated: {'joke': 'Why did the ice cream go to school? To get a sundae education!'}
流式输出格式(v2)
传递 version="v2" to stream() or astream() 以获取统一的输出格式。每个数据块都是一个 StreamPart dict,具有一致的形状——无论流式输出模式、模式数量或子图设置如何:
{
"type": "values" | "updates" | "messages" | "custom" | "checkpoints" | "tasks" | "debug",
"ns": (), # namespace tuple, populated for subgraph events
"data": ..., # the actual payload (type varies by stream mode)
}
每个流式输出模式都有对应的 TypedDict 包含 ValuesStreamPart, UpdatesStreamPart, MessagesStreamPart, CustomStreamPart, CheckpointStreamPart, TasksStreamPart, DebugStreamPart。您可以从 langgraph.types导入这些类型。联合类型 StreamPart 是在 part["type"]上的不相交联合,支持编辑器和类型检查器的完整类型收窄。
使用 v1(默认),输出格式会根据您的流式输出选项而变化(单模式返回原始数据,多模式返回 (mode, data) 元组,子图返回 (namespace, data) 元组)。使用 v2,格式始终相同:
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"}}
for chunk in graph.stream(inputs, stream_mode="updates"):
print(chunk) # {"node_name": {"key": "value"}}
v2 格式还支持类型收窄,这意味着您可以按 chunk["type"] 过滤数据块并获得正确的载荷类型。每个分支将 part["data"] 收窄到该模式对应的特定类型:
for part in graph.stream(
{"topic": "ice cream"},
stream_mode=["values", "updates", "messages", "custom"],
version="v2",
):
if part["type"] == "values":
# ValuesStreamPart — full state snapshot after each step
print(f"State: topic={part['data']['topic']}")
elif part["type"] == "updates":
# UpdatesStreamPart — only the changed keys from each node
for node_name, state in part["data"].items():
print(f"Node `{node_name}` updated: {state}")
elif part["type"] == "messages":
# MessagesStreamPart — (message_chunk, metadata) from LLM calls
msg, metadata = part["data"]
print(msg.content, end="", flush=True)
elif part["type"] == "custom":
# CustomStreamPart — arbitrary data from get_stream_writer()
print(f"Progress: {part['data']['progress']}%")
流式输出模式
将以下一个或多个流式输出模式作为列表传递给 stream 或 astream 方法:
| 模式 | 类型 | 描述 |
|---|---|---|
| values | ValuesStreamPart | 每步后的完整状态。 |
| updates | UpdatesStreamPart | 每步后的状态更新。同一步骤中的多个更新会单独流式输出。 |
| messages | MessagesStreamPart | LLM调用的2元组(LLM token,元数据)。 |
| custom | CustomStreamPart | 通过 get_stream_writer. |
| checkpoints | CheckpointStreamPart | 检查点事件(格式与 get_state()相同)。需要检查pointer。 |
| tasks | TasksStreamPart | Task start/finish events with results and errors. Requires a checkpointer. |
| debug | DebugStreamPart | 所有可用信息——结合 checkpoints 和 tasks 并附带额外元数据。 |
图形状态
使用流模式 updates 和 values 来在图形执行时流式传输其状态。
- *
updates流式传输 **updates** 到图形每步之后的状态。 - *
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()
)
updates
使用此选项仅流式传输 **state updates** ,即每步后节点返回的状态更新。流式输出包括节点名称和更新内容。
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}")
Node `refine_topic` updated: {'topic': 'ice cream and cats'}
Node `generate_joke` updated: {'joke': 'This is a joke about ice cream and cats'}
values
使用此选项流式传输 **完整状态** ,即图形每步之后的状态。
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']}")
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
LLM tokens
使用 messages 流式模式来流式传输大语言模型(LLM)输出 **逐token** ,包括图形中的节点、工具、子图形或任务。
从 messages mode 模式流式传输的输出是一个元组 (message_chunk, metadata) where:
- *
message_chunk:LLM的token或消息段。 - *
metadata:包含图形节点和LLM调用详细信息的字典。
> 如果你的LLM没有作为LangChain集成,你可以通过使用 custom mode来流式传输其输出。另请参见 与任何 LLM 配合使用 查看详情。
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):
"""Call the LLM to generate a joke about a topic"""
# Note that message events are emitted even when the LLM is run using .invoke rather than .stream
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()
)
# The "messages" stream mode streams LLM tokens with metadata
# Use version="v2" for a unified StreamPart format
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。
from langchain.chat_models import init_chat_model
# model_1 is tagged with "joke"
model_1 = init_chat_model(model="gpt-5.4-mini", tags=['joke'])
# model_2 is tagged with "poem"
model_2 = init_chat_model(model="gpt-5.4-mini", tags=['poem'])
graph = ... # define a graph that uses these LLMs
# The stream_mode is set to "messages" to stream LLM tokens
# The metadata contains information about the LLM invocation, including the tags
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"]
# Filter the streamed tokens by the tags field in the metadata to only include
# the tokens from the LLM invocation with the "joke" tag
if metadata["tags"] == ["joke"]:
print(msg.content, end="|", flush=True)
Extended example: filtering by tags
from typing import TypedDict
from langchain.chat_models import init_chat_model
from langgraph.graph import START, StateGraph
# The joke_model is tagged with "joke"
joke_model = init_chat_model(model="gpt-5.4-mini", tags=["joke"])
# The poem_model is tagged with "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...")
# Note: Passing the config through explicitly is required for python < 3.11
# Since context var support wasn't added before then: https://docs.python.org/3/library/asyncio-task.html#creating-tasks
# The config is passed through explicitly to ensure the context vars are propagated correctly
# This is required for Python < 3.11 when using async code. Please see the async section for more details
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()
)
# The stream_mode is set to "messages" to stream LLM tokens
# The metadata contains information about the LLM invocation, including the tags
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)
从流中省略消息
使用 nostream 标签可完全从流中排除 LLM 输出。使用标记的调用 nostream 仍会运行并产生输出;其 token 不会在 messages mode.
这在以下情况下很有用:
- - 你需要 LLM 输出用于内部处理(例如结构化输出),但不想将其流式传输到客户端
- - 你通过其他通道(例如自定义 UI 消息)流式传输相同内容,并希望避免在
messages流中重复输出
按节点筛选
要仅从特定节点流式传输 token,请使用 stream_mode="messages" 并按流式元数据中的 langgraph_node 字段筛选输出:
# The "messages" stream mode streams LLM tokens with metadata
# Use version="v2" for a unified StreamPart format
for chunk in graph.stream(
inputs,
stream_mode="messages", # [!code highlight]
version="v2", # [!code highlight]
):
if chunk["type"] == "messages":
msg, metadata = chunk["data"]
# Filter the streamed tokens by the langgraph_node field in the metadata
# to only include the tokens from the specified node
if msg.content and metadata["langgraph_node"] == "some_node_name":
...
Extended example: streaming LLM tokens from specific nodes
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)
# write both the joke and the poem concurrently
.add_edge(START, "write_joke")
.add_edge(START, "write_poem")
.compile()
)
# The "messages" stream mode streams LLM tokens with metadata
# Use version="v2" for a unified StreamPart format
for chunk in graph.stream(
{"topic": "cats"},
stream_mode="messages", # [!code highlight]
version="v2", # [!code highlight]
):
if chunk["type"] == "messages":
msg, metadata = chunk["data"]
# Filter the streamed tokens by the langgraph_node field in the metadata
# to only include the tokens from the write_poem node
if msg.content and metadata["langgraph_node"] == "write_poem":
print(msg.content, end="|", flush=True)
自定义数据
要发送 **自定义用户数据** 从 LangGraph 节点或工具内部发送,请按照以下步骤操作:
- 使用 @[
get_stream_writer访问流写入器并发出自定义数据。 - 设置
stream_mode="custom"调用时.stream()or.astream()以在流中获取自定义数据。你可以组合多种模式(例如,["updates", "custom"]),但至少一个必须为"custom".
node
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):
# Get the stream writer to send custom data
writer = get_stream_writer()
# Emit a custom key-value pair (e.g., progress update)
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"}
# Set stream_mode="custom" to receive the custom data in the stream
for chunk in graph.stream(inputs, stream_mode="custom", version="v2"):
if chunk["type"] == "custom":
print(f"Custom event: {chunk['data']['custom_key']}")
tool
from langchain.tools import tool
from langgraph.config import get_stream_writer
@tool
def query_database(query: str) -> str:
"""Query the database."""
# Access the stream writer to send custom data
writer = get_stream_writer() # [!code highlight]
# Emit a custom key-value pair (e.g., progress update)
writer({"data": "Retrieved 0/100 records", "type": "progress"}) # [!code highlight]
# perform query
# Emit another custom key-value pair
writer({"data": "Retrieved 100/100 records", "type": "progress"})
return "some-answer"
graph = ... # define a graph that uses this tool
# Set stream_mode="custom" to receive the custom data in the stream
for chunk in graph.stream(inputs, stream_mode="custom", version="v2"):
if chunk["type"] == "custom":
print(f"{chunk['data']['type']}: {chunk['data']['data']}")
子图输出
要包含来自 子图 的输出到流式输出中,可以设置 subgraphs=True 在 .stream() 方法的父图中。这将从父图和任何子图流式传输输出。
输出将作为元组流式传输 (namespace, data),其中 namespace 是一个包含调用子图的节点路径的元组,例如 ("parent_node:<task_id>", "child_node:<task_id>").
<section class="mdx-block">= 1.1)"> 使用 version="v2",子图事件使用相同的 StreamPart 格式。字段 ns 标识来源:
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"]) # () for root, ("node_name:<task_id>",) for subgraph
print(chunk["data"]) # {"node_name": {"key": "value"}}
v1 (default)
for chunk in graph.stream(
{"foo": "foo"},
# Set subgraphs=True to stream outputs from subgraphs
subgraphs=True, # [!code highlight]
stream_mode="updates",
):
print(chunk)
Extended example: streaming from subgraphs
from langgraph.graph import START, StateGraph
from typing import TypedDict
# Define subgraph
class SubgraphState(TypedDict):
foo: str # note that this key is shared with the parent graph state
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()
# Define parent graph
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",
# Set subgraphs=True to stream outputs from subgraphs
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'}}
注意 我们不仅接收节点更新,还接收命名空间,这些命名空间告诉我们正在从哪个图(或子图)流式传输。
检查点
使用 checkpoints 流式传输模式可在图执行时接收检查点事件。每个检查点事件的格式与 get_state()的输出格式相同。需要 检查点.
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 流式传输模式可在图执行时接收任务开始和完成事件。任务事件包括正在运行的节点、其结果及任何错误的相关信息。需要 检查点.
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"></a> ### 调试
使用 debug 流式传输模式可在图执行过程中尽可能多地流式传输信息。流式输出包括节点名称和完整状态。
for chunk in graph.stream(
{"topic": "ice cream"},
stream_mode="debug", # [!code highlight]
version="v2", # [!code highlight]
):
if chunk["type"] == "debug":
print(chunk["data"])
同时使用多个模式
您可以将列表作为 stream_mode 参数传递以同时流式传输多个模式。
使用 version="v2",每个数据块都是一个 StreamPart 字典。使用 chunk["type"] 区分不同模式:
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']}")
for mode, chunk in graph.stream(inputs, stream_mode=["updates", "custom"]):
print(chunk)
高级
适用于任何 LLM
您可以使用 stream_mode="custom" 从 **任何 LLM API**流式传输数据——即使该 API **没有** 实现 LangChain 聊天模型接口。
这使您可以集成原始 LLM 客户端或提供自己流式接口的外部服务,使 LangGraph 在自定义设置中具有极高的灵活性。
from langgraph.config import get_stream_writer
def call_arbitrary_model(state):
"""Example node that calls an arbitrary model and streams the output"""
# Get the stream writer to send custom data
writer = get_stream_writer() # [!code highlight]
# Assume you have a streaming client that yields chunks
# Generate LLM tokens using your custom streaming client
for chunk in your_custom_streaming_client(state["topic"]):
# Use the writer to send custom data to the stream
writer({"custom_llm_chunk": chunk}) # [!code highlight]
return {"result": "completed"}
graph = (
StateGraph(State)
.add_node(call_arbitrary_model)
# Add other nodes and edges as needed
.compile()
)
# Set stream_mode="custom" to receive the custom data in the stream
for chunk in graph.stream(
{"topic": "cats"},
stream_mode="custom", # [!code highlight]
version="v2", # [!code highlight]
):
if chunk["type"] == "custom":
# The chunk data will contain the custom data streamed from the llm
print(chunk["data"])
Extended example: streaming arbitrary chat model
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}
# this is our tool
async def get_items(place: str) -> str:
"""Use this tool to list items one might find in a place you're asked about."""
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]
# this is the tool-calling graph node
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 来调用图:
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)
为特定聊天模型禁用流式传输
如果您的应用程序混合使用支持流式传输和不支持流式传输的模型,您可能需要为 不支持流式传输的模型显式禁用流式传输。
在初始化模型时设置 streaming=False 。
init_chat_model
from langchain.chat_models import init_chat_model
model = init_chat_model(
"claude-sonnet-4-6",
# Set streaming=False to disable streaming for the chat model
streaming=False # [!code highlight]
)
Chat model interface
from langchain_openai import ChatOpenAI
# Set streaming=False to disable streaming for the chat model
model = ChatOpenAI(model="o1-preview", streaming=False)
迁移到 v2
v2 流式传输格式(贯穿本页使用)提供统一的输出格式。以下是关键差异和迁移方法的总结:
| 场景 | v1(默认) | v2(version="v2") |
|---|---|---|
| 单一流式模式 | 原始数据(dict) | StreamPart 带有 type, ns, data |
| 多个流式模式 | (mode, data) 元组 | 相同 StreamPart dict,按 chunk["type"] |
| 子图流式传输 | (namespace, data) 元组 | 相同 StreamPart dict,检查 chunk["ns"] |
| 多模式 + 子图 | (namespace, mode, data) 三元组 | 相同 StreamPart 字典 |
invoke() 返回类型 | 普通字典(状态) | GraphOutput 使用 .value 和 .interrupts |
| 中断位置(流) | __interrupt__ 状态字典中的键 | interrupts 上的字段 values 流部分 |
| 中断位置(调用) | __interrupt__ 结果字典中的键 | .interrupts 上的属性 GraphOutput |
| Pydantic/dataclass output | Returns plain dict | Coerces to model/dataclass instance |
v2 调用格式
当您传递 version="v2" to invoke() or ainvoke()时,它返回一个 GraphOutput 对象,包含 .value 和 .interrupts attributes:
from langgraph.types import GraphOutput
result = graph.invoke(inputs, version="v2")
assert isinstance(result, GraphOutput)
result.value # your output — dict, Pydantic model, or dataclass
result.interrupts # tuple[Interrupt, ...], empty if none occurred
使用任何非默认的流模式 "values", invoke(..., stream_mode="updates", version="v2") 返回 list[StreamPart] 而不是 list[tuple].
这将状态与中断元数据分离。在 v1 中,中断嵌入在返回的字典中的 __interrupt__:
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")
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)
Pydantic 和数据类的状态强制转换
当您的图状态是 Pydantic 模型或数据类时,v2 values 模式自动将输出强制转换为正确的类型:
from pydantic import BaseModel
from typing import Annotated
class MyState(BaseModel):
value: str
items: Annotated[list[str], operator.add]
# With version="v2", chunk["data"] is a MyState instance
for chunk in graph.stream(
{"value": "x", "items": []}, stream_mode="values", version="v2"
):
print(type(chunk["data"])) # <class 'MyState'>
<a id="async"></a> ### Python < 3.11 的异步
在 Python 版本 < 3.11 中, asyncio 任务 不支持 context parameter. 这限制了 LangGraph 自动传播上下文的能力,并从两个关键方面影响 LangGraph 的流式机制:
- 您 **必须** 显式传递
RunnableConfig到异步 LLM 调用中(例如,ainvoke()),因为回调不会自动传播。 - 您 **不能** 在异步节点或工具中使用
get_stream_writer——您必须直接传递一个writer参数。
Extended example: async LLM call with manual config
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
# Accept config as an argument in the async node function
async def call_model(state, config):
topic = state["topic"]
print("Generating joke...")
# Pass config to model.ainvoke() to ensure proper context propagation
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()
)
# Set stream_mode="messages" to stream 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)
Extended example: async custom streaming with stream writer
from typing import TypedDict
from langgraph.types import StreamWriter
class State(TypedDict):
topic: str
joke: str
# Add writer as an argument in the function signature of the async node or tool
# LangGraph will automatically pass the stream writer to the function
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()
)
# Set stream_mode="custom" to receive the custom data in the stream # [!code highlight]
async for chunk in graph.astream(
{"topic": "ice cream"},
stream_mode="custom",
version="v2",
):
if chunk["type"] == "custom":
print(chunk["data"])