作为LLM提示词的一个重要组成部分表示对话历史的消息列表在结构上有一个基本的要求如果LLM返回的AIMessage包含ToolCall对象那么Agent会期望每个ToolCall对象都有对应的ToolMessage。但是Agent在执行过程会因为一些异常导致LLM返回的AIMessage中的ToolCall对象没有得到正确的处理最终导致后续的消息列表中缺失了对应的ToolMessage。PatchToolCallsMiddleware就在面临这种情况是提供了一种补救机制自动创建并添加缺失的ToolMessage。1. ReAct工作模式导致的对话历史的交替结构作为Agent对话历史的消息列表之所以具有确定的结构是源于Agent基于ReAct的工作模式。ReActReasoning and Acting是一种让大语言模型LLM将推理与行动相结合的交互模式。它解决了模型空谈不干活只推理不调用工具或盲目干活只调工具不分析原因的问题。ReAct的核心是一个循环过程通常被称为Thought-Action-Observation循环Thought推理模型先写下当前的思考分析任务目标、目前的进度以及下一步需要做什么Action行动模型根据推理结果决定调用哪个工具如搜索、计算器、API并给出参数Observation观察系统执行工具后将结果反馈给模型循环模型根据“观察”到的结果开始新一轮的 Thought直到得出最终答案create_agent函数创建的Agent由model和tools两个核心节点组成model节点负责生成推理内容和生成工具调用tools节点则负责执行工具调用并返回结果。消息列表是Agent状态的核心成员ReAct模式清晰地体现在Agent的执行流程上当用户指定输入调用Agent时model节点生成提示词调用LLM这里的提示词包括封装了用户输入的消息列表用户可以输入描述任务的文本此时生成的消息列表中会包含一个HumanMessage对象用户也可以输入一个消息列表来模拟一段对话、工具集描述和系统指令提示词。经过LLM的推理如果它觉得此时能够提供最终的答案它会将答案封装成一个AIMessage对象返回如果它觉得还需要调用工具来获取更多信息或进行计算它会在生成的AIMessage对象中包含一个或多个ToolCall对象来调用工具Agent会将接收到的AIMessage对象添加到消息列表中。如果此AIMessage的ToolCall列表为空执行流程到此结束否则Agent会根据工具调用的信息来调用tools节点执行工具。相关的工具以并发形式的执行后执行的结果会以ToolMessage的形式被添加到消息列表中。即使工具返回的是一个Command对象也明确要求它必需返回一个ToolMessage对象来描述工具调用的结果包含ToolMessage的消息列表会被再次提交给model节点进入下一轮的推理循环。这就决定了在一个推理步骤结束时作为对话历史的消息应该具有这样的结构如果某条AIMessage中包含了一个或多个ToolCall对象那么在消息列表中必须有对应的一个或多个ToolMessage来描述工具调用的结果每条ToolMessage必须包含一个tool_call_id字段来与对应的ToolCall进行匹配以保证消息列表的结构完整性;2. 对话历史的结构为何会被破坏在 ReAct 模式中消息列表的严格交替结构Assistant提议 - Tool执行 - Assistant总结是其逻辑闭环的基础以下是导致结构破坏的常见原因并发调用与乱序返回模型一次性发出了三个工具调用Action A, B, C但外部执行环境返回结果的顺序变成了 B, A, C。如果直接按接收顺序插入消息列表而没有与其对应的tool_call_id严格匹配模型会把B的结果当成A的反馈强制插入用户干预在模型发出Action之后、Tool返回结果之前用户突然又发了一条新消息如“算了别查了换个任务”。这会导致 Assistant (Action) 后面直接跟了一个 User 消息中间缺失了必要的 Tool 响应溢出导致的截断由于ReAct循环非常多历史记录太长开发者简单粗暴地删除了中间的某些消息。如果删除了某个 Tool 消息但保留了后面基于该工具结果做的 Thought逻辑链条就会出现断层模型解析失败模型由于幻觉或受干扰没有输出预定的 Action 格式而是直接输出了乱码。系统无法提取出 Action导致流程卡死在 Assistant 这一步无法触发后续的 Tool 消息3. 错乱消息结构导致的问题ReAct模式的循环特性决定了消息列表的具有上述的交替结构而这种消息结构也反映了LLM过去的推理流程并用以指导后续的推理。一旦这种链条断裂模型就会陷入逻辑精神分裂,会产生以下后果逻辑幻觉 (Hallucination)模型会试图脑补缺失的Observation。如果它提出了查询请求但没收到结果它可能会根据训练数据编造一个虚假的结果并以此为基础继续推理;无限递归循环 (Infinite Loop)模型发现上一个Action没有对应的Observation它会认为我刚才没做成于是再次发出同样的Action。如果系统逻辑不健壮会陷入请求-等待-再次请求的死循环极快地消耗Token;拒绝执行 (Model Refusal)现代对齐后的模型如GPT-4对上下文一致性有要求。如果AIMessage消息包含ToolCall但紧接着不是ToolMessageAPI 往往会直接报错导致程序崩溃因果倒置 (Causal Confusion)如果Observation错位模型会基于错误的前提得出结论。例如搜索“A 的股价”结果返回了“B 的股价”模型会非常自信地告诉你 A 涨了实际上是B涨了4. 利用PatchToolCallsMiddleware修补错乱的消息结构我们利用下面这个实例来演示一下PatchToolCallsMiddleware针对对话历史的修补功能。我们创建了一个Agent并注册了一个工具get_weather用于获取天气信息。我们在调用Agent的时候构造了一个AIMessage其中包含了一个针对get_weather工具的ToolCall对象但是我们没有提供对应的ToolMessage。fromlangchain.agentsimportcreate_agentfromdeepagents.middleware.patch_tool_callsimportPatchToolCallsMiddlewarefromlangchain.toolsimporttoolfromlangchain_openaiimportChatOpenAIfromlangchain_core.messagesimportHumanMessage,AIMessage,ToolCallfromdotenvimportload_dotenvimportasyncio load_dotenv()tooldefget_weather(location:str)-str:Get the current weather for a given location.returnfThe current weather in{location}is sunny with a high of 25°C.agentcreate_agent(modelChatOpenAI(modelgpt-5.2-chat),tools[get_weather],middleware[PatchToolCallsMiddleware()])human_messageHumanMessage(contentWhats the weather like in Suzhou today?)ai_messageAIMessage(content)tool_call:ToolCall{id:tool_call_1,name:get_weather,args:{location:Suzhou}}ai_message.tool_calls.append(tool_call)asyncdefmain():resultawaitagent.ainvoke(input{messages:[human_message,ai_message]})formessageinresult[messages]:message.pretty_print()asyncio.run(main())注册的PatchToolCallsMiddleware会自动检测到这个问题并且为这个ToolCall对象创建一个对应的ToolMessage。虽然这个ToolMessage中的内容会使用预定义的模板来生成但它至少保证了消息列表的结构完整性使得Agent可以继续执行后续的推理和行动。在如下所示的对话列表中出现的第一个ToolMessage就是PatchToolCallsMiddleware自动创建的。 Human Message Whats the weather like in Suzhou today? Ai Message Tool Calls: get_weather (tool_call_1) Call ID: tool_call_1 Args: location: Suzhou Tool Message Name: get_weather Tool call get_weather with id tool_call_1 was cancelled - another message came in before it could be completed. Ai Message Tool Calls: get_weather (call_Exlkyg84lyVTMvOEfGuzgu6N) Call ID: call_Exlkyg84lyVTMvOEfGuzgu6N Args: location: Suzhou, China Tool Message Name: get_weather The current weather in Suzhou, China is sunny with a high of 25°C. Ai Message Today in **Suzhou, China**, the weather is **sunny** with a **high around 25 °C (77 °F)**. It should be a pleasant day overall—great for being outdoors.PatchToolCallsMiddleware的实现非常简单它通过重写before_agent/after_agent方法来检测AIMessage中的ToolCall对象并且在发现缺失对应的ToolMessage时自动创建一个新的ToolMessage来进行补充。这个新创建的ToolMessage会使用一个预定义的模板来生成内容。classPatchToolCallsMiddleware(AgentMiddleware):defbefore_agent(self,state:AgentState,runtime:Runtime[Any])-dict[str,Any]|None