Claude Code 的工作原理:代理循环与工具调用
「它刚才一连读了五个文件、跑了两遍测试,最后才回我一句话。这中间到底发生了什么?」
上一章你已跑通会话,并熟悉了 Slash 命令 面板。本章把那种「它自己在干活」的直觉,换成可核对的一套机制:代理循环、工具、权限。读完后,你能根据终端里的工具名和权限弹窗,判断 Agent 处在循环的哪一步,以及为什么会被拦住或停下来。
官方机制说明见 How the agent loop works 与 How Claude Code works。CLI 与 Agent SDK 共用同一套循环;下文以终端会话为主,并标注与 SDK 选项的对应关系。
代理循环是什么
Section titled “代理循环是什么”用最直白的话说:你给出意图,模型在每一轮里决定要不要调用工具;若要,运行时执行工具并把结果塞回上下文,再进入下一轮,直到模型只回复文字、不再要工具为止。
这和「聊一句答一句」的网页聊天不同。网页聊天没有把你的 repo 当工作区,也不能替你执行 npm test。Claude Code 在循环里反复 观察 → 行动 → 读结果 → 再观察,直到任务完成、触达限制,或你中断。
你输入目标 ↓模型评估当前上下文(历史、CLAUDE.md、工具结果) ↓输出:纯文字 和/或 一个或多个工具调用 ↓若有工具调用 → 运行时执行 → 工具结果写回上下文 ↓重复,直到本轮没有工具调用 ↓把最终文字呈现给你(你可审查 diff、继续追问)一轮指模型发出带工具调用的回复,加上这些工具执行完毕、结果写回上下文,尚未开始下一次模型推理的完整过程。简单问题可能一两轮结束;重构加测可能几十轮。官方把「只有工具调用、没有最终纯文字」的往返计入 max_turns 上限;见 Turns and budget。
走一遍真实循环
Section titled “走一遍真实循环”假设你输入:修复 auth.ts 里导致测试失败的问题,改完跑测试确认。
下面是一种典型顺序,名称与 Tools reference 一致:
| 轮次 | 模型可能做的事 | 你终端里会看到 |
|---|---|---|
| 1 | 调用 Bash 跑 npm test | 权限弹窗或已预授权的 Bash |
| 2 | 调用 Read 读 auth.ts、测试文件 | 通常无弹窗 |
| 3 | 调用 Edit 改源码,再 Bash 重跑测试 | Edit 与 Bash 可能各弹一次 |
| 末轮 | 只输出文字,不再要工具 | 总结:修了哪、测试是否通过 |
要点有三条:
- 每一轮的决定权在模型,但能不能执行由权限规则、模式和你的确认决定。
- 工具输出会留在上下文里,所以后面轮次会基于真实测试输出改代码,而不是凭空猜。
- 你看到的「最终回复」只是循环的出口;中间多轮工具调用可能并不逐条展开成聊天段落,但会驱动同一条任务链。
动手: 在已有测试的项目里输入:只读方式列出 src 下所有 .ts 文件,不要改任何文件。 观察是否主要出现 Glob 或 Grep,且很少出现 Edit。再输入:运行测试并告诉我失败用例的文件名。 对比 Bash 是否出现。能区分「只读探索」和「要执行命令」两类轮次即可。
内置工具:能做什么、要不要你点头
Section titled “内置工具:能做什么、要不要你点头”Claude Code 通过具名工具操作环境,而不是只在聊天框里贴代码。工具名与权限规则、Hook、子代理配置里写的是同一套字符串。
日常开发最常遇到的
Section titled “日常开发最常遇到的”| 工具 | 作用 | 默认是否要批准 |
|---|---|---|
Read | 读文件内容 | 否 |
Glob | 按文件名模式找文件 | 否 |
Grep | 在文件内容里搜模式 | 否 |
Edit | 精确替换片段改文件 | 是 |
Write | 创建或整文件覆盖 | 是 |
Bash | 跑 shell 命令 | 是 |
WebSearch | 搜索网络 | 是 |
WebFetch | 抓取指定 URL | 是 |
Agent | 派生子代理处理子任务 | 启动本身不批;子代理内部工具仍受权限约束 |
完整列表与细节行为见 Tools reference。还有 EnterPlanMode、Skill、MCP 相关工具等,会在后续章节展开;本章只需记住:读多写少时弹窗少,一改文件或一跑命令就容易弹窗。
只读工具也会消耗上下文
Section titled “只读工具也会消耗上下文”Read、Grep、Glob 不触发编辑类批准,但并非「免费」。大仓库里广撒网式搜索会把大量路径和片段塞进上下文,带来费用和变慢。你在 第一个会话 里学过的 @ 引用和 /compact,就是在管这件事。
同一轮里多个工具怎么跑
Section titled “同一轮里多个工具怎么跑”官方说明:只读类工具可并行;会改状态的工具顺序执行,避免冲突。你在终端里若看到短时间多次 Read,属于正常优化。
模型如何决定「下一步」
Section titled “模型如何决定「下一步」”你无法逐步单步调试模型的内部推理,但可以观察输入侧和输出侧,建立稳定预期。
输入侧主要包括:
- 你的当前与历史消息
- 项目中的
CLAUDE.md等说明 - 工作区文件结构与会话内已读内容
- 各轮工具的返回:测试日志、grep 结果、编辑成功或失败信息
输出侧每一轮只能是:
- 纯自然语言
- 一个或多个工具调用
- 两者兼有
模型根据「目标是否达成、还缺什么证据」选择工具。例如测试仍失败时会再 Read 或再 Edit;若 Edit 因 old_string 不匹配失败,官方 Edit 行为 要求先 Read 且磁盘未变,模型通常会加长匹配片段或先重新 Read。
你能施加的影响:
| 手段 | 影响的是 |
|---|---|
| 把目标、约束、验收写清楚 | 模型倾向选哪些工具、何时停 |
CLAUDE.md | 默认习惯,例如先跑哪条测试命令 |
/permissions 与 settings | 工具能否执行,与模型想不想无关 |
| 拒绝某次 Bash | 该轮工具结果变成「被拒绝」,模型常改道或向你说明 |
重要边界:提示词和 CLAUDE.md 管的是「它想做什么」;权限管的是「允许做什么」。 在 CLAUDE.md 里写「禁止删库」不能代替 deny 规则。见 Configure permissions。
循环何时结束
Section titled “循环何时结束”| 结束方式 | 含义 | 你可怎么做 |
|---|---|---|
| 自然结束 | 模型认为目标完成,末轮无工具调用 | 审查 diff 与测试结果 |
| 你中断 | Esc 或停止当前生成 | 用 /resume 或说明从哪继续 |
| 权限拒绝 | 关键工具未获批,模型无法继续 | 批准、改规则,或缩小任务 |
| 预算或轮次上限 | SDK 场景下 max_turns、max_budget_usd | CLI 日常较少遇;自动化时要设 |
| 上下文撑满 | 需 /compact 或 /clear | 长任务定期压缩 |
「跑偏」往往不是循环坏了,而是停止条件不清晰:你说「优化项目」,模型缺少可验证的完成信号,就会一直探索。把验收写成可运行命令,循环更容易在合适时刻停下。
权限:三层结构
Section titled “权限:三层结构”官方权限文档 把工具分成三类:
| 类型 | 例子 | 首次使用 |
|---|---|---|
| 只读 | Read、Grep、Glob | 一般不弹窗 |
| 改文件 | Edit、Write | 需批准;可选「本会话不再问」 |
| 执行 | Bash、WebFetch 等 | 需批准;可对命令模式做持久 allow |
在设置文件或 /permissions 里使用 Tool 或 Tool(specifier):
{ "permissions": { "allow": [ "Bash(npm run test *)", "Bash(git status *)", "Read" ], "deny": [ "Bash(git push *)", "Read(./.env)" ], "ask": [ "Bash(curl *)" ] }}评估顺序:deny → ask → allow,先匹配先生效。Bash(npm run *) 只匹配以该前缀开头的命令;复合命令 cmd1 && cmd2 会对每一段子命令分别匹配。
通过 /config 或 settings 的 defaultMode 切换整体策略:
| 模式 | 行为 | 适用 |
|---|---|---|
default | 未在 allow 列表中的敏感操作逐个问 | 日常开发 |
acceptEdits | 自动接受工作区内编辑类操作 | 信任度高的迭代 |
plan | 只读探索,不改源码 | 与 Plan Mode 配合 |
dontAsk | 未预批准的一律拒绝 | CI、严格环境 |
bypassPermissions | 跳过提示 | 仅隔离容器;有管理员可禁用 |
bypassPermissions 仍会拦截删根目录等极端命令,且不应在含密钥的本机长期使用。
动手: 运行 /permissions,为 Bash(npm run test *) 添加 allow。再让 Agent 跑测试,确认弹窗消失。然后 deny 一条你绝不会点的命令,看模型是否改道。
安全边界:权限之外还有什么
Section titled “安全边界:权限之外还有什么”权限是应用层闸门。还有几层值得单独记住:
沙箱在操作系统层限制 Bash 进程的路径与网络,与 allow/deny 叠加使用。机制、/sandbox 开关与环境选型见 沙箱隔离机制。
允许 Bash 时,模型仍可能用 curl 访问任意 URL,不限于 WebFetch 的域名规则。若只要查文档,可 deny 通用 curl,仅 allow WebFetch(domain:...)。
Hooks 在工具执行前后插入你的脚本,可做格式化、审计或二次拒绝。检查顺序上 Hook 早于 allow 规则,适合组织级策略。
子代理 Agent
Section titled “子代理 Agent”子代理在独立上下文里跑完再回报一行结果;父会话看不到中间每一步。后台子代理不会再弹权限窗,只能用会话里已授予的权限,未批准的调用会被自动拒绝。委派大任务前,先想清楚权限是否过宽。
后台任务与长命令
Section titled “后台任务与长命令”开发服务器、watch 测试等长时间进程,模型可用 Bash 的 run_in_background 挂到后台,主对话继续。列出与停止用 /tasks,与 Bash 工具行为 一致。
注意:
- 默认单条 Bash 有约两分钟超时,长任务应显式放后台或拆命令。
- 输出过长会写入会话目录中的文件,模型通过路径再读,你在
/tasks里也能跟进。
动手: 让 Agent「在后台启动项目的 dev 脚本,告诉我任务 ID」。用 /tasks 查看状态,再让它停掉。确认你理解「前台一轮对话」与「后台进程」是两条线。
失败模式:症状与排查
Section titled “失败模式:症状与排查”| 症状 | 常见原因 | 下一步 |
|---|---|---|
| 一直 Read 不改 | 目标含糊或权限只给了只读 | 写清验收;检查是否在 plan 模式 |
| Edit 反复失败 | 文件已被外部修改或未 Read | 让 Agent 先 Read;你本地勿并行改同一文件 |
| 测试通过它仍继续 | 未定义「完成」 | 用 /goal 写可验证终点,或明确「测试全绿即可停」 |
| 突然变「笨」 | 上下文接近上限 | /context,必要时 /compact |
| 子代理说做不到 | 后台权限不足 | 改 allow 规则或改前台执行 |
| 批准了仍危险 | 过于宽泛的 Bash(*) | 收窄前缀;敏感路径写 deny |
决策边界:何时信循环、何时加模式
Section titled “决策边界:何时信循环、何时加模式”| 场景 | 直接循环即可 | 建议加 Plan Mode 或拆任务 |
|---|---|---|
| 单文件小改、明确测试 | 是 | 否 |
| 跨多模块重构 | 可,但要写阶段验收 | 是 |
| 不熟代码库摸底 | 只读探索 | 先 plan 再执行 |
| 生产数据、支付、鉴权 | 严格 deny + 人工审查每步 Edit | 是 |
循环能力强,不等于每个任务都应一次扔给它。先只读弄清结构,再写改代码,通常比边搜边改更省 token,也更少误改。
继续读下一章之前
Section titled “继续读下一章之前”合上文档,试着回答:
- 「一轮」和「一整次会话」差在哪?
- 为什么
CLAUDE.md写「不要 push」仍可能 push? - 只读工具和 Edit 在权限上有何本质区别?
- 子代理后台跑时,权限弹窗为什么可能不再出现?
有卡壳,回到对应小节重做「动手」。
自检清单:
- 能根据工具名判断本轮是探索还是执行
- 在
/permissions里加过一条 allow 和一条 deny - 见过至少一次自然结束与一次因拒绝而改道
- 知道
/tasks用于查看后台命令
上一章:多平台运行环境全览 · 下一章:Plan Mode——在代理循环之上,把「想」和「做」拆开:先只读规划,再经你批准进入执行,减少灾难性乱改。