AI Agent 的工程闭环:从 ReAct 到规划-执行-反思
读完这篇,你该能回答三个问题:一个 Agent 该不该上、它通常会死在哪一步、以及怎么用数字判断它到底好不好用——而不是停留在"看起来会自己干活"。
为什么 Agent 不是"会聊天的模型"
普通对话模型一次性生成回答,本质是"给一段输入,吐一段输出"。Agent 的区别在于它把"想"和"做"交错起来:模型先决定下一步要不要调用某个工具、调用什么、传什么参数,工具返回真实结果后再继续推理,如此循环直到任务完成。这种"推理+行动"交错的模式叫 ReAct(Reason + Act)。
它解决的是纯思维链(Chain-of-Thought,让模型在内部一步步想)的根本缺陷:模型想得再多,也接触不到外部世界的真实状态。要查今天的订单、改一条数据库记录、发一封邮件,光靠"想"是做不到的,必须落到真实动作上。ReAct 用"行动结果"给推理提供了反馈锚点,这也是它能处理多步任务的原因。
但 ReAct 有明显局限。它是串行的:上一步没返回,下一步想不了,长任务里 token 消耗与延迟线性累积;推理链会被工具结果打断,模型容易"忘记"最初的子目标;更麻烦的是错误会沿循环累积——一旦某步工具调用传错参数,后续每一步都建立在错误事实上,越走越偏。这也是为什么单靠 ReAct 撑不起严肃业务,需要下面这套更完整的循环。
规划—执行—反思循环
工程上更稳的做法是把 Agent 拆成三段职责:规划(Plan)把模糊目标拆成可验证的子任务;执行(Execute)调用工具逐个完成;反思(Reflect)在关键节点回头看是否偏离、是否该回溯重做。
任务分解的几种策略
最朴素的是自顶向下分解:让模型先列出步骤清单,再逐条执行。但平铺清单不表达依赖——第 3 步可能要等第 1 步的输出。更可靠的是依赖图(DAG):明确"哪些步骤可并行、哪些必须先做"。对探索型任务,可以用树状搜索(如 Tree-of-Thought),一条路走不通就剪枝换分支。2025 年后主流 Agent 框架(如 LangGraph、AutoGen、OpenAI Agents SDK)普遍支持把"规划"做成可暂停、可人工介入的显式节点,而非塞进一段 prompt。
反思不是可有可无
反思可以是同模型"你现在离目标还差多少",也可以是独立的小模型做校验器。它的关键作用是给出"停止/回溯/求助"的信号。没有反思的 Agent 容易陷入"看起来在努力、其实在打转"的状态,直到步数耗尽。
工具调用的执行闭环
Agent 真正的"执行"发生在工具调用这一环。一个健壮的闭环必须显式经过这几步,而不是把模型吐出的参数直接拿去跑:
(Thought)→工具调用请求
(Action)→参数校验
+权限判断→沙箱执行
(Observation)→结果回灌上下文
下面是一段最小可参考的 ReAct 循环实现,重点是显式兜底:到达步数上限必须返回明确结论,绝不允许静默失败。
def run_agent(task, max_steps=12):
history = [{"role": "user", "content": task}]
for step in range(max_steps):
resp = llm.chat(history, tools=TOOL_SCHEMAS)
# 模型不再请求工具调用 → 视为任务完成,返回最终答案
if resp.tool_calls is None:
return resp.content
for call in resp.tool_calls:
result = dispatch(call) # 真实执行:校验→权限→沙箱→运行
history.append({
"role": "tool",
"name": call.name,
"content": result, # observation 喂回上下文,形成闭环
})
return "未能在步数上限内完成" # 必须显式兜底记忆机制:短期、长期与压缩
Agent 要"记得"东西,但上下文窗口(context window)又贵又有限,所以记忆要分层:
- 短期记忆:就是当前上下文里的内容,包含对话、工具结果。它最"新鲜"但容量受限,塞太多会稀释注意力。
- 长期记忆:存到外部(通常是向量数据库)。需要"记住"的知识先
embed成向量写入,用的时候再做向量检索(相似度搜索)把相关片段拉回上下文。适合用户偏好、历史订单、产品知识库这类跨会话信息。 - 上下文压缩:对话一长,早期内容该被摘要而非原样保留。常见做法是对旧轮次做摘要、用滑动窗口保留最近 N 轮、或对工具结果按"重要度打分"截断。
# 长期记忆的写入与检索(伪代码)
vec_db.upsert(embed("用户偏好:报告用中文、不要表格"), meta={"user": uid})
hits = vec_db.search(embed("他上次怎么要求报告的"), top_k=3)
history += [{"role": "system", "content": "相关记忆:" + fmt(hits)}]典型失败模式与对策
把 Agent 跑"通"很容易,跑"稳"难在这几类故障。下表是实战中最常遇见的:
| 失败模式 | 表现 | 对策 |
|---|---|---|
| 死循环 | 反复调用同一工具、步骤不收敛 | 设最大步数上限 + 检测重复 action 指纹 + 强制反思 |
| 幻觉工具调用 | 调用了不存在的工具或捏造参数 | 严格按 schema 校验,失败即返回错误而非放行 |
| 上下文溢出 | 工具结果太长撑爆窗口 | 结果截断/摘要、长期记忆外置、分页检索 |
| 错误传播 | 某步失败,后续建立在其错误事实上 | 每步校验 observation,失败即回溯而非硬撑 |
getattr 调用,就可能执行到意外函数。务必用白名单 + schema 双重校验,再放行执行。怎么评估一个 Agent
不能只看"答得像不像"。要把 Agent 当成系统来度量,核心指标有四组:
| 指标 | 含义 | 为什么看它 |
|---|---|---|
| 任务成功率 | 在 Benchmark / 真实样本上跑通的比例 | 最根本的"能不能用" |
| 平均步数 | 完成一个任务平均调用几轮工具 | 步数直接决定延迟与成本 |
| Token / 成本 | 单次任务消耗多少 token | 决定能否规模化、是否赔本 |
| 人工干预率 | 需要人接手多少次 | 反映真实自主性,越低越接近"可依赖" |
2025–2026 年一个明显趋势是:用推理模型(如带长链思考的 o 系列、DeepSeek-R1 类)做规划,用小模型做单步工具执行,在"成功率"和"成本"之间找平衡。评估时也要分开测——规划模型的失误和工具执行层的失误要能归因,否则优化无从下手。
Agent 落地的关键,是把"会做事"变成"可度量、可兜底、可观察"的工程系统:用规划-执行-反思替代裸 ReAct,用分层记忆控制上下文,用显式闭环和失败对策把不确定性关进笼子。先把边界和回退设清楚,再谈自主,你的 Agent 才会从一次演示变成日常能依赖的流程。