# 10.3 上下文窗口管理

Claude Opus 4.7、Opus 4.6 和 Sonnet 4.6 拥有 1 百万 Token 的上下文窗口 (Context Window)，并按标准 token 价格计费；其他 Claude 模型通常为 200K。这并不意味着应该无限制地往里塞东西。 **长下文腐烂 (Context Rot)** 和 **注意力稀释 (Attention Dilution)** 是真实存在的问题。而且，越长的 Context 意味着越慢的响应速度和更高的延迟。

本节介绍几种高级的上下文压缩与管理策略。

## 10.3.1 滑动窗口

这是最简单粗暴的策略，但在 Chatbot 中依然有效。

```python
history = [...]

# 仅保留最近的 N 轮
if len(history) > 20:
    history = history[-20:]
```

**问题**: 容易丢失早期的关键指令（如 “我不吃辣”）。 **改进**: **Pinned System Message + Sliding History**。永远保留第 1 条 System Prompt，只对后续的对话进行滑动。

## 10.3.2 递归摘要

这是一种像“滚雪球”一样的记忆方式。每隔 N 轮，就让 Claude 把之前的对话浓缩成一段摘要。

**Workflow**:

1. 对话达到 10 轮。
2. 后台触发 Summarizer: “请将这 10 轮对话总结为 200 字以内的摘要，保留关键事实（姓名、时间、偏好）。”
3. 将摘要存入 `previous_summary` 变量。
4. 清空历史，只保留 System Prompt + `previous_summary` + 最新对话。

**优点**: 理论上支持无限长的对话。 **缺点**: 每一次 summary 都会丢失细节信息（Lossy Compression）。

## 10.3.3 关键信息提取

与其总结，不如提取。 当用户说了一大堆废话时，只提取对后续有帮助的 **State**。

* User: “今天天气不错...对了，帮我把背景色改成蓝色...还要加个 Logo...”
* Distillation Agent: `update_state(background="blue", logo=True)`
* Context: 只保留 State Object，丢弃原始对话。

这在 Agentic Coding 中非常常用：只保留 **当前的文件内容** 和 **代办清单 (TODO)**，而不需要保留 “尝试修 Bug A 失败了 3 次” 的完整日志。

## 10.3.4 RAG-based Long-term Memory

对于超长会话（如陪伴型 AI），最好的策略不是把所有历史都塞进 Context，而是存入向量数据库。

**Retrieve Strategy**: 当用户说 “像上次一样” 时：

1. 用 “上次” 为 Query 去搜 Vector DB。
2. 检索出 3 个月前的那段对话片段。
3. 注入当前 Context。

这种方式实现了“无限记忆”与“有限 Context”的完美平衡。

**实现示例**:

```python
from chromadb import Client
import anthropic

chroma = Client()
collection = chroma.get_or_create_collection("chat_history")
client = anthropic.Anthropic()

def chat_with_memory(user_message, conversation_id):
    # 检索相关历史
    relevant_history = collection.query(
        query_texts=[user_message],
        n_results=3,
        where={"conversation_id": conversation_id}
    )

    # 构建上下文
    context = f"相关历史记录：\n{relevant_history['documents']}"

    # 调用 Claude
    response = client.messages.create(
        model="claude-sonnet-4-6",
        system=context,
        messages=[
            {"role": "user", "content": user_message}
        ]
    )

    # 存储新对话
    reply = response.content[0].text
    collection.add(
        documents=[f"User: {user_message}\nAssistant: {reply}"],
        metadatas=[{"conversation_id": conversation_id}]
    )

    return reply
```

## 10.3.5 原生上下文压缩

随着智能体任务越来越长（跨越数十分钟到数小时），传统的滑动窗口和递归摘要策略显得力不从心。业界正在向 **原生压缩**（Native Compaction）方向演进——由模型本身而非外部脚本来执行上下文压缩。

### 原生压缩的工作原理

原生压缩的核心思想是：让经过专门训练的模型自动分析对话历史，生成一个高效的“压缩项（Compacted Summary）”，以紧凑的 Token 表示方式保留关键历史状态。

```
压缩前：180,000 tokens（完整对话历史）
   ↓ 模型执行原生压缩
压缩后：45,000 tokens（压缩项 + 最近对话 + 关键文件）
```

压缩完成后，新的上下文窗口包含压缩项以及之前窗口中最有价值的部分内容，使长时程工作流能够保持连贯而不丢失关键决策和状态。

### Claude Code 的 `/compact` 命令

Claude Code 提供了 `/compact` 命令来手动触发上下文压缩。当对话历史接近上下文限制时，也会自动触发压缩：

```bash
# 手动压缩
> /compact

# 带自定义重点的压缩
> /compact --preserve architecture_decisions,test_results
```

Claude Code 的压缩策略遵循“缓存安全”原则（详见 [10.2.8 节](/claude_guide/di-si-bu-fen-shi-zhan-pian/10_optimization/10.2_caching.md)）：压缩请求复用完整的系统前缀以命中提示词缓存，只有新增的压缩指令本身按全价计费。

## 压缩前的记忆刷写

压缩是有损操作，必然丢失部分细节。为避免关键信息丢失，推荐在压缩触发前先执行 **记忆刷写（Memory Flush）**——将需要长期保留的决策、偏好和事实写入外部文件（如 `CLAUDE.md` 或项目笔记），然后再执行压缩。

```
上下文接近 75% 窗口限制
   ↓ 记忆刷写：关键决策写入 CLAUDE.md
   ↓ 执行压缩：对话历史 → 精简摘要
   ↓ 继续工作：摘要 + 最近对话 + 读取 CLAUDE.md 恢复状态
```

这种“先存后压”的模式确保了即使经过多次压缩，核心的项目知识和用户偏好也不会丢失。

## 服务端压缩 vs 客户端压缩

| 维度       | 服务端压缩         | 客户端压缩         |
| -------- | ------------- | ------------- |
| **触发方式** | 设置阈值，API 自动执行 | 客户端显式调用       |
| **优点**   | 零客户端逻辑，对开发者透明 | 完全可控，可自定义保留策略 |
| **缺点**   | 保留策略不够灵活      | 需要额外的编排代码     |
| **适用场景** | 标准对话机器人       | 复杂智能体工作流      |

对于大多数应用，建议从服务端自动压缩起步，随着需求复杂化再切换到客户端精细控制。

## 10.3.6 上下文管理的黄金法则

在实际应用中，请牢记以下原则：

1. **质量优于数量**：100 条精选的历史比 1000 条冗余信息更有价值。
2. **结构化存储**：使用 JSON 或 XML 格式存储状态，便于精确检索。
3. **定期清理**：为历史记录设置 TTL（生存时间），自动淘汰过期数据。
4. **A/B 测试**：不同的压缩策略效果因场景而异，务必用真实数据验证。

***

如果说上下文管理是“节流”，那么模型路由就是“开源”。 不需要总是用最贵的模型。合理的模型搭配可以用白菜价享受到顶级的服务。

➡️ [模型选择与路由策略](/claude_guide/di-si-bu-fen-shi-zhan-pian/10_optimization/10.4_selection.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yeasy.gitbook.io/claude_guide/di-si-bu-fen-shi-zhan-pian/10_optimization/10.3_context_mgmt.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
