avatar
原创2026/04/10

深入理解 Claude Code /loop 命令的底层实现

AI 总结

深入分析 Claude Code /loop 命令的底层实现原理,涵盖 Agentic Loop 的运行机制、Tool Use 的多轮调用流程、循环终止条件、工具执行结果如何被整合进上下文,以及与单次对话模式的本质区别。

Claude Code 中大多数操作都是"一问一答"的模式。但面对多步骤的复杂任务时——比如"把所有测试跑通"或"调查这个 bug 的根因"——Claude Code 会进入 Agentic Loop 模式。/loop 就是手动触发这个模式的入口。

单次对话 vs Agentic Loop

单次对话模式是标准的 Request-Response 模式。每次用户输入触发一次 API 请求,模型生成响应后控制权交回用户:

用户输入 → API 请求 → 模型响应 → 等待用户下一次输入

Agentic Loop 模式则是模型自主驱动的连续执行模式。模型在完成一个工具调用后,分析工具的执行结果,决定下一步动作,持续循环直到任务完成或触发终止条件:

用户输入
    │
    ▼
API 请求 → 模型响应(tool_use)
    │
    ▼
本地执行工具 → tool_result
    │
    ▼
API 请求 → 模型响应(tool_use 或 end_turn)
    │              │
    └──────────────┘
      (持续循环直到 end_turn)

Tool Use 的多轮调用

/loop 的底层实现建立在 Anthropic 的 Tool Use API 之上。每一轮循环都是一次独立的 HTTP 请求,携带截至当前的完整对话历史(包含所有前序工具调用和结果)。

以"帮我跑测试并修复失败的用例"为例:

第一轮:决定运行测试

{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "text",
      "text": "我先运行测试套件,看看哪些用例失败了。"
    },
    {
      "type": "tool_use",
      "id": "toolu_01",
      "name": "run_in_terminal",
      "input": { "command": "pnpm test" }
    }
  ]
}

第二轮:携带测试结果,决定读取文件

Claude Code 客户端在本地执行 pnpm test,将输出封装为 tool_result,追加到 messages 数组,发起第二次请求:

[
  { "role": "user",      "content": "帮我跑测试并修复失败的用例" },
  {
    "role": "assistant",
    "content": [
      { "type": "text",     "text": "我先运行测试套件..." },
      { "type": "tool_use", "id": "toolu_01", "name": "run_in_terminal",
        "input": { "command": "pnpm test" } }
    ]
  },
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_01",
        "content": "FAIL src/utils/math.test.ts\n  ✕ add() should handle negative numbers\n  ✕ divide() should throw on zero\n\nTests: 2 failed, 18 passed"
      }
    ]
  }
]

模型收到结果后决定读取失败的测试文件:

{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "text",
      "text": "有两个用例失败,我来查看测试文件和对应的实现。"
    },
    {
      "type": "tool_use",
      "id": "toolu_02",
      "name": "read_file",
      "input": { "filePath": "src/utils/math.test.ts", "startLine": 1, "endLine": 60 }
    }
  ]
}

这个过程会持续——读取源文件、修改代码、再次运行测试——直到所有测试通过,模型返回 stop_reason: "end_turn",循环终止。

messages 数组的增长

Agentic Loop 过程中,messages 数组随每轮循环线性增长。每完成一个工具调用,就追加两条消息:

  • assistant 消息:包含模型的思考文本 + tool_use 对象
  • user 消息:包含 tool_result 对象
初始: [user_input]                         ~50 tokens
第1轮后: [..., assistant(tool_use), user(tool_result)]   ~300 tokens
第2轮后: [..., assistant(tool_use), user(tool_result)]   ~800 tokens
第3轮后: [..., assistant(tool_use), user(tool_result)]   ~1500 tokens
...

对于一个涉及大量文件读取和命令执行的复杂任务,经过十几轮循环后,context window 的消耗可能相当可观。这也是为什么 loop 模式下更需要 /compact 命令——参见深入理解 Claude Code /compact 命令的底层实现。

循环终止条件

Loop 不会无限运行,有以下几种终止条件:

模型主动结束(end_turn)

模型判断任务完成时,将 stop_reason 设为 end_turn,不再生成新的 tool_use:

while (true) {
  const response = await anthropic.messages.create({ messages })
 
  messages.push({ role: 'assistant', content: response.content })
 
  if (response.stop_reason === 'end_turn') {
    break
  }
 
  const toolResults = await executeTools(response.content)
  messages.push({ role: 'user', content: toolResults })
}

最大轮次限制

为防止失控循环,Claude Code 设有最大轮次上限(默认约 10 轮,可通过配置调整)。超过上限后强制终止并提示用户:

const MAX_ITERATIONS = 10
let iteration = 0
 
