05:流式输出
在上一篇文章中,我们详细介绍了什么是持久化执行,以及在LangGraph中有哪些持久化执行模式。本文将主要介绍LangGraph的流式输出,以及流式输出有哪些模式,它们是如何使用的。
一、什么是流式输出
流式输出是指程序在还没有生成完整结果之前,就把已经生成的部分结果,持续、逐步地输出给用户的一种方式。
打个比方,我们编辑微信消息发给朋友,都是先编辑好完整的消息内容,再一起发送给对方,流式输出则是每编辑好一个字就发送给对方一个字,最终将这些信息合并在一起就是完整的消息内容,我们平常使用的ChatGPT、DeepSeek等聊天机器人也是采用流式输出进行问题回复,这样能使用户减少等待的时间,提升用户体验。
在LangGraph中也支持流式输出,在LangGraph中,图经过编译之后返回的是一个可运行组件Runnable,Runnable可运行组件接口支持invoke/ainvoke/stream/astream等方法,其中,stream方法表示同步流式输出,astream表示异步流式输出。
二、LangGraph流式输出示例
下方示例是一个使用LangGraph流式输出的基础示例,我们构建了一个关于A公司信息回复的Agent,在图中有三个节点,其中节点retrieval_a_node和节点retrieval_b_node为模拟根据用户query进行知识库检索,将检索到的数据保存到State中的retrieval_result中。

