mirror of
https://github.com/shareAI-lab/learn-claude-code.git
synced 2026-04-28 06:19:32 +00:00
better doc
This commit is contained in:
parent
aea8844bac
commit
665831c774
46 changed files with 1217 additions and 3505 deletions
|
|
@ -1,22 +1,17 @@
|
|||
# s06: Compact (上下文压缩)
|
||||
# s06: Context Compact (上下文压缩)
|
||||
|
||||
> 三层压缩管道让智能体可以无限期工作: 策略性地遗忘旧的工具结果, token 超过阈值时自动摘要, 以及支持手动触发压缩。
|
||||
`s01 > s02 > s03 > s04 > s05 > [ s06 ] | s07 > s08 > s09 > s10 > s11 > s12`
|
||||
|
||||
> *"Strategic forgetting"* -- 有策略地遗忘, 换来无限会话。
|
||||
|
||||
## 问题
|
||||
|
||||
上下文窗口是有限的。工具调用积累到足够多时, 消息数组会超过模型的上下文限制, API 调用直接失败。即使在到达硬限制之前, 性能也会下降: 模型变慢、准确率降低, 开始忽略早期消息。
|
||||
|
||||
200,000 token 的上下文窗口听起来很大, 但一次 `read_file` 读取 1000 行源文件就消耗约 4000 token。读取 30 个文件、运行 20 条 bash 命令后, 你就已经用掉 100,000+ token 了。没有某种压缩机制, 智能体无法在大型代码库上工作。
|
||||
|
||||
三层管道以递增的激进程度来应对这个问题:
|
||||
第一层 (micro-compact) 每轮静默替换旧的工具结果。
|
||||
第二层 (auto-compact) 在 token 超过阈值时触发完整摘要。
|
||||
第三层 (manual compact) 让模型自己触发压缩。
|
||||
|
||||
教学简化说明: 这里的 token 估算使用粗略的 字符数/4 启发式方法。生产系统使用专业的 tokenizer 库进行精确计数。
|
||||
上下文窗口是有限的。读一个 1000 行的文件就吃掉 ~4000 token; 读 30 个文件、跑 20 条命令, 轻松突破 100k token。不压缩, 智能体根本没法在大项目里干活。
|
||||
|
||||
## 解决方案
|
||||
|
||||
三层压缩, 激进程度递增:
|
||||
|
||||
```
|
||||
Every turn:
|
||||
+------------------+
|
||||
|
|
@ -47,7 +42,7 @@ continue [Layer 2: auto_compact]
|
|||
|
||||
## 工作原理
|
||||
|
||||
1. **第一层 -- micro_compact**: 每次 LLM 调用前, 找到最近 3 条之前的所有 tool_result 条目, 替换其内容。
|
||||
1. **第一层 -- micro_compact**: 每次 LLM 调用前, 将旧的 tool result 替换为占位符。
|
||||
|
||||
```python
|
||||
def micro_compact(messages: list) -> list:
|
||||
|
|
@ -59,24 +54,22 @@ def micro_compact(messages: list) -> list:
|
|||
tool_results.append((i, j, part))
|
||||
if len(tool_results) <= KEEP_RECENT:
|
||||
return messages
|
||||
to_clear = tool_results[:-KEEP_RECENT]
|
||||
for _, _, part in to_clear:
|
||||
for _, _, part in tool_results[:-KEEP_RECENT]:
|
||||
if len(part.get("content", "")) > 100:
|
||||
tool_id = part.get("tool_use_id", "")
|
||||
tool_name = tool_name_map.get(tool_id, "unknown")
|
||||
part["content"] = f"[Previous: used {tool_name}]"
|
||||
return messages
|
||||
```
|
||||
|
||||
2. **第二层 -- auto_compact**: 当估算 token 超过 50,000 时, 保存完整对话记录并请求 LLM 进行摘要。
|
||||
2. **第二层 -- auto_compact**: token 超过阈值时, 保存完整对话到磁盘, 让 LLM 做摘要。
|
||||
|
||||
```python
|
||||
def auto_compact(messages: list) -> list:
|
||||
TRANSCRIPT_DIR.mkdir(exist_ok=True)
|
||||
# Save transcript for recovery
|
||||
transcript_path = TRANSCRIPT_DIR / f"transcript_{int(time.time())}.jsonl"
|
||||
with open(transcript_path, "w") as f:
|
||||
for msg in messages:
|
||||
f.write(json.dumps(msg, default=str) + "\n")
|
||||
# LLM summarizes
|
||||
response = client.messages.create(
|
||||
model=MODEL,
|
||||
messages=[{"role": "user", "content":
|
||||
|
|
@ -84,75 +77,39 @@ def auto_compact(messages: list) -> list:
|
|||
+ json.dumps(messages, default=str)[:80000]}],
|
||||
max_tokens=2000,
|
||||
)
|
||||
summary = response.content[0].text
|
||||
return [
|
||||
{"role": "user", "content": f"[Compressed]\n\n{summary}"},
|
||||
{"role": "user", "content": f"[Compressed]\n\n{response.content[0].text}"},
|
||||
{"role": "assistant", "content": "Understood. Continuing."},
|
||||
]
|
||||
```
|
||||
|
||||
3. **第三层 -- manual compact**: `compact` 工具按需触发相同的摘要机制。
|
||||
3. **第三层 -- manual compact**: `compact` 工具按需触发同样的摘要机制。
|
||||
|
||||
```python
|
||||
if manual_compact:
|
||||
messages[:] = auto_compact(messages)
|
||||
```
|
||||
|
||||
4. Agent loop 整合了全部三层。
|
||||
4. 循环整合三层:
|
||||
|
||||
```python
|
||||
def agent_loop(messages: list):
|
||||
while True:
|
||||
micro_compact(messages)
|
||||
micro_compact(messages) # Layer 1
|
||||
if estimate_tokens(messages) > THRESHOLD:
|
||||
messages[:] = auto_compact(messages)
|
||||
messages[:] = auto_compact(messages) # Layer 2
|
||||
response = client.messages.create(...)
|
||||
# ... tool execution ...
|
||||
if manual_compact:
|
||||
messages[:] = auto_compact(messages)
|
||||
messages[:] = auto_compact(messages) # Layer 3
|
||||
```
|
||||
|
||||
## 核心代码
|
||||
|
||||
三层管道 (来自 `agents/s06_context_compact.py`, 第 67-93 行和第 189-223 行):
|
||||
|
||||
```python
|
||||
THRESHOLD = 50000
|
||||
KEEP_RECENT = 3
|
||||
|
||||
def micro_compact(messages):
|
||||
# Replace old tool results with placeholders
|
||||
...
|
||||
|
||||
def auto_compact(messages):
|
||||
# Save transcript, LLM summarize, replace messages
|
||||
...
|
||||
|
||||
def agent_loop(messages):
|
||||
while True:
|
||||
micro_compact(messages) # Layer 1
|
||||
if estimate_tokens(messages) > THRESHOLD:
|
||||
messages[:] = auto_compact(messages) # Layer 2
|
||||
response = client.messages.create(...)
|
||||
# ...
|
||||
if manual_compact:
|
||||
messages[:] = auto_compact(messages) # Layer 3
|
||||
```
|
||||
完整历史通过 transcript 保存在磁盘上。信息没有真正丢失, 只是移出了活跃上下文。
|
||||
|
||||
## 相对 s05 的变更
|
||||
|
||||
| 组件 | 之前 (s05) | 之后 (s06) |
|
||||
|----------------|------------------|----------------------------|
|
||||
| Tools | 5 | 5 (基础 + compact) |
|
||||
| 上下文管理 | 无 | 三层压缩 |
|
||||
| Micro-compact | 无 | 旧结果 -> 占位符 |
|
||||
| Auto-compact | 无 | token 阈值触发 |
|
||||
| Manual compact | 无 | `compact` 工具 |
|
||||
| Transcripts | 无 | 保存到 .transcripts/ |
|
||||
|
||||
## 设计原理
|
||||
|
||||
上下文窗口有限, 但智能体会话可以无限。三层压缩在不同粒度上解决这个问题: micro-compact (替换旧工具输出), auto-compact (接近限制时 LLM 摘要), manual compact (用户触发)。关键洞察是遗忘是特性而非缺陷 -- 它使无限会话成为可能。转录文件将完整历史保存在磁盘上, 因此没有任何东西真正丢失, 只是从活跃上下文中移出。分层方法让每一层在各自的粒度上独立运作, 从静默的逐轮清理到完整的对话重置。
|
||||
|----------------|------------------|--------------------------------|
|
||||
| Tools | 5 | 5 (基础 + compact) |
|
||||
| 上下文管理 | 无 | 三层压缩 |
|
||||
| Micro-compact | 无 | 旧结果 -> 占位符 |
|
||||
| Auto-compact | 无 | token 阈值触发 |
|
||||
| Transcripts | 无 | 保存到 .transcripts/ |
|
||||
|
||||
## 试一试
|
||||
|
||||
|
|
@ -161,9 +118,8 @@ cd learn-claude-code
|
|||
python agents/s06_context_compact.py
|
||||
```
|
||||
|
||||
可以尝试的提示:
|
||||
试试这些 prompt (英文 prompt 对 LLM 效果更好, 也可以用中文):
|
||||
|
||||
1. `Read every Python file in the agents/ directory one by one`
|
||||
(观察 micro-compact 替换旧的结果)
|
||||
1. `Read every Python file in the agents/ directory one by one` (观察 micro-compact 替换旧结果)
|
||||
2. `Keep reading files until compression triggers automatically`
|
||||
3. `Use the compact tool to manually compress the conversation`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue