我看完Claude Code源码后,总结出AI Agent最核心的Harness应该这么搭

Anthropic 刚刚亲手教会了你,怎样搭出最好的 AI Agent Harness。
Claude Code 的源码就摆在那里:55 个目录、331 个模块,这是当下生产环境里经历过最多实战检验的 Agent 架构之一。我把每一个文件都拆开看了。每一个架构决策、每一条重试路径、每一种压缩策略、每一个权限阶段。
这不是一次拆解。
这是一份蓝图。
下面,我会把它里面的每一条原则都摊开讲清楚,以及你该如何把这些原则用到自己的系统里,构建一个真正扛得住生产环境的 Harness。
Claude Code 的架构,揭示了行业最爱谈的 Framework 到底漏了什么
你一定听过那三个层次:模型权重、上下文、Harness。
行业里几乎每一场大会、每一篇教程、每一个框架 README 都在重复这三层。
Model Weights(模型权重):被冻结的智能本体,也就是你通过 API 调用的那个东西。
Context(上下文):提示词、对话历史、检索到的文档。
Harness:包在模型外面的那层脚手架,包括工具、循环、错误处理。
这个划分本身并没有错。
Princeton NLP 的 SWE-agent 论文已经证明过:在 SWE-bench 上,只改界面设计、不改模型,效果就能带来 64% 的相对提升。同样的 GPT-4,同样的任务,变化的只有环境。性能增益主要来自第 2 层和第 3 层,不是第 1 层。
但当你真正打开 Claude Code 的源码时,你会意识到:Anthropic 不是在为“模型”构建系统,他们是在为“系统”本身构建系统。
在 Claude Code 内部:
-
一个 四级 CLAUDE.md 指令层级,让企业管理员可以通过 MDM 强制执行策略; -
项目维护者可以设定团队约定; -
个人开发者还能在本地做私有覆盖; -
一个磁盘持久化的任务列表加上基于文件的锁,确保并行子 Agent 不会互相污染状态; -
Git worktree 隔离,让 5 个 Agent 能在同一个仓库上开 5 条分支同时工作,互不冲突; -
一个权限流水线,把拒绝规则从企业级一路级联到项目级、用户级、会话级。
这些都不是 Harness。
这些都不是 Context。
这些也都不是 Weights。
这些是 Infrastructure(基础设施):
-
多租户(multi-tenancy) -
基于角色的访问控制(RBAC) -
资源隔离(resource isolation) -
状态持久化(state persistence) -
分布式协同(distributed coordination)
所以,真正的框架其实有四层:
Model Weights:冻结的智能。
Context:运行时输入。
Harness:Agent 被精心设计过的执行环境。
Infrastructure:多租户、RBAC、资源隔离、状态持久化、分布式协同。
大多数团队只谈前三层,因为前三层更“有意思”,更适合拿来讨论。
但真正让产品死掉的,是第四层。
Claude Code 是我见过第一个把四层都认真对待的 Agent 系统,而它的架构在每一个细节里都体现了这一点。
核心 Agent Loop:它是 Async Generator,不是 While Loop
Claude Code 的心脏在 query.ts 里:1729 行 TypeScript。
而最关键的决策,藏在一个函数签名里:
ounter(lineasync function* query(...) { ... }
这个 function* 的分量,比它看起来重要得多。
Async Generator 可以持续地产出值、按需暂停,并且允许任意调用方在任意时刻中断流程。
Agent Loop 从来都不应该是一个“请求—响应”的一次性循环。
它应该是一个长时间运行、支持流式输出、可取消的过程。
Generator 天然就把这些能力带进来了,不需要你再往外硬补。
而大多数教程教你的,通常是这种东西:
ounter(lineounter(lineounter(lineounter(lineounter(linewhile (!done) {const response = await callModel(...)const toolResults = await runTools(response)messages.push(response, toolResults)}
这种写法在教程里能跑,到了生产环境会迅速崩掉,原因有五个。
1)没有 Streaming
用户只能盯着空白屏幕,等模型 10 到 30 秒。
而 Claude Code 的 Generator 会随着 token 到达,不断产出 StreamEvent 对象。
用户能逐字逐句看到模型正在工作。
当用户能“看见” Agent 在做什么时,他们会更信任它。
而用户越信任它,就越愿意给它更高的自主权。
真正有价值的工作,往往发生在这种自主权之上。
2)没有 Cancellation
在 while-loop 版本里,按下 Ctrl+C 往往意味着你还得从外部额外接一套 abort 机制。
但对于 Generator 来说,调用方只要不再调用 .next(),流程就会自然终止;finally 会被执行,清理逻辑也会自动触发。
Claude Code 把 AbortSignal 一层层往下传,而 Generator 让这种设计变得非常自然。
3)没有 Composability
Claude Code 的 REPL UI 消费这个 Generator。
子 Agent 消费这个 Generator。
测试也消费这个 Generator。
一个 query() 函数,三个调用方,零重复。
Generator 本质上是一个通用的流式接口。
4)没有 Backpressure
如果模型生成速度比终端渲染速度更快,while-loop 会把所有内容都堆在内存里。
Generator 则会在消费者暂时不拉取时,暂停生产。
在超长会话里,这个差异直接决定了:
你的内存占用是保持可控,还是一路膨胀到把进程撑死。
5)循环内部没有 Error Recovery
这才是真正严肃的地方。
每一轮迭代都分五个阶段
Claude Code 的每一次 Agent Loop 迭代,都要经过五个阶段。
这五个阶段,就是它之所以稳健的原因。
Phase 1:Setup
在调用模型之前,Loop 会先:
-
应用 tool result budget; -
如果对话已经很长,则运行 compaction 策略; -
校验 token 数量。
大多数 Harness 做法是:直接把原始消息数组扔给模型,然后祈祷别炸。
Claude Code 不是。
Phase 2:Model Invocation
Loop 会通过依赖注入接口调用 queryModelWithStreaming(),外面再包一层能处理 10 类错误的重试系统。
更关键的是:流式工具执行器会在这个阶段就开始执行工具,而不是等模型整段输出完。
比如一个 Grep 调用,只要它的输入 JSON 在流里一完整,执行器就立刻启动它——甚至下一个工具调用都还没完全流出来。
Phase 3:Error Recovery & Compaction
模型响应结束后,Loop 会检查是否存在可恢复错误:
-
prompt-too-long?压缩后重试; -
max_output_tokens撞上限?从 32K 升到 64K 再重试; -
context overflow?对媒体较重的消息做响应式压缩。
这些在 Claude Code 里不是外围 try/catch 的边角料,而是状态机里的一级状态。
Phase 4:Tool Execution
那些还没被流式执行器提前跑掉的工具,会在这里执行。
工具结果一完成,就立刻 yield 给 UI。
同时,Claude Code 还会让 Haiku 异步生成工具使用摘要,避免主模型把 token 浪费在“记账”这种事情上。
Phase 5:Continuation Decision
模型的 stop_reason 决定这一轮之后是否还需要继续发起工具调用。
系统还会检查:
-
turn counter 是否超过 maxTurns; -
hooks 是否请求停止; -
abort signal 是否触发。
如果要继续,状态递增,重新回到 Phase 1。
错误恢复是在循环内部,而不是循环外围。
每一个阶段都知道自己可能出什么错,也知道该怎么恢复。
这就是“一个遇到 rate limit 就崩的 Agent”和“一个会退避、重试、切换备选模型、继续把活干完的 Agent”之间的本质差异。
依赖注入,让它变得可测试
这个 Loop 通过一个 QueryDeps 接口接收自己的依赖。
也就是说,你可以注入一个 mock 的 callModel,让它产出预设事件,然后验证:
-
context overflow 是否处理正确; -
tool failure 是否处理正确; -
cancellation 是否处理正确。
大多数 Agent Harness 无法测试,不是因为它们太复杂,而是因为它们把 API 调用硬编码进了 Loop。
Claude Code 的 Loop 更像一个纯状态机 + 注入式副作用的系统,因此天然可测。
工具执行:为什么“并发分类”会改变一切
Claude Code 自带 45+ 内置工具。
重点不是数量,而是它们的执行方式。
大部分 Harness 的做法是:
-
模型生成工具调用; -
Harness 顺序执行这些工具; -
把结果回传给模型。
这样当然安全,但速度慢。
另一种极端做法是:全部并行。
这样很快,但也很危险——两个并行文件写操作写到同一路径,状态就污染了。
Claude Code 的做法是:先给每个工具打并发标签。
编排层 toolOrchestration.ts 会把工具调用拆成批次:
-
只读工具(如 Glob、Grep、Read、WebFetch)并发执行,最高 10 个并行; -
写操作工具(如带副作用的 Bash、Edit、Write)串行执行。
于是你得到的是:
-
并行的速度; -
串行的安全性; -
没有 race condition。
Claude Code 可以一边并行搜 5 个文件,一边最后只顺序改 1 个文件。
多工具回合能拿到 2 到 5 倍加速,一整个 session 累积下来,就是几分钟的差距。
Streaming Tool Executor:模型还没说完,工具就已经开跑了
StreamingToolExecutor 才是更有意思的组件。
大多数 Harness 会等模型把整段输出写完,再开始执行工具。
Claude Code 不等。它在流式输出中途就启动执行。
对于一个包含 3 个工具调用的回合,这意味着你能直接隐藏 2 到 5 秒的延迟。
模型一边继续生成自己下一步的描述,前面的工具一边已经在后台跑起来了。
等模型说完,前面工具的结果可能已经等在那里。
而且,那些麻烦情况它也处理到了:
-
如果并行批次里某个工具失败了,会用每个工具自己的 siblingAbortController杀掉同批次兄弟进程,但父级 query controller 还活着,整段对话不会死。模型会收到错误,然后继续恢复。 -
如果流式过程失败并回退到非流式模式,执行器会丢弃排队中的工具,并为在途任务生成 synthetic error result。 -
即使工具 2 比工具 1 更早完成,结果也会按原始顺序 yield,确保对模型和用户来说,叙事仍然连贯。
Tool Result Budgeting:别让 1MB 日志把上下文窗口塞爆
如果用户跑了一个 Bash 命令,直接吐出 1MB 日志,而你原封不动塞给模型,上下文窗口马上就会被垃圾信息占满。
Claude Code 为此设计了一整套 budgeting 系统:
-
每个工具声明自己的 maxResultSizeChars; -
超过上限的结果会持久化到磁盘; -
模型拿到的是:文件路径引用 + 前 N 个字符预览; -
applyToolResultBudget()会在每次 API 调用前运行,把工具结果总体 token 数控制住。
用户一定会:
-
对超大文件执行 cat; -
跑出成 MB 的日志流; -
把上下文灌满噪音。
如果没有 budgeting,Agent 很快就会失去连贯性。
这种细节从来不会出现在那些好看的架构图里,但它决定了一个 Agent 到底能不能活过真实使用。
大规模 Prompt Engineering:System Prompt 本质上是缓存问题
Claude Code 里的 system prompt 不是一整块字符串。
它是一个带缓存元数据的结构化 section 数组。
其中最关键的设计,是一个边界标记:
SYSTEM_PROMPT_DYNAMIC_BOUNDARY
这个边界把 prompt 切成两个区域。
边界以上的内容:
-
所有用户都一样; -
所有 session 都一样; -
因此可以在 API 层命中全局 prompt cache。
这大概占了整个 prompt 的 80%。
也就是说,你不需要为每个用户、每次 API 调用,都重新 tokenize 那 577+ 行的固定内容。
边界以下的 section,则分成两类:
-
memoized:每个 session 只算一次; -
volatile:每一轮都重新计算。
而 volatile section 会被尽量压缩,因为它每变一次,都会让它之后的缓存全部失效。
我几乎没在任何 Agent 教程、框架文档、会议分享里,看到有人认真讨论“如何为缓存效率来设计 prompt”。
但它恰恰是整个代码库里杠杆最高的决策之一。
在规模化场景里,这可能直接决定你的 Agent 是每次会话 0.02 美元,还是 0.20 美元。
CLAUDE.md 层级:可组合的记忆系统
Claude Code 有一个四级指令层级,本质上是一套可组合的记忆系统。
更高层覆盖更低层:
-
企业管理员在组织级统一下发编码规范; -
用户层配置个人偏好; -
项目层定义团队约定; -
开发者还可以保留私有 override,并且不进入版本控制。
它还支持 @include 指令进行组合:
ounter(lineounter(line@./docs/coding-standards.md@~/shared-rules.md
企业级配置甚至可以与 **MDM(移动设备管理)**联动,实现策略强制下发。
这已经不是 Harness Engineering 了。
这是基础设施工程。
为什么上下文注入放在 System Prompt 之外
用户上下文,比如:
-
git status -
CLAUDE.md内容 -
当前日期
Claude Code 会把它们包在 <system-reminder> 标签里,作为第一条 user message 注入进去,而不是塞进 system prompt。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line<system-reminder>As you answer the user's questions, you can use the following context:# claudeMd[contents of CLAUDE.md files]# currentDateToday's date is 2026-04-07.</system-reminder>
原因很简单:
这些上下文每一轮都会变化。
如果你把它们放进 system prompt,那么缓存会从变化点开始全部失效。
而把它们移动到 user message,就能让 system prompt 在每一轮里都保持 cache-stable。
这是个很小的实现细节。
但对成本的影响,非常大。
上下文窗口管理:四种 Compaction 策略
大多数 Agent Harness 在快撞上下文上限时只有两种结局:
-
截断旧消息; -
直接崩溃。
Claude Code 不一样。
它通过四种压缩策略,让会话长度可以理论上无限延展。并且这四种策略按从便宜到昂贵的顺序触发。
Strategy 1:Microcompact
每一轮 API 调用前都会跑。
如果某个工具已经调用过,且结果和上一次相比没有变化,系统就会把完整结果替换成缓存引用。
例如对同一个文件反复执行 Read,这种方式每个 session 就能省掉几千个 token。
成本几乎为零。
Strategy 2:Snip Compact
当 token 即将逼近上限时触发,而且会先于昂贵的 summarization。
它会从对话开头移除消息,但保留最近消息构成的一个 protected tail(受保护尾部)。
整个过程不需要额外模型调用。
有损,但很快。
Strategy 3:Auto Compact
当 token 使用量越过阈值,而 snip 还不够时触发。
这时系统会发起一次独立模型调用,对先前对话做摘要,然后用摘要替换旧消息。
系统还会记录 compaction state,避免出现“对摘要再摘要、再对摘要的摘要做摘要”的死循环。
Strategy 4:Context Collapse
这是给超长时间运行 session 准备的,通常受 feature flag 控制。
它会进行多阶段压缩:
-
先压工具结果; -
再压 thinking blocks; -
最后压整个 section。
这是一种昂贵方案,只留给那些已经连续跑了几个小时的会话。
为什么这个层级顺序如此重要
最便宜的先跑,最贵的最后跑。
只有在前面都不够用时,昂贵策略才会出场。
很多 Harness 一上来就做 summarization。
但 summarization 的成本是双重的:
-
你要花一次模型调用成本; -
你还要把生成出来的 summary 再放进后续上下文。
而 microcompact + snip 在大量场景里,根本不需要任何额外模型调用。
这套层级意味着:
你只在便宜方案失效时,才为昂贵压缩买单。
另一个关键点是那个 protected tail。
做压缩时,最近的 N 轮对话永远不被摘要掉。
这样模型就能在保持当前计划完整性的同时,把更早的历史压缩掉,不会“刚做完什么就忘了”。
权限系统:七级信任流水线
大多数 Agent Harness 的权限机制只有一个开关:
-
允许 -
拒绝
Claude Code 不是。它有一个七阶段权限流水线。
规则支持基于工具名与输入的 glob-like 匹配。
例如:
-
“允许所有 bash” 太粗暴了,没有人希望 Agent 可以直接跑 rm -rf /; -
“拒绝所有 bash” 又会让 Agent 直接失去生产力; -
真正合理的做法是:允许 git 命令和 npm test,其他 bash 操作都弹确认。
Claude Code 的规则引擎就是按这个粒度设计的。
更重要的是,它允许渐进式信任:
-
新用户先从默认模式开始,对每次动作逐个批准; -
随着信任增加,再升级到 acceptEdits或bypassPermissions。
不是安全和速度二选一。
而是一条连续谱。
Hooks 是最后的逃生阀
Claude Code 还允许你通过 hooks 挂自定义策略:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line{"hooks": {"PreToolUse": [{"matcher": { "tool": "bash", "action": ".*rm.*" },"commands": [{ "cmd": "/path/to/safety-check.sh" }]}]}}
你的脚本会收到工具调用详情,然后返回:
-
{"decision": "approve"} -
或 {"decision": "block"}
企业组织可以借这个能力构建自己的 guardrails:
-
阻止破坏性操作; -
完成任务后自动发 Slack; -
每次文件写入后自动跑 linter。
并且完全不需要改源码。
错误恢复:那 823 行 Retry 系统不是装饰品
services/api/withRetry.ts 有 823 行。
这里面的每一行,几乎都对应过一次真实的生产故障。
429:Rate Limited
检查 Retry-After 头:
-
小于 20 秒:重试,继续保留 fast mode; -
超过 20 秒:进入 30 分钟 cooldown; -
如果带有 overage-disabled头:永久关闭 fast mode,并解释原因。
529:Server Overloaded
连续跟踪 529 次数:
-
如果连续 3 次 529,并且有 fallback model:切换模型; -
如果是后台任务:直接退出,避免级联故障; -
如果是前台任务:退避后重试。
400:Context Overflow
从错误里解析出实际 token 和上限 token。
然后重算:
available = limit - input - 1000(安全缓冲)
同时强制要求最小输出 token 下限为 3000,再按新预算重试。
401 / 403:Auth
清空 API key 缓存,强制刷新 OAuth token,再用新凭证重试。
网络错误:ECONNRESET / EPIPE / timeout
关闭 keep-alive socket pooling,重新建立连接后再试。
退避公式
ounter(linedelay = min(500ms × 2^attempt, 32s) + random(0, 0.25 × baseDelay)
对于无人值守会话,比如:
-
CI/CD 流水线 -
后台 Agent
持久重试模式会对 429 和 529 无限重试:
-
最大退避 5 分钟; -
6 小时 reset cap; -
每 30 秒发一次 heartbeat,防止空闲超时被干掉。
流式层本身也有自己的可靠性机制:
-
90 秒没有 chunk 到达,就 watchdog 中止流; -
45 秒时先发 warning; -
若首字节到达后,相邻 chunk 之间间隔超过 30 秒,会被记录为 stall; -
如果 streaming 完全失败,则切换到 non-streaming,同时保留连续 529 计数,避免 fallback 逻辑重复计数。
“fetch 重试三次”不叫生产级可靠性。
只有当你的系统真正理解每一种错误语义,并为每一类错误设计专门恢复路径时,它才算可靠。
Sub-Agent 架构:并行,但必须隔离
Claude Code 会生成子 Agent。
这些子 Agent 都是独立的 Agent Loop 实例,拥有各自的:
-
上下文 -
工具 -
工作目录
父 Agent 一旦被终止,所有子 Agent 会被级联终止。
但子 Agent 不能直接修改父 Agent 的状态:appState 对它来说是 no-op setter。
文件状态缓存也会被克隆,防止一个 Agent 的读取污染另一个 Agent 的缓存。
Git Worktree Isolation
需要修改代码的子 Agent,会拿到自己独立的 worktree:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(linegetOrCreateWorktree(repoRoot, slug)→ 校验 slug(最长 64 字符,防路径穿越)→ 检查 worktree 是否已存在(支持快速恢复)→ git fetch(关闭交互提示)→ git worktree add(新分支:worktree-<slug>)→ 对大目录做符号链接(如 node_modules、.cache)→ 复制 CLAUDE.md、项目设置、.env 文件→ 返回 { path, branch, headCommit }
一个 Agent,一个 worktree。
如果多个并行 Agent 共用同一个 workspace,冲突迟早会发生。
而 worktree 隔离让每个 Agent 都站在自己的分支上,验证完再合并。
同时,node_modules 的 symlink 避免了磁盘膨胀:
5 个并行 Agent 不需要 5 份依赖副本。
三种 Spawn Backend
整个多 Agent 系统支持三种执行后端:
-
In-process:直接跑在 Node.js 进程内,最快,共享内存; -
Tmux pane:通过终端多路复用器隔离,每个 Agent 在一个 pane/tab 里可见; -
Remote:跑在 CCR 环境里,做到整机级隔离。
任务协同依赖一个磁盘持久化任务列表,路径类似:
~/.claude/tasks/<taskListId>/<taskId>.json
配合文件锁和指数退避:
-
最多重试 30 次; -
每次等待 5–100ms; -
还会维护 high water mark,防止 reset 后 task ID 被复用。
第四层:Infrastructure
上面这些内容,仍然主要是在讲 Harness,也就是第三层。
现在请看 Claude Code 在它外面搭起来的第四层。
1)多租户(Multi-Tenancy)
CLAUDE.md 的层级本身就是一套多租户系统。
-
企业策略: /etc/claude-code/CLAUDE.md -
项目策略: .claude/CLAUDE.md -
用户偏好: ~/.claude/CLAUDE.md -
本地私有覆盖: CLAUDE.local.md
这实际上就是Agent 行为层面的 RBAC:
-
企业管理员设 guardrails; -
项目维护者设 convention; -
开发者设个人偏好。
每层覆盖下层,冲突按确定性规则解析。
2)跨 Session 的状态持久化
Compaction 策略本质上就是状态持久化。
Auto-compact 生成的 summary,会成为下一轮循环的新起点。
CLAUDE.md 文件负责把项目级记忆带到多个 session 之间。
Hooks 可以把任意状态持久化到磁盘。
任务协调系统则在多个 Agent 进程之间维持状态。
Claude Code 实际上把 session management 拆成了三层:
-
单一 session 内:靠 compaction; -
跨 session:靠 CLAUDE.md; -
跨 agent:靠 task list。
3)资源隔离(Resource Isolation)
Git worktree isolation 给每个子 Agent 独立文件系统。siblingAbortController 把工具失败控制在兄弟范围内,避免级联扩散。
企业级 deny 规则则阻止 Agent 触碰不该访问的资源。
4)分布式协同(Distributed Coordination)
任务列表上的文件锁就是典型的分布式协同。
父子 Agent 之间的 prompt cache 共享,是分布式资源优化。
持久重试模式里的 heartbeat,是 keepalive 模式。
而 worktree 管理,则是在处理多个 Agent 对同一代码仓的并发访问。
这些问题,本质上都不是“Agent 提示工程”问题。
它们是基础设施问题,必须用基础设施思维解决:
-
锁 -
协同 -
隔离 -
状态管理 -
访问控制 -
资源共享
这为什么对你的系统至关重要
这些问题,你迟早都会遇到。
区别只在于:你是用胶带和补丁临时糊上去,还是从第一天起就按系统工程去设计。
三层模型并不能让你做好准备。
因为 Harness 描述的只是:一个模型实例,在一个会话里,如何与一组工具交互。
但一旦你进入这些场景:
-
多用户 -
多会话 -
多 Agent -
部署到你无法完全控制的环境
你就已经进入第四层了。
对于 Agent Builder 来说,分布式系统工程正在成为一项核心能力。
生产级 Agent 系统会跑在 CI 服务器上,会生成子进程,会跨 session 共享状态,会服务不同权限级别的用户。
真正理解这些问题的团队,才能做出能工作的 Agent。
停留在 Harness 层的团队,只能做出演示版。
可扩展性:四种机制,零源码修改
Claude Code 提供了四种扩展机制,而且都不要求改源码。
1)Skills:把 Markdown 文件变成命令
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line---name: commitdescription: Commit staged changes with a generated messageallowed-tools: [Bash, Read, Grep]model: haikucontext: inlineuser-invocable: true---Review the staged changes and create a commit message...
这是一种带 YAML frontmatter 的 Markdown 文件。
它可以来自五个来源:
-
bundled -
project -
user -
plugin -
MCP
而且支持基于路径的发现机制。
比如 skill 指定 ["*.tsx"],那它只有在 Agent 触碰到匹配文件时才激活。
Agent 看到的是相关 skill,不是全部 skill。
2)Hooks:事件驱动自动化
支持六种类型:
-
shell commands -
LLM evaluation -
agentic verification -
HTTP endpoints -
TypeScript callbacks -
in-memory functions
触发时机包括:
-
PreToolUse -
PostToolUse -
SessionStart -
FileChanged -
Stop
这使 Harness 能直接接入既有基础设施。
例如:
-
任务完成后发 Slack; -
每次 bash 前先跑安全扫描; -
每次文件改动后自动触发 CI。
而 Agent Loop 本身一行都不用改。
3)MCP(Model Context Protocol)
支持五种传输方式:
-
stdio -
SSE -
HTTP streaming -
WebSocket -
in-process
并且可以在三个层级上配置:
-
enterprise managed -
project -
user
MCP 让 Agent 能通过统一协议,访问数据库、API、内部工具等外部系统。
4)Plugins
Plugin 本质上是包含以下内容的目录:
-
skills -
agents -
hooks -
configuration
它是最高层的组合机制:
通过“新增能力”来扩展系统,而不是“修改既有文件”。
这四种机制都遵循同一条原则:
组合优于修改。
扩展时通过添加,而不是改动。
这样核心更新不会打碎扩展,扩展彼此之间也不会互相踩踏。
UI 不是装饰,而是信任机制
Claude Code 的终端 UI 跑在一个定制版 Ink(React for terminals)之上,渲染引擎大约 251KB。
听起来对 CLI 来说很重。
其实一点也不。
它提供:
-
实时逐字流式渲染; -
根据 stall 持续时间从正常色插值到错误红色的 spinner; -
带语法高亮、三行上下文、词级差异标记的 diff 渲染; -
显示多 Agent 层级关系的状态树; -
六套主题,其中包含色盲友好主题; -
一条状态栏,显示模型名、美元成本、上下文窗口使用率、限流使用率。
用户越能看清一个 Agent:
-
正在做什么; -
调了什么工具; -
结果长什么样; -
消耗了多少上下文; -
花了多少钱;
他们就越愿意给这个 Agent 更高的自主权。
而更高的自主权,意味着更多真正有用的工作。
UI 是一个杠杆。
上下文窗口使用条也很关键。
它让用户不需要理解 tokenization 细节,也能直观感知剩余容量。
条快满了,他们就知道该收尾,或者该让 Agent 主动压缩上下文了。
你真正该从 Claude Code 学走什么
你不需要重写一个 Claude Code。
你需要学走的是它背后的工程决策。
1)用 Async Generator 写 Agent Loop
Streaming、Cancellation、Composability、Backpressure——这些能力都内建在这个抽象里。
如果你还是用一个返回完整结果的 while loop,那么这四样东西你就全都要自己补。
2)为工具做并发分类
只读工具并行,改状态工具串行。
这样可以拿到 2–5 倍加速,同时避免 race condition。
3)在 Streaming 过程中就启动工具执行
增量解析工具调用,只要输入 JSON 完整,就立刻开跑。
这是每一个多工具回合都能白捡到的延迟优化。
4)System Prompt 要围绕缓存边界设计
静态内容放前面,动态内容放后面,用明确边界隔开。
这可能是生产环境里杠杆最高的成本优化点。
5)压缩不要只有一种策略
先便宜后昂贵:
-
microcompact -
snip -
summarization -
collapse
只有便宜方案失败时,才为昂贵方案付费。
6)把错误恢复设计成 Loop 的一级状态
Rate limit、context overflow、auth failure、network error——每一类都应该有自己的恢复路径,而且要写进状态机里,而不是糊在外围 try/catch 上。
7)从 Day 1 就考虑 Layer 4
状态跨 session 放哪?
权限怎样扩展到团队级?
加上并行后如何做协同?
这些问题如果后补,难度会高一个数量级。
8)扩展点必须做到“无需改代码”
Markdown 文件、shell 脚本、协议化工具,通常就能覆盖 95% 的扩展需求。
如果用户为了定制行为必须 fork 你的代码,那你的架构已经有缺口了。
模型正在商品化,真正决定结果的是环境
Princeton NLP 用 SWE-agent 证明了这一点:
同样的模型,更好的环境,效果就能提升 64%。
Anthropic 则在 Claude Code 上每天都在证明这一点:
一个拥有 55 个目录、331 个模块 的 TypeScript 应用,可以把同一个 Claude 模型——那个原本只是用在聊天界面里的模型——变成一个可以:
-
连续数小时无人值守运行; -
在 API 故障后自动恢复; -
在上千轮会话中自己管理上下文; -
在同一代码库上协调多个并行子 Agent;
的真正生产级 Coding Agent。
真正的世界有四层。
而今天行业里大多数团队,还在拼命优化第 1 层:
-
更大的模型 -
更高的 benchmark 分数
但真正赢下来的团队,正在把资源投进第 3 层和第 4 层:
-
更好的环境 -
更强的错误恢复 -
更成熟的权限系统 -
更稳的上下文管理 -
更高效的协同机制
源码就在那里。
模式都写清楚了。
设计决策也都很清晰。
先把 Harness 搭起来。
然后,把它外面的基础设施也一起搭起来。
编者按
如果你过去一直把 Agent 当成“Prompt + Tool Call + Loop”的问题来理解,那么 Claude Code 这份实现给出的最大提醒其实只有一句话:
Agent 不只是模型调用器,它本质上是一套带状态、带权限、带隔离、带协同能力的系统工程。
也就是说,真正的护城河,越来越不在模型本身,而在你为模型搭出来的那一整套执行环境里。
夜雨聆风