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,成本较高
  • 推理链越长,模型越容易偏离目标
  • 模型可能陷入重复循环(做了同样的事情却期望不同的结果)

要点总结

  1. ReAct 让模型"边想边做",交替进行推理和行动,而不是一次规划好所有步骤。
  2. 思维链是 Agent 推理循环的核心组件,不只是提示词技巧——它让决策可追溯、减少错误。
  3. 如果一步的结果影响下一步的决策,就需要多步推理循环。
  4. 自我纠正能力是 Agent 的关键优势,但依赖于工具返回有意义的错误信息。
  5. 循环必须有终止条件——最大步骤数是必不可少的安全网。