目标:建立对 Claude Code 的整体认知框架。后续每一章都是这个框架某一项的展开。 受众:专业程序员。本机版本
2.1.220,npm-global 安装。
0.1 本质定义
Claude Code 是一个 Agent Runtime —— 让 LLM 能持续循环、调用工具、操作真实环境的运行时。
它不属于这两类常见东西:
| 类型 | 模式 | 例子 |
|---|---|---|
| CLI 工具 | 给命令 → 执行 → 结束 | git |
| 聊天客户端 | 对话 → 结束 | ChatGPT |
| Agent Runtime | 给目标 → 自己规划并执行动作序列 → 直到完成 | Claude Code |
差别:你给 git 一个具体命令它执行完就停;你给 Claude Code 说"把这个函数重命名并更新所有调用点",它自己决定先读文件、再搜调用、再改、再跑测试。规划是它做的,不是你写的脚本。
0.2 Agent 循环的技术机制
关键认知:循环由 runtime 驱动,不是 LLM
LLM 本身是无状态的,只是个"输入消息 → 输出消息"的函数,调一次返回一次,不会自己循环。是 Claude Code 这个 runtime 在反复调用它、执行它要的工具、把结果喂回去。
循环伪代码(Claude Code 心脏的真实形态)
messages = [system_prompt, user_message]
while True:
# 1. 调 LLM,拿到 assistant 消息
assistant_msg = llm.complete(messages)
# 2. LLM 没要调工具 → 任务结束,跳出循环
if not assistant_msg.tool_calls:
break
messages.append(assistant_msg)
# 3. LLM 要调工具 → runtime 执行(副作用在这)
for call in assistant_msg.tool_calls:
result = execute_tool(call.name, call.input)
messages.append(tool_result(call.id, result))
# 4. 回到 1,LLM 基于新结果继续想
循环流程图
┌──────────────────────────┐
│ messages = [system, user]│
└────────────┬─────────────┘
▼
┌──────────────────────────┐
┌───▶│ ① 调 LLM (llm.complete) │
│ └────────────┬─────────────┘
│ ▼
│ ┌──────────────────────────┐
│ │ ② 有 tool_calls 吗? │
│ └────┬───────────────┬─────┘
│ │ 否 │ 是
│ ▼ ▼
│ ┌─────────┐ ┌──────────────────────┐
│ │ 任务结束 │ │ ③ runtime 执行工具 │
│ │ break │ │ (副作用+权限+hook) │
│ └─────────┘ └──────────┬───────────┘
│ ▼
│ ┌──────────────────────┐
│ │ ④ 结果包成 tool_result│
│ │ 塞回 messages │
│ └──────────┬───────────┘
└─────────────────────────────┘
五个推论(决定 Claude Code 所有设计)
| # | 事实 | 推论 / 关联章节 |
|---|---|---|
| ① | LLM 无状态,状态全在 messages 数组 | 会话 = 消息序列;~/.claude/projects/*.jsonl 就是它的持久化;--resume = 读 jsonl 回内存重进循环 |
| ② | 工具调用是结构化 JSON,不是文本解析 | {"type":"tool_use","id":"toolu_xxx","name":"Read","input":{"file_path":"..."}};sdk-tools.d.ts(3807 行) 是完整契约 |
| ③ | 副作用只发生在 step 3(工具执行层) | 权限、沙箱、hook 拦截全插在这层 → Ch5 权限 / Ch11 Hooks 的着力点 |
| ④ | 终止条件由 LLM 自己决定 | runtime 不能预判循环次数 → 不可预测的 token/时长 → --budget 等限制存在 |
| ⑤ | 每轮要把完整 messages 发给 LLM | transformer 自注意力需全上下文 → 对话越长每轮越贵越慢 → /compact、context 管理是物理约束,不是体验优化 |
0.3 三类控制面(最重要的心智模型)
Claude Code 用三种不同的语言分别控制系统的不同侧面。理解这个分类,配置、记忆、插件、hooks 就都是同一棵树的不同枝。
三类控制面一览图
┌─────────────────────────────────────────────────────────────────┐
│ Claude Code 系统 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ A. 目录约定 │ │ B. JSON 配置 │ │ C. Markdown │ │
│ │ │ │ │ │ 提示词 │ │
│ │ 文件路径 │ │ settings.json│ │ CLAUDE.md │ │
│ │ │ │ plugin.json │ │ commands/ │ │
│ │ 发现机制读 │ │ runtime 读 │ │ agents/ │ │
│ │ │ │ │ │ skills/ │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ 决定"加载哪些" 决定"怎么跑" 决定"LLM 怎么想" │
│ 命名空间+生命周期 行为开关+鉴权 指令+人格+流程 │
│ │
└─────────────────────────────────────────────────────────────────┘
三类控制面对照表
| 控制面 | 存储形式 | 控制什么 | 谁执行 |
|---|---|---|---|
| A. 目录约定 | 文件路径 | 发现、命名空间、生命周期 | 发现机制(runtime 启动时扫描) |
| B. 运行时硬配置 | JSON | 行为开关、阈值、鉴权、env | runtime(机器解析) |
| C. 自然语言程序 | Markdown | 给 LLM 的指令、人格、流程 | LLM(模型读) |
同一个目标,三种做法
想加一个能力
│
▼
┌────────────────────────┐
│ 这是什么类型的规则? │
└────────────────────────┘
│
┌────────┼────────┐
▼ ▼ ▼
"规则" "权限" "扩展入口"
│ │ │
▼ ▼ ▼
C 面 B 面 A 面
CLAUDE.md settings .claude/commands/
Markdown JSON 放对位置
给 LLM 给 runtime 给发现机制
| 目标 | 属于哪面 | 怎么做 |
|---|---|---|
| 提交前永远跑 lint(规则) | C 面 | 写进 CLAUDE.md |
允许跑 npm test 不用每次问(权限) | B 面 | settings.json 的 permissions.allow |
加一个 /deploy 命令(扩展入口) | A 面 | 创建 .claude/commands/deploy.md |
新手最大的坑:把三件事混在一起
- 往
CLAUDE.md塞权限规则 ❌(LLM 不强制权限,runtime 才强制) - 往
settings.json写自然语言 ❌(JSON 不传给 LLM,是 runtime 解析的) - 想加命令却到处找配置项 ❌(命令靠"放对目录"被发现,没有配置开关)
一句话:Markdown 给模型读,JSON 给 runtime 读,目录位置给发现机制读。三者各司其职,别串。
0.4 核心公式
公式组成图
┌─────────────────────────────────────────────────────────────────┐
│ Claude Code │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Agent Runtime ← 底座(0.2 的循环) │ │
│ │ LLM + 工具协议 + 循环驱动 │ │
│ └─────────────────────┬───────────────────────┘ │
│ │ │
│ ┌────────────────┼────────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 文件系统约定 │ │ JSON 配置 │ │ Markdown │ │
│ │ (A 面) │ │ (B 面) │ │ 提示词(C面) │ │
│ │ 哪些目录放啥 │ │ settings/ │ │ CLAUDE.md/ │ │
│ │ │ │ plugin/ │ │ commands/ │ │
│ │ │ │ marketplace │ │ agents/ │ │
│ └─────────────┘ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ┌───────────────┘ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ 可执行脚本 │ │
│ │ hooks / MCP / │ │
│ │ workflows │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Agent Runtime 是底座,其他四项是"喂给底座的东西":
· 目录约定 → runtime 启动时扫描,决定"加载哪些东西"
· JSON 配置 → runtime 读取后调整自己的行为
· Markdown → 注入 messages 数组,成为 LLM 看到的上下文
· 可执行脚本 → 在循环 step 3 被 runtime 调起
文字版公式
Claude Code ≈
Agent Runtime ← 0.2 的循环:LLM + 工具协议
+ 文件系统约定 ← A 面:哪些目录放什么
+ JSON 配置 ← B 面:settings.json / plugin.json / marketplace.json
+ Markdown 提示词 ← C 面:CLAUDE.md / commands / agents / skills
+ 可执行脚本 ← hooks / MCP servers / workflows
怎么读这个公式
Agent Runtime 是底座,其他四项是"喂给底座的东西":
- 文件系统约定:runtime 启动时扫描特定目录,决定"加载哪些东西"。位置错了就不被发现。
- JSON 配置:runtime 读取后调整自己的行为(鉴权、放行哪些工具、env 变量)。机器读。
- Markdown 提示词:被注入
messages数组,成为 LLM 看到的上下文。模型读。 - 可执行脚本:在循环 step 3(工具执行层)被 runtime 调起。hooks 在工具调用前后触发,MCP 是外部工具进程,workflows 是动态任务。
一个具体例子串起来(code-review 插件)
| 公式项 | 在这个例子里是什么 |
|---|---|
| Agent Runtime | Claude Code 本体,跑那个循环 |
| 文件系统约定 | 插件放在 ~/.claude/plugins/.../ 才被发现(A 面) |
| JSON 配置 | 插件的 plugin.json manifest 声明自己提供什么(B 面) |
| Markdown 提示词 | 插件里的 commands/review.md 定义 /review 命令的提示词(C 面) |
| 可执行脚本 | 插件可能带 hook 脚本,比如提交前跑检查 |
一个插件就把公式五项全用上——这就是为什么这个公式值得记:它不是抽象口号,是 Claude Code 所有功能的解剖图。
0.5 本机实证(验证以上概念)
# ① 会话转录 = messages 数组的持久化
ls ~/.claude/projects/ # 按项目分目录的 .jsonl
# 例: ~/.claude/projects/<项目路径转义>/<session-uuid>.jsonl
# ② 工具协议契约(3807 行类型定义 = 工具的完整 input/output schema)
wc -l …/@anthropic-ai/claude-code/sdk-tools.d.ts
# ③ 三类控制面的实物
ls ~/.claude/settings.json # B 面 JSON(本机存在)
ls ~/.claude/ # A 面 目录约定(commands/agents/skills/plugins...)
# C 面 Markdown:本机暂未创建 ~/.claude/CLAUDE.md;可在任意项目根建 CLAUDE.md 验证
本章核心带走
- Claude Code = Agent Runtime,不是工具也不是聊天客户端。
- 循环由 runtime 驱动,LLM 只是无状态决策函数;状态在
messages数组里。 - 副作用/权限/hook 全在工具执行层(伪代码 step 3)。
- 三类控制面:Markdown 给模型、JSON 给 runtime、目录给发现机制——别串。
- 核心公式 = Agent 循环底座 + 四类喂给它的东西(目录/JSON/Markdown/脚本)。