ReAct 循环
ReAct 让语言模型交替思考和调用工具:Thought → Action → Observation。模型写下下一步需要的信息,请求工具获取信息,再根据结果继续推理。这样,回答可以使用模型参数中没有的信息,每一步也有据可查。
可以把上下文看成研究笔记。任务中的问题、思考、动作和工具结果都写在其中。每次调用,模型读取这些记录,再补充内容。本课没有独立保存任务状态的隐藏记忆;下一轮需要的信息必须留在上下文中。模型仍有训练所得的知识,但那些知识不能代替对本次检索结果的记录。
本课的模型输出来自 claude-opus-5-5 的真实录制。每种策略各选一次能呈现其特点的运行,文字未经修改。工具会针对同一知识库重新执行,画面中的观察结果就是搜索函数的返回值。
ReAct 循环如何工作
循环在轮次上限内重复三个动作。模型在同一次生成中先写 Thought:,说明下一步;再写 Action:,例如 search["Mara Voss"] 或 finish[1974]。finish 提交答案并结束循环。工具调用由运行时执行,返回值以 Observation: 写入记录。
模型生成文本,运行时解析动作、执行工具并追加结果。画面中,每个查询和结果都经过 runtime 入口,跨越虚线边界:左侧是模型上下文,右侧是外部世界。如果让模型自行编写观察结果,就失去了工具返回值这一证据来源。
观察先到,后续思考才能引用它。录制中,模型从检索页面读到电影导演 Mara Voss,画面也会用连线标出思考所引用的观察。本课让三种策略回答同一个两步问题:Aurora Run 的导演出生于哪一年? 知识库有四页。第一页介绍同名小说,包含干扰信息:小说作者出生于 1951 年。答案所需的信息就在旁边:2011 年的电影由 Mara Voss 执导。
逐步演示
默认策略是完整 ReAct。下列步骤对应录制中的运行;页码与画面的知识库一致,为 1–4。
- 开始。 上下文只有问题,右侧知识库尚未读取。
- 第 1 轮:思考。 模型先要弄清 Aurora Run 的导演是谁。这是子目标,还不是答案。
- 第 1 轮:动作。 模型写下
search["Aurora Run"]。此时工具尚未执行,动作仍是文本。 - 第 1 轮:查询。 运行时把查询交给知识库,匹配第 1 页标题。
- 第 1 轮:观察。 返回内容说明:Aurora Run 是 Henrik Dahl(生于 1951 年)在 1998 年出版的小说;2011 年的电影由 Mara Voss 执导。记录中同时出现了相关事实和干扰年份。
- 第 2 轮。 模型写明电影导演是 Mara Voss,而生于 1951 年的 Henrik Dahl 是小说作者,所以还需要 Voss 的出生年份:干扰年份被明确点名并排除。实线标出它引用的观察。随后
search["Mara Voss"]找到第 2 页:Mara Voss 是挪威电影导演,生于 1974 年。 - 第 3 轮:思考。 模型根据刚读到的页面确认:Mara Voss 执导了 2011 年的电影,出生于 1974 年。
- 第 3 轮:动作。
finish[1974]结束循环。 - 判定。 答案 1974 正确,两次检索都能追溯到知识库页面。
切换策略可以看到另一种结果。仅推理(Reason only)思考两次,不调用工具。录制中的模型承认记不清这部电影及其导演,但输出格式要求给出具体年份,于是称这是“对一位典型当代导演的低置信度猜测”,答 1975。很接近,但仍然错误。最后,画面点亮第 1、2 页,展示始终没有被查询的事实。
仅行动(Act only)执行与 ReAct 相同的两次搜索,回答 1974,也跳过了 1951 这个干扰年份。但记录旁没有证据连线,因为它没有写出思考。选择 Aurora Run、提取 Mara Voss、排除干扰项的过程都没有记录下来。比较三种策略时,注意连线:ReAct 的思考用实线连接观察;仅推理用红色虚线连接前面的思考;仅行动没有思考与证据之间的连线。
为什么需要循环
第二次查询 search["Mara Voss"] 依赖第一次查询的结果:导演姓名来自检索页面。在读到它之前,模型还无法写出这个具体查询。循环让下一步决策使用刚获得的证据。
既然仅行动也答对了,写出思考有什么用?在这个两步任务中,主要价值是便于检查。模型每次都读取全部记录,录制过的仅行动运行通常也能完成两步检索。但 ReAct 会写下为什么跳过干扰年份,并连接到所引用的页面。仅行动如果某次回答 1951,记录里没有对应的子目标可供排查。仅推理则无法查证外部事实,即使承认不能核实,在本课要求给出年份的格式下仍会猜测。
本课每轮调用模型一次,同时生成思考和动作,最多调用一次工具。记录随轮次增长,每次调用都要重读已有内容,因此长任务的单步成本也会增加。这里设置 max_turns = 4,三次录制都在上限内结束。写出思考会增加每次调用的 token,而不是调用次数,却没有提高这个小任务的答题准确率。在更长的任务中,子目标记录既能供模型后续参考,也能供人检查,但收益仍需结合任务验证。
循环中的判断由模型作出。运行框架提供工具、解析动作并反馈观察;本课画出的运行时不会替模型决定思考内容或工具选择。构建应用时,要区分这两层职责:框架为模型提供执行和反馈通道。
边界情况
- 搜索未命中。 工具返回 “No matching page found.”,作为普通观察交给模型。模型可以改写查询。本课三次录制都未遇到未命中,但协议同样支持这种返回值。
- 轮次耗尽。 在
max_turns内未出现finish,循环就会退出,不返回答案。本课录制均按时完成。 - 动作格式错误。 本课的最小流程没有处理无法解析的动作。实际运行时需要捕获错误,并返回错误观察,让模型有机会修正。
- 证据本身有误。 ReAct 可以追溯工具结果,但不能保证知识库正确。错误来源也可能产生带有完整引用的错误答案。
常见错误
- 以为模型会保留上下文之外的任务状态。 本课没有隐藏的任务记忆。需要供下一轮使用的信息必须写入记录。
- 让模型编写工具观察。 工具由运行时执行。省略这一步,所谓观察就可能只是模型猜测。
- 把一次相同答案当成相同能力。 仅行动在这个两步任务中答对,不代表它在实际任务中表现相同;其未写出的推理也无法直接检查、重放或调试。
- 凭语气判断可靠性。 仅推理承认不确定,仍给出了错误年份。输出格式会影响行为;核对证据比判断语气更有用。
- 先调用工具,再检查
finish。 应先判断结束动作,否则最终答案可能被当成搜索查询。
本课的简化
本课只有两个工具、一个小型知识库和两步检索。三种策略的文字来自 claude-opus-5-5 的录制,未经改写,工具则重新执行。实际系统还需要工具结构定义、动作解析与错误处理、重试、词元预算,以及长记录的压缩或摘要。这些机制都建立在同一个循环上。