Skip to content

06.1 终端接管逻辑与工具循环

Claude Code 凭什么敢跑你的命令?这一章拆开 Agent Loop 的每一步。

读完你能做什么:准确预测它下一步会做什么,从而在正确的时机介入。


一、一个根本澄清:它不"控制"终端

很多人以为 Claude Code 像远程桌面一样"接管"了你的终端。不是的。

真相是:

text
Claude 生成一个"我想运行 X"的结构化请求

Claude Code 客户端(本地程序)拦截这个请求

客户端检查权限(见 05.5)

客户端在一个受控子进程里执行 X

客户端把 stdout/stderr/exit code 截取下来

作为"工具结果"追加到 Claude 的上下文

Claude 看到结果,继续下一步

Claude 本身从头到尾没有碰过你的 shell。 它只是不断生成"请求",由客户端代为执行。这个区分是理解一切的基础:

  • 权限提示为什么能拦住它 → 因为拦截发生在客户端,执行之前
  • hook 为什么能干预 → hook 也在客户端侧
  • 它为什么"看不到"没读过的东西 → 它只知道工具回填给它的内容

二、Agent Loop:完整的一圈

text
┌─────────────────────────────────────────────────┐
│  1. 组装上下文                                    │
│     系统提示词 + 工具定义 + CLAUDE.md +           │
│     历史 + 你的输入                               │
└───────────────────┬─────────────────────────────┘

┌─────────────────────────────────────────────────┐
│  2. 前向传播,生成                                 │
│     可能生成:普通文本 / 一个或多个 tool_use       │
└───────────────────┬─────────────────────────────┘

              生成了 tool_use?
              ┌────┴────┐
             否          是
              ↓          ↓
        ┌─────────┐  ┌──────────────────────────┐
        │ 3a.回复  │  │ 3b. 客户端拦截             │
        │ 结束本轮 │  │   ↓ 检查权限(05.5)        │
        └─────────┘  │   ↓ 运行 hook(PreToolUse) │
                     │   ↓ 执行工具                │
                     │   ↓ 运行 hook(PostToolUse)│
                     │   ↓ 截取结果                │
                     └──────────┬───────────────┘

                     ┌──────────────────────────┐
                     │ 4. 结果追加到上下文        │
                     │    回到第 1 步             │
                     └──────────────────────────┘

这个循环会一直转,直到 Claude 生成一个不含 tool_use 的回复(表示它认为任务完成或需要你输入)。


三、内置工具清单

Claude Code 的核心工具(MCP 工具是额外叠加的):

工具作用权限敏感度
Read读文件低(但能读到敏感文件)
Edit精确替换文件内容
Write写/覆盖文件
Bash执行 shell 命令
Glob文件名模式匹配
Grep内容搜索
Task / 子代理派生子代理
WebFetch / WebSearch联网

--tools 限制可用工具:

bash
claude --tools "Bash,Edit,Read"          # 只给这三个
claude --tools ""                        # 禁用所有内置工具

四、它怎么决定用哪个工具

这不是硬编码的规则,而是概率生成。但有可预测的模式:

你的意图它倾向的工具序列
"这个函数在哪定义的"GrepRead
"改一下这个 bug"ReadEditBash(测试)
"这个项目怎么跑起来"Read(README/package.json) → Bash
"重构整个模块"GlobGrepRead × N → Edit × N → Bash
"为什么测试挂了"Bash(测试) → Read(失败文件) → EditBash(重跑)

4.1 引导它的工具选择

你可以在提示词里明确指定策略,避免它用低效的路径:

text
❌ 它可能一上来 Read 一堆文件
✅ 先用 grep 定位 reconcile 相关的函数,只输出匹配行。
   看清楚后再决定读哪几个文件,不要一次读整个目录。

这直接对抗了"把大量文件读进中段衰减区"的问题(见 03.2)。


五、并行 vs 串行工具调用

5.1 它可以在一轮里发多个工具调用

当多个操作互不依赖时,Claude 可以在同一轮并行发起:

text
同时读三个不相关的文件
同时跑 lint 和 typecheck

这比串行快得多。

5.2 引导并行

text
如果多个操作之间没有依赖关系,在同一批次并行执行,不要一个一个来。
比如:同时读 config.ts、routes.ts、和跑一次 git status。

5.3 什么时候必须串行

当后一步依赖前一步的结果时,必须串行,且不要预先规划超过两步

text
每次只执行一步,看到结果再决定下一步。
前一步的结果可能改变你的判断,不要一次性规划 10 步然后盲目执行。

原理见 02.5 第七节


六、长工具调用的后台化

某些工具调用很慢(跑完整测试套件、构建)。Claude Code 会自动把长时间运行的工具调用挪到后台,避免阻塞。

你也可以显式用后台执行:

bash
# 把一个 shell 命令作为后台任务跑
claude --bg --exec 'pytest -x'

查看和管理:

text
> /tasks       # 查看后台工作(含已完成的子代理)

七、它"看到"的工具结果长什么样

理解这一点能帮你优化。工具结果是文本,追加到上下文。这意味着:

7.1 大输出会撑爆上下文

text
跑一次测试 → 3000 行输出 → 全部进上下文 → 挤占后续空间

对策:让它过滤。

text
运行测试时用管道只保留失败信息:
npm test 2>&1 | grep -A5 -E "(FAIL|✕|Error)" | head -50
不要把完整输出读进上下文。

7.2 它可能误读结果

工具结果是文本,Claude 对它的理解仍是概率性的。长输出、相似的多个结果,都可能被误读。对关键判断,让它引用具体行:

text
基于测试输出回答时,引用具体的失败行,不要笼统概括。

八、MCP 工具的叠加

MCP server 提供的工具会叠加到内置工具之上,进入同一个循环。区别:

  • MCP 工具名形如 mcp__<server>__<tool>
  • 它们的定义常驻上下文(这是成本源,见 03.1
  • 工具太多时用 tool search 延迟加载(见 07.5

Claude Code 自己也能作为 MCP server 暴露给其他程序:

bash
claude mcp serve       # 把 Claude 作为 stdio MCP server 启动

九、把机制转化为控制力

机制你的操作杠杆
它只生成请求,客户端执行用权限/hook 在执行前拦截(05.5、06.5)
循环转到无 tool_use 才停给可验证的完成标准,避免它过早停
工具结果是文本,会累积过滤大输出,控制上下文
工具选择是概率的在提示词里指定搜索/读取策略
可并行可串行明确告诉它哪些能并行
不预先规划太多步要求"一步一看"

十、一个观察练习

想真正理解 Agent Loop,跑这个并全程观察:

text
> 用 --verbose 的心态给我讲解你的每一步:
  我要你找出这个项目里所有直接拼接 SQL 字符串的地方(潜在注入风险)。
  每次调用工具前,先说明:你要调什么工具、为什么、期望看到什么。
  调用后,说明结果是否符合预期、下一步怎么走。

你会清楚看到"思考 → 工具 → 观察 → 再思考"的循环,以及它如何根据每一步的真实结果调整策略。


延伸阅读

基于 VitePress 构建 · 内容采用原作者授权