设计启示

Agent 系统作者:前面几章源码级观察抽出的 10 条跨主题原则,每条绑定到具体源文件——这是一张可以直接评估自研 agent 的检查表。

这一章的用途

前 4 章(system-prompt / compaction / memory / sandbox)讲了 Claude Code 源码层面做了什么。这一章把 “为什么那样做” 的原则抽出来,每条都绑定到具体的源文件,让你能直接评估和修正你自研 agent 的设计

每条按同一结构展开:

  1. 原则——一句话
  2. 源码证据——path:line 级引用
  3. 自研 agent 怎么落地——可执行的动作
  4. 违反信号 (smell)——什么现象说明你违反了这条

1. 渐进披露 (Progressive Disclosure) 是通用模式

原则:任何会随时间无界增长的信息,永远保持”索引常驻 + 详细内容按需”,别一次性塞进 prompt

源码证据

  • Auto memory:memdir/memdir.tsMAX_ENTRYPOINT_LINES = 200 + MAX_ENTRYPOINT_BYTES = 25_000——双上限 + 截断警告
  • Tools:Claude Code 的工具 schema 用 deferred tools 机制,只把名字常驻,schema 按需 ToolSearch 加载
  • Skills:L1 metadata 进 prompt,L2/L3 按需(见 系统提示词组装
  • MCP:mcp_instructions delta 机制——新连接的 MCP 只发 delta,不重发全量 instruction

自研 agent 怎么落地

  • 任何文档 / 记忆 / 技能:先问”能不能索引 + 按需”。如果能就做
  • 索引本身要有硬上限(如 200 行),并且配用户可见的截断警告——告诉他们怎么 fix(“move detail into topic files”)
  • 多维上限(行数 + 字节)——p97 / p100 数据告诉你单一上限必漏 case

违反信号:system prompt > 20k tokens 还在涨;每加一个 skill / memory / tool prompt 就变长;明显有”老的没人用但占着地”的条目。


2. Subagent 是上下文防火墙,不是并行原语

原则:任何 5+ 步的 ReAct 循环都应该被包成 subagent——不是为了并行,是为了防止探索细节污染主上下文

源码证据

  • tools/AgentTool/AgentTool.tsx 第 622-633 行——默认路径 forkContextMessages: undefined——子 agent 不继承父对话
  • 只有 fork path 才继承父 messages + 父 system prompt(特殊场景)
  • tools/AgentTool/agentMemory.ts——subagent 有自己 3-scope 的独立持久化记忆(user/project/local)

自研 agent 怎么落地

  • 识别:哪些”一次工具调用”实际含 10+ 步循环?(browser、复杂查询、探索搜索)
  • 把它们包成 subagent:一次调用进,摘要出
  • Subagent 可以有独立 model(haiku 更便宜)、独立 permission mode、独立 working directory / worktree isolation
  • 记忆也要隔离——不同 subagent type 学到的经验不串门

违反信号:主对话长期充斥 grep 残渣、ls 输出;每轮 token 消耗在膨胀但决策密度没增加。


3. Hook 和 Prompt 回答不同问题

原则prompt 说应该做什么(模型判断)hook 说一定做什么(harness 强制)。两者不能互换

源码证据

  • utils/hooks.ts 定义了 10 种 hook 事件 + 完整 JSON schema(continue / decision / permissionDecision / updatedInput / systemMessage / additionalContext)
  • TOOL_HOOK_EXECUTION_TIMEOUT_MS = 10 * 60 * 1000 ——hook 是一等公民(10 分钟超时),不是快速拦截器
  • Hook 可以修改工具参数 (updatedInput)、注入 system message打断 agent 循环 (continue: false)

自研 agent 怎么落地

  • 列清所有”从现在起每次 X 都要 Y”的需求,评估严重性——严重 → 必须 hook
  • Hook API 分三层:plain text(简单)→ JSON decision(中)→ updatedInput + systemMessage(高级)
  • 必须同时提供 prompt 层和 hook 层——只有 prompt 时自动化需求会悄悄失效

违反信号:“我在 prompt 里写了要 X,但 agent 经常不做”——prompt 层解决不了,需要 hook。


4. 信任是产品 Surface,不是配置

原则:用户的信任级别必须UI 一等公民 + per-turn 可切换,不能是启动参数或配置文件里的布尔值。

源码证据

  • utils/permissions/PermissionMode.tsPERMISSION_MODE_CONFIG——7 种 mode 各自有 titleshortTitlesymbolcolorexternal
  • Plan mode 的 PAUSE_ICONacceptEdits/bypass⏵⏵ fast-forward 符号——视觉强化当前状态
  • --dangerously-skip-permissions——刻意起得难看的 CLI flag

自研 agent 怎么落地

  • Permission modes 做成 UI 可切换(per-turn),不是启动 flag
  • 状态栏 / title bar 持续显示当前 mode
  • 危险 mode 的命名违反 UX 惯例——bypass / dangerously

违反信号:“agent 突然自己跑了 rm”——几小时前的启动 flag 被忘了;config 里有 auto_approve: true——错误的抽象。


5. Trust-but-verify 是对抗幻觉的基础设施

原则:任何 agent 的自述都要被当作 intent,不是 fact。关键断言需要读回或 grep 验证。

源码证据

  • utils/claudemd.ts 注入的 memory guidance 第 3 条:“A memory that names a specific function, file, or flag is a claim that it existed when the memory was written. It may have been renamed… Before recommending it [verify]”
  • Edit 工具的 description:“You must use your Read tool at least once in the conversation before editing”——强制读再改
  • Compaction summary 第 9 段:“Optional Next Step: include direct quotes from the most recent conversation”——强制原文引用防意图漂移

自研 agent 怎么落地

  • 关键工具操作后,下一步强制是一次验证读取
  • 记忆系统必须配”引用前 grep”的操作守则
  • 多 agent 系统:子 agent 摘要要可被父 agent 校验

违反信号:agent 说”改好了”实际没改;记忆里有 “X 文件在 Y 位置” 搜了没有;log 里 “完成了 task” 但交付物有问题。


6. Error Message 是 Prompt 的延伸

原则:工具返回的 error 不是状态码,是教 agent 下一步怎么走的文本。认真写每一条。

源码证据

  • Edit 工具的 error text:“This tool will error if you attempt an edit without reading the file”——读到这个 agent 自动去先 Read
  • Claude Code 的工具描述整体读起来像使用教程而不是 schema doc——包括失败模式、对齐要求、uniqueness 约束
  • Compaction 的 NO_TOOLS_PREAMBLE + NO_TOOLS_TRAILER——长 prompt 前后都重复关键约束(遥测数据:Sonnet 4.6 工具尝试率 2.79% vs 4.5 的 0.01%)

自研 agent 怎么落地

  • 每个自研工具的 error,过一遍:“读到这条,模型知道下一步做什么吗”
  • 错: "Invalid argument";对: "This tool requires parameter X to be set first. Typical usage: ..."
  • 长 prompt 的前后都要重复硬约束——模型会忘中间的指令

违反信号:agent 遇 error 死循环重试;工具描述只有 schema 类型没有用例;同一个错反复问用户。


7. 晚绑定 (Late Binding) 保 Prompt Cache

原则:System prompt 的层按稳定性排序——越稳定越靠前。时间戳 / session state / 动态内容放到后面。

源码证据

  • constants/prompts.ts 第 572 行:SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'——字面量 marker,注释明写 “BOUNDARY MARKER - DO NOT MOVE OR REMOVE”
  • DANGEROUS_uncachedSystemPromptSection 工厂函数——名字刻意吓人,让破 cache 的段在 review 时无法隐身
  • services/api/promptCacheBreakDetection.tsnotifyCompaction——压缩时主动通知 cache miss 告警,避免 20% false positive(源码 BQ 数据)

自研 agent 怎么落地

  • 画 prompt 层级表:每层稳定性是什么
  • 不稳定的内容不管多”重要”都放后面
  • 不要为了”让模型先看到重要的东西”把时间戳 / user id 放前面——prompt cache 加载付不起这个成本
  • 任何会破 cache 的段要显式标注,评审时可见

违反信号:prompt cache 命中率 < 50%;每次请求都全价;prompt 长度不大但 token 成本高。


8. 短期对话和长期记忆是两层

原则:对话历史是短期(每次任务结束可清空),记忆层是长期(跨 session 存)。两者不要混

源码证据

  • utils/messages.tscreateCompactBoundaryMessage——压缩是消息流事件,不是上下文重置
  • /clear 命令只清对话,不动 CLAUDE.md / auto memory / session memory
  • services/compact/compact.ts三档:auto-compact(防线)/ /compact(主动)/ /clear(重开)
  • getMessagesAfterCompactBoundary——压缩边界之后的消息才参与下次压缩,不重复压

自研 agent 怎么落地

  • 提供三档控制:自动防线 / 用户主动压 / 彻底清空
  • 写”学到的偏好”到记忆层,不留在对话里等着被 clear
  • 压缩不触 system prompt——保护 cache prefix
  • 用显式 boundary message 标记压缩事件,而不是静默改消息流

违反信号:用户 “reset conversation” 后 agent 忘了基础偏好——偏好本该在记忆层;/clear 等价于重装产品——记忆层设计失效。


9. 数据驱动的 Prompt 工程

原则:prompt 里的每条指令都应该有”为什么这么写”的答案——不靠数据的指令长期必失控。

源码证据

  • constants/prompts.tsnumeric_length_anchors“Length limits: keep text between tool calls to ≤25 words”——注释明写 “research shows ~1.2% output token reduction vs qualitative ‘be concise’“。ant-only 先灰度测
  • services/compact/autoCompact.tsMAX_OUTPUT_TOKENS_FOR_SUMMARY = 20_000——注释 “Based on p99.99 of compact summary output being 17,387 tokens”。基于生产遥测调的
  • memdir/memdir.tsMAX_ENTRYPOINT_BYTES = 25_000——注释 “At p97 today; catches long-line indexes… (p100 observed: 197KB under 200 lines)“——p100 数据直接写在注释里
  • services/compact/prompt.tsNO_TOOLS_PREAMBLE——注释 “(2.79% on 4.6 vs 0.01% on 4.5)“——遥测数字直接作为 prompt 设计依据

自研 agent 怎么落地

  • 每条 prompt 指令 / 每个 threshold 常量,都要能回答”为什么是这个数”
  • 量化措辞(“≤25 words”)先用数据验证再推广——先 ant-only / 灰度
  • 把 prompt 当作可 A/B test 的代码,不是无序的规则堆积
  • 产品遥测要能回流到 prompt 设计——p97、p99.99、百分点差异都是有效信号

违反信号:prompt 里有一条没人能解释的指令;threshold 常量是”拍脑袋”写的;改 prompt 前后没有量化对比。


10. Fail-closed + Circuit Breaker

原则:关键路径必须 fail-closed(出错时拒绝,不是放行);无法恢复的失败必须 circuit break(连续失败 N 次就停),不能无限重试。

源码证据

  • services/compact/autoCompact.tsMAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3——注释 “BQ 2026-03-10: 1,279 sessions had 50+ consecutive failures (up to 3,272) in a single session, wasting ~250K API calls/day globally”。没熔断器 = 每天浪费 25 万次 API 调用
  • Permission 系统:deny 始终胜过 allowbypassPermissions 也不能绕 deny list / hook),fail-closed 默认
  • utils/permissions/dangerousPatterns.ts:auto-mode 启动时自动剥离 Bash(python:*) 这种”表面限制但实际放任”的规则——不信任用户的宽松配置

