ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【三个月 AI Agent 实战学习】Day 25:Agent 的“思考”过程解析 —— 深入调试与回调

【三个月 AI Agent 实战学习】Day 25:Agent 的“思考”过程解析 —— 深入调试与回调 Day 25Agent 的“思考”过程解析 —— 深入调试与回调欢迎来到第二十五天昨天我们成功构建了第一个 Agent并看到它在verboseTrue时打印出了思考、行动和观察的轨迹。但作为开发者我们需要更深入地理解 Agent 的内部运作以便在出现问题时能够快速定位和修复。今天我们将聚焦于Agent 的思考过程解析学习如何使用回调函数、日志和中间步骤来追踪 Agent 的每一步推理并理解输出解析器是如何工作的。这将为你后续构建复杂、可靠的 Agent 打下坚实的调试基础。一、今日学习目标理解 AgentExecutor 的内部循环它是如何调用 Agent、执行工具、更新 scratchpad 的。掌握使用verboseTrue之外的调试手段自定义回调函数Callback Handlers来捕获每个步骤的详细信息。学会从 AgentExecutor 的结果中提取中间步骤intermediate_steps并分析其结构。了解 LangChain 的 ReAct 输出解析器的工作原理以及它如何从模型输出中提取“行动”和“行动输入”。能够编写自定义回调在 Agent 运行过程中记录日志或执行其他操作。二、详细实现步骤步骤 1回顾 AgentExecutor 的执行流程在 LangChain 中AgentExecutor的核心循环大致如下初始化agent_scratchpad空字符串。调用 AgentRunnable传入用户输入和当前 scratchpad获得一个AgentAction或AgentFinish。如果是AgentFinish返回最终答案。如果是AgentAction根据 action 中的工具名称找到对应工具执行工具获得观察结果observation。将 action 和 observation 格式化后追加到 scratchpad 中。重复步骤 2-5直到达到终止条件获得最终答案或达到最大迭代次数。在这个过程中模型每次看到的提示词都包含了之前的思考、行动和观察这使它能基于历史进行推理。步骤 2使用return_intermediate_stepsTrue获取中间步骤昨天我们运行 Agent 时只获得了最终答案。现在让我们运行同一个 Agent但开启return_intermediate_stepsTrue看看返回结果中的中间步骤长什么样。继续使用昨天的代码假设已定义好llm、tools、agent_executor我们重新运行并提取中间步骤resultagent_executor.invoke({input:请帮我计算 (12 7) * 3然后搜索一下北京天气},return_intermediate_stepsTrue)print(最终答案,result[output])print(\n中间步骤)forstepinresult[intermediate_steps]:action,observationstepprint(f行动{action.tool}输入{action.tool_input})print(f观察{observation})print(-*40)运行后你会看到类似输出行动calculator输入(12 7) * 3 观察57 ---------------------------------------- 行动search输入北京天气 观察北京今天晴26°C湿度 40%。 ----------------------------------------intermediate_steps是一个列表每个元素是一个元组(AgentAction, observation)。AgentAction对象包含tool工具名称和tool_input输入参数而 observation 是工具返回的字符串。这为我们提供了完整的执行轨迹。步骤 3深入理解 AgentAction 对象让我们打印一个AgentAction对象的全部属性actionresult[intermediate_steps][0][0]print(类型,type(action))print(工具名,action.tool)print(工具输入,action.tool_input)print(日志,action.log)# 记录模型输出的原始文本片段action.log包含了模型在生成该行动时的原始输出文本通常包括 Thought 和 Action 部分这对于调试非常有用因为它显示了模型当时是如何思考的。步骤 4使用回调函数记录详细日志LangChain 提供了回调机制允许我们在运行过程中插入自定义逻辑。我们可以创建一个自定义回调处理器在特定事件如 LLM 调用开始、结束工具调用开始、结束时打印信息。新建debug_agent.py定义回调类fromlangchain_core.callbacksimportBaseCallbackHandlerclassMyCallbackHandler(BaseCallbackHandler):defon_llm_start(self,serialized,prompts,**kwargs):print(f[LLM Start] 提示词前100字符{prompts[0][:100]}...)defon_llm_end(self,response,**kwargs):print(f[LLM End] 输出{response.generations[0][0].text[:100]}...)defon_tool_start(self,serialized,input_str,**kwargs):print(f[Tool Start] 工具{serialized.get(name)}输入{input_str})defon_tool_end(self,output,**kwargs):print(f[Tool End] 输出{output})然后创建 AgentExecutor 时传入回调agent_executorAgentExecutor(agentagent,toolstools,verboseFalse,# 关闭默认 verbosecallbacks[MyCallbackHandler()],return_intermediate_stepsTrue,max_iterations5)resultagent_executor.invoke({input:计算 5 * 8})运行后你会在控制台看到每个阶段的自定义日志。这比verboseTrue更灵活因为你可以控制日志格式、记录到文件、或发送到监控系统。步骤 5理解 ReAct 输出解析器LangChain 的 ReAct Agent 使用一个解析器通常是ReActSingleInputOutputParser来从模型输出中提取AgentAction或AgentFinish。它依赖于正则表达式来匹配Action:和Action Input:等标记。如果模型输出不符合预期格式解析器会抛出异常AgentExecutor的handle_parsing_errorsTrue会捕获这个异常并将其反馈给模型让模型重新输出。我们可以手动测试解析器的行为fromlangchain.agents.output_parsersimportReActSingleInputOutputParserfromlangchain_core.agentsimportAgentAction,AgentFinish parserReActSingleInputOutputParser()# 模拟一个包含 Action 的输出output1 Thought: 我需要计算 Action: calculator Action Input: 5 * 8 parsed1parser.parse(output1)print(type(parsed1),parsed1)# 模拟一个包含 Final Answer 的输出output2 Thought: 我已经算出来了 Final Answer: 40 parsed2parser.parse(output2)print(type(parsed2),parsed2)运行后你会看到第一个解析结果为AgentAction第二个为AgentFinish。这有助于你理解解析器的规则并知道如何调整提示词来让模型输出符合要求。步骤 6处理模型输出格式不规范的情况有时模型会输出多余的文字、错误的标记如使用“行动”而不是“Action”或者将输入放在多行。我们可以通过以下方式改善在提示词中提供更明确的示例。使用正则表达式更宽松的解析器自定义。降低温度至 0提高格式遵循度。在AgentExecutor中设置handle_parsing_errorsTrue让框架自动修复。我们尝试故意制造一个格式错误例如修改提示词不提供示例观察 AgentExecutor 如何处理# 创建一个不包含格式要求的简单提示词bad_promptPromptTemplate.from_template(你是一个助手回答问题{input}。你可以使用工具{tools}。思考{agent_scratchpad})bad_agentcreate_react_agent(llm,tools,bad_prompt)bad_executorAgentExecutor(agentbad_agent,toolstools,verboseTrue,handle_parsing_errorsTrue,max_iterations3)resultbad_executor.invoke({input:计算 3 5})print(result[output])观察到模型输出可能不包含Action:标记导致解析失败然后handle_parsing_errors会反馈错误模型可能重新输出最终成功。这个实验能加深你对解析错误处理的理解。三、常见问题与调试Q1如何查看 Agent 每一步的完整提示词→ 使用自定义回调on_llm_start打印prompts或者在verboseTrue时观察控制台输出LangChain 会打印提示词。更高级的调试可以使用 LangSmith后续会介绍。Q2AgentAction中的log字段是什么→log字段记录了生成该 Action 时模型的原始输出文本包括 Thought、Action、Action Input 等。这对于回溯模型推理过程非常有用。Q3我想自定义输出解析器应该怎么做→ 可以继承AgentOutputParser并实现parse方法返回AgentAction或AgentFinish。然后在create_react_agent中传入自定义解析器使用output_parser参数。但通常默认解析器已经足够除非你有特殊的格式要求。Q4handle_parsing_errorsTrue是否会掩盖真正的问题→ 它只会处理解析错误并将错误信息反馈给模型重新生成。如果模型持续输出错误格式可能导致死循环受max_iterations限制。如果频繁出现解析错误应优先改进提示词或降低温度。Q5能否将中间步骤保存到数据库或日志文件→ 使用自定义回调在on_tool_end中将工具调用信息写入日志文件或数据库。这是生产环境中的常用做法。Q6LangSmith 是什么我们需要用吗→ LangSmith 是 LangChain 官方的调试和监控平台可以可视化 Agent 的每个步骤、Token 消耗、延迟等。需要注册并获取 API Key配置后即可自动追踪。今天我们使用自定义回调但 LangSmith 功能更强大。后续可以尝试。四、今日总结与作业今天你完成了✅ 通过return_intermediate_stepsTrue获取并分析了 Agent 的中间步骤。✅ 深入了解了AgentAction和AgentFinish对象的结构。✅ 编写了自定义回调处理器实现了细粒度的日志记录。✅ 理解了 ReAct 输出解析器的工作原理并体验了解析错误处理。✅ 学会了如何调试 Agent 的推理过程。今日作业必做使用自定义回调记录日志运行三个不同的问题并将日志保存到文件中例如使用 Python 的logging模块。分析每个问题的执行步骤和 Token 消耗如果可以获取。修改 Agent 的提示词故意去掉格式示例观察 Agent 是否会出现解析错误以及handle_parsing_errors如何帮助恢复。记录你的观察。尝试手动调用解析器解析你自己写的一段包含多个换行和多余文字的模型输出看看解析器能否正确识别 Action如果不能分析原因并考虑如何改进提示词。思考在构建生产级 Agent 时你需要记录哪些信息以便于故障排查请列出至少 5 项。明日预告我们将继续深入 Agent 的工程化学习如何编写高质量的工具描述以及如何处理工具调用中的错误让 Agent 更加健壮。有任何问题欢迎随时提问
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进