Claude Certified 知识点复习:深入 Agent Loop:Claude Code 到底怎么决定下一步调哪个工具

0 阅读7分钟

1773666262232.jpg 从外面看 Claude Code 像魔法。你扔一个含糊的需求 —— "给这个 endpoint 加个鉴权" —— 它打开三个文件、跑一次 grep、写一个 patch、跑测试、提交。每一步它都在决定下一个工具调什么。怎么做到的?

如果你用 Claude Code 或 Claude Agent SDK 有一段时间,你大概已经有自己的心智模型。但我敢打赌这个模型比你以为的模糊 —— 至少我自己的在写这篇之前是。

这篇是一次彻底的走查:agent loop 是什么、每一轮里工具选择到底怎么发生、loop 在哪里终止,以及一个关于 sub-agent 边界的反直觉事实 —— 很多人第一次都搞错。

所有内容都基于 Anthropic 公开文档(Messages API、Claude Agent SDK、Claude Code reference),不涉及任何内部实现 / 反编译。

Loop 一张图讲完

Agent loop 的核心就是这样:

user → model → tool_use → tool_result → model → ...
                               ↑____________|

展开写:

  1. 调用方向 Messages API 发请求。system prompt 描述"模型是谁",user turn 是用户请求,tools 参数列出所有允许调用的工具。
  2. 模型读完整轮对话 + 工具定义,决定下一步。如果它需要信息 / 想执行动作,它输出一个 tool_use content block,带工具名和参数。否则它输出 text 并结束本 turn。
  3. 运行时执行工具,然后把新的一个 user turn 追加进对话,里面放一个 tool_result content block,引用前一步的 tool_use_id
  4. 模型再读一次更新后的对话,再决定。工具调用、文本、或者停止。
  5. 循环直到 stop_reasonend_turn(模型主动结束)或者触发硬上限。

就这些。关键是:loop 不在模型里跑,loop 在你的代码(或 Claude Code 的代码)里跑,模型只是你在循环里反复调用的"决定下一步"函数。

这点很重要,它会改变你推理"模型在第 N 步到底知道什么"的方式。

模型具体怎么选工具?

没有规则引擎、没有决策树。工具选择是模型驱动的:每一步,Claude 看到到目前为止的完整对话(system prompt + user turn + 之前所有的 tool call 和 tool result)+ 完整工具定义,然后一次 forward pass 输出下一条消息。选哪个工具、参数是什么,都是这次 forward pass 的输出。

实战中几个关键点:

  • 工具 description 远比工具名重要。模型读每个工具的 description 字段就像人读一个函数的 docstring。如果两个工具的描述相似,模型就会在它们之间反复横跳,看起来像 bug。先修描述,再怀疑模型。
  • 参数是从上下文里 ground 出来的,不是从用户字面意思。用户说"修那个跑挂的测试",模型可能先调 grep,然后根据 grep 结果调 read_file,再调 edit_file 改 patch —— 用户一个文件名都没提过。这种 grounding 来自 tool result 在对话里不断积累。
  • System prompt 能"驯化"工具选择模式。一句"总是先用 grep 定位再读文件"就会一致地 nudge 模型选择。只列工具名不加引导,选择就全看感觉。

这也是为什么工具 description 会吃 prompt 预算。每一步每个工具定义都会被重新发一次,工具太多会烧 token。

上下文怎么累积、什么东西又被漏掉

这里最容易搞混。工具跑完返回时,运行时不是"把结果交给模型做变量",它是把一个新的 turn 追加进对话:

// 伪代码
conversation.push({
  role: "user",
  content: [
    {
      type: "tool_result",
      tool_use_id: previousCall.id,
      content: toolOutput,
    },
  ],
});

从模型视角看,tool result 是一条 user 消息。模型没有"侧信道"记住工具跑过;下一次 forward pass,它看到的是对话历史里的那一条,跟其他用户消息一样。

