LangChain Agent底层循环机制:推理-行动-反馈实战解析 之前在业务迭代中使用 LangChain Agent 时我经常陷入一个困境Agent 的输出结果看起来没问题但中间到底发生了什么完全是一个黑盒。尤其是当模型反复调用同一个“不可用”的工具时我很想知道它是如何从错误反馈里提取信息、修正下一步行动的。后来我把 Agent 的中间步骤全部打印出来才真正看懂了它底层的“推理 - 行动 - 反馈”循环机制。这篇笔记是 LangChain Agent 学习系列的一部分。本文将围绕 Agent 的底层闭环原理用一个自定义“工具不可用”的实战案例完整演示 Agent 如何完成推理、发起行动、接收反馈再根据反馈修正行动直到得出最终答案。如果你正在学习 LangChain Agent 开发或者准备深入了解 LangGraph这篇文章适合作为你的底层原理基础篇。文章会覆盖以下内容Agent 的核心概念与常见应用场景。推理Reasoning、行动Action、反馈Observation三要素的底层含义。如何用tool自定义一个“不可用”工具。如何使用create_react_agent和AgentExecutor搭建可运行的 Agent。如何通过intermediate_steps观察循环过程。常见的调试手段、工程实践与版本迁移建议。无论你是刚接触 LangChain 的新手还是已经在项目里使用 Agent 的开发者都可以通过本文的实验设计把 Agent 的运行机制看清楚。1. 背景与核心概念1.1 什么是 LangChain AgentAgent 可以理解为一种“会自主决策”的 AI 程序。与传统的 Chain 不同Agent 不只是按写死的顺序调用大模型或工具而是根据当前任务的输入、之前执行的结果动态决定下一步做什么。在 LangChain 中Agent 核心解决的是两个问题怎么根据用户的目标选择合适的工具。怎么根据工具返回的结果修正自己的计划。Agent 常见应用场景包括智能客服根据用户问题查询订单、物流、售后政策。数据分析助手根据自然语言问题自动生成 SQL 并执行查询。自动化运维工具读取日志、调用监控接口、标注异常节点。个人知识库助手先检索相关文档再调用模型生成回答。相比“大模型直接回答”Agent 多了一层“工具使用”能力因此能够处理更复杂、需要外部信息或动作的任务。1.2 推理-行动-反馈Agent 的工作闭环Agent 的工作过程并不是一次性给出答案而是进入一个循环推理 - 行动 - 反馈 - 推理 - 行动 - 反馈 - ...这个过程可以类比我们平时解决复杂问题的方式推理Reasoning根据当前已知信息判断下一步需要做什么。行动Action选择一个工具并传入参数执行。反馈Observation拿到工具执行的返回结果这个结果可能成功也可能失败。如果反馈说明方向不对Agent 会基于新的反馈重新推理再次行动直到达到终止条件。这就是标题里强调的“这个过程是循环的”。很多初学者把 Agent 理解成“一个大模型调用一次工具”其实完整的 Agent 执行路径是非常典型的循环结构。1.3 ReAct 范式与 AgentExecutorLangChain 中实现这种循环最经典的范式之一是 ReActReasoning Acting。ReAct 的思路是让模型在每一轮都输出类似下面的结构化内容Thought: 我需要先查询保单信息 Action: lookup_policy Action Input: P-123 Observation: 未找到保单 P-123正确编号格式为 POL- 后跟 6 位数字 Thought: 原来编号格式不对我应该改成 POL-000123 Action: lookup_policy Action Input: POL-000123 Observation: 保单 POL-000123 存在保额 500000 元 Thought: 现在已经拿到保额了 Final Answer: 保额为 500000 元在 LangChain 中AgentExecutor负责驱动这个循环。它会将模型生成的文本解析成“动作”执行工具然后将“观察结果”拼接到对话历史中再次交给模型。整个过程会一直持续直到模型生成Final Answer或达到最大迭代次数。1.4 Agent 与 Chain、LangGraph 的关系LangChain 中还有几个容易混淆的概念Chain固定流程比如“先检索再生成”模型不决定下一步。Agent动态流程模型根据反馈决定下一步。LangGraphLangChain 团队推出的底层编排框架把 Agent 流程建模成一张图节点可以串联、并联、条件跳转适合更复杂的 Agent 应用。理解 AgentExecutor 的循环逻辑之后再看 LangGraph 就会容易很多。LangGraph 本质上是在这个循环基础上加入更细粒度的状态控制、人工介入和多级路由能力。2. 环境准备与版本说明2.1 运行环境本文示例使用 Python 3.9操作系统不限Windows、macOS、Linux 都可以。注意LangChain 的接口迭代速度比较快。下面示例基于langchain 0.2.x和langchain-openai 0.1.x接口编写。如果你使用的是 0.1.x 或其他版本导入路径可能稍有不同建议以你的实际环境为准。2.2 安装依赖创建项目目录并安装所需包mkdir langchain-agent-demo cd langchain-agent-demo pip install python-dotenv langchain langchain-openai这里安装的依赖说明langchainLangChain 核心库提供 Agent、Tool、AgentExecutor 等能力。langchain-openaiOpenAI 模型适配器负责把ChatOpenAI模型接入 LangChain。python-dotenv用于加载.env文件中的环境变量方便管理 API Key。如果你使用的是 DeepSeek、Moonshot 等支持 OpenAI 兼容接口的模型也可以只安装langchain-openai在创建ChatOpenAI时指定base_url。2.3 配置大模型 API在项目根目录创建.env文件OPENAI_API_KEY你的_API_Key OPENAI_API_BASEhttps://api.openai.com/v1如果你的模型服务商不是 OpenAI可以把OPENAI_API_BASE改成对应服务的地址。然后在代码中通过load_dotenv()加载这些变量。2.4 实验场景让自定义工具“不可用”为了演示 Agent 的循环机制本文准备构造两个工具正常的加法计算工具calculate_sum。故意构造出“不可用”状态的保单查询工具lookup_policy。lookup_policy并不会真的抛出异常而是会在参数格式不对时返回非常明确的错误提示。这样模型就能根据反馈修正参数再次调用工具从而让我们观察到“推理 - 行动 - 反馈 - 再推理”的循环现象。3. 核心机制拆解一个循环的三要素在开始写完整代码之前先深入理解循环的三个核心要素。3.1 推理Reasoning推理是 Agent 循环的起点。模型拿到用户输入后首先需要判断当前任务需要哪些信息我手上有什么工具可用应该怎么拆解这个问题create_react_agent生成的 Agent 会把“当前问题 工具列表 此前的历史观察结果”一起拼接成 Prompt交给模型。模型输出内容通常包含Thought和下一步动作。推理质量直接受到两个因素影响工具描述是否清晰。模型本身的规划能力。如果工具描述模糊模型可能不知道什么时候该调用、该传什么参数。3.2 行动Action行动阶段Agent 会从模型输出中解析出Action要调用的工具名。Action Input传给工具的参数。LangChain 使用ReActSingleInputOutputParser这类解析器从模型输出文本里提取动作信息。这也是为什么在 ReAct 模板中需要保留Action、Action Input这些英文关键词——如果模型输出“行动查询保单”框架可能无法解析。行动执行后工具可能成功返回数据也可能因参数错误、网络异常而返回错误信息。无论结果如何都会成为下一步的观察结果。3.3 反馈Observation观察结果Observation是循环中非常关键的一环却最容易被忽略。很多初学者以为“工具调用失败就是 Agent 报错”但实际上工具返回的任何内容都会以Observation的形式反馈给模型。模型会基于这个反馈重新推理。例如未找到保单 P-123正确编号格式为 POL- 后跟 6 位数字例如 POL-001234。这个反馈不仅说明了失败还提示了正确格式。于是模型下一步可能会尝试POL-000123。反过来如果工具只返回错误模型就很难明白错在哪里。因此工具返回信息的质量直接影响 Agent 循环的收敛速度。3.4 循环什么时候终止Agent 循环不会永远持续常见的终止条件有模型生成了Final Answer表示已经得到最终答案。达到了max_iterations设置的最大迭代次数。出现无法解析的模型输出且handle_parsing_errors也未能恢复。在某些自建循环中由人工或外部程序终止。生产环境中必须设置max_iterations否则一旦模型陷入循环会消耗大量 Token 和请求费用。4. 完整实战用“不可用工具”演示 Agent 循环接下来进入完整实战。我们会让 Agent 执行这样一个任务请先查询保单号 P-123 的保额再把保额加上 1000最后告诉我结果。由于P-123不是合法保单编号Agent 第一次调用lookup_policy会收到“参数错误”的反馈然后模型要根据反馈修正参数再次调用。整个循环过程会被完整打印出来。4.1 创建项目结构项目结构如下langchain-agent-demo/ ├── .env └── agent_demo.py4.2 定义自定义工具创建agent_demo.py先导入依赖并定义两个工具# 文件路径agent_demo.py import os from datetime import datetime from dotenv import load_dotenv from langchain_core.tools import tool from langchain.agents import create_react_agent, AgentExecutor from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI load_dotenv() tool def calculate_sum(a: float, b: float) - str: 计算两个数字相加的结果。 result a b return f{a} {b} {result} tool def lookup_policy(policy_number: str) - str: 查询保单详情。policy_number 必须是有效的保单编号格式为 POL- 后跟 6 位数字例如 POL-001234。如果编号格式不正确或未找到将返回错误提示。 # 模拟一个偶尔会“不可用”的工具参数格式不对时返回明确错误信息 if policy_number.startswith(POL-) and len(policy_number) 10 and policy_number[4:].isdigit(): amount 500000 return f保单 {policy_number} 存在投保人张三保额{amount} 元。 return f未找到保单 {policy_number}正确编号格式为 POL- 后跟 6 位数字例如 POL-001234。这里有几个细节需要注意tool装饰器会把普通函数变成 LangChain 的 Tool 对象。函数的 docstring 会被当作工具描述模型会读取这段描述来判断何时调用、如何传参。lookup_policy故意在policy_number不合法时返回“错误提示”而不是抛出异常。这样模型能看到反馈并有机会修正。4.3 创建模型与 ReAct Agent继续编写代码创建模型、模板和 Agent# 文件路径agent_demo.py继续 llm ChatOpenAI( modelgpt-4o-mini, temperature0, api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_API_BASE), ) tools [lookup_policy, calculate_sum] prompt PromptTemplate.from_template( Answer the following questions as best you can. You have access to the following tools: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Question: {input} Thought: {agent_scratchpad} ) agent create_react_agent( llmllm, toolstools, promptprompt, )这段代码中prompt模板里必须包含三个关键变量{tools}会被替换成工具列表。{tool_names}会被替换成工具名列表。{agent_scratchpad}用于存放历史Thought/Action/Observation保证多步循环时可以拼接上下文。模板中的Question:、Thought:、Action:、Action Input:、Observation:、Final Answer:是 ReAct 框架的解析关键词建议保留英文原样。4.4 运行 Agent Executor 并观察输出创建AgentExecutor设置为打开详细日志并限制最多循环 5 次# 文件路径agent_demo.py继续 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue, max_iterations5, return_intermediate_stepsTrue, ) if __name__ __main__: result agent_executor.invoke({ input: 请先查询保单号 P-123 的保额再把保额加上 1000最后告诉我结果。 }) print(\n最终答案) print(result[output])参数解释verboseTrue会在控制台打印模型的 Thought、Action、Observation。handle_parsing_errorsTrue如果模型输出无法解析会把解析错误信息当作 Observation 返回给模型让模型自行修正。max_iterations5限制最大循环次数防止死循环。return_intermediate_stepsTrue让返回结果中携带中间步骤方便后续分析。运行代码python agent_demo.py4.5 查看 intermediate_stepsreturn_intermediate_stepsTrue后result字典中会包含intermediate_steps字段它是(AgentAction, Observation)的列表。我们可以单独写一个脚本打印这个结构# 文件路径inspect_steps.py import os from dotenv import load_dotenv from langchain_core.tools import tool from langchain.agents import create_react_agent, AgentExecutor from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI load_dotenv() tool def calculate_sum(a: float, b: float) - str: 计算两个数字相加的结果。 return f{a} {b} {a b} tool def lookup_policy(policy_number: str) - str: 查询保单详情。policy_number 必须是有效的保单编号格式为 POL- 后跟 6 位数字例如 POL-001234。如果编号格式不正确或未找到将返回错误提示。 if policy_number.startswith(POL-) and len(policy_number) 10 and policy_number[4:].isdigit(): return f保单 {policy_number} 存在投保人张三保额500000 元。 return f未找到保单 {policy_number}正确编号格式为 POL- 后跟 6 位数字例如 POL-001234。 llm ChatOpenAI(modelgpt-4o-mini, temperature0) tools [lookup_policy, calculate_sum] prompt PromptTemplate.from_template( Answer the following questions as best you can. You have access to the following tools: {tools} Use the following format: Question: the input question you must answer Thought: you should always think about what to do Action: the action to take, should be one of [{tool_names}] Action Input: the input to the action Observation: the result of the action ... (this Thought/Action/Action Input/Observation can repeat N times) Thought: I now know the final answer Final Answer: the final answer to the original input question Begin! Question: {input} Thought: {agent_scratchpad} ) agent create_react_agent(llmllm, toolstools, promptprompt) agent_executor AgentExecutor( agentagent, toolstools, verboseFalse, handle_parsing_errorsTrue, max_iterations5, return_intermediate_stepsTrue, ) result agent_executor.invoke({ input: 请先查询保单号 P-123 的保额再把保额加上 1000最后告诉我结果。 }) print( 中间步骤 ) for index, (action, observation) in enumerate(result[intermediate_steps], start1): print(f第 {index} 步) print(f 动作{action.tool}) print(f 参数{action.tool_input}) print(f 反馈{observation}) print() print(最终输出, result[output])运行这个脚本可以看到类似下面的结构第 1 步 动作lookup_policy 参数{policy_number: P-123} 反馈未找到保单 P-123正确编号格式为 POL- 后跟 6 位数字例如 POL-001234。 第 2 步 动作lookup_policy 参数{policy_number: POL-000123} 反馈保单 POL-000123 存在投保人张三保额500000 元。 第 3 步 动作calculate_sum 参数{a: 500000.0, b: 1000.0} 反馈500000.0 1000.0 501000.0这就非常直观地展示了“推理-行动-反馈”循环Agent 首先推理出应该查询保单于是调用lookup_policy参数是用户提供的P-123。工具不可用返回错误反馈。Agent 根据反馈修正参数重新行动第二次查询成功。Agent 继续规划下一步计算调用calculate_sum。拿到计算结果后输出最终答案。4.6 运行结果说明如果打开verboseTrue你会在控制台看到模型每一步的完整思考过程。网上很多教程只展示了最终输出但调试 Agent 时中间过程的观察价值远大于最终答案。这个实验也解释了“不可用工具如何驱动循环”当工具返回一条足够明确的错误提示时模型会把这条提示当作 Observation重新进入推理阶段而不是直接放弃。这正是 Agent 与普通函数调用最大的区别。5. 常见问题与排查思路在实际开发中Agent 循环会遇到各种问题。下面整理高频问题与排查建议。5.1 高频问题速查表问题现象常见原因解决思路ValueError: Missing some input variables: {tools}自定义 Prompt 模板中没有tools或tool_names变量检查模板补齐{tools}、{tool_names}、{agent_scratchpad}等变量Could not parse LLM output模型输出没有按Action:/Action Input:格式返回开启handle_parsing_errorsTrue检查 Prompt 模板关键词是否完整Agent 一直重复调用同一个工具工具返回错误信息不够明确让工具返回可读的错误提示说明正确格式明明传入了正确参数工具还是报错工具内部对参数类型要求严格模型生成参数类型不符合在工具描述中写明参数类型和示例在函数内部做类型转换或校验循环到max_iterations都没有结果问题太复杂模型规划能力不足拆解任务补充中间步骤更换更强模型使用from langchain.tools import tool报错LangChain 版本差异新版推荐from langchain_core.tools import tool工具内部抛异常导致循环中断未做异常兜底在工具内部捕获异常返回结构化错误信息5.2 排查 Agent 循环异常的顺序当 Agent 行为不符合预期时建议按以下顺序排查打开verboseTrue观察每一步的 Thought、Action、Observation。检查工具返回的 Observation 是否清晰、可读。检查工具名称和描述是否准确描述里是否写清楚了参数格式。检查max_iterations是否设置得太小。检查模型是否频繁出现解析失败考虑是否切换模型或调整 Prompt。检查result[intermediate_steps]定位循环在哪一步走偏。最后再考虑是否需要更换 Agent 范式或升级到 LangGraph。6. 最佳实践与工程建议理解了循环原理后设计 Agent 工具时要把“反馈”当作一等公民。6.1 工具设计原则工具是 Agent 感知外部世界的“手”也是 Agent 获得反馈的主要来源。好的工具设计应该满足以下要求命名清晰工具名要能直接表达用途如query_policy、send_email。描述完整在 docstring 中写明参数格式、示例、返回值、错误提示。返回结构化信息不要只返回True/False尽量返回可读的文本例如“未找到保单 P-123正确格式为 POL- 后跟 6 位数字”。内部做异常兜底工具内部要捕获异常把失败原因转成文本反馈而不是直接抛出堆栈。示例tool def query_stock(code: str) - str: 查询股票最新价格。code 是 6 位数字股票代码例如 600519。如果代码无效返回错误信息。 if not (code.isdigit() and len(code) 6): return f股票代码 {code} 格式错误应为 6 位数字例如 600519。 # 模拟查询 return f股票 {code} 最新价格 1688.00 元。6.2 循环控制与安全边界Agent 循环虽然灵活但在生产环境必须设置边界限制最大迭代次数设置max_iterations避免模型无限循环。限制 Token 消耗监控 Prompt 长度防止agent_scratchpad过长导致上下文溢出。工具权限最小化给工具配置最小权限不要在工具中暴露不必要的敏感信息。输入校验不能因为是模型生成的参数就直接执行系统命令或 SQL必须做严格的合法性校验。人工审批节点对于删除、转账、发消息等高危操作建议用人工审批或二次确认而不是让 Agent 直接执行。6.3 日志与可观测性Agent 调试不能只依赖最终输出。建议在项目中记录完整日志[agent] 用户输入xxx [agent] Thought需要先查询保单 [agent] Actionlookup_policy参数{policy_number: P-123} [agent] Observation未找到保单 P-123正确编号格式为 POL- 后跟 6 位数字 [agent] Thought参数格式不对修正为 POL-000123 [agent] Final Answer保额 500000 元加上 1000 后为 501000 元这样即使 Agent 在线上出了问题也能通过日志还原决策路径。6.4 LangChain 与 LangGraph 的选择AgentExecutor 适合单 Agent、单轮问题的场景。如果业务开始复杂起来例如需要多轮对话记忆、人机交互确认、多个 Agent 协作、条件分支跳转建议评估是否升级到 LangGraph。LangGraph 把“推理-行动-反馈”建模为图中的节点和边开发者可以对循环做更精细的控制例如指定工具执行节点的并发策略。在循环中加入人工审查节点。根据观察结果自由跳转不同分支。但升级到 LangGraph 也会增加状态管理和调试成本建议先从 AgentExecutor 跑通原型再根据业务复杂度决定是否迁移。7. 总结与学习路线通过本文的实验你应该已经理解了 LangChain Agent 的核心机制Agent 不是单次调用而是“推理 - 行动 - 反馈”不断循环的过程。AgentExecutor负责驱动循环create_react_agent负责构建 ReAct Agent。自定义工具时反馈信息的质量会直接影响 Agent 是否能在循环中自我修正。通过verboseTrue和intermediate_steps可以把 Agent 内部过程完整观察出来。接下来可以继续学习的方向阅读 ReAct 论文理解 Reasoning 与 Acting 结合的动机。学习 LangGraph掌握用状态图编排 Agent 循环。探索 Tool Calling 模式了解现代模型原生的工具调用能力。研究 MCP 等工具标准化方案为 Agent 接入更多外部工具。当你下次调试 Agent 时先别急着换模型打开 verbose 完整看一遍循环。很多时候问题答案就藏在 Observation 里。