第 0 章:系统鸟瞰

# 第 0 章:系统鸟瞰

> 在动手之前,先爬到山顶,把整座山的轮廓看清楚。

---

## 0.1 我们要构建什么

一个 **Coding Agent** 是一种能够:

1. 接受人类用自然语言描述的编程任务
2. 自主地分析代码库、编写代码、执行命令、调试错误
3. 将任务完成情况持续反馈给人类

的软件系统。它不是一个聊天机器人,而是一个能够在真实操作系统上产生真实副作用(读写文件、执行程序)的**自主代理(Agent)**。

Claude Code 就是这样的系统。它的代码库包含约 **50 万行 TypeScript**,支撑着超过 40 种内置工具、60 余条斜杠命令、7 种任务类型、以及完整的 IDE 远程控制协议。

本系列的目标是:**从 0 开始,逐层复刻出一个功能完备的 Coding Agent。**

---

## 0.2 十六层抽象

Claude Code 的实现可以分解为 **16 个相互堆叠的抽象层**,从最底层的进程 I/O,到最顶层的后台智能调度。

```
┌─────────────────────────────────────────────────────┐  第 16 章(选修)
│      后台智能框架(forked Agent / Magic Docs)         │
├─────────────────────────────────────────────────────┤  第 15 章
│              完整 Coding Agent(集成)                │
├─────────────────────────────────────────────────────┤  第 14 章
│           插件系统 & MCP Server 集成                  │
├────────────────────────────┬────────────────────────┤  第 13 章
│      多 Agent 派生          │    任务调度器            │
├────────────────────────────┴────────────────────────┤  第 12 章
│               斜杠命令系统(/help /cost …)           │
├─────────────────────────────────────────────────────┤  第 11 章
│            会话持久化 & 历史回放                       │
├────────────────────────────┬────────────────────────┤  第 10 章
│    记忆系统(MEMORY.md)     │   文件历史快照(--rewind)│
├────────────────────────────┴────────────────────────┤  第 9 章
│   系统提示(System Prompt)& 上下文感知(CLAUDE.md)   │
├─────────────────────────────────────────────────────┤  第 8 章
│               权限与安全仲裁层                        │
├─────────────────────────────────────────────────────┤  第 7 章
│       Agentic Loop(LLM ↔ 工具 反复执行)             │
├────────────────────────────┬────────────────────────┤  第 6 章
│      对话管理(多轮历史)     │   消息格式化              │
├────────────────────────────┴────────────────────────┤  第 5 章
│               LLM API 客户端(流式)                  │
├─────────────────────────────────────────────────────┤  第 4 章
│         工具抽象 & 工具执行引擎                        │
├────────────────────────────┬────────────────────────┤  第 3 章
│    CLI 命令路由(Commander) │   子命令调度              │
├────────────────────────────┴────────────────────────┤  第 2 章
│          终端 UI 外壳(Ink + React)                  │
├─────────────────────────────────────────────────────┤  第 1 章
│              进程 & 标准 I/O                          │
└─────────────────────────────────────────────────────┘
    硬件 / 操作系统(Node.js 运行时)
```

每一层:
- **依赖**下面各层提供的能力
- **向上**暴露更高级别的抽象
- **对应** Claude Code 源码中的一个或多个模块

---

## 0.3 一次完整请求的数据流

当用户在终端输入 `"请帮我修复 main.ts 中的类型错误"` 并回车,发生了以下事情:

```
用户按下 Enter
    │
    ▼
[第 2 层  终端 UI]  ─── Ink/React 捕获键盘事件,收集输入字符串
    │
    ▼
[第 12 层 斜杠命令] ─── 检查是否是 /xxx 命令,不是则继续
    │
    ▼
[第 9 层  上下文]   ─── 读取 CLAUDE.md、git log、目录结构,构造 system prompt
    │  ↑ [第 10 层 记忆系统] 将 MEMORY.md 注入此处的 system prompt
    ▼
[第 6 层  对话管理] ─── 将本次输入与历史消息拼接,组成 messages 数组
    │
    ▼
[第 5 层  LLM 客户端] ── 流式调用 Claude API,收到第一个 token 时开始渲染
    │
    ▼  LLM 返回 tool_use(要执行工具)
    │
    ▼
[第 7 层  Agentic Loop]
    │   ┌──────────────────────────────────────┐
    │   │  取出 tool_use 请求                   │
    │   │      │                               │
    │   │      ▼                               │
    │   │  [第 8 层 权限层]  ── 需要确认? ──→ 用户确认弹窗
    │   │      │ 通过                           │
    │   │      ▼                               │
    │   │  [第 4 层 工具引擎] ── 执行工具,得到结果│
    │   │      │  若 tool = AgentTool → [第 13 层 多 Agent 调度]
    │   │      │  若 tool = MCP 工具  → [第 14 层 插件/MCP]
    │   │      ▼                               │
    │   │  将 tool_result 追加到 messages       │
    │   │      │                               │
    │   └──────┘ (再次调用 LLM,循环直到完成)
    │
    ▼  (postSamplingHook 异步触发)
[第 16 层 后台智能] ── fork 子 Agent 提炼记忆 / 更新 Magic Docs(不阻塞主线程)
    │
    ▼
[第 11 层 持久化]  ─── 将本轮完整对话写入磁盘
    │
    ▼
[第 2 层  终端 UI]  ─── 渲染最终回复,等待下一次输入
```

