跳到主要内容
SecondHome

ReAct 轨迹逐行拆解:从 Thought/Action/Observation 到 tool_call_id

7 分钟入门1bL2 p.8-11 ↗p.18-25 ↗录像 ↗支撑材料 3 篇

ReAct 给 agent 循环起了名字,今天的 API 给循环换了零件:Thought 折进 assistant 消息、Action 变成带唯一 call ID 的结构化 tool_call、Observation 按 tool_call_id 回填成 role 为 tool 的消息。逐行拆一条完整轨迹,看清论文思想如何落进消息列表、harness 在每步承担了什么、哪几行决定成败。

先亮对象:一条 ReAct 原始格式的六行轨迹,问的是加纳的首都。它出自 ReAct: Synergizing Reasoning and Acting in Language Models(Yao 等,Princeton 与 Google Brain,ICLR 2023)。

本篇逐行拆这条轨迹,一次看清三件事:论文思想怎么落进今天的消息列表,harness 在中间干了什么活,轨迹上哪几行决定成败。

一、轨迹原件:Thought / Action / Observation 三件套

ReAct 的核心主张一句话讲完:让模型交替生成推理痕迹(Thought)与面向任务的动作(Action),再把环境返回的观测(Observation)接回上下文。推理指导行动,行动为推理取证。

关键的机制细节是:推理痕迹不会用完即弃,它留在上下文里,持续引导后续的工具调用。第一步想的是为什么检索。到了第三步,问题变成证据够不够、要不要换打法,第一步的思考仍在发挥作用。

整条轨迹就是决策依据本身。也正因为只是文本格式 + 循环,小模型靠 few-shot 示范就能超越当时的大模型。几乎所有现代 agent loop 都是它的思想后代。

用课程的例子跑一遍(slides L2 p.8-11),问的是加纳的首都:

user: 加纳的首都是哪里?Thought: 我需要检索加纳首都的权威信息。Action: search_web [“加纳 首都”]Observation: Accra(阿克拉),加纳的首都。Thought: 观测已足够,可以作答收尾。Action: finish [“加纳的首都是 Accra。”]

最后一行最容易被忽略:finish (text) 是 harness 控制的调用,不是外部程序。模型显式发出它,循环才终止,harness 用其中的文本向用户归纳(slides L2 p.8-11)。

同一组幻灯片给出五类接口的光谱,finish 之外:

  • search_web(‘site:cmu.edu 今日公告’),取新鲜证据。
  • execute_code (‘sum ([18,23,41,54])/4’),做精确计算。
  • generate_image (query),产出图像。
  • create_grocery_cart (items) 是真正的外部动作,购物车返回 ‘Cart C17・3 items・$31.42’。

二、词元层:text continuation 与 tool continuation

在 ReAct 的年代,上面整条轨迹是塞进一条提示词里的文本续写。今天的 API 把它拆成带角色的消息,但拼装逻辑没变(slides L2 p.18-25)。

请求中的 system/user/assistant 消息经 chat template 串成单条模型输入,Qwen 用 <|im_start|> 序列分隔消息,工具清单也随请求注入:

{
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "parameters": {
          "type": "object",
          "properties": {
            "location": { "type": "string" },
            "units": { "enum": ["C", "F"] }
          }
        }
      }
    }
  ]
}

到了该行动的时刻,模型有两个续写方向:text continuation 接着说话,tool continuation 吐出结构化调用:

<tool_call>{"name": "get_weather", "arguments": {"location": "上海", "units": "F"}}</tool_call>

API 层解析这段输出并附上唯一 call ID。执行结果不写进 assistant 消息,而是按 tool_call_id 回填一条 role 为 tool 的新消息。

一轮并行发出多个调用时,同样靠 ID 一一配对,谁也不会认错观测。

你以为自己在传消息列表,模型实际看到的仍是 chat template 拼出来的一条长文本。ReAct 时代的整段轨迹输入,和今天的 messages 数组,在词元层面是同一种东西,只是切分与角色标注变成了 API 的合同。

call ID 为什么必须唯一

模型一轮可以并行发出多个 tool_call,每份观测都要靠 tool_call_id 找回自己那次调用。ID 是把哪次调用与哪份观测钉在一起的凭据,丢了它,轨迹立刻串线。

