ReAct 与推理循环
光有工具还不够
上一章我们看到,工具让 LLM 能"做事"。但一个关键问题是:模型怎么知道该按什么顺序使用工具?
如果只是"查个天气",一步就够了。但现实任务通常是这样的:
"我们的 API 响应时间在过去一小时突然变慢了,帮我查一下原因。"
这个任务没有固定的步骤。你需要先看监控、再看日志、可能还要查数据库慢查询、看最近的部署记录……每一步的结果决定下一步做什么。
ReAct 就是解决这个问题的模式。
ReAct:边想边做
ReAct 是 "Reasoning + Acting" 的缩写,由 Yao et al. 在 2022 年提出。核心思想是让模型交替进行推理和行动,而不是一次想好所有步骤再执行。
传统的做法可能是:
1. 模型先制定完整计划(5 个步骤)
2. 按顺序执行每个步骤
3. 返回最终结果
ReAct 的做法是:
1. 想一步 → 做一步 → 看结果
2. 根据结果想下一步 → 做 → 看结果
3. 重复直到完成
这更接近人类解决问题的方式——你不会在排查 bug 前就写好完整的排查计划。你会看到一个线索,跟着它走,根据发现调整方向。
一个完整的 ReAct 示例
我们来看模型在 ReAct 模式下是怎么工作的:
用户: 我们的 /api/users 接口响应时间从 50ms 飙升到了 2s,帮我排查原因。
思考: 响应时间突然飙升,可能是数据库慢查询、服务负载过高、或者最近的代码变更导致的。
我先检查一下最近是否有部署。
行动: 调用 get_recent_deployments(service="api", hours=2)
观察: 45 分钟前有一次部署,commit message 是 "add user activity logging"
思考: 45 分钟前有部署,时间上吻合。这个改动加了用户活动日志,可能在请求路径上
增加了额外的数据库写入。我来看看这个改动的具体内容。
行动: 调用 get_commit_diff(commit="a3f7b2c")
观察: 改动在 /api/users 的 handler 里加了一行:
await db.insert("user_activity_log", { userId, action, timestamp })
思考: 找到了。每次请求都在同步写一张日志表。我来确认一下这张表的情况。
行动: 调用 run_query("EXPLAIN SELECT * FROM user_activity_log LIMIT 1")
观察: 表没有索引,已经有 200 万行数据
思考: 问题确认了:每个请求都在同步往一个没有索引的大表写入数据。
解决方案是把日志写入改为异步,并给表加上合适的索引。
让我把分析结果汇总给用户。
最终回答: 问题定位到了。45 分钟前的部署在 /api/users 路径上加了同步的数据库
日志写入,目标表(user_activity_log)没有索引且已有 200 万行数据。
建议:1) 将日志写入改为异步(消息队列)2) 给 user_activity_log 表
添加适当索引 3) 如需快速恢复,可先回滚这次部署。
注意每一步"思考"都在做两件事:解释观察到的结果 和 决定下一步行动。这就是 ReAct 的核心。
思维链在 Agent 中的角色
你可能听过思维链(Chain of Thought, CoT)——让模型"一步一步想"来提高推理能力。
在 Agent 中,思维链不只是一个 prompting 技巧,它是推理循环的核心组成部分:
没有思维链:
行动 → 观察 → 行动 → 观察 → 行动
有思维链:
思考 → 行动 → 观察 → 思考 → 行动 → 观察 → 思考 → 行动
思维链在 Agent 中的价值:
- 让决策可追溯:你能看到模型为什么选择了这个工具、为什么传了这些参数
- 减少错误:在行动前"想一想",能避免很多冲动的错误行动
- 支持复杂推理:需要综合多个信息源才能做决策时,思维链提供了"工作记忆"
这也是为什么很多 Agent 框架会在系统提示中加一句类似"在每次行动前,先解释你的推理过程"的指令。
单步 vs 多步:什么时候需要循环
不是所有任务都需要多轮推理循环:
单步工具调用(不需要循环):
- "今天北京天气怎么样" → 调用天气 API → 回答
- "帮我把这段文字翻译成英文" → 直接回答(甚至不需要工具)
- "计算 sin(45°)" → 调用计算器 → 回答
多步推理(需要循环):
- "帮我排查这个 bug" → 需要看日志、看代码、尝试修复、验证
- "写一个完整的登录功能" → 需要创建文件、写代码、写测试、运行测试
- "分析这份数据并出报告" → 需要读数据、清洗、分析、生成图表
判断标准很简单:如果一步的结果会影响下一步的决策,就需要循环。
错误处理与自我纠正
Agent 在执行过程中不可避免会遇到错误。好的 Agent 能识别并修正错误:
思考: 我需要读取配置文件
行动: 调用 read_file(path="/etc/app/config.yaml")
观察: 错误——文件不存在
思考: 路径可能不对。让我找一下配置文件的实际位置。
行动: 调用 search_files(pattern="config.yaml")
观察: 找到 ./config/app.yaml
思考: 找到了,路径是 ./config/app.yaml
行动: 调用 read_file(path="./config/app.yaml")
观察: 成功读取到配置内容
这种自我纠正能力是 Agent 的关键优势之一。但它有个前提:工具需要返回有意义的错误信息。 如果工具只返回一个空结果而不解释原因,模型很难判断出了什么问题。
循环的终止
Agent 的推理循环不能无限进行。它需要知道什么时候该停下来:
自然完成:任务完成了,模型给出最终回答。
达到限制:预设的最大步骤数或 token 上限。防止模型陷入无意义的循环。
模型主动放弃:模型判断自己无法完成任务,返回说明。
人类中断:用户取消或调整方向。
在实际系统中,通常会设置一个最大迭代次数(比如 20 轮),作为安全网。没有这个限制,Agent 可能在遇到无解的问题时无限循环,消耗大量 token。
ReAct 的优势与局限
优势:
- 灵活:能应对事先无法预见的情况
- 可解释:每步推理都是可见的
- 自我纠正:能从错误中恢复
局限:
- 每步推理都消耗 token,成本较高
- 推理链越长,模型越容易偏离目标
- 模型可能陷入重复循环(做了同样的事情却期望不同的结果)
要点总结
- ReAct 让模型"边想边做",交替进行推理和行动,而不是一次规划好所有步骤。
- 思维链是 Agent 推理循环的核心组件,不只是提示词技巧——它让决策可追溯、减少错误。
- 如果一步的结果影响下一步的决策,就需要多步推理循环。
- 自我纠正能力是 Agent 的关键优势,但依赖于工具返回有意义的错误信息。
- 循环必须有终止条件——最大步骤数是必不可少的安全网。