docs: Add comprehensive agentic design documentation (2.5-hour learning session)

新增 docs/agentic-design/ 教育文档,全中文+英文技术术语,覆盖:

- README.md: 总索引,整体架构图,阅读指南
- 00-codebase-tour.md: 代码库全景,递归覆盖所有重要目录
- 01-agent-loop.md: Query loop 状态机,token budget,compaction
- 02-tool-system.md: Tool interface,权限检查,并发执行,sibling abort
- 03-multi-agent-coordination.md: Agent 类型,coordinator 4 阶段,XML 通信
- 04-permission-system.md: Permission mode,rule system,YOLO 分类器,denial tracking
- 05-context-and-memory.md: System prompt 分层,compaction,auto-dream 后台巩固
- 06-feature-gating.md: Compile-time feature,runtime flags,Bun dead-code elimination

总计 ~2.5 小时阅读,每篇含 Design Decision 专栏和常见误解纠正

https://claude.ai/code/session_017Vdqo9B8eTXiEDcqnv9DxM
This commit is contained in:
Claude 2026-03-31 16:07:29 +00:00
parent 0cf2fa2edb
commit 808d5a61b3
No known key found for this signature in database
8 changed files with 2697 additions and 0 deletions

View file

@ -0,0 +1,416 @@
# Context 和 Memory有限的脑容量
## 为什么这很重要?
Claude 的 context window比如 200K tokens是**有限的**。一个长时间运行的 agent task
```
第 1 轮用户输入200 tokens+ 响应1000 tokens
第 2 轮新输入300 tokens+ 响应2000 tokens
...
第 100 轮:?
总 tokens: 200 + 300 + 400 + ... + 整个历史
在第 50 轮时messages 数组有 100 条消息,超过 context window 的 80%
```
一旦超限系统必须做些什么。Claude Code 的答案是:
1. **System prompt 优化**:区分静态和动态部分,利用 prompt cache
2. **主动 compaction**:在超限前压缩历史
3. **长期 memory**:用户的持久化知识库
---
## System Prompt 架构
不是简单的一个大 string而是**模块化的**
```typescript
const systemPrompt = [
// 第 1 部分:静态,会被缓存
SYSTEM_PROMPT_PREFIX, // 关于 Claude Code 的基础指导
TOOL_DESCRIPTIONS, // 所有 tool 的描述
SYSTEM_PROMPT_SAFETY, // 安全指导
// 缓存边界 ← 这之后的部分会变化,不会被缓存
"SYSTEM_PROMPT_DYNAMIC_BOUNDARY",
// 第 2 部分:动态,每个 session 不同
USER_MEMORY, // 用户的个人知识库
RECENT_CONTEXT, // 最近的几轮对话(总结)
CURRENT_TASK, // 当前任务的上下文
]
```
**Cache boundary marker** 的妙处:
```
初始化时: │首个请求:
cache_key = MD5(prefix) │所有文本
缓存大小10 KB │2000 tokens (40 KB)
│缓存命中 10 KB
│处理 30 KB 新文本
│总成本40 KB 请求
同一 session 中再次使用: │再次请求:
cache 仍有效 │新文本 1000 tokens (20 KB)
直接跳过了 10 KB 的重复传输 │缓存 hit10 KB
成本20 KB │总成本20 KB 请求(节省 10 KB
```
所以,通过把 system prompt 分为静态和动态两部分,我们:
- 减少了重复 token 消耗
- 加快了推理速度(缓存的部分直接使用)
- 提高了吞吐量(多个请求共享缓存)
相关文件:`utils/messages/systemInit.ts`, `context.ts`
---
## Token Budget 生命周期
### 阶段 1初始化
```typescript
const CONTEXT_WINDOW = 200_000 // 比如 200K tokens
const SYSTEM_PROMPT_TOKENS = 5_000
const RESERVED_BUFFER = 10_000 // 总是留出来,防止意外超限
available = CONTEXT_WINDOW - SYSTEM_PROMPT_TOKENS - RESERVED_BUFFER
= 185_000 tokens // 工作预算
```
### 阶段 2追踪使用
每一轮 API 调用后,系统获得实际使用数据:
```typescript
response = await claude.messages.create({...})
remaining = available - response.usage.input_tokens - response.usage.output_tokens
if (remaining < available * 0.2) { // 还剩 20% 以下
console.warn("Token budget 即将耗尽,触发 compaction")
}
if (remaining < 10_000) { // 紧急状态
console.error("无法继续context 严重超限")
return ERROR_OUT_OF_TOKENS
}
```
### 阶段 3警告和恢复
```
Threshold 180% 已用):显示警告,继续
Agent 执行更多 tool ← 看不到警告,继续工作
Threshold 290% 已用):触发 compaction
← 在后台自动压缩,不中断 agent 工作
Threshold 399% 已用):停止接受新请求
← 返回错误,等待用户决定
```
相关文件:`query/tokenBudget.ts`
---
## Compaction自动历史压缩
当 token 预算吃紧时,系统自动压缩历史。这是一个 4 步流程:
### 第 1 步:分析历史
```typescript
// 扫描所有消息,找出"能压缩的部分"
messages = [
system_prompt, // 不能压缩
message_1, // 用户说:"帮我读个文件" → 不压缩(用户消息)
message_2, // Claude 说:"我读了,代码如下..." → 能压缩(输出很长)
message_3, // 用户说:"现在修复 bug" → 不压缩
message_4, // Claude 说:"修复完,改了 5 处..." → 能压缩
...
]
优先级:越早的消息优先级越高(假设最早的对话不再相关)
```
### 第 2 步:优先排序
```
对能压缩的消息排序:
1. 最早的长消息4 messages ago
2. 次早的长消息6 messages ago
3. ...
实际压缩的可能是:
messages 1-10最早的 10 条)被替换为 1 条 summary
```
### 第 3 步:调用 Claude 做总结
```typescript
const summary = await claude.messages.create({
messages: [
{ role: "user", content: "Summarize this conversation in 200 lines max:\n" + messagesText }
],
system: "You are a memory consolidation assistant..."
})
// 返回:
// "用户让我读了 package.json 和 tsconfig.json发现项目是 TypeScript monorepo。"
```
### 第 4 步:替换
```typescript
// 替换前:
messages = [msg_1, msg_2, ..., msg_100] // 100 条消息
// 替换后:
messages = [msg_summary, msg_51, msg_52, ..., msg_100]
// ^被压缩成1条 ^保留后面的消息(近期的)
```
结果:从 100 条消息减少到 51 条token 预算恢复。
相关文件:`query.ts` 中的 `compactMessages()` 函数,大约 200 行
---
## Memory 系统:长期知识库
Compaction 是**临时的**(压缩 session 内的历史)。但如果用户有**多个 session**,同一信息会被重复压缩。
解决方案:**持久化 memory**。
### Memory 文件结构
```
~/.claude/memory/
├── MEMORY.md ← 用户的长期知识库(最多 200 行)
├── projects/
│ └── claude-code.md ← 项目特定的知识
├── patterns/
│ └── async-patterns.md ← 学到的模式
└── people/
└── team.md ← 关于人的信息
```
**MEMORY.md** 的内容示例:
```markdown
# 我的知识库
## 工作偏好
- 不喜欢在 main 分支上直接提交
- 总是用 TypeScript避免 JavaScript
## 项目信息
- Claude Code 仓库: ~/projects/claude-code
- 技术栈: TypeScript + React + Ink
## 编码习惯
- 函数 <100 行为最佳
- 避免 deep nesting最多 3 层
- 总是加 error handling
```
### Auto-Dream后台内存巩固
不是用户手动维护 MEMORY.md而是系统**自动**从 session 中提取和更新。
过程("dream"
```
触发条件(三门齐全):
1. 时间门:距离上次 dream ≥ 24 小时?
2. 会话门:距离上次 dream产生了 ≥ 5 个新 session
3. Lock 门:能获得 consolidation lock防并发
都满足 → 执行 dream
[1] ORIENT
读取用户的 MEMORY.md
扫描最近 N 个 session 的 transcripts
[2] GATHER
提取新的学习点:
- "用户在这个 session 中学到了异步 Rust"
- "发现项目从 4.1 升级到了 4.2"
- "优化了 CI/CD速度提升 30%"
[3] CONSOLIDATE
合并到 MEMORY.md
- 如果已经有同类笔记,合并
- 如果是新的,添加
- 保持在 200 行以内(做优先级选择,删除过时的)
[4] PRUNE & INDEX
- 删除 24 小时内不用的 session transcript
- 更新搜索索引(便于后续 memory 查询)
```
相关文件:
- `services/autoDream/autoDream.ts`:主逻辑(~11 KB
- `services/autoDream/consolidationPrompt.ts`:告诉 Claude 怎么总结
- `memdir/memdir.ts`Memory 目录管理
### 为什么自动化很关键?
```
手动维护:
用户要记得更新 MEMORY.md
→ 大部分人会忘记
→ Memory 逐渐变陈旧
自动化:
后台每 24 小时自动 consolidate
→ 用户无感
→ Memory 总是最新的
```
---
## Context 使用的优化层级
当 context 变紧时,系统依次采取行动:
```
Tier 1还有 70% token
✓ 正常工作
✗ 触发自动 compaction
Tier 2还有 20% token
✓ 继续工作(使用压缩后的历史)
✗ 不接受新的大型工具输出(如读 10 MB 文件)
Tier 3还有 10% token
✓ 只允许低 token 成本的操作
✗ 禁止高 token 成本的操作
Tier 4还有 < 5% token
✗ 停止工作,返回错误
→ 用户可以:
a) 启动新 session重置 token 预算)
b) 手动编辑 MEMORY.md删除过时信息
```
---
## Design Decision 专栏
### 为什么区分静态和动态 system prompt?
不区分(一大块):
```
发第 1 个请求:发 5000 tokens 的 system prompt + 输入
发第 2 个请求:又发 5000 tokens 的 system prompt + 输入
...
浪费了大量 token 在重复发送
```
区分cache boundary
```
发第 1 个请求:发 5000 tokens静态+ 1000 tokens动态
API 缓存这 5000 tokens
发第 2 个请求:直接用缓存的 5000 tokens + 1000 tokens新的动态
节省了 5000 tokens
```
如果 session 很长100 次请求),节省 = 5000 * 99 = 495K tokens
### 为什么 Compaction 触发阈值是 20%,而不是 1%
等到 1% 时才 compact
```
已用 99%,只有 1% 剩余(~2000 tokens
这时启动 compaction自己就需要消耗大量 tokens调用 Claude 做总结)
可能没有足够 tokens 来完成 compaction
→ 失败,系统崩溃
```
提前到 20% 时 compact
```
已用 80%,还有 20% 剩余(~40K tokens
这时启动 compaction有充足 tokens 来做总结
完成后token 预算恢复到 50%
→ 继续工作,很多时间都有富足的 token 可用
```
权衡:早点 compact 意味着多付出一些 token压缩的成本但换来系统稳定性。
### 为什么用后台 dream 而不是 inline compaction?
Inline同步
```
在 query loop 中调用 compaction
等待 compaction 完成5-10 秒)
用户看到卡顿
```
后台 dream
```
query loop 继续运行
在空闲时或定时触发 dream24 小时一次)
用户感觉不到延迟
```
代价:增加系统复杂性(需要处理并发)、需要文件锁(防同时 dream。但值得因为用户体验好得多。
---
## 常见误解
**误解 1**"Context window 满了就完蛋?"
实际:有多层缓冲和恢复机制。系统会逐步:
1. 显示警告
2. 触发 compaction
3. 拒绝新操作
4. 要求用户决定
完全"卡住"很少发生。
**误解 2**"Compaction 会丢失所有细节?"
实际Compaction 由 Claude 做,所以**关键信息被保留**。丢失的是:
- 冗余的解释
- 尝试失败的细节(对后续不相关)
- 已解决的问题的讨论过程
**误解 3**"MEMORY.md 会无限增长?"
实际:维持在 ~200 行(可配置),通过 dream 的 prune 阶段定期清理过时信息。
---
## 关键要点
1. **System prompt 分两部分**:静态(可缓存)+ 动态(每次变化)
2. **Token budget 主动追踪**:到达 80% 时触发 compaction
3. **Compaction 自动压缩历史**:用 Claude 总结 N 条消息为 1 条
4. **Memory 系统持久化知识**MEMORY.md 最多 200 行,用户的长期知识库
5. **Auto-dream 后台巩固**:每 24 小时自动更新 MEMORY.md无需用户介入
6. **优化层级**70% → 20% → 10% → 5% → 停止,每个阈值有对应策略
---
## 深入阅读
- `query/tokenBudget.ts`Token 追踪逻辑
- `services/autoDream/autoDream.ts`Dream 实现
- `memdir/memdir.ts`Memory 目录管理
- `utils/messages/systemInit.ts`System prompt 组装
下一步:了解**Feature Gating 系统**,看看 Claude Code 怎么区分内部功能和公开功能。