while (iteration < MAX_ITERATIONS) {
  // ...
  iteration++
}
 
if (iteration >= MAX_ITERATIONS) {
  console.warn('Loop reached maximum iterations. Stopping.')
}

Context Window 逼近上限

当 usage.input_tokens 超过模型 context window 的 95% 时,Claude Code 会提前终止 loop:

const CONTEXT_LIMIT_RATIO = 0.95
 
if (response.usage.input_tokens / MODEL_CONTEXT_WINDOW > CONTEXT_LIMIT_RATIO) {
  console.warn('Context window nearly full. Loop terminated early.')
  break
}

工具执行失败

若某个工具调用返回了错误(如命令执行超时、文件不存在),Claude Code 默认将错误信息作为 tool_result 传回模型,由模型决定是重试、绕过还是放弃。但对于不可恢复的错误(如权限拒绝),会直接终止 loop。

并行工具调用

Anthropic API 支持在单次响应中返回多个 tool_use 对象,Claude Code 会并行执行它们,然后将所有结果一起打包为 tool_result 列表返回:

// 模型一次性请求读取两个文件
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_03",
      "name": "read_file",
      "input": { "filePath": "src/utils/math.ts" }
    },
    {
      "type": "tool_use",
      "id": "toolu_04",
      "name": "read_file",
      "input": { "filePath": "src/utils/math.test.ts" }
    }
  ]
}

客户端并行执行两次文件读取,将两个结果一起返回:

{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_03", "content": "..." },
    { "type": "tool_result", "tool_use_id": "toolu_04", "content": "..." }
  ]
}

并行工具调用可以显著减少总轮次。

/loop 与自动模式的区别

Claude Code 中有两种方式进入 Agentic Loop:

手动 /loop:用户明确触发,Claude Code 在每次需要执行写操作(修改文件、执行命令)前会暂停并请求用户确认。适合需要审查每一步操作的场景。

自动模式(-p / --print):通过命令行标志直接进入完全自动的 loop,不在中途请求确认,直到任务完成或触发终止条件:

Terminal
claude -p "修复所有 ESLint 错误,不要改动任何业务逻辑"

两者的底层 API 调用逻辑完全相同,区别仅在于客户端是否在写操作前插入人工确认步骤。

Loop Engineering

Agentic Loop 能否稳定完成任务,很大程度上取决于如何设计这个 loop——这就是 Loop Engineering 关注的问题。它本质上是 Prompt Engineering 在 Agentic 场景下的延伸。

任务指令的颗粒度

Loop 的第一个输入是任务描述。指令太宽泛,模型会在 loop 过程中不断"发散",消耗大量 context;指令太细,又失去了 loop 的意义。

一个好的 loop 任务指令应该满足:目标明确(有可判断的完成条件)、范围受限(明确哪些文件或模块在范围内)、失败可接受(当工具调用失败时模型能决定继续还是放弃)。

Terminal
# 差:目标模糊,loop 可能永远不知道什么时候算"完成"
claude -p "优化这个项目的代码质量"
 
# 好:目标明确,有可验证的完成条件
claude -p "运行 pnpm test,修复所有失败的测试用例,直到全部通过。
          只修改 src/utils/ 目录下的文件,不要修改测试文件本身。"

工具定义的边界

Loop Engineering 的另一个核心是工具的设计。工具太多,模型可能在不必要的工具调用间"漂移";工具太少,模型可能因缺少必要能力而陷入死循环。

一个常见技巧是为特定任务提供专用工具而不是通用工具:

// 通用工具:模型需要自己构造正确的命令
{ name: "run_in_terminal", input: { command: "pnpm test --reporter=verbose" } }
 
// 专用工具:工具内部封装了正确的调用方式,模型只需声明意图
{ name: "run_tests", input: { filter: "src/utils/math.test.ts" } }

专用工具减少了模型在工具调用参数上犯错的概率,使 loop 更稳定。

显式的终止信号

依赖模型自己判断"任务完成"有时不够可靠。一个实践是在任务描述中嵌入显式的终止信号——一个可被客户端检测的输出模式:

Terminal
claude -p "运行测试,修复所有失败项。完成后,在最后一行输出 DONE: <通过数>/<总数>"

客户端检测到 DONE: 前缀后立即终止 loop,而不依赖 stop_reason: end_turn。这在自动化流水线中尤其有用。

的共同点是:不把模糊性留给 AI,在给 AI 任务之前先把结构想清楚。

总结

/loop 的本质是将标准的 Request-Response 对话改造成由模型自主驱动的工具调用循环。每一轮循环都是一次独立的 HTTP 请求,messages 数组是贯穿整个 loop 的状态容器。模型通过分析历史工具结果来决定下一步,所有上下文都在客户端维护,以 messages 数组的形式随每次请求携带。

点击播放