附录 B:从 Claude Code 源码学到的 10 个设计模式
以下分析基于 2025 年 5 月 Claude Code 公开源码版本。Claude Code 处于活跃开发中,部分实现细节可能已经更新。
Claude Code 是 Anthropic 推出的 AI 编程 CLI 工具,也是目前 Node.js + TypeScript 技术栈下最复杂的 CLI 应用之一。它的代码库展示了很多值得借鉴的架构模式。以下 10 个模式提炼自其公开源码和技术文档。
1. React-in-Terminal 架构
Claude Code 用 React 构建终端界面,但没有直接使用 Ink 的默认 reconciler,而是实现了自定义的 React reconciler。
标准 Ink 的渲染策略是”状态变化时重新计算整个可见区域的字符矩阵”。对于静态内容这没问题,但 Claude Code 的核心场景是流式输出——AI 的回复逐 token 到达,每秒可能触发几十次状态更新。如果每次都完整重绘,性能开销不可接受。
Claude Code 的自定义 reconciler 做了两件关键优化:一是增量追加——流式文本直接 append 到输出缓冲区的末尾,不触发全局重绘;二是脏区域标记——只有被修改的组件节点才参与 diff 计算。这和游戏引擎的”脏矩形”渲染技术是同一个思路。
此外,Claude Code 内置了一个 ANSI 转义序列解析器。终端中的子进程输出(如 git diff、cat 等命令的结果)包含颜色转义码,需要正确解析才能在 React 组件中渲染。解析器把 ANSI 序列拆分为文本片段和样式信息,再由 React 组件分别渲染。
借鉴点:当标准框架的性能不满足需求时,可以在 reconciler 层面做定制,而不是放弃声明式架构回退到命令式。
2. 中间件链式权限系统
Claude Code 在每次工具调用(读文件、写文件、执行命令等)前后都执行一系列检查。这些检查以中间件的形式组织,形成一条处理链。
核心概念是两个 Hook:PreToolUse 和 PostToolUse。每个 Hook 是一个函数数组,按顺序执行。PreToolUse 在工具执行前运行,可以放行、拦截或要求用户确认。PostToolUse 在工具执行后运行,可以审计日志或修改输出。
用户请求 → PreToolUse[权限检查] → PreToolUse[路径校验] → PreToolUse[用户确认]
↓
工具执行
↓
PostToolUse[审计日志] ← PostToolUse[输出过滤] ← PostToolUse[结果校验]中间件可以返回三种结果:allow(放行,继续执行下一个中间件)、deny(拒绝,终止链并返回错误)、ask(暂停链,弹出交互式确认界面,用户同意后继续)。
这种设计的好处是权限逻辑和业务逻辑完全分离。添加新的安全规则只需要注册一个新的 PreToolUse 中间件,不需要修改任何工具的实现代码。
借鉴点:对于需要权限控制的 CLI 工具,中间件模式比在每个命令中写 if-else 更优雅、更可维护。
3. 四级权限模型
Claude Code 把工具分为四个安全等级,每个等级有独立的权限策略:
| 等级 | 工具 | 默认策略 |
|---|---|---|
| 只读 | Read(读文件)、WebFetch(网络请求) | 自动放行 |
| 受限写入 | Edit(编辑文件) | 首次需确认,确认后同类操作自动放行 |
| 命令执行 | Bash(执行 shell 命令) | 每次需确认,支持白名单规则 |
| 危险操作 | 删除文件、force push 等 | 始终需要确认,不支持自动放行 |
权限规则支持通配符配置。用户可以在项目的 .claude/settings.json 中定义规则:
{
"permissions": {
"allow": [
"Bash(npm test)",
"Bash(git *)",
"Edit(src/**)"
],
"deny": [
"Bash(rm *)",
"Edit(.env*)"
]
}
}Bash(npm test) 表示允许执行 npm test 命令无需确认;Edit(src/**) 表示允许编辑 src 目录下任何文件无需确认;Bash(rm *) 表示禁止任何以 rm 开头的命令。
借鉴点:权限不是全有或全无的开关。按操作类型分级,配合通配符规则,可以在安全性和使用体验之间取得平衡。
4. 流式输出组件化
AI 回复是逐 token 流式到达的,Claude Code 用一个专门的 StreamingText 组件处理这个场景。
这个组件的核心挑战不是”逐字显示”——那很简单。难点在于:
-
Markdown 渲染的时机:AI 回复是 Markdown 格式,但在流式到达过程中,Markdown 结构是不完整的——代码块可能只有开头的 ``` 还没有结尾。组件需要判断何时可以安全地渲染 Markdown(段落结束、代码块闭合),何时只能暂时作为纯文本展示。
-
暂停和恢复:用户可以按 Escape 暂停流式输出(AI 继续生成但不显示),再按一次恢复。这需要一个缓冲区来存储暂停期间的文本。
-
取消:用户按 Ctrl+C 取消当前生成。组件需要立即停止渲染,清理已建立的 SSE 连接,并在界面上显示”已取消”状态。
-
性能:长回复可能有几千 token。如果每个 token 都触发 React 状态更新和重渲染,帧率会显著下降。Claude Code 的做法是用
requestAnimationFrame风格的批处理——累积多个 token 后一次性更新状态。
借鉴点:流式输出不是简单的字符串拼接。把它封装成一个有状态的组件,内部处理缓冲、暂停、取消等复杂逻辑,外部使用者只需要传入一个 stream。
5. 多层配置合并
Claude Code 的配置来源有五层,按优先级从低到高:
硬编码默认值 < 全局配置 < 企业配置 < 项目配置 < 环境变量 < 命令行参数- 硬编码默认值:代码中定义的 fallback
- 全局配置:
~/.claude/settings.json,用户级偏好 - 企业配置:企业部署时通过 MDM 下发的策略,不可被用户覆盖
- 项目配置:
.claude/settings.json,项目级设置,可以提交到 Git - 环境变量:
CLAUDE_*前缀的环境变量 - 命令行参数:
--model、--verbose等
合并逻辑不是简单的 Object.assign。不同类型的配置有不同的合并策略:
- 标量值(字符串、数字、布尔):高优先级覆盖低优先级
- 数组值(如权限规则的
allow列表):合并(concat),不是覆盖 - 对象值:递归合并
企业配置有特殊地位——它的某些字段(如允许的模型列表、网络访问策略)标记为 enforced,即使项目配置或命令行参数也不能覆盖。这确保了企业安全策略的强制执行。
借鉴点:多层配置合并是 CLI 工具的常见需求。关键是明确定义每一层的优先级和合并策略,特别是数组类型字段是”覆盖”还是”合并”要想清楚。
6. 错误上报 fallback-channel
Claude Code 的错误上报不是简单的 try-catch + 日志。它实现了一个 fallback-channel 机制:当主通道异常时自动切换到备用通道。
正常情况下,错误通过结构化日志写入本地文件(~/.claude/logs/),并在用户同意的情况下上报到远程监控服务。但如果主上报通道本身出了问题——比如网络断开、磁盘满了、日志文件被锁定——错误信息会丢失。
fallback-channel 的设计是一个降级链:
远程上报 → 本地日志文件 → stderr 输出 → /tmp 临时文件每一层失败后自动降级到下一层。最后一层(/tmp 临时文件)几乎不可能失败(除非整个文件系统只读)。即使所有持久化方案都失败,至少 stderr 能让用户看到错误信息。
这个机制还处理了一个微妙的问题:错误处理代码本身的错误。如果 try-catch 的 catch 块里的代码抛异常,通常会导致无限递归或静默丢失。fallback-channel 通过逐层降级避免了这个问题——上层的异常被下层捕获处理。
借鉴点:生产级 CLI 工具需要考虑”如果错误处理失败了怎么办”。降级链是一个简洁有效的解决方案。
7. 约定式命令发现
Claude Code 支持项目级自定义命令:把 Markdown 文件放在 .claude/commands/ 目录下,自动注册为可调用的斜杠命令。
.claude/
commands/
review.md → /project:review
deploy.md → /project:deploy
fix-lint.md → /project:fix-lint文件名就是命令名,文件内容就是命令的 prompt 模板。不需要写 JavaScript、不需要注册、不需要配置——放进去就能用。
这种”约定式”设计降低了扩展门槛:任何团队成员都可以创建新命令,不需要了解 CLI 框架的内部实现。命令文件可以提交到 Git 仓库,团队共享。
命令发现的实现很简单:
- 启动时扫描
.claude/commands/目录 - 读取所有
.md文件 - 文件名转换为命令名(去掉
.md后缀,-保留) - 文件内容作为命令的 prompt,支持
$ARGUMENTS变量占位符
这个模式不限于 AI 工具。任何 CLI 都可以用类似的方式支持用户自定义扩展——约定一个目录,扫描其中的文件,按文件名注册功能。repox 的插件系统也可以参考这种方式实现轻量级扩展。
借鉴点:约定优于配置。文件系统本身就是最好的注册表。
8. 沙箱隔离执行
Claude Code 的 Bash 工具(执行 shell 命令)运行在操作系统级别的沙箱中。这不是一个简单的命令过滤器,而是利用 OS 安全原语实现的强隔离。
macOS 上使用 Seatbelt(sandbox-exec),通过 .sb 配置文件定义允许和禁止的操作。Linux 上组合使用 Bubblewrap(文件系统命名空间隔离)、Landlock(用户态文件系统访问控制)和 seccomp(系统调用过滤)。
沙箱的核心原则是最小权限:
- 文件系统:只允许读写项目目录和必要的系统目录(
/usr、/tmp) - 网络:默认禁止所有网络访问(防止数据外泄)
- 进程:禁止创建特权进程
- 设备:禁止访问硬件设备
沙箱配置是动态的——用户可以通过权限规则放宽限制(比如允许 npm install 的网络访问),但不能超出 Claude Code 定义的最大权限边界。
实现中的一个细节:沙箱创建有性能开销,特别是 Bubblewrap 需要设置新的命名空间。Claude Code 通过预创建沙箱环境来减少延迟——在 AI 开始思考时就准备好沙箱,等 AI 决定执行命令时沙箱已经就绪。
借鉴点:应用层的命令过滤不可靠(总有绕过方式),OS 级沙箱才能提供真正的安全保证。但沙箱的配置复杂度很高,建议渐进式实现——先做应用层白名单,再逐步引入 OS 级隔离。
9. 上下文压缩
AI 对话有 token 上限(Claude 的上下文窗口是 200K tokens)。长时间使用 Claude Code 后,对话历史会逼近这个限制。Claude Code 的做法不是简单地截断历史,而是压缩。
压缩流程:
- 监测当前对话的 token 数量
- 当接近上限(比如超过 80%)时触发压缩
- 把历史消息发送给 AI,要求生成一个”摘要”——保留关键信息(文件路径、决策依据、未完成的任务),丢弃冗余内容(重复的代码片段、已解决的讨论)
- 用摘要替换原始历史,释放 token 空间
这比简单截断好得多。截断会丢失早期的重要上下文(比如用户在对话开头定义的需求),而压缩可以保留要点。
压缩还有一个副作用的好处:它相当于定期”刷新”AI 的注意力。长对话中 AI 可能被中间的无关讨论分散注意力,压缩后的摘要重新聚焦在核心任务上。
对 CLI 开发的启示:任何涉及长时间 AI 对话的工具都需要考虑上下文管理。不只是 token 限制——即使窗口足够大,过长的上下文也会降低 AI 的回答质量(“大海捞针”问题)。
借鉴点:资源受限时,压缩优于截断。保留结构化摘要而不是随机丢弃内容。
10. MCP 集成
Claude Code 同时扮演两个 MCP 角色:
作为 MCP Client:Claude Code 可以连接外部 MCP Server,调用第三方工具。比如连接一个数据库 MCP Server 查询数据,或连接一个 Jira MCP Server 读取任务信息。这使得 Claude Code 的能力可以无限扩展,而不需要在自身代码中集成每一个外部服务。
作为 MCP Server:其他 AI 应用可以通过 MCP 协议调用 Claude Code 的能力(文件读写、代码搜索、命令执行等)。这使得 Claude Code 可以被嵌入更大的 AI 工作流中。
MCP 集成的实现要点:
-
工具发现:启动时从配置文件读取 MCP Server 列表,连接每个 Server,获取可用工具的 schema。这些工具和内置工具合并在一起,AI 可以根据需要选择调用。
-
生命周期管理:MCP Server 作为子进程启动,Claude Code 负责管理它们的生命周期——启动、健康检查、崩溃重启、退出清理。
-
权限透传:通过 MCP 调用的外部工具同样经过权限系统的检查。一个恶意的 MCP Server 不能绕过 Claude Code 的安全策略。
-
协议版本协商:MCP 协议在快速演进中,不同 Server 可能实现了不同版本的协议。Claude Code 在连接时进行版本协商,确保兼容性。
借鉴点:MCP 不只是一个”对外暴露接口”的方案,更是一个”引入外部能力”的方案。一个 CLI 工具同时作为 MCP Client 和 Server,就有了连接整个 AI 工具生态的能力。
本书资源
- 源码仓库 · github.com/diguike/repox
- 在线阅读 · inferloop.dev/repox
- 所有书目 · inferloop.dev
继续阅读 · 同作者其他书
- 《Transformer 工程实战》从注意力机制到生产部署
- 《自己动手写 AI Agent》从 Claude Code 开源架构到你的第一个编程助手
- 《LLM Infra 工程实战》从入门到实践
- 《Hermes Agent 实战》构建会成长的个人 AI Agent
- 《OpenClaw 源码解析》现代 Agent 系统的架构设计与工程实践
- 《一切皆插件》DeepSeek Harness 源码精读、Mini 实现与插件开发实战
- 《Agent Memory 工程实战》从 claude-mem 源码到企业级记忆平台
- 《AI Token 中转站实战》从 0 搭建企业级 LLM 网关
- 《LangChain.js Agent 开发权威指南》从 1.x 抽象到生产级 Agent
- 《百万级 AI Agent 平台架构》智能客服 SaaS 实战
- 《AI Agent 评测工程实战》从 0 用 TypeScript 构建你的评测平台
- 《Agent Harness 评测工程》用评测建设并守护一个 agent harness
- 《源码精读》每章一个开源仓库 · 从架构到品味
- 《Claude Code Skill 指南》
- 《Claude 插件官方指南》