04.1 — CoT 与 ReAct
定位: Agent 推理能力的核心范式——理解 Chain-of-Thought 如何让 LLM "学会思考",以及 ReAct 如何将推理与行动结合 面试高频度: ⭐⭐⭐⭐⭐ 考查方式: 概念对比题(CoT vs ReAct)、实现题(实现 ReAct 循环)、应用题(什么场景用什么推理范式)
一、这是什么?为什么需要它?
是什么
Chain-of-Thought(CoT,思维链) 是一种让 LLM 在回答问题前先生成中间推理步骤的提示技术。 ReAct(Reasoning + Acting) 是将推理(Thought)与行动(Action)交替进行的 Agent 架构模式。
为什么需要推理范式?
❌ 没有推理的 LLM(直接回答):
Q: "一个球棒和球共 1.1 美元,球棒比球贵 1 美元,球多少钱?"
A: "球 0.1 美元" ← 错误!直觉性回答
✅ 有推理的 LLM(CoT):
Q: "一个球棒和球共 1.1 美元,球棒比球贵 1 美元,球多少钱?"
A: "设球 x 元,球棒 x+1 元。
x + (x+1) = 1.1
2x + 1 = 1.1
2x = 0.1
x = 0.05
所以球是 0.05 美元"
← 正确!因为一步步推理避免了直觉陷阱核心洞察:LLM 的"思考"质量与它"展示思考过程"的程度成正比。CoT 和 ReAct 本质上是让 LLM 把"内隐的推理"变成"外显的推理链"。
二、原理拆解
2.1 Chain-of-Thought(思维链)
动机:为什么直接回答不准确?
LLM 的训练数据中包含大量"直接问答对",这导致 LLM 倾向于"快速回答"而非"仔细推理"。CoT 通过让 LLM 在给出答案前先写出推理过程,激活了 LLM 的推理能力。
CoT 的核心机制
标准 Prompt:
Q: 用户问题
A: [LLM 直接生成答案]
CoT Prompt:
Q: 用户问题
A: 让我们一步一步思考。
[LLM 生成推理步骤]
因此,答案是 [最终答案]CoT 的三种变体
| 变体 | 做法 | 适用场景 |
|---|---|---|
| Zero-shot CoT | 只说"让我们一步一步思考" | 通用,无需示例 |
| Few-shot CoT | 给出几个推理示例 | 需要格式引导的复杂推理 |
| Self-Consistency CoT | 多次推理 + 投票选最佳 | 需要高准确率的场景 |
python
# Zero-shot CoT 实现
def zero_shot_cot(question: str) -> str:
prompt = f"""
问题: {question}
让我们一步一步思考。
"""
response = llm.call(prompt)
return response
# Few-shot CoT 实现
def few_shot_cot(question: str) -> str:
prompt = f"""
问题: 小明有 5 个苹果,给了小红 2 个,又买了 3 个,现在有几个?
推理: 一开始有 5 个,给小红 2 个后剩 3 个,又买 3 个,所以现在有 6 个。
答案: 6
问题: 火车从北京到上海需要 4.5 小时,如果速度提升 20%,需要多久?
推理: 原时间 4.5 小时,速度提升 20% 意味着新速度是原来的 1.2 倍。
时间与速度成反比,新时间 = 4.5 / 1.2 = 3.75 小时 = 3 小时 45 分钟。
答案: 3.75 小时
问题: {question}
推理: """
response = llm.call(prompt)
return response
# Self-Consistency CoT
def self_consistency_cot(question: str, n_samples: int = 5) -> str:
"""多次推理,取最一致的答案"""
answers = []
for _ in range(n_samples):
# 用较高的 temperature 获取多样化的推理路径
response = llm.call(cot_prompt(question), temperature=0.7)
answers.append(extract_answer(response))
# 投票选出最常见的答案
from collections import Counter
most_common = Counter(answers).most_common(1)[0][0]
return most_common2.2 ReAct(推理 + 行动)
动机:CoT 只能"想"不能"做"
CoT 解决了 LLM 的推理质量问题,但它仍然是"纯文本推理"——LLM 只能使用训练时学到的知识,无法获取实时信息或调用外部工具。
ReAct 将推理(Thought)和行动(Action)结合,让 Agent 可以:
- 思考需要什么信息(Thought)
- 调用工具获取信息(Action)
- 观察工具返回结果(Observation)
- 基于新信息继续推理(Thought)
ReAct 的核心循环
问题: "特斯拉 2025 年的营收是多少?和 2024 年比增长了多少?"
Thought 1: 我需要查找特斯拉的财报数据。先搜 2025 年的。
Action 1: search("Tesla 2025 annual revenue")
Observation 1: Tesla 2025 年营收 980 亿美元
Thought 2: 然后需要 2024 年的数据来做对比。
Action 2: search("Tesla 2024 annual revenue")
Observation 2: Tesla 2024 年营收 820 亿美元
Thought 3: 980 / 820 = 1.195,同比增长 19.5%。我得确认一下计算。
Action 3: calculator("(980-820)/820*100")
Observation 3: 19.5122...
Thought 4: 数据确认完毕,可以给出完整回答。
Final Answer: 特斯拉 2025 年营收 980 亿美元,同比增长约 19.5%。CoT vs ReAct 的深入对比
| 维度 | CoT | ReAct |
|---|---|---|
| 核心 | 纯推理链 | 推理 + 行动交替 |
| 外部交互 | ❌ 无 | ✅ 可以调用工具 |
| 信息源 | 仅模型参数中的知识 | 模型知识 + 实时检索 |
| 错误累积 | 推理链越长越可能出错 | 可通过工具验证纠正 |
| 延迟 | 低(一次生成) | 高(多轮交互) |
| 可解释性 | 高(可看到推理过程) | 很高(看到推理+行动+结果) |
| 适用场景 | 数学、逻辑、常识推理 | 需要外部信息的复杂任务 |
什么时候用 CoT,什么时候用 ReAct?
CoT → 任务纯靠"想"就能解决
数学题、逻辑题、写作、翻译、代码生成
"不需要外部信息,靠推理就能完成"
ReAct → 任务需要"想 + 查 + 做"
研究分析、数据查询、问题排查、多步骤操作
"需要外部信息或工具,行动也是推理的一部分"2.3 ReAct 的实现架构
python
class ReActAgent:
"""最小 ReAct Agent 实现"""
def __init__(self, tools: dict, max_steps: int = 10):
self.tools = tools # {tool_name: function}
self.max_steps = max_steps
def run(self, question: str) -> str:
history = []
for step in range(self.max_steps):
# 构建 ReAct prompt
prompt = self._build_react_prompt(question, history)
response = llm.call(prompt)
# 解析 LLM 输出
thought = self._extract(response, "Thought")
action = self._extract(response, "Action")
final_answer = self._extract(response, "Final Answer")
# 如果 LLM 给出了最终答案,返回
if final_answer:
return final_answer
# 执行行动
if action:
tool_name, args_str = action.split("(", 1)
args_str = args_str.rstrip(")")
args = self._parse_args(args_str)
try:
result = self.tools[tool_name](args)
except Exception as e:
result = f"Error: {e}"
history.append(f"Thought {step+1}: {thought}")
history.append(f"Action {step+1}: {action}")
history.append(f"Observation {step+1}: {result}")
return "达到最大推理步数"
def _build_react_prompt(self, question: str, history: list) -> str:
tools_desc = "\n".join([f"- {name}: {fn.__doc__}" for name, fn in self.tools.items()])
prompt = f"""你是一个 ReAct Agent。可用工具:
{tools_desc}
请交替输出 Thought(推理)和 Action(工具调用),
格式:
Thought: 你的推理
Action: 工具名(参数)
Observation: 工具结果(由系统提供)
当信息足够时输出:
Final Answer: 最终答案
问题: {question}
"""
prompt += "\n".join(history)
return prompt
# 使用示例
tools = {
"search": lambda q: f"关于 {q} 的搜索结果...",
"calculate": lambda expr: eval(expr),
}
agent = ReActAgent(tools)
result = agent.run("2025 年全球 AI 市场规模是多少?")三、图解全景
┌──────────────────────────────────────────────────────────┐
│ CoT vs ReAct 对比图 │
│ │
│ CoT (Chain-of-Thought) │
│ ┌─────────────────────────────────────┐ │
│ │ Q: ... │ │
│ │ Thought 1 → Thought 2 → ... → 答案 │ │
│ │ 纯推理,不接触外部世界 │ │
│ └─────────────────────────────────────┘ │
│ │
│ ReAct (Reasoning + Acting) │
│ ┌─────────────────────────────────────┐ │
│ │ Q: ... │ │
│ │ │ │
│ │ Thought 1 ──→ Action 1 ──→ Obs 1 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Thought 2 ←──── 工具结果 │ │
│ │ │ │
│ │ Thought 2 ──→ Action 2 ──→ Obs 2 │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ Thought 3 ←──── 工具结果 │ │
│ │ │ │
│ │ Final Answer │ │
│ └─────────────────────────────────────┘ │
│ │
│ 关键区别: │
│ CoT: Thought → Thought → Thought → Answer │
│ ReAct: Thought → [Action ↔ Observation] → Answer │
└──────────────────────────────────────────────────────────┘四、实战验证
4.1 对比测试:同一问题两种范式
python
# === 对比:CoT vs ReAct ===
question = """
我有一笔投资:2024 年初买入 1000 股某股票,价格 50 元/股。
2024 年中分红 2 元/股,2024 年底价格 68 元/股。
我的年化收益率是多少?
"""
# CoT 方式(纯推理)
cot_response = llm.call(f"{question}\n让我们一步一步计算。")
print(f"[CoT]: {cot_response}")
# ReAct 方式(推理 + 行动)
tools = {
"calculate": lambda expr: f"结果: {eval(expr)}",
"get_stock_data": lambda ticker: "此股票的历史数据..."
}
react_agent = ReActAgent(tools)
react_response = react_agent.run(question)
print(f"[ReAct]: {react_response}")
# 结果对比:
# CoT 可能在某些步骤的计算上出错(LLM 算术不精确)
# ReAct 用计算器工具避免了算术错误
# → ReAct 在需要精确计算的场景更可靠4.2 性能基准
bash
# 对比不同推理范式的准确率和延迟
# 测试:100 道数学推理题
# Zero-shot(无推理): 准确率 58%, 延迟 0.5s
# CoT: 准确率 82%, 延迟 1.2s
# Few-shot CoT: 准确率 88%, 延迟 1.5s
# Self-Consistency CoT: 准确率 92%, 延迟 5.0s (5次)
# ReAct: 准确率 90%, 延迟 3.0s (含工具调用)
# 结论:
# 1. CoT 显著提升推理准确率(+24%)
# 2. Self-Consistency 最高但最慢
# 3. ReAct 在需要工具的复杂场景才有优势五、面试视角
| 追问 | 答案要点 |
|---|---|
| CoT 的原理?为什么能提升推理能力? | CoT 让 LLM "外显推理过程",激活了 LLM 在预训练中学到的推理能力。直接回答会触发"快速回答"模式,CoT 强制 LLM 进入"仔细推理"模式 |
| ReAct 和 CoT 的本质区别? | CoT 是纯推理链(Thought→Thought),ReAct 是推理与行动交替(Thought→Action→Observation→循环)。ReAct 可以获取外部信息并验证推理 |
| ReAct 如何防止推理偏离? | 工具验证(用搜索/计算器验证中间结果)、观察约束(每一步 Observation 必须基于工具返回,不能编造)、步数限制(避免无限制循环) |
| Self-Consistency CoT 的优缺点? | 优点:通过多次采样投票提高准确率。缺点:成本增加 N 倍、延迟增加 N 倍。适合对准确率要求极高但不要求实时的场景 |
| 什么时候应该用 CoT 而不是 ReAct? | 任务不需要外部信息(数学、逻辑、写作),或者工具调用成本太高(每次工具调用都有延迟和费用)。纯推理任务用 CoT 更高效 |
| ReAct 的 Thought 步骤可以省略吗? | 可以,但会退化为 Tool Use 模式。Thought 步骤的价值在于:提高可解释性、帮助 LLM 保持推理连贯性、便于调试和审计 |
📚 相关链接
- **任务分解** — 将复杂任务拆解为子任务
- **自我反思与修正** — 从推理错误中学习
- **Agent 架构模式** — ReAct 是架构模式之一
- **感知-行动循环** — ReAct 是循环的一种实现
- ← 返回 **Agent规划与推理索引**