第4章 分层架构
引言
在软件工程中,分层架构是一种经典的设计模式,它将系统划分为多个层次,每个层次负责特定的职责。Claude Code 作为一个复杂的 AI 编程助手系统,采用了清晰的五层架构设计。这种设计不仅让系统易于理解和维护,更重要的是为"对话即编程"范式提供了坚实的技术基础。
本章将深入剖析 Claude Code 的分层架构,从 CLI 层到基础设施层,逐层揭示其设计思想和实现细节。我们将看到,良好的分层架构如何让复杂的系统变得优雅而强大。
概念讲解:五层架构全景
Claude Code 的五层架构从上到下依次为:
- CLI 层:命令行界面,负责用户交互
- 查询引擎层:核心业务逻辑,管理对话流程
- 工具命令层:工具注册和执行
- 服务层:各种业务服务(API、MCP、文件服务等)
- 基础设施层:底层支撑(状态管理、费用追踪、日志等)
架构图
┌─────────────────────────────────────────┐
│ CLI 层 (React + Ink) │
│ - 命令解析 - UI 渲染 - 用户输入处理 │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ 查询引擎层 (QueryEngine) │
│ - 消息提交 - 状态机 - 流式响应 │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ 工具命令层 (Tools + Commands) │
│ - 工具注册 - 命令解析 - 权限管理 │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ 服务层 (Services) │
│ - API 服务 - MCP 服务 - 文件服务 │
└────────────────┬────────────────────────┘
│
┌────────────────▼────────────────────────┐
│ 基础设施层 (Infrastructure) │
│ - 状态管理 - 费用追踪 - 日志系统 │
└─────────────────────────────────────────┘
这种分层设计的核心优势在于:
- 职责清晰:每层专注于自己的职责,降低耦合
- 易于测试:可以独立测试每一层
- 灵活扩展:新增功能只需修改相关层次
- 团队协作:不同团队可以负责不同层次
源码分析:CLI 层
CLI 层是用户与系统交互的入口。从项目结构可以看到,CLI 相关的代码包括:
src/commands.ts:命令定义src/dialogLaunchers.tsx:对话启动器src/replLauncher.tsx:REPL 启动器src/interactiveHelpers.tsx:交互辅助组件
CLI 层使用了 Commander.js 作为命令行框架,React + Ink 作为终端 UI 框架。这种组合让 CLI 既强大又美观。
从 QueryEngine.ts 的导入可以看到 CLI 层与查询引擎层的连接:
import type { Command } from './commands.js'
import { getSlashCommandToolSkills } from './commands.js'
Command 类型定义了命令的结构,getSlashCommandToolSkills 函数将斜杠命令转换为工具,使得 AI 可以理解并执行这些命令。
源码分析:查询引擎层
查询引擎层是整个系统的核心,负责管理"对话即编程"的流程。QueryEngine.ts 是这一层的主要实现。
从导入部分可以看到查询引擎层依赖了大量其他层次:
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/messages.mjs'
import { randomUUID } from 'crypto'
import last from 'lodash-es/last.js'
import {
getSessionId,
isSessionPersistenceDisabled,
} from 'src/bootstrap/state.js'
import { accumulateUsage, updateUsage } from 'src/services/api/claude.js'
import { getModelUsage, getTotalAPIDuration, getTotalCost } from './cost-tracker.js'
import { query } from './query.js'
import type { Tools, type ToolUseContext, toolMatchesName } from './Tool.js'
查询引擎层的核心职责包括:
- 消息管理:接收用户消息,管理对话历史
- 上下文收集:调用
getSystemContext()和getUserContext() - 查询执行:调用
query()函数进入状态机循环 - 结果处理:将 AI 响应转换为 UI 可显示的格式
ToolUseContext 的贯穿作用
ToolUseContext 是一个关键类型,它贯穿了查询引擎层、工具命令层和服务层:
export type ToolUseContext = {
// 从 Tool.ts 中可以看到完整的定义
// 包括:消息历史、文件状态、权限信息、任务状态等
}
从 query.ts 的导入可以看到:
import { findToolByName, type ToolUseContext } from './Tool.js'
ToolUseContext 携带了执行工具所需的所有上下文信息,包括:
- 当前对话的消息历史
- 文件系统状态
- 权限和配置信息
- 任务状态
- 会话信息
这种设计让每一层都能访问到必要的上下文,同时保持了类型安全。
源码分析:工具命令层
工具命令层负责工具的注册、管理和执行。tools.ts 是这一层的主要文件。
从导入可以看到工具命令层的结构:
import { toolMatchesName, type Tool, type Tools } from './Tool.js'
import { AgentTool } from './tools/AgentTool/AgentTool.js'
import { SkillTool } from './tools/SkillTool/SkillTool.js'
import { BashTool } from './tools/BashTool/BashTool.js'
import { FileEditTool } from './tools/FileEditTool/FileEditTool.js'
import { FileReadTool } from './tools/FileReadTool/FileReadTool.js'
import { FileWriteTool } from './tools/FileWriteTool/FileWriteTool.js'
import { GlobTool } from './tools/GlobTool/GlobTool.js'
工具命令层的核心设计包括:
- 工具注册:每个工具都是一个独立的模块,通过统一的接口注册
- 工具匹配:
toolMatchesName函数用于匹配工具名称 - 工具执行:通过
runTools函数执行工具调用
从 query.ts 的导入可以看到工具命令层与服务层的交互:
import { StreamingToolExecutor } from './services/tools/StreamingToolExecutor.js'
import { runTools } from './services/tools/toolOrchestration.js'
StreamingToolExecutor 负责流式执行工具,runTools 负责工具编排。
工具类型定义
Tool.ts 定义了工具的核心类型:
export type ToolInputJSONSchema = {
[x: string]: unknown
type: 'object'
properties?: {
[x: string]: unknown
}
}
这是工具输入的 JSON Schema 定义,确保了类型安全。每个工具都必须明确定义其输入参数的结构。
源码分析:服务层
服务层提供了各种业务服务,包括 API 服务、MCP 服务、文件服务等。从 query.ts 的导入可以看到服务层的组成:
import {
calculateTokenWarningState,
isAutoCompactEnabled,
type AutoCompactTrackingState,
} from './services/compact/autoCompact.js'
import { buildPostCompactMessages } from './services/compact/compact.js'
import {
logEvent,
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
} from 'src/services/analytics/index.js'
import { ImageSizeError } from './utils/imageValidation.js'
import { ImageResizeError } from './utils/imageResizer.js'
服务层的核心服务包括:
- API 服务:与 Anthropic API 通信
- 压缩服务:管理上下文压缩
- 分析服务:日志和分析
- 文件服务:文件操作
- MCP 服务:Model Context Protocol 集成
服务层的设计特点:
- 模块化:每个服务都是独立的模块
- 可测试:服务层可以独立测试
- 可扩展:新增服务只需添加新模块
源码分析:基础设施层
基础设施层提供了底层支撑功能,包括状态管理、费用追踪、日志系统等。cost-tracker.ts 和 Task.ts 是这一层的重要文件。
费用追踪设计
cost-tracker.ts 展示了基础设施层的设计精髓:
import type { BetaUsage as Usage } from '@anthropic-ai/sdk/resources/beta/messages/messages.mjs'
import chalk from 'chalk'
import {
addToTotalCostState,
addToTotalLinesChanged,
getCostCounter,
getModelUsage,
getSdkBetas,
getSessionId,
getTokenCounter,
getTotalAPIDuration,
getTotalAPIDurationWithoutRetries,
getTotalCacheCreationInputTokens,
getTotalCacheReadInputTokens,
getTotalCostUSD,
getTotalDuration,
getTotalLinesAdded,
getTotalLinesRemoved,
getTotalOutputTokens,
getTotalToolDuration,
getTotalWebSearchRequests,
getUsageForModel,
hasUnknownModelCost,
resetCostState,
resetStateForTests,
setCostStateForRestore,
setHasUnknownModelCost,
} from './bootstrap/state.js'
费用追踪的核心功能包括:
- 费用计算:根据 token 使用量计算费用
- 费用累积:累积整个会话的费用
- 费用展示:格式化展示费用信息
- 费用持久化:将费用保存到项目配置
费用追踪的关键函数:
export function addToTotalSessionCost(
cost: number,
usage: Usage,
model: string,
): number {
const modelUsage = addToTotalModelUsage(cost, usage, model)
addToTotalCostState(cost, modelUsage, model)
const attrs =
isFastModeEnabled() && usage.speed === 'fast'
? { model, speed: 'fast' }
: { model }
getCostCounter()?.add(cost, attrs)
getTokenCounter()?.add(usage.input_tokens, { ...attrs, type: 'input' })
getTokenCounter()?.add(usage.output_tokens, { ...attrs, type: 'output' })
getTokenCounter()?.add(usage.cache_read_input_tokens ?? 0, {
...attrs,
type: 'cacheRead',
})
getTokenCounter()?.add(usage.cache_creation_input_tokens ?? 0, {
...attrs,
type: 'cacheCreation',
})
let totalCost = cost
for (const advisorUsage of getAdvisorUsage(usage)) {
const advisorCost = calculateUSDCost(advisorUsage.model, advisorUsage)
logEvent('tengu_advisor_tool_token_usage', {
advisor_model:
advisorUsage.model as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
input_tokens: advisorUsage.input_tokens,
output_tokens: advisorUsage.output_tokens,
cache_read_input_tokens: advisorUsage.cache_read_input_tokens ?? 0,
cache_creation_input_tokens:
advisorUsage.cache_creation_input_tokens ?? 0,
cost_usd_micros: Math.round(advisorCost * 1_000_000),
})
totalCost += addToTotalSessionCost(
advisorCost,
advisorUsage,
advisorUsage.model,
)
}
return totalCost
}
这个函数展示了基础设施层的设计特点:
- 精确追踪:追踪所有类型的 token 使用
- 多模型支持:支持多个模型的费用追踪
- 顾问工具:支持顾问工具的费用追踪
- 事件记录:记录分析事件
任务类型和状态机设计
Task.ts 定义了任务系统的核心类型:
export type TaskType =
| 'local_bash'
| 'local_agent'
| 'remote_agent'
| 'in_process_teammate'
| 'local_workflow'
| 'monitor_mcp'
| 'dream'
export type TaskStatus =
| 'pending'
| 'running'
| 'completed'
| 'failed'
| 'killed'
任务类型涵盖了系统中的各种任务:
local_bash:本地 bash 命令local_agent:本地 agentremote_agent:远程 agentin_process_teammate:进程内队友local_workflow:本地工作流monitor_mcp:MCP 监控dream:梦境任务
任务状态定义了任务的生命周期:
pending:等待中running:运行中completed:已完成failed:失败killed:被终止
任务状态机的关键函数:
export function isTerminalTaskStatus(status: TaskStatus): boolean {
return status === 'completed' || status === 'failed' || status === 'killed'
}
这个函数判断任务是否处于终止状态,用于防止向已完成的任务注入消息。
任务 ID 的生成:
export function generateTaskId(type: TaskType): string {
const prefix = getTaskIdPrefix(type)
const bytes = randomBytes(8)
let id = prefix
for (let i = 0; i < 8; i++) {
id += TASK_ID_ALPHABET[bytes[i]! % TASK_ID_ALPHABET.length]
}
return id
}
任务 ID 的生成展示了基础设施层的设计细节:
- 类型前缀:每个任务类型有独特的前缀
- 随机生成:使用加密安全的随机数生成器
- 足够空间:36^8 ≈ 2.8 万亿种组合,防止暴力攻击
设计启示
1. 分层的价值
Claude Code 的五层架构展示了分层的价值:
- 职责分离:每层专注于自己的职责,降低耦合
- 易于维护:修改某一层不会影响其他层
- 灵活扩展:新增功能只需修改相关层次
- 团队协作:不同团队可以负责不同层次
2. 上下文传递的优雅设计
ToolUseContext 的设计展示了如何在分层架构中优雅地传递上下文:
- 类型安全:TypeScript 确保类型安全
- 按需访问:每层可以访问需要的上下文
- 最小耦合:通过接口而非实现进行通信
- 易于测试:可以模拟上下文进行测试
3. 基础设施的重要性
cost-tracker.ts 和 Task.ts 展示了基础设施层的重要性:
- 精确追踪:费用追踪精确到每个 token
- 状态管理:任务状态管理清晰明确
- 持久化支持:支持状态的持久化和恢复
- 分析支持:为分析提供数据支持
4. 类型安全的保证
TypeScript 的类型系统在分层架构中发挥了关键作用:
- 接口定义:清晰的接口定义
- 类型推导:自动类型推导减少错误
- 编译时检查:编译时发现错误
- 文档作用:类型即文档
思考题
-
分层的权衡:五层架构是否过多?在某些场景下是否可以合并某些层次?如何判断分层的粒度?
-
上下文传递的性能:
ToolUseContext在每一层之间传递,是否会带来性能问题?如何优化上下文传递的性能? -
基础设施层的扩展:如果要在基础设施层添加新的功能(例如性能监控),应该如何设计?需要修改哪些文件?
-
跨层通信的模式:在五层架构中,跨层通信应该遵循什么原则?是否允许跳层通信?如何避免循环依赖?
-
类型系统的局限:TypeScript 的类型系统在分层架构中发挥了重要作用,但它有什么局限?如何弥补这些局限?
小结
Claude Code 的五层架构设计展示了软件工程中分层架构的威力。从 CLI 层到基础设施层,每一层都有明确的职责和清晰的接口。这种设计让复杂的系统变得易于理解和维护。
关键设计要点:
- 职责分离:每层专注于自己的职责
- 上下文传递:
ToolUseContext优雅地传递上下文 - 基础设施支撑:费用追踪、任务管理等基础设施提供支撑
- 类型安全:TypeScript 确保类型安全
分层架构不仅仅是一种设计模式,更是一种思维方式。它让我们能够以结构化的方式思考和构建复杂的软件系统。在"对话即编程"范式中,分层架构为这一范式提供了坚实的技术基础。