要补齐这块短板需要理解 LangGraph 流式机制的核心astream_events是理解这一切的关键。它提供的是事件流能让你监控图执行的每一步包括 LLM 的逐词输出、工具调用的开始与结束而不仅仅是最终结果。 核心概念澄清流式与工具调用并非互斥你之前的理解——“流式输出时不能调用工具”是一个常见的误区。实际上LangGraph 的流式机制会持续不断地产生事件其中包括模型输出的 Token 流和工具调用的生命周期事件。astream()/stream()以“超级步”Superstep为单位输出状态更新或变化你可以得到每个节点执行后的结果但无法看到模型生成时的中间 Token。astream_events()核心 API。它提供了最细粒度的控制可以流式输出所有事件包括on_chat_model_streamLLM 生成的每一个 Token。on_tool_start/on_tool_end工具调用的开始和结束。on_chain_stream节点内部通过get_stream_writer()发送的自定义数据。简单说用astream_events你可以告诉前端“模型正在生成答案… - 正在调用工具查询数据库… - 工具查询完毕正在生成最终回复…”。 实战代码一个能跑通的完整示例下面这个例子演示了如何用astream_events监控一个包含工具调用的简单 Agent并实时打印其执行过程。importasyncioimportrandomfromlangchain_core.toolsimporttoolfromlangchain_core.messagesimportHumanMessagefromlanggraph.prebuiltimportcreate_react_agentfromlangchain_openaiimportChatOpenAI# 1. 定义模型和工具llmChatOpenAI(modelgpt-3.5-turbo,temperature0)tooldefget_weather(city:str)-str:获取指定城市的天气。weathers[晴朗,多云,小雨,大风]returnf{city}的天气是{random.choice(weathers)}。tools[get_weather]agentcreate_react_agent(llm,toolstools)# 2. 核心使用 astream_events 进行流式输出asyncdefrun_agent():inputs{messages:[HumanMessage(content北京今天天气怎么样)]}# 关键APIastream_eventsversionv2 是推荐的稳定版本asyncforeventinagent.astream_events(inputs,versionv2):kindevent[event]# --- 场景1捕获模型生成的文本片段 ---ifkindon_chat_model_stream:chunkevent[data][chunk]ifchunk.content:# 这里就是逐字输出的效果可以实时推送给前端print(chunk.content,end,flushTrue)# --- 场景2捕获工具调用的开始 ---elifkindon_tool_start:tool_nameevent[name]tool_inputevent[data].get(input)# 此时可以向前端发送状态“正在调用工具get_weather”print(f\n\n[系统] 正在调用工具{tool_name}参数{tool_input})# --- 场景3捕获工具调用的结束 ---elifkindon_tool_end:tool_outputevent[data].get(output)# 工具执行完毕可以发送状态“工具返回结果”print(f\n[系统] 工具返回结果{tool_output})# 运行asyncio.run(run_agent())运行结果分析运行这段代码你将清晰地看到事件的完整链条模型开始生成并判断需要调用get_weather工具。on_tool_start事件被触发打印“正在调用工具…”。工具执行模拟返回天气数据。on_tool_end事件被触发打印“工具返回结果…”。模型继续生成最终的回复内容。这完美展示了“流式输出”和“工具调用”可以且应当共存。 面试标准回答话术如果在面试中再次被问到这个问题可以参考下面的回答框架“在 LangGraph 中流式输出和工具调用完全可以并存处理这个问题的关键是使用astream_eventsAPI。astream_events返回的是整个图执行过程中的事件流而不是只有最终结果。这意味着我可以清晰地监控到两种核心事件LLM 的逐词输出通过捕获on_chat_model_stream事件可以实现类似 ChatGPT 的打字机效果。工具调用的完整生命周期通过监听on_tool_start和on_tool_end等事件我能在前端展示“正在查询数据库…”或“正在调用工具…”等中间状态用户体验会更好。这样即使在调用工具时流式输出也不会中断用户能持续看到进度反馈。我之前在这方面的理解确实不够深入后来我专门研读了 LangGraph 官方文档中关于astream_events的部分并写了一个包含工具调用的完整 Demo 来验证。总的来说我的理解是astream_events提供了在同一个流中同时处理文本生成和工具状态的能力关键在于根据不同的事件类型进行分发处理。” 面试官可能追加的追问如果模型调用了多个工具astream_events能处理吗答可以。astream_events会为每一个工具调用分别触发on_tool_start和on_tool_end事件你可以根据事件中的name或run_id来区分不同的工具调用。如何在前端展示“思考中…”或“调用工具中…”的状态答后端通过astream_events捕获到on_tool_start事件时可以通过 WebSocket 或 SSE 向前端发送一个特定格式的状态消息例如{type: status, content: 正在查询天气...}前端接收到后更新 UI。在on_tool_end时再发送一个完成状态。astream_events和astream(stream_modemessages)有什么区别答stream_modemessages主要用于流式输出 LLM 的 Token同时会携带一些元数据。而astream_events是一个更底层的 API它能提供整个图执行过程中的所有事件包括 Token 流、工具调用、自定义事件等。astream_events功能更强但stream_modemessages更轻量如果只需要 LLM 输出用后者更简单。这个问题触及了 LangChain 和 LangGraph 流式机制的核心。简单来说stream()系列方法提供的是粗粒度的结果流而astream_events()提供的是细粒度的事件流。它们最核心的区别在于stream()是“看结果”而astream_events()是“看过程”。 核心对比一目了然特性stream()/astream()astream_events()核心定位流式传输最终或阶段性结果流式传输执行过程中的所有事件能看到的细节只能看到每个节点执行完毕后的状态快照或LLM生成的Token能看到每一步模型何时开始/结束思考、生成了哪些Token、工具何时被调用及返回了什么结果等返回数据根据stream_mode参数返回图的状态更新、完整的图状态或LLM的Token等返回一个StreamEvent事件流每个事件都包含event、name、data等详细字段使用复杂度相对简单API直观更复杂需要理解事件类型并进行过滤和处理控制粒度较粗极细可以监控到图执行的每一个环节主要应用场景构建聊天应用流式输出文字、监控Agent执行进度实现复杂的前端交互如显示“思考中”、“调用工具中…”等状态、深度调试和可观测性适用框架广泛应用于 LangChain 和 LangGraph主要在 LangGraph 中发挥最大威力但 LangChain 的Runnable也支持 深入解析stream()/astream()这是最基础的流式接口用于以流的方式获取 Runnable 或 Graph 的执行结果。工作原理在 LangGraph 中当你调用graph.stream()时它会以“超级步Superstep”为单位来输出。一个“超级步”可以理解为图的一次完整执行周期比如从开始到调用完一个工具。通过stream_mode控制输出你可以通过stream_mode参数来精确控制你想看到什么values在每个超级步结束后输出完整的图状态State。updates在每个超级步结束后仅输出状态中被更新的部分。messages专门用于流式输出 LLM 生成的Token并附带上对应的元数据。custom用于流式输出在图的节点中通过StreamWriter自定义的数据。适用场景它非常适合需要实时展示 Agent执行进度比如“正在执行第一步…”、“正在调用工具…”或流式展示 LLM回复内容的场景。 深入解析astream_events()这是 LangChain/LangGraph 提供的更强大、更底层的流式接口它用于流式传输执行过程中的每一个事件能提供前所未有的细粒度控制。工作原理它会将整个执行过程拆解成一个个独立的事件并将它们以流的形式逐个发出。你可以订阅这些事件并针对不同类型的事件做出不同的响应。事件类型 (event)每个事件都遵循on_[runnable_type]_(start|stream|end)的命名格式。你可以通过解析event字段来了解当前发生了什么on_chat_model_start/on_chat_model_stream/on_chat_model_endLLM 开始/流式生成/结束生成。on_tool_start/on_tool_end工具开始/结束执行。on_chain_start/on_chain_end一个 Chain或图节点开始/结束执行。如何区分不同事件除了event字段每个事件还包含name哪个组件产生的、data事件携带的数据如LLM的Token块或工具调用的参数等字段。你可以通过过滤event和name来只处理你关心的事件。适用场景它是构建极致用户体验的关键。比如你可以利用它实现在 LLM “思考”时前端显示“正在思考…”。在调用工具时前端显示“正在查询数据库…”。在流式输出文字时实现“打字机”效果。深度调试和性能分析。 总结如果只是想简单地在界面上流式显示大模型的回复文字或者监控 Agent 的整体进度使用stream(modemessages)或stream(modeupdates)就足够了。如果需要精细控制前端界面在 LLM 思考、工具调用等不同阶段展示不同状态或者需要对整个执行过程进行深度监控和调试那么astream_events是更强大的工具。在面试中如果能清晰地阐述stream()是“结果流”而astream_events()是“事件流”并能举例说明它们各自的应用场景就能展现出你对 LangChain/LangGraph 流式机制有相当深入的理解。能。流式输出完全可以和工具调用同时进行它们并不冲突。你可以在模型一边生成文本的同时一边执行工具调用。关键点在于LangGraph 的流式输出本身就是一个事件流其中就包含了工具调用的生命周期事件。1. 流式输出究竟在“流”什么LangGraph 的流式输出远不止是模型生成的文字它是一系列事件的集合messages模式流式输出模型生成的 Token实现“打字机”效果。updates/values模式流式输出图状态的变化你可以看到每次节点执行后的状态更新。tools模式专门流式输出工具调用的生命周期事件。2. 如何具体实现LangGraph 提供了几种方式来让你在流式输出时获得工具调用的信息通过stream_mode参数stream()/astream()你可以通过设置stream_modetools来专门获取工具调用的事件。如果同时想看到模型输出和工具事件可以传入一个列表例如stream_mode[messages, tools]。# 同时流式传输 LLM Token 和工具调用事件asyncformode,chunkingraph.astream(inputs,stream_mode[messages,tools]):ifmodemessages:# 处理 LLM 输出的 Tokenelifmodetools:# 处理工具调用事件开始、结束等通过更细粒度的事件 APIastream_events()astream_events()提供了更底层的事件流你可以捕获on_tool_start、on_tool_end等事件。通过监听这些事件你可以在工具开始执行时向前端发送一条“正在查询…”的状态消息。3. 代码示例下面是一个核心逻辑的代码示例展示如何捕获工具调用事件fromlanggraph.prebuiltimportcreate_react_agentfromlangchain_openaiimportChatOpenAIfromlangchain_core.toolsimporttooltooldefget_weather(city:str)-str:获取指定城市的天气returnf{city}的天气是晴朗agentcreate_react_agent(modelChatOpenAI(modelgpt-4),tools[get_weather])inputs{messages:[(user,北京天气如何)]}asyncforeventinagent.astream_events(inputs,versionv2):kindevent[event]# 捕获模型生成的 Token实现流式输出ifkindon_chat_model_stream:chunkevent[data][chunk]ifchunk.content:print(chunk.content,end,flushTrue)# 捕获工具调用开始elifkindon_tool_start:tool_nameevent[name]print(f\n[系统] 正在调用工具{tool_name}...)# 捕获工具调用结束elifkindon_tool_end:tool_outputevent[data].get(output)print(f\n[系统] 工具返回{tool_output})3. 核心要点工具调用的“流”是分阶段的工具调用本身是一个过程。在模型生成工具调用参数时可以流式输出参数片段message.tool_calls在工具执行时可以流式输出执行进度stream.tool_calls。不会“卡住”当模型决定调用工具时它会生成一个tool_calls消息。此时LLM 的文本生成流会暂停但整个执行流并不会停止。stream()方法会紧接着产生一个包含工具调用信息的事件你可以在前端展示“正在查询…”的状态。工具执行完毕后执行流会继续LLM 会基于工具返回的结果生成最终的回复文本。所以流式输出完全可以和工具调用同时进行。关键在于理解你“流”的是整个执行过程的事件而不仅仅是最终生成的文字。