Claude Code 架构深度解析
本篇主要是对这篇文章的解读:Data Structures & The Information Architecture。也是因为最近开发Agent,所以研究了cc的架构和代码设计,cc还是很值得学习和借鉴的。
一、总体架构视角
Claude Code 不是一个“聊天工具”,而是一个:
事件驱动 + 流式处理 + 多层抽象的数据系统
核心链路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 用户输入 ↓ ContentBlock(结构化内容) ↓ CliMessage(本地状态) ↓ API Message(协议格式) ↓ LLM(流式输出) ↓ Streaming Delta ↓ Accumulator(拼接) ↓ CliMessage(更新) ↓ UI 渲染
|
2.1 设计目标
解决三个核心问题:
| 问题 |
解决方式 |
| UI 需要丰富状态 |
CLI Message |
| API 需要干净协议 |
API Message |
| Streaming 不完整 |
Accumulator |
2.2 三层结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| interface MessageTransformPipeline { cliMessage: { type: "user" | "assistant" | "attachment" | "progress" uuid: string timestamp: string message?: APICompatibleMessage attachment?: AttachmentContent progress?: ProgressUpdate }
apiMessage: { role: "user" | "assistant" content: string | ContentBlock[] }
streamAccumulator: { partial: Partial<APIMessage> deltas: ContentBlockDelta[] buffers: Map<string, string> } }
|
2.3 核心思想
✅ 1. 多表示层(Multi-representation)
同一条消息:
- CLI → UI状态
- API → 网络传输
- Streaming → 增量构建
✅ 2. Streaming ≠ Message
1 2
| Streaming = 生成过程 Message = 最终结果
|
👉 必须有中间态(Accumulator)
三、Message Lifecycle(完整生命周期)
3.1 输入来源(多入口统一)
flowchart TD
A[输入源] --> A1[粘贴图像/文本]
A --> A2[shell 命令]
A --> A3[内存笔记]
A --> A4["/command"]
A --> A5[用户输入]
A1 --> D[检测类型]
D --> D1[文本]
D --> D2[图像]
D1 --> B1[TextBlock]
D2 --> B2[ImageBlock]
A2 --> S[BashTool]
S --> B2
A3 --> U[更新 CLAUDE.md]
A4 --> C[处理命令]
A5 --> E[CliMessage]
B1 --> MT
B2 --> MT
U --> MT
C --> MT
E --> MT
MT --> R[移除CLI字段]
R --> C1[API Message]
C1 --> T[Token计算]
T -->|超限| CP[压缩]
T -->|正常| S1[发送LLM]
CP --> SM[摘要]
SM --> S1
3.2 核心流程说明
1️⃣ 输入统一为 ContentBlock
1 2 3
| 文本 → TextBlock 图像 → ImageBlock 命令 → ToolUseBlock
|
2️⃣ 转换为 API Message
移除 CLI 专属字段:
3️⃣ Token 控制
4️⃣ Streaming 返回
1
| LLM → delta → accumulator → message
|
四、ContentBlock:统一内容模型
4.1 多态结构
1 2 3 4 5 6 7 8 9 10 11
| type ContentBlock = | TextBlock | ImageBlock | ToolUseBlock | ToolResultBlock | ThinkingBlock | DocumentBlock | VideoBlock | GuardContentBlock | ReasoningBlock | CachePointBlock
|
4.2 核心思想
❗消息 = 内容块数组,而不是字符串
4.3 示例
1 2 3 4
| [ { "type": "text", "text": "我来帮你查一下" }, { "type": "tool_use", "name": "weather_api", "input": {"city":"Tokyo"} } ]
|
4.4 优势
✅ 结构化语义
不需要:
✅ 多模态支持
✅ 隐式信息
五、Streaming JSON 问题(核心难点)
5.1 问题本质
LLM 输出:
👉 JSON 是“破碎的”
5.2 解决方案:Streaming JSON Parser
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class StreamingToolInputParser { private buffer = '' private depth = 0 private inString = false private escape = false
addChunk(chunk: string) { this.buffer += chunk
for (const char of chunk) { if (!this.inString) { if (char === '{') this.depth++ if (char === '}') this.depth-- }
if (char === '"' && !this.escape) { this.inString = !this.inString }
this.escape = char === '\\' && !this.escape }
if (this.depth === 0) { try { return JSON.parse(this.buffer) } catch { if (this.inString) { return JSON.parse(this.buffer + '"') } } } } }
|
5.3 关键机制
🧠 depth(结构闭合)
🧠 inString(字符串状态)
防止误判:
🧠 自动修复
六、Streaming 协议(基础)
6.1 SSE 数据格式
6.2 处理流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| async *processStream(stream) { const reader = stream.getReader() let partial = ''
while (true) { const { done, value } = await reader.read() if (done) break
partial += decode(value)
const lines = partial.split('\n') partial = lines.pop()
for (const line of lines) { if (line.startsWith('data: ')) { yield JSON.parse(line.slice(6)) } } } }
|
6.3 核心问题解决
❗chunk 不完整
👉 用 partial 拼接
七、Streaming Accumulator(消息构建器)
7.1 数据结构
1 2 3 4 5
| streamAccumulator: { partial: Partial<APIMessage> deltas: ContentBlockDelta[] buffers: Map<string, string> }
|
7.2 作用
| 字段 |
作用 |
| partial |
当前消息 |
| deltas |
增量 |
| buffers |
JSON 拼接 |
八、Mutation 控制(状态安全)
8.1 为什么要控制?
防止:
8.2 三个允许修改点
1️⃣ Streaming 拼接
1
| lastBlock.text += delta.text
|
1 2 3 4 5
| history.push({ type: 'user', isMeta: true, content: [toolResult] })
|
3️⃣ 成本计算
1
| message.costUSD = calculateCost(...)
|
九、执行系统总览(从“说”到“做”)
在 Part 1 中,系统已经可以:
但还缺一个关键能力:
❗执行任务(而不仅仅是生成文本)
🧠 执行链路
1 2 3 4 5 6 7 8 9 10 11
| LLM(决策) ↓ ToolUseBlock ↓ Tool / MCP ↓ ToolResultBlock ↓ 注入对话 ↓ LLM 下一轮
|
👉 这就是经典:
ReAct Loop(思考 → 行动 → 观察)
10.1 完整结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| interface ToolDefinition { name: string description: string prompt?: string
inputSchema: ZodSchema inputJSONSchema?: JSONSchema
call: AsyncGenerator<ToolProgress | ToolResult, void, void>
checkPermissions?: (...)
mapToolResultToToolResultBlockParam: (...)
isReadOnly: boolean isMcp?: boolean
renderToolUseMessage?: (input) => ReactElement }
|
10.2 核心设计点
✅ 1. 工具不是函数,而是“协议对象”
普通函数:
1
| function readFile(path: string): string
|
Tool:
1
| 函数 + 类型 + 权限 + streaming + UI
|
✅ 2. 双 Schema 设计
1 2
| inputSchema: ZodSchema inputJSONSchema: JSONSchema
|
👉 本质:
一套给代码,一套给模型
✅ 3. AsyncGenerator(关键)
1
| call: AsyncGenerator<ToolProgress | ToolResult>
|
为什么不是 Promise?
因为:
示例
1 2 3
| yield { type: "progress", message: "扫描中..." } yield { type: "progress", message: "找到 120 个文件" } return { result: files }
|
✅ 4. 输出统一进入 ContentBlock
1
| mapToolResultToToolResultBlockParam(...)
|
👉 保证:
十一、ToolUseContext:执行环境(运行时上下文)
11.1 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| interface ToolUseContext { abortController: AbortController
readFileState: Map<string, { content, timestamp }>
getToolPermissionContext: () => ToolPermissionContext
options: { tools mainLoopModel debug maxThinkingTokens }
mcpClients?: McpClient[] }
|
11.2 核心能力
🧠 1. 可取消执行
👉 支持:
📂 2. 文件缓存
👉 避免重复 IO
🔌 3. MCP 接入点
👉 工具可以调用远程服务
十二、权限系统(安全核心)
12.1 权限上下文
1 2 3 4 5 6
| interface ToolPermissionContext { mode: "default" | "acceptEdits" | "bypassPermissions"
alwaysAllowRules alwaysDenyRules }
|
12.2 分层优先级
1
| cliArg > local > project > policy > user
|
12.3 设计思想
❗模型不能直接操作系统,必须经过权限层
12.4 示例
1 2
| cliArg: 禁止删除文件 project: 允许删除 /tmp
|
👉 最终:
十三、MCP:分布式工具协议
13.1 本质
❗MCP = 远程工具调用协议(RPC)
13.2 JSON-RPC 结构
1 2 3 4 5 6
| interface McpRequest { jsonrpc: "2.0" id method params }
|
13.3 示例
1 2 3 4 5 6 7
| { "method": "tools.call", "params": { "name": "read_file", "input": {...} } }
|
13.4 Capability 协商
1 2 3 4 5 6
| interface McpCapabilities { tools?: boolean resources?: boolean prompts?: boolean sampling?: boolean }
|
13.5 核心能力
| 能力 |
作用 |
| tools |
提供工具 |
| resources |
提供数据 |
| prompts |
动态 prompt |
| sampling |
反向调用 LLM |
十四、MCP 状态机
stateDiagram-v2
[*] --> Initializing
Initializing --> Connecting
Connecting --> Ready
Ready --> Closing
Closing --> Disconnected
14.1 状态说明
| 状态 |
含义 |
| Initializing |
初始化 |
| Connecting |
建立连接 |
| Ready |
可用 |
| Closing |
关闭中 |
| Disconnected |
断开 |
十五、SessionState:全局状态系统
15.1 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| interface SessionState { sessionId cwd
totalCostUSD modelTokens
mainLoopModelOverride
sessionCounter locCounter commitCounter
lastInteractionTime }
|
15.2 核心作用
❗让系统从“无状态”变成“有状态”
15.3 分类
🧭 环境
💰 成本
📊 行为
⚠️ 状态
- rate limit fallback
- unknown cost
十六、双向 Streaming(核心通信)
16.1 协议结构
1 2 3 4
| clientPayload: { bytes: string encoding: 'base64' }
|
16.2 数据流
1
| Event → JSON → bytes → base64 → SSE
|
16.3 事件类型
1 2 3 4
| ContentBlockDeltaEvent ToolUseRequestEvent ErrorEvent MetadataEvent
|
16.4 处理流程
1 2 3 4 5
| for each line: if "data: " parse JSON decode base64 parse event
|
16.5 本质
❗事件流系统(Event Streaming System)
十七、性能优化体系(关键)
17.1 Lazy Parsing
1 2 3
| parse(raw) { return JSON.parse(raw) }
|
👉 只在访问时解析
17.2 String Intern
👉 相同字符串只存一份
17.3 WeakRef 缓存
1
| Map<string, WeakRef<FileContent>>
|
👉 不阻止 GC
17.4 FinalizationRegistry
17.5 总策略
十八、完整执行链路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 用户输入 ↓ System Prompt(动态组装) ↓ LLM ↓ Streaming Event ↓ ToolUseBlock ↓ Tool / MCP 执行 ↓ ToolResultBlock ↓ 注入 Message ↓ 下一轮 LLM
|
十九、核心设计哲学总结
🌟 1. 一切皆流(Everything is streaming)
🌟 2. 一切皆结构(Everything is structured)
- ContentBlock
- Tool schema
- MCP protocol
🌟 3. 一切皆可扩展
🌟 4. LLM 是调度器