把两个时代放进同一张图:

三、harness 两阶段:轨迹背后的调度机

轨迹的每一步背后都有 harness 在调度。OpenHands 的 dispatch(官方 references,源码级)把这件事拆成两阶段:

阶段流程产物
初始化Tool spec → resolve_tooltools_map[name] = ToolDefinition(schema · Action · executor)
每次调用Parse → Lookup → action_from_arguments 校验 → executor 执行ObservationEvent(call_id)

初始化时,工具注册成一张表:名字 → schema + 动作 + 执行器。模型每次输出 tool_call,harness 先解析、再查表、再按 schema 校验参数,最后交给 executor,观测打包成带 call_id 的事件回填。首尾相接,就是完整的循环:

四、决断点:同一份观测,两种命运

轨迹的质量取决于几个关键行怎么写。文采无关。

Observation 已给出 Accra。 Thought: 观测足量,直接作答。 Action: finish [“加纳的首都是 Accra。”] harness 收到 finish,循环终止,归纳返回。

Observation 已给出 Accra。 assistant: 加纳的首都是 Accra。(普通文本,无 tool_call) 不触发终止,循环带着全量历史再跑一轮,token 白烧。

最常见的空转

在带 finish 接口的工具集里,observation 足量时务必显式 finish—— 普通文本不终止循环,模型会带着完整历史空转,token 一路烧到 max_turns。

另外两个高频决断点。检索返回 0 条结果时,该换 query 重搜,不该原样重发;工具报错时,把错误消息原样回灌往往是最优解,模型能自纠:

assistant: tool_call:get_weather {“location”: “上海”, “units”: “摄氏度”}tool: 报错:units 必须是枚举 [“C”, “F”] 之一Thought: 参数不合法,换成枚举值重发。Action: get_weather {“location”: “上海”, “units”: “F”}

三个决断点位置不同,都发生在观测之后的那一行。Observation 回填之后,模型显式收尾、换个打法,还是无视报错原样重发,基本决定了这条轨迹收敛得好不好,会不会一路空转到 max_turns。

与本讲的连接

本篇是讲次页「ReAct:推理与行动的交替」一节的下钻。几处线索在本文里各有落点:

  • 伴读的范式三级跳(prompt 示范 → Toolformer 自监督 → 原生 function calling),中段对应本文第一节,现代段对应第二节。
  • 概念卡里的 harness 与 Function Calling 两条,落到地上就是第三阶段的 tools_map 与 tools 注入。
  • 实践视角那句「工具描述是给模型看的 UI」,机制版就是 tools 清单里的 JSON Schema 与报错回灌。

做完讲次页 quiz 里「ReAct 循环的核心结构」一题,再回看第一个 trace 块。六个前缀本身就是答案。

本节论文与规范

本节资料

  • OpenAI Function Calling 指南 规范入门官方引用

    tools=[{type:function,…}] 进请求、模型返回 tool_calls、以 call_id 配对回填结果的形状,已成行业事实标准。指南给出从定义工具 schema 到拿到结构化输出的完整链路,是动手第一站,本讲调用机制板块多次对照此文档。

  • 提出 Thought/Action/Observation 交替的推理-行动范式:先想再动、观察结果再想,构成现代 agent loop 的思想源头。论文 Fig.1 的轨迹对比是逐行拆解 agent trace 的现成教材,几乎所有框架的 trace 格式都能在此找到祖先。本讲官方同步读物之一。

  • 从语言模型视角系统回答「什么是工具」:给出统一的形式化定义,把工具知识与事实性知识、动作性知识区分开,并建立覆盖工具创造、选择、使用的统一分析框架。本讲「工具定义」板块(p.3-5)的概念地基即出自此文,是官方指定的第一篇入门读物。

AI 生成 · 仅供参考,与课程方无关。选中正文里的任何一段也能直接问我(解释一下 / 举个例子)。

本详解以 CMU 11-768 AI Agents (Fall 2026) 课程大纲为参考框架独立撰写,AI 辅助起草、人工抽审。 涉及的官方课件、论文与课程材料版权归原所有方,讲义为站内镜像、其余外链原文出处; 观点与文字(除标注引用外)为作者原创。