两个推论:

  1. 不在对话历史里的东西,模型看不到。 如果你的运行时在两次工具调用之间做了什么 —— 日志、埋点、side effect —— 模型完全不知道,除非你显式以 tool result 或注入消息的形式把它放进对话。
  2. 每一 turn 上下文都在涨。 长 agent loop 会烧 context 非常快。这是你 agent 能跑多深的最紧约束,也是为什么 context management(刚好是 CCA 考试的一个 Domain)在生产里是一门承重墙级别的学问。

Loop 什么时候停?

Loop 终止于三种情况之一:

  • 模型返回 stop_reason: "end_turn" 且没有 tool_use block。这是模型主动说"我干完了"。
  • 运行时配置的 hard step limit 触发。Claude Code 有一个,你自己写的 SDK runner 也应该有一个。
  • 某个工具抛错,运行时决定抛出而不是重试。

最有意思的是第一种,因为是模型在决定什么时候停。如果你看到 Claude Code 在不该继续的时候还在继续循环,通常不是模型的锅,是 tool result 给了歧义信号 —— 一个"没找到匹配"的 grep 输出如果格式不清楚,可能被模型解读成"还没找够继续搜",而不是"搜不到"。修工具不修模型就好。

Sub-agent 边界,以及那个很多人都搞错的事

Claude Code 通过 Task 工具把任务 delegate 给 sub-agent(Agent SDK 里有类似的原语)。大部分人的心智模型是:"sub-agent 就是另一个并行跑的 agent loop"。

接近,但漏了最关键的部分。

当你用 Task 启动一个 sub-agent,它跑在一个独立的、隔离的对话里。它有自己的 system prompt、自己的工具集(可配置)、自己的 context 窗口。父 agent 的对话对 sub-agent 完全不可见。边界之间只有两样东西能跨:

  • 进去:你传给 sub-agent 的任务描述
  • 出来:sub-agent 的 final text response

反直觉的推论:父 agent 看不到 sub-agent 沿途看到的任何东西,只能看到最终答案。如果 sub-agent 读了十个文件并综合出一个结论,父 agent 只拿到结论。它问不到"你都读了哪些文件",除非 sub-agent 在最终响应里显式把这个信息塞进去。

这就是为什么 CCA 考试死磕"结构化交接协议":如果你想让父 agent 用 sub-agent 找到的中间数据,sub-agent 必须在最终消息里把它返回,用父 agent 能解析的格式。不是"记住"—— 边界之间没有记住。能返就返,不返就丢。

模型驱动 vs 决策树 —— 什么时候用哪个

不是所有工作流都该做成模型驱动 loop。有时候你就要一棵僵硬的决策树:如果 X 就 Y,否则 Z。问题是什么时候

一个大致的判断:

  • 模型驱动 loop:状态空间大、模糊、未知 —— 代码导航、开放式研究、对话式客服。这种场景本来就是 LLM 泛化能力的主场。
  • 决策树:状态空间小、定义清晰、走错一步代价高 —— 支付流程、合规校验、任何有监管的流程。

把两个混起来用是常见且正确的:外层用决策树(这笔是支付吗?这是合规查询吗?),在需要灵活性的分支里再嵌套模型驱动 loop。CCA 考试 Domain 1 里这类题出得非常多 —— "什么时候让模型决定、什么时候强制走确定性路线"是反复出现的题型。

收尾

Agent loop 不是魔法。它就是一个顺序:模型推理 → 工具跑 → 结果追加为 user turn → 模型再推理。工具选择是模型根据完整对话做的一次选择。Sub-agent 是隔离的,要父 agent 看到什么就必须,不然就丢了。

如果你在备考 Claude Certified Architect,上面这些内容在 Domain 1(Agent 架构与编排),占 27% —— 全考最大的一块。对应的知识点在 claudecertified.io/zh/knowledg… 上有社区整理好的版本 —— 一个社区搭的免费练习站,按 Domain 分题,中英双语。不是 Anthropic 官方,题目是社区根据公开考试指南原创的。

如果你不是为了考试 —— 理解 loop 的运作方式在生产里一样值钱。生产 agent 的 bug 几乎永远是上下文在 loop 里怎么流动出了问题,不是模型本身的问题。


Claude Certified 是一个独立的社区备考平台,与 Anthropic 无官方关联。