> **未出现在此流图中的章节**:第 1 章(进程 I/O)和第 3 章(CLI 路由)属于**启动期**逻辑,仅在进程初始化时执行一次;第 15 章是集成章,无独立运行时层。

---

## 0.4 Claude Code 项目的物理结构

```
src/
├── main.tsx                 ← 程序入口(第 1、3 章)
├── bootstrap/state.ts       ← 全局单例状态(第 1 章)
├── ink.ts / replLauncher.tsx← Ink 启动器(第 2 章)
├── components/              ← 200+ 个 React/Ink 组件(第 2 章)
├── cli/                     ← CLI 子命令处理(第 3 章)
├── Tool.ts / tools.ts       ← 工具接口 & 注册表(第 4 章)
├── tools/                   ← 40+ 具体工具实现(第 4 章)
├── services/api/claude.ts   ← Anthropic SDK 封装(第 5 章)
├── query/                   ← Token 预算 / 停止条件(第 5、7 章)
├── history.ts               ← 消息历史管理(第 6 章)
├── assistant/sessionHistory.ts← 会话历史(第 6 章)
├── QueryEngine.ts / query.ts← Agentic Loop 核心(第 7 章)
├── types/permissions.ts     ← 权限模型(第 8 章)
├── utils/permissions/       ← 权限检查实现(第 8 章)
├── context.ts               ← 系统上下文收集(第 9 章)
├── context/                 ← Context React Hooks(第 9 章)
├── setup.ts                 ← 项目初始化 & CLAUDE.md(第 9 章)
├── memdir/                  ← 记忆目录 & 三层 Memory(第 10 章)
├── utils/fileHistory.ts     ← 文件历史快照 & --rewind(第 10 章)
├── services/autoDream/      ← autoDream 记忆整合(第 10 章)
├── projectOnboardingState.ts← 会话元数据(第 11 章)
├── commands.ts              ← 斜杠命令路由(第 12 章)
├── commands/                ← 具体命令实现(第 12 章)
├── Task.ts / tasks/         ← 任务类型 & 状态机(第 13 章)
├── coordinator/             ← 多 Agent 调度(第 13 章)
├── plugins/                 ← 插件加载(第 14 章)
├── services/                ← MCP、成本追踪等(第 14 章)
├── utils/forkedAgent.ts     ← forked Agent 模式核心(第 16 章)
├── services/MagicDocs/      ← Magic Docs 自维护(第 16 章)
├── services/awaySummary.ts  ← Away Summary(第 16 章)
├── services/PromptSuggestion/speculation.ts ← Speculation 预测执行(第 16 章)
└── state/                   ← 不可变中心状态树(贯穿全部)
```

---

## 0.5 关键设计决策

在深入每一层之前,有 5 个贯穿全书的设计决策值得先记住:

### 决策 1:不可变中心状态

Claude Code 使用单一不可变状态树(`AppState`),所有更新都通过纯函数 `(prev) => next` 进行。
这与 Redux 的思想一致,但没有使用 Redux,而是用了 `useSyncExternalStore` + 自定义 store。

```typescript
// 所有状态变更的样板
setState((prev: AppState): AppState => ({
  ...prev,
  messages: [...prev.messages, newMessage],
}))
```

### 决策 2:权限优先

每个工具调用都要经过权限仲裁。权限规则来自 6 个来源(CLI参数 > 会话 > 企业策略 > 项目 > 用户偏好 > 默认值),优先级从左到右递减。

### 决策 3:流式优先

LLM 响应从第一个 token 起就开始渲染。工具调用在流中的 `content_block_stop` 事件处同步执行,而不是等到全部响应完成。

### 决策 4:Ink = 终端里的 React

所有 UI 使用 React 组件编写,由 Ink 将 React 虚拟 DOM 渲染为终端控制序列(ANSI 转义码)。
这意味着你可以在终端里用 `useState`、`useEffect`、Flexbox……

### 决策 5:工具即接口

每个工具是一个满足 `Tool` 接口的对象:包含 input_schema(Zod schema)、权限声明、以及 `call()` 方法。
LLM 在调用工具时只传 JSON,工具运行时负责校验和执行。

---

## 0.6 本章小结

| 概念 | 要记住的要点 |
|------|-------------|
| Coding Agent | 能在真实系统上产生副作用的自主代理 |
| 16 层抽象 | 从进程 I/O 到后台智能调度,逐层堆叠 |
| 数据流 | 用户输入 → 上下文 → LLM → 工具循环 → UI 渲染 |
| 不可变状态 | 所有状态变更是纯函数,防止状态污染 |
| 权限优先 | 任何副作用都需经过 6 级权限规则链仲裁 |

---

## 下一章

→ [第 1 章:进程与标准 I/O](01-process-and-io)

*本章没有可运行产出物;从第 1 章开始,每章都有一个 `chapters/N/` 目录,里面是可以直接运行的最小实现。*