所有 Agent 推理框架的共同祖先是 Chain-of-Thought(思维链)。Wei et al. 2022 年发现:让模型在给出答案前先写出推理过程,准确率大幅提升。这个发现开启了「让 LLM 慢思考」的研究方向。
1. Standard CoT vs Zero-Shot CoT
Q: 小明有 3 个苹果,又买了 2 袋苹果,每袋 4 个,共几个?
A: 先算购买量:2袋×4=8个。再加原有:3+8=11个。答:11个。
新问题:
Q: 小红有 5 个橙子,买了 3 袋...
「Q: 小明有 3 个苹果...
Let's think step by step.」
模型就会自动展开推理过程
2. CoT 的局限性(为什么需要 ReAct)
- 无法获取实时信息:CoT 只在 LLM 的参数知识范围内推理,不能搜索、不能查数据库
- 事实性错误无法自纠:推理链里如果某步用了错误的知识,后续步骤会在错误基础上继续推
- 单次输出:只能生成文字,不能执行代码、调用 API 等行动
ReAct 的诞生就是为了解决这三个问题——让推理(Reasoning)和行动(Acting)交替进行。
ReAct(Yao et al., 2022)把 LLM 的工作方式从「一次性输出」变成「推理 → 行动 → 观察」的循环,每次行动的结果成为下次推理的输入。
1. ReAct 的三个原语
「我现在需要搜索 X,因为...」
search("关键词")「搜索结果:...」
2. ReAct vs 普通 CoT 的核心区别
一个生产可用的 ReAct System Prompt 包含:工具列表、输出格式规范、终止条件。
1. System Prompt 模板
你是一个智能助手,可以使用以下工具完成任务:
## 可用工具
- search(query: str): 搜索互联网,返回相关信息片段
- calculator(expr: str): 计算数学表达式,如 "2 * 3.14 * 5"
- finish(answer: str): 当任务完成时调用,输出最终答案
## 输出格式
每次只输出以下格式之一:
Thought: [你的推理过程,说明为什么要做下一步]
Action: [工具名]([参数])
或者当观察到结果后继续:
Thought: [基于 Observation 的分析]
Action: [下一步动作]
## 重要规则
- 每次只输出一个 Thought + 一个 Action,不要一次性规划所有步骤
- Action 之后等待 Observation,不要自己编造 Observation
- 信息足够时立刻调用 finish()
2. 完整对话示例(带 Observation 注入)
Thought: 这是关于 2024 年巴黎奥运会的实时信息,我的训练数据可能不完整,需要搜索。
Action: search("2024 巴黎奥运会 游泳 冠军 世界纪录")
Observation: [搜索结果] 潘展乐在 100 米自由泳以 46.40 秒打破世界纪录夺金...
Thought: 已获得主要冠军信息,但想确认是否有更多记录被打破,再查一次。
Action: search("2024 奥运会 游泳 世界纪录 全部")
Observation: [搜索结果] 本届共打破 2 项世界纪录:潘展乐 100m 自由泳 46.40s...
Thought: 信息已足够,可以回答。
Action: finish("2024 巴黎奥运会,潘展乐在 100 米自由泳以 46.40 秒打破世界纪录夺冠。本届共打破 2 项游泳世界纪录。")
3. Observation 注入方式
当 LLM 输出 Action: search("xxx") 后,你的代码需要:① 解析出工具名和参数;② 执行工具调用;③ 把结果以 Observation: ... 的形式追加到对话历史;④ 再次调用 LLM。
while not finished:
response = llm.generate(messages)
thought, action = parse_react_output(response)
if action.tool == "finish":
return action.args # 任务完成
result = execute_tool(action.tool, action.args)
messages.append({"role": "tool", "content": f"Observation: {result}"})
steps += 1
if steps > max_steps: break # 防止无限循环
1. Reflexion(带记忆的 ReAct)
在 ReAct 基础上加入「失败反思」:任务失败 → LLM 写一段反思(「哪里错了?为什么?」)→ 反思存入持久记忆 → 下次重试时先读取历史反思。相当于给 Agent 加了「从错误中学习」的能力。
[尝试1失败] 反思:我直接搜索了太宽泛的关键词,下次应该先明确搜索范围再细化。
[尝试2失败] 反思:我错误地把百分比当绝对数值用了。
2. Plan-and-Execute(计划再执行)
标准 ReAct 是「边想边做」,步骤顺序不固定。Plan-and-Execute 把它分成两阶段:
生成完整步骤计划
逐步执行,遇到意外可回到①
优点:步骤可检查、可人工审核;缺点:初始计划可能不准确,灵活性不如标准 ReAct。
3. LATS(Language Agent Tree Search)
把 ReAct 和 Monte Carlo Tree Search(MCTS)结合:在每个 Action 决策点,采样多个候选 Action,用 Value Model 评估哪条路最有希望,类似 AlphaGo 的搜索策略。效果最好但计算成本极高,适合离线任务。
4. Structured ReAct(强制格式)
利用 LLM 的 JSON/结构化输出能力,强制 Action 输出为 JSON:
{
"thought": "需要搜索 2024 年奥运会游泳结果",
"action": {
"tool": "search",
"parameters": {"query": "2024 Olympics swimming results"}
}
}
优点:解析稳定,不会因为 LLM 输出格式不规范而报错;缺点:token 消耗更多。
ToT(Yao et al., 2023)把单条推理链扩展成树形结构:在每个推理步骤,生成多个候选续写,用 Value Model 评估每条分支的前景,选最优的继续搜索。
1. ToT 的四个组件
| 组件 | 作用 | 实现方式 |
|---|---|---|
| Thought Decomposer | 把问题分解成适合逐步探索的格式 | Prompt 工程,让 LLM 输出「下一步可能性」 |
| Thought Generator | 在当前节点生成 k 个候选续写 | 采样 k 次(temperature > 0)或让 LLM 生成 k 个选项 |
| State Evaluator | 对每个候选节点打分(有希望/没希望) | Value Prompt 让 LLM 评分,或启发式规则 |
| Search Algorithm | 决定如何遍历树 | BFS(广度优先)或 DFS(深度优先) |
2. ToT 适合什么任务
- 数学证明(多种证明路径)
- 代码规划(哪种架构方案)
- 创意写作(探索不同风格/情节走向)
- 谜题/游戏(24 点游戏、填字)
- 需要工具调用的任务(用 ReAct)
- 简单问答(CoT 就够了)
- 实时性要求高(ToT 比 ReAct 慢 5~20 倍)
- 预算有限的生产环境
GoT(Besta et al., 2023)把 ToT 的树形结构进一步扩展成有向无环图(DAG)。树中每个节点只有一个父节点,而图中多个分支可以汇聚——允许把多条推理路径的结果聚合合并,再继续推理。
GoT 目前主要在研究阶段,生产落地案例少。对于大多数工程场景,ReAct 仍然是最实用的选择。
| 框架 | 复杂度 | 速度 | 精度 | 工具调用 | 推荐使用场景 |
|---|---|---|---|---|---|
| CoT | ⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ❌ | 推理题、分析型任务 |
| ReAct | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ✅ | 需要工具的多步骤任务(首选) |
| Reflexion | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ✅ | 允许重试的任务(代码、搜索) |
| Plan-Execute | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ✅ | 步骤可预期、需要人工审核的任务 |
| ToT | ⭐⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ | ❌/有限 | 探索性问题、离线批量任务 |
| GoT | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐⭐⭐⭐ | ❌/有限 | 研究场景,暂不推荐生产 |
1. 先试 CoT,如果准确率够就不需要更复杂的框架
2. 需要工具(搜索、代码、API)→ 用 ReAct
3. 任务允许多次重试,且有明确的失败信号 → 加 Reflexion
4. 任务需要探索多种方案且时间充裕 → 考虑 ToT
5. 对延迟敏感的生产环境 → 永远优先用最简单够用的框架
1. 防止无限循环
ReAct 循环没有自然终止条件,必须设置:
- 最大步骤数(通常 10~20 步):超过则强制停止并返回当前最优结果
- 最大 token 预算:超过上下文窗口限制时截断早期对话历史
- 重复检测:连续两步 Action 完全相同时中断(Agent 卡住了)
2. 上下文窗口管理
ReAct 每次循环都在往对话历史里追加内容,步骤多了会超出 token 限制。解决方案:
- 对 Observation 做摘要压缩(长搜索结果 → 只保留最相关的段落)
- 只保留最近 N 步的完整历史,早期步骤只保留摘要
3. 解析 Action 的鲁棒性
LLM 的输出不总是严格按照格式。工程上需要处理:
- Action 里的工具名拼错 → 模糊匹配或提示 LLM 重试
- JSON 格式不合法 → 用 JSON 修复库(如
json_repair) - 没有 Action 只有 Thought → 判断是否已经是 finish 意图