本文介绍LangChain支持的4中流式输出方式。流式输出让应用能够在 LLM 生成完整响应之前,就实时地将结果逐步返回给用户。这对于提升用户体验至关重要。LangChain 支持的流式模式:
| 模式 | 参数值 | 输出内容 | 使用场景 |
|---|---|---|---|
| Agent Progress | stream_mode="updates" |
每个 agent step 后的状态更新 | 监控执行流程、调试 |
| LLM Tokens | stream_mode="messages" |
LLM 生成的每个 token(含元数据) | 实时显示文本、UI渲染 |
| Custom Updates | stream_mode="custom" |
用户自定义的任意数据 | 显示进度、工具执行状态 |
| 混合模式 | stream_mode=["updates", "custom"] |
元组 (mode, chunk) |
需要多种流式数据 |
要想激活流式输出,请将invoke改成stream 或 astream,并指定stream_mode参数。
一、Agent Progress 模式
将stream方法里面传入stream_mode="updates",来开启Agent Progress模式。
python
from langchain.agents import create_agent
from dotenv import load_dotenv
# 加载 .env 文件中的环境变量(如 API Key),override=True 确保覆盖已有的环境变量
load_dotenv(override=True)
# 定义一个普通的 Python 函数作为工具
def get_weather(city: str) -> str:
"""获取所给城市的天气"""
return f"{city}现在是晴天"
# 创建 Agent 实例
agent = create_agent(
model="deepseek-chat", # 指定使用的模型,这里是 deepseek-chat
tools=[get_weather] # 将函数作为工具列表传入。
# LangChain 很智能,即使没有使用 @tool 装饰器,
# 它也能自动识别该函数的类型注解和文档字符串,将其封装为 Agent 可用的工具。
)
# --- 演示 1:打印原始的流式输出块 ---
print("--- 原始流式数据 ---")
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "长沙今天天气如何"}]}, # 输入用户消息
stream_mode="updates" # 指定流模式为 "updates",意味着只返回有更新的状态块
):
print(chunk) # 打印原始 chunk。你会看到每一行都是一个字典,包含 Agent 的中间步骤(如思考、调用工具)或最终回复。
# --- 演示 2:解析并美化输出 ---
print("\n--- 解析后的内容 ---")
for chunk in agent.stream(
{"messages": [{"role": "user", "content": "长沙今天天气如何"}]},
stream_mode="updates"
):
# 遍历 chunk 字典中的键值对(通常是 Agent 的名称或步骤名称)
for k, v in chunk.items():
print(f'当前{k}输出:')
# v['messages'] 是该步骤产生的消息列表
# 我们取列表中最后一条消息 (-1) 的内容,这在 Agent 思考或回复时通常是最关键的部分
print(f'{v["messages"][-1].content}')
二、LLM tokens模式
将stream方法里面传入stream_mode="messages",来开启LLM tokens模式,实现打字机效果。
python
from langchain.agents import create_agent
# 定义一个普通的 Python 函数作为工具
def get_weather(city: str) -> str:
"""获取所给城市的天气"""
# 这里模拟了返回结果
return f"{city}现在是晴天"
# 创建 Agent 实例
agent = create_agent(
model="deepseek-chat",
tools=[get_weather], # 当一个不使用 tool 装饰器的函数传入的时候也可以被当做工具
)
# 使用流式输出,stream_mode="messages" 会逐步生成消息对象
for token, metadata in agent.stream(
{"messages": [{"role": "user", "content": "长沙今天天气如何"}]},
stream_mode="messages",
):
# metadata['langgraph_node'] 显示当前是由哪个节点产生的输出(如 agent 或 tools)
print(f"node: {metadata['langgraph_node']}")
# token.content_blocks 包含当前消息的具体内容块
print(f"content: {token.content_blocks}")
print("\n")
三、Custom Updates 模式
自定义模式的最佳使用场景就是将工具的输出内容也变成流式输出。
- 在stream或astream方法里面传入 sream_mode="custom"
- 使用 get_stream_writer() 从工具内部发送自定义更新
python
from langgraph.config import get_stream_writer
python
def get_weather(city: str) -> str:
""" 获取指定城市的天气信息 """
writer = get_stream_writer() # 流式写入器
# 自定义进度更新
writer("正在访问网络"*20) # 无论我加多长,这里面的内容并不会一个个蹦出来(无流式效果)
writer(f"正在搜索{city}天气"*20) # get_stream_writer是实现不同的writer之间的间隔停顿
result = f"{city}现在是晴天"
writer(result) # 但是可以通过这种方式来输出,或者使用混合模式
return result # return的结果是不会被输出的
agent_with_custom = create_agent(
model='deepseek-chat',
tools=[get_weather]
)
for chunk in agent_with_custom.stream(
input={"messages": [{"role": "user", "content": "长沙今天天气如何"}]},
stream_mode="custom"
):
print(chunk)
【注】 如果在工具中添加 get_stream_writer(),该工具将无法在 LangGraph 执行上下文之外被调用。
python
get_weather({"city": "长沙"}) # 报错
四、混合模式
可以同时使用多种流式模式,接收 (mode, chunk) 元组,传入一个列表stearm_mode=[]
python
# --- 循环 1:直接打印原始输出,用于观察数据结构 ---
for chunk in agent_with_custom.stream(
input={"messages": [{"role": "user", "content": "长沙今天天气如何"}]},
# stream_mode 是一个列表,表示我们希望同时获取两种流数据:
# "updates": 标准的节点更新数据(包含 Agent 的思考和回复)
# "custom": 开发者自定义的输出数据(通常用于传递特定的 Token 计数、调试信息等)
stream_mode=["updates", "custom"]
):
# 打印原始 chunk 观察
# 输出格式是一个元组,第一个元素是模式名称(如 "updates" 或 "custom")
# 第二个元素是该模式下的具体数据内容
print(chunk)
print("\n--- 解析并分类输出 ---\n")
# --- 循环 2:根据模式类型处理数据 ---
for chunk in agent_with_custom.stream(
input={"messages": [{"role": "user", "content": "长沙今天天气如何"}]},
stream_mode=["updates", "custom"]
):
# chunk 是一个元组:(mode_name, mode_data)
# 判断当前 chunk 属于哪种模式
if chunk[0] == "custom":
# 如果是 "custom" 模式,直接打印自定义数据(chunk[1])
print(chunk[1])
else:
# 如果是 "updates" 模式(chunk[0] == "updates"),处理标准节点更新数据
# chunk[1] 是一个字典,键是节点名称,值是节点数据
for k, v in chunk[1].items():
# 提取该节点最后一条消息的内容并打印,通常包含 Agent 的思考或最终回复
print(v["messages"][-1].content)
如何让 Agent 同时以多种模式(这里是 updates 和 custom)进行流式输出。这在需要同时监控 Agent 的标准思考过程(updates)和获取自定义中间数据(custom)时非常有用。