执行完知识库检索节点后,继续执行llm节点,将知识库检索内容格式化到系统提示词中,并构建一个系统消息,将消息列表传递给LLM,返回AI消息,本文后续讲解其他知识点时,也会以这个Agent为基础。
我们在调用图时,使用stream()方法接收流式输出内容。
import operator
from typing import TypedDict, Annotated
import dotenv
from langchain.chat_models import init_chat_model
from langchain_core.messages import AnyMessage, HumanMessage, SystemMessage
from langgraph.constants import END, START
from langgraph.graph import StateGraph
dotenv.load_dotenv()
# 1.定义llm和工具
llm = init_chat_model(
"gpt-4o-mini",
temperature=0
)
# 2.定义State
class MessagesState(TypedDict):
# 图运行的消息列表
messages: Annotated[list[AnyMessage], operator.add]
query: str
retrieval_result: Annotated[list[str], operator.add]
# 3.定义检索节点a
def retrieval_a_node(state: MessagesState):
"""调用数据检索a节点"""
# todo:模拟根据query进行数据检索
return {
"retrieval_result": [
"A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平”。" +
"A公司致力于通过物联网技术连接家庭设备,实现智能控制与能源优化,为用户提供舒适、安全、节能的家居体验。"]
}
# 4.定义检索节点b
def retrieval_b_node(state: MessagesState):
"""调用数据检索b节点"""
# todo:模拟根据query进行数据检索
return {
"retrieval_result": [
"""
主要业务与产品:
- 智能家居控制系统:支持语音、手机APP远程控制家电和照明设备。
- 家庭安全监控设备:智能摄像头、门窗传感器及报警系统。
- 能源管理平台:智能插座、智能照明及家电能耗分析。
- 定制化物联网解决方案:为房地产、酒店等提供智能化方案。
核心竞争力:
- 自主研发的AI算法与物联网通信技术。
- 完整的软硬件生态系统。
- 强大的客户服务与售后体系。
- 多项智能家居专利技术。
发展历程:
- 2015年:公司成立,初期聚焦智能插座和照明设备。
- 2016年:推出首款智能家居控制系统,获得天使轮融资。
- 2018年:完成B轮融资,引入战略投资者。
- 2020年:产品线扩展至家庭安防及能源管理领域。
- 2022年:进入海外市场,主要面向东南亚和欧洲。
- 2023年:用户数量突破100万,市值估计达5亿美元。
"""
]
}
# 5.定义llm节点
def llm_node(state: MessagesState):
"""调用LLM节点"""
system_prompt = """
你是一个公司内部的智能助手,只能回答跟公司A有关的问题,你可以根据知识库检索信息进行问题的回答,不能凭空捏造答案
检索到的知识库信息如下:{retrieval_info}
"""
retrieval_info = ""
for retrieval_item in state["retrieval_result"]:
retrieval_info += retrieval_item + "\n"
system_message = SystemMessage(content=system_prompt.format(retrieval_info=retrieval_info))
ai_message = llm.invoke([system_message, *state["messages"]])
return {"messages": [ai_message]}
# 7.构建图
graph = StateGraph(MessagesState)
graph.add_node("llm", llm_node)
graph.add_node("retrieval_a", retrieval_a_node)
graph.add_node("retrieval_b", retrieval_b_node)
graph.add_edge(START, "retrieval_a")
graph.add_edge("retrieval_a", "retrieval_b")
graph.add_edge("retrieval_b", "llm")
graph.add_edge("llm", END)
# 8.编译并运行图
agent = graph.compile()
query = "你好,A公司是一家有实力的公司吗?"
for chunk in agent.stream({"messages": [HumanMessage(content=query)],
"query": query}):
print(chunk)输出结果如下,stream输出的结果逐步输出的而不是一起输出的,这就是流式输出的特点,并且输出的内容是State更新的内容,并且输出是由哪个节点进行更新的,使用stream方法可以指定不同的流式输出模式。
{'retrieval_a': {'retrieval_result': ['A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平”。A公司致力于通过物联网技术连接家庭设备,实现智能控制与能源优化,为用户提供舒适、安全、节能的家居体验。']}}
{'retrieval_b': {'retrieval_result': ['\n 主要业务与产品:\n - 智能家居控制系统:支持语音、手机APP远程控制家电和照明设备。\n更多内容省略...']}}
{'llm': {'messages': [AIMessage(content='你好!从现有资料来看,A公司确实是一家有实力的公司,理由如下: \n\n1. **技术实力强** \n - 公司自主研发AI算法和物联网通信技术,拥有多项智能家居专利,这说明在技术上具有核心竞争力。 \n - 产品覆盖智能家居控制、家庭安全监控和能源管理等多个领域,形成了完整的软硬件生态系统。 \n\n2. **市场和业务拓展稳健** \n - 自2015年成立以来,业务从智能插座和照明逐步扩展到安防、能源管理及定制化物联网解决方案。 \n - 2022年进入海外市场,面向东南亚和欧洲,显示公司有国际化发展能力。 \n\n3. **融资和财务状况** \n - 公司经历了天使轮到B轮融资,并引入战略投资者,说明投资者对其商业模式和发展前景认可。 \n - 2023年用户数量突破100万,市值估计达5亿美元,这在智能家居行业中属于较有影响力的规模。 \n\n4. **客户服务和生态优势** \n - 完整的客户服务与售后体系,加上自主研发的技术,使得公司在市场竞争中有较强的抗风险能力。 \n\n总结来看,A公司在技术研发、市场拓展、融资能力和客户服务方面都显示出了较强实力,是一家值得认可的高科技智能家居企业。 \n\n如果你需要,我可以帮你把A公司的实力做一个**简明的竞争力总结表**,一目了然地看到优势和核心能力。你希望我做吗?', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 370, 'prompt_tokens': 438, 'total_tokens': 808, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini', 'system_fingerprint': 'fp_c9aa9c0491', 'id': 'chatcmpl-PHNerBMlFvOnHEC0z9cjhZrX2Imuw', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--24c8878c-d2c7-4ae0-8782-088c11c912ee-0', usage_metadata={'input_tokens': 438, 'output_tokens': 370, 'total_tokens': 808, 'input_token_details': {}, 'output_token_details': {}})]}}三、 流式输出模式
LangGraph根据stream/astream返回输出内容的不同,将流式输出分为五种模式,其中默认的模式是updates。
values:在图执行完每个节点之后,返回完整的图状态数据updates:在图执行完每一个节点后,只返回图状态数据中更新的字段的完整数据custom:从图中的节点内部传递流式传输自定义数据messages:LangGraph对图中所有调用大语言模型(LLM)的节点进行监听,并将模型在生成过程中产生的内容按顺序实时输出。debug:在图执行过程中,尽可能的输出更多信息,一般用于调试。
3.1 values模式
在values模式下,每一个节点执行完,无论这个节点是否更新图状态数据,都会返回完整的图状态信息,这种流式输出模式返回数据是最全的,不容易遗漏信息,但这种模式的缺点是随着节点的执行state返回的数据量就会越来越大,而且也不容易找到到底哪个字段发生了更新。
代码示例如下:
query = "你好,A公司是一家有实力的公司吗?"
for chunk in agent.stream({"messages": [HumanMessage(content=query)],
"query": query}, stream_mode="values"):
print(chunk)执行结果如下,每一次返回都返回了整个图状态数据的完整信息。
{'messages': [HumanMessage(content='你好,A公司是一家有实力的公司吗?', additional_kwargs={}, response_metadata={})], 'query': '你好,A公司是一家有实力的公司吗?', 'retrieval_result': []}
{'messages': [HumanMessage(content='你好,A公司是一家有实力的公司吗?', additional_kwargs={}, response_metadata={})], 'query': '你好,A公司是一家有实力的公司吗?', 'retrieval_result': ['A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平”。A公司致力于通过物联网技术连接家庭设备,实现智能控制与能源优化,为用户提供舒适、安全、节能的家居体验。']}
{'messages': [HumanMessage(content='你好,A公司是一家有实力的公司吗?', additional_kwargs={}, response_metadata={})], 'query': '你好,A公司是一家有实力的公司吗?', 'retrieval_result': ['A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平“。省略...']}
{'messages': [HumanMessage(content='你好,A公司是一家有实力的公司吗?', additional_kwargs={}, response_metadata={}), AIMessage(content='你好!根据公司A的情况来看,它是一家具有较强实力的高科技企业,具体可以从几个方面分析: \n\n1. **技术实力**: \n - A公司拥有自主研发的AI算法和物联网通信技术,这是智能家居领域的核心竞争力。 \n - 拥有完整的软硬件生态系统和多项智能家居专利技术,说明在技术和产品创新上具有一定优势。 \n\n2. **产品与市场**:省略...', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 378, 'prompt_tokens': 438, 'total_tokens': 816, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini', 'system_fingerprint': 'fp_c9aa9c0491', 'id': 'chatcmpl-Z9hr5g1DiXTAztBVEd8XKOhyD4qiG', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--3a049eb6-1134-4d6e-85d7-4accc5571356-0', usage_metadata={'input_tokens': 438, 'output_tokens': 378, 'total_tokens': 816, 'input_token_details': {}, 'output_token_details': {}})], 'query': '你好,A公司是一家有实力的公司吗?', 'retrieval_result': ['A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平”。省略...']}3.2 updates模式
在updates模式下,只有在图状态数据发生变化时才会输出数据,如果某个节点没有更新任何状态(即返回空字典 {}),则 stream() 不会为该节点输出任何信息。并且,在updates模式下,只有发生更改的字段才会被输出,不会完整将整个图状态数据都输出,这也是stream方法的默认流式输出模式。
# updates模式
for chunk in agent.stream({"messages": [HumanMessage(content=query)],
"query": query}, stream_mode="updates"):
print(chunk)执行结果如下:
{'retrieval_a': {'retrieval_result': ['A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平”。A公司致力于通过物联网技术连接家庭设备,实现智能控制与能源优化,为用户提供舒适、安全、节能的家居体验。']}}
{'retrieval_b': {'retrieval_result': ['\n 主要业务与产品:\n - 智能家居控制系统:支持语音、手机APP远程控制家电和照明设备。\n - 家庭安全监控设备:智能摄像头、门窗传感器及报警系统。\n - 能源管理平台:智能插座、智能照明及家电能耗分析。\n - 定制化物联网解决方案:为房地产、酒店等提供智能化方案,省略...']}}
{'llm': {'messages': [AIMessage(content='你好!根据公司A的资料来看,它是一家有实力的高科技企业,理由如下: \n\n1. **技术实力强** \n - 公司自主研发了AI算法和物联网通信技术,拥有多项智能家居专利,这说明在核心技术上有竞争力,省略...', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 339, 'prompt_tokens': 438, 'total_tokens': 777, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini', 'system_fingerprint': 'fp_c9aa9c0491', 'id': 'chatcmpl-HhgQSTj4ozM6Y2se0oEb2guUhMxsb', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--f33247d2-2749-4b8b-85d4-a9b54ccc5148-0', usage_metadata={'input_tokens': 438, 'output_tokens': 339, 'total_tokens': 777, 'input_token_details': {}, 'output_token_details': {}})]}}3.3 custom模式
在custom模式下,LangGraph不会流式输出任何内容,需要流式输出哪些数据,需要我们在节点内自行输出。
如下代码,将stream_mode改为custom,如果我们不在节点中自定义输出内容,这里的内容打印内容将会为空,因此我们对节点中的代码进行一下修改。
# custom模式
for chunk in agent.stream({"messages": [HumanMessage(content=query)],
"query": query}, stream_mode="custom"):
print(chunk)在retrieval_a_node和retrieval_b_node节点中,使用get_stream_writer()方法获取writer,使用writer传入retrieval_progress,表示数据检索的进度。
# 3.定义检索节点a
def retrieval_a_node(state: MessagesState):
"""调用数据检索a节点"""
# todo:模拟根据query进行数据检索
writer = get_stream_writer()
writer({"retrieval_progress": 50})
return {
"retrieval_result": [
"A公司(Company A)成立于2015年,总部位于上海,是一家专注于智能家居和物联网解决方案的高科技企业。公司使命是“让科技融入生活,提高家庭智能化水平”。" +
"A公司致力于通过物联网技术连接家庭设备,实现智能控制与能源优化,为用户提供舒适、安全、节能的家居体验。"]
}
# 4.定义检索节点b
def retrieval_b_node(state: MessagesState):
"""调用数据检索b节点"""
# todo:模拟根据query进行数据检索
writer = get_stream_writer()
writer({"retrieval_progress": 100})
return {
"retrieval_result": [
"""
主要业务与产品:
- 智能家居控制系统:支持语音、手机APP远程控制家电和照明设备。
- 家庭安全监控设备:智能摄像头、门窗传感器及报警系统。
- 能源管理平台:智能插座、智能照明及家电能耗分析。
- 定制化物联网解决方案:为房地产、酒店等提供智能化方案。
省略...
"""
]
}在llm节点中,同样使用writer流式输出LLM执行的最终结果。
# 5.定义llm节点
def llm_node(state: MessagesState):
"""调用LLM节点"""
system_prompt = """
你是一个公司内部的智能助手,只能回答跟公司A有关的问题,你可以根据知识库检索信息进行问题的回答,不能凭空捏造答案
检索到的知识库信息如下:{retrieval_info}
"""
retrieval_info = ""
for retrieval_item in state["retrieval_result"]:
retrieval_info += retrieval_item + "\n"
system_message = SystemMessage(content=system_prompt.format(retrieval_info=retrieval_info))
ai_message = llm.invoke([system_message, *state["messages"]])
writer = get_stream_writer()
writer({"llm_result": ai_message.content})
return {"messages": [ai_message]}执行结果如下,输出的内容非常简洁,除了我们自定义输出的内容外,不包含任何其他内容。
{'retrieval_progress': 50}
{'retrieval_progress': 100}
{'llm_result': '你好!根据公司A的资料来看,A公司确实是一家有实力的高科技企业,理由可以从几个方面说明: \n\n1. **技术实力** \n - 公司拥有自主研发的AI算法和物联网通信技术,这意味着核心技术不依赖外部,具备竞争力。 \n - 拥有多项智能家居专利技术,显示其在智能家居领域具有创新能力。 \n\n2. **产品与业务布局** \n - 产品线完整,包括智能家居控制系统、家庭安全监控、能源管理平台及定制化物联网解决方案。 \n - 能覆盖家庭、房地产、酒店等不同场景,市场应用广泛。 \n\n3. **发展历程与市场表现** \n - 成立8年来,逐步拓展产品线和市场,从智能插座和照明起步,到家庭安防和能源管理。 \n - 2022年已进入海外市场,用户数量突破100万,市值约5亿美元,说明商业模式和市场接受度良好。 \n\n4. **服务与生态** \n - 拥有完整的软硬件生态系统和强大的客户服务体系,能够提升用户体验和品牌忠诚度。 \n\n综合来看,A公司在技术、产品、市场和服务方面都有明显实力,是一家具有竞争力的智能家居高科技公司。 \n\n如果你愿意,我可以帮你分析一下**与同行相比,A公司的实力处于什么水平**,这样更直观地看出其竞争力。'}3.4 messages模式
在messages模式下,只要在节点内调用了LLM,就会将LLM输出的内容进行流式输出,并且会传递metadata元数据,如用于区分不同节点和不同LLM对象等元数据信息。
这里需要注意的一点是,messages模式只在使用LangChain框架调用LLM的情况下,才会对LLM输出内容进行流式输出,如果使用原生的API,则需要使用custom模式进行自定义的流式输出,才能实现这个功能。
# messages模式
for chunk in agent.stream({"messages": [HumanMessage(content=query)],
"query": query}, stream_mode="messages"):
print(chunk)执行结果如下:
(AIMessageChunk(content='', additional_kwargs={}, response_metadata={'model_provider': 'openai'}, id='lc_run--f273269d-da8a-40c7-80d1-43e326910b65'), {'langgraph_step': 3, 'langgraph_node': 'llm', 'langgraph_triggers': ('branch:to:llm',), 'langgraph_path': ('__pregel_pull', 'llm'), 'langgraph_checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.0})
(AIMessageChunk(content='你好!', additional_kwargs={}, response_metadata={'model_provider': 'openai'}, id='lc_run--f273269d-da8a-40c7-80d1-43e326910b65'), {'langgraph_step': 3, 'langgraph_node': 'llm', 'langgraph_triggers': ('branch:to:llm',), 'langgraph_path': ('__pregel_pull', 'llm'), 'langgraph_checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.0})
中间步骤省略....
(AIMessageChunk(content='对比,这样会更直观。', additional_kwargs={}, response_metadata={'model_provider': 'openai'}, id='lc_run--f273269d-da8a-40c7-80d1-43e326910b65'), {'langgraph_step': 3, 'langgraph_node': 'llm', 'langgraph_triggers': ('branch:to:llm',), 'langgraph_path': ('__pregel_pull', 'llm'), 'langgraph_checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.0})
(AIMessageChunk(content='你希望我做吗?', additional_kwargs={}, response_metadata={'finish_reason': 'stop', 'model_name': 'gpt-4o-mini', 'system_fingerprint': 'fp_c9aa9c0491', 'model_provider': 'openai'}, id='lc_run--f273269d-da8a-40c7-80d1-43e326910b65', chunk_position='last'), {'langgraph_step': 3, 'langgraph_node': 'llm', 'langgraph_triggers': ('branch:to:llm',), 'langgraph_path': ('__pregel_pull', 'llm'), 'langgraph_checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'checkpoint_ns': 'llm:17ce8936-246a-05aa-c2cd-8cb16273445d', 'ls_provider': 'openai', 'ls_model_name': 'gpt-4o-mini', 'ls_model_type': 'chat', 'ls_temperature': 0.0})3.5 debug模式
在 debug 模式下,在图执行过程中会尽可能多地输出信息。输出内容包括节点名称以及图状态数据,debug模式适合在调试时使用,不适合用于实际生产环境。
# debug模式
for chunk in agent.stream({"messages": [HumanMessage(content=query)],
"query": query}, stream_mode="debug"):
print(chunk)执行结果如下
{'step': 1, 'timestamp': '2025-12-27T16:04:30.900866+00:00', 'type': 'task', 'payload': {'id': '0c44d10d-6254-3b77-3bee-f4ea6f79ed1c', 'name': 'retrieval_a', 'input': {'messages': [HumanMessage(content='你好,A公司是一家有实力的公司吗?', additional_kwargs={}, response_metadata={})], 'query': '你好,A公司是一家有实力的公司吗?', 'retrieval_result': []}, 'triggers': ('branch:to:retrieval_a',)}}
省略部分内容......
{ additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 413, 'prompt_tokens': 438, 'total_tokens': 851, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini', 'system_fingerprint': 'fp_c9aa9c0491', 'id': 'chatcmpl-neVuoChdRv2CI1aWzTiAVYR040GnR', 'finish_reason': 'stop', 'logprobs': None}, id='lc_run--136a7c49-e356-4d97-a6c3-f5e0aab2cabf-0', usage_metadata={'input_tokens': 438, 'output_tokens': 413, 'total_tokens': 851, 'input_token_details': {}, 'output_token_details': {}})]}, 'interrupts': []}}四、总结
本文围绕 LangGraph 的流式输出机制 进行了系统讲解。
首先,从概念入手,解释了什么是流式输出,通过流式输出可以提升交互体验、减少等待时间。随后介绍了 LangGraph 中 stream / astream 的基本使用方式。
接着,通过一个完整的 Agent 示例,展示了 LangGraph 在真实执行过程中是如何逐步进行流式输出的,并重点讲解了 五种流式输出模式:
values:每一步返回完整数据状态,但是数据量最大updates:只输出发生变化的字段,是默认的流式输出模式,也是最常用的模式custom:由开发者在节点中主动控制输出内容,自定义程度非常高messages:专门用于监听 LLM 的生成过程,对LLM输出的token进行流式输出,但前提必须使用LangChain调用LLMdebug:输出尽可能多的执行细节,适合调试,不建议用于生产
通过对比可以看出,不同流式模式关注的重点不同,在实际使用中,可以根据不同场景选择合适的输出模式。