自研 agent 怎么落地

  • 所有重试路径配熔断器:N 次连续失败后停下记日志,而不是继续消耗资源
  • 权限合并算法:deny 永远胜过 allow;“最大权限” mode 不能跳过 deny
  • 危险的宽松配置要预处理时主动剥离,不是在运行时逐个拦截

违反信号:生产日志里看到同一错误在短时间内重复 100+ 次;有用户把自己限制了一半的 config 开成了完全放行;每次 “bypass all” 都真的 bypass 了所有。


自检清单

对自研 agent 的快速评分表:

#原则做到了吗
1渐进披露(索引 + 按需)
2Subagent 作为上下文防火墙
3Hook 和 Prompt 分层
4信任作为产品 surface
5Trust-but-verify 的操作守则
6Error message 写得像 prompt
7Late binding 保 cache
8短期 / 长期记忆分层
9数据驱动的 prompt 工程
10Fail-closed + circuit breaker

超过 2 条没做到,回前面 4 章精读对应部分


落到代码:继续读下一章

以上 10 条是原则——抽象层。如果你要立刻把这些原则落到 AI SDK 的代码行,下一章 应用到自研 Agent (AI SDK) 把每条原则映射到 streamText 生命周期的具体嵌入点,告诉你在哪行代码、怎么做、什么时候该做什么时候不该做

如果你已经在用 AI SDK 写 agent,下一章还有一张10 项自检清单——直接对着扫你现在的 codebase。


不是普世真理,是当前最佳实践

三条 caveat:

  1. Agent 工程是新学科——三年后这些原则可能部分被推翻。不要把它们当教条
  2. Claude Code 也在迭代——三年后源码会变;回看如果不一样了,是它们在变
  3. 真正的资产不是这 10 条,是”遇到问题先看好实现怎么做”的方法——把 Claude Code 源码当 case 拆过一次,下次遇到 Devin、Cursor、Codex CLI 的新特性也能拆

源码阅读指南:本章所有引用的文件都在 claude-code/ 目录。按重要性排:

  1. services/compact/{compact, autoCompact, microCompact, prompt}.ts —— 最精彩的部分
  2. constants/prompts.ts + utils/systemPrompt.ts —— prompt 装配的核心
  3. utils/claudemd.ts + memdir/ —— 记忆系统
  4. types/permissions.ts + utils/hooks.ts —— 权限 + hook 完整模型

这些文件加起来约 15000 行。过一遍的时间约等于一周——但每一行都是真实 production 系统的凝练,远胜读任何 agent 论文。


延伸阅读

这页有帮助吗?