Skip to content

上下文管理最佳实践

本文档整理上下文窗口管理的最佳实践,帮助在有限上下文中最大化信息价值。

核心挑战

txt
┌─────────────────────────────────────────────────────┐
│                  上下文管理挑战                       │
├─────────────────────────────────────────────────────┤
│                                                     │
│  输入受限 ──────────────────── 输出受限              │
│     │                              │                │
│     ↓                              ↓                │
│  ┌─────────────────────────────────────────┐       │
│  │           上下文窗口限制                  │       │
│  │     (4K / 8K / 32K / 128K / 1M)         │       │
│  └─────────────────────────────────────────┘       │
│     │                              │                │
│     ↓                              ↓                │
│  信息选择 ──────────────────── 信息压缩              │
│                                                     │
│  挑战:                                             │
│  - 如何选择最相关的信息?                           │
│  - 如何压缩信息而不丢失关键内容?                    │
│  - 如何管理长期记忆?                               │
│  - 如何处理超长文档?                               │
│                                                     │
└─────────────────────────────────────────────────────┘

模型上下文窗口参考

模型上下文窗口输入限制输出限制
GPT-4 Turbo128K~128K tokens4K tokens
GPT-48K / 32K~8K/32K tokens4K tokens
GPT-3.5 Turbo16K~16K tokens4K tokens
Claude 3 Opus200K~200K tokens4K tokens
Claude 3 Sonnet200K~200K tokens4K tokens
Gemini 1.5 Pro1M~1M tokens8K tokens

策略 1:上下文优先级

优先级框架

yaml
上下文优先级分层:
  P0 - 必须保留:
    - 系统指令
    - 当前用户请求
    - 关键约束和规则
    - 格式要求

  P1 - 高优先级:
    - 最近 N 轮对话
    - 关键实体和定义
    - 当前任务状态
    - 重要决策记录

  P2 - 中优先级:
    - 相关历史上下文
    - 检索到的文档片段
    - 中间推理过程

  P3 - 低优先级:
    - 详细的历史记录
    - 完整的原始文档
    - 冗余信息

  P4 - 可丢弃:
    - 已处理的信息
    - 不相关的历史
    - 错误和重试记录

实现示例

python
class ContextPrioritizer:
    def __init__(self, max_tokens: int, tokenizer):
        self.max_tokens = max_tokens
        self.tokenizer = tokenizer

    def prioritize(self, context: dict) -> str:
        """按优先级构建上下文"""
        result = []
        current_tokens = 0

        # P0: 系统指令(必须保留)
        system = context.get("system", "")
        system_tokens = self._count_tokens(system)
        if current_tokens + system_tokens <= self.max_tokens:
            result.append(system)
            current_tokens += system_tokens

        # P0: 当前请求(必须保留)
        request = context.get("request", "")
        request_tokens = self._count_tokens(request)
        if current_tokens + request_tokens <= self.max_tokens:
            result.append(request)
            current_tokens += request_tokens

        # P1: 关键信息
        key_info = context.get("key_info", [])
        for info in key_info:
            info_tokens = self._count_tokens(info)
            if current_tokens + info_tokens <= self.max_tokens * 0.8:  # 留 20% 余量
                result.append(info)
                current_tokens += info_tokens

        # P2: 相关上下文
        relevant_context = context.get("relevant_context", [])
        for ctx in relevant_context:
            ctx_tokens = self._count_tokens(ctx)
            if current_tokens + ctx_tokens <= self.max_tokens * 0.9:
                result.append(ctx)
                current_tokens += ctx_tokens

        return "\n\n".join(result)

    def _count_tokens(self, text: str) -> int:
        """计算 token 数量"""
        return len(self.tokenizer.encode(text))
```txt

## 策略 2:上下文压缩

### 压缩技术

```python
class ContextCompressor:
    def __init__(self, model):
        self.model = model

    def compress(self, text: str, target_ratio: float = 0.5) -> str:
        """压缩文本到目标比例"""
        if target_ratio >= 1.0:
            return text

        prompt = f"""
        Compress the following text to approximately {target_ratio * 100}% of its original length.
        Preserve all key information, entities, and relationships.
        Remove redundancy and verbosity.

        Original text:
        {text}

        Compressed text:
        """

        return self.model.generate(prompt)

    def summarize_sections(self, text: str, section_size: int = 1000) -> str:
        """分段摘要"""
        sections = self._split_text(text, section_size)
        summaries = []

        for section in sections:
            summary = self._summarize_section(section)
            summaries.append(summary)

        return "\n\n".join(summaries)

    def extract_key_points(self, text: str, max_points: int = 5) -> list[str]:
        """提取关键点"""
        prompt = f"""
        Extract the top {max_points} key points from the following text.
        Each point should be a single, concise sentence.

        Text:
        {text}

        Key points (one per line):
        """

        result = self.model.generate(prompt)
        return result.strip().split("\n")

    def _split_text(self, text: str, size: int) -> list[str]:
        """分割文本"""
        words = text.split()
        sections = []
        current = []
        current_size = 0

        for word in words:
            current.append(word)
            current_size += len(word) + 1

            if current_size >= size:
                sections.append(" ".join(current))
                current = []
                current_size = 0

        if current:
            sections.append(" ".join(current))

        return sections

    def _summarize_section(self, section: str) -> str:
        """摘要单个段落"""
        prompt = f"Summarize the following in 1-2 sentences:\n\n{section}"
        return self.model.generate(prompt)

压缩策略选择

策略压缩比信息保留适用场景
关键点提取10-20%事实性文本
分段摘要20-40%中高长文档
语义压缩40-60%对话历史
截断可变最后手段

策略 3:记忆管理

记忆层次架构

txt
┌─────────────────────────────────────────────────────┐
│                   记忆层次架构                        │
├─────────────────────────────────────────────────────┤
│                                                     │
│  ┌─────────────────────────────────────────────┐   │
│  │           工作记忆 (Working Memory)           │   │
│  │           当前上下文窗口                       │   │
│  │           容量: 4K - 1M tokens               │   │
│  └─────────────────────────────────────────────┘   │
│                       ↓                             │
│  ┌─────────────────────────────────────────────┐   │
│  │           短期记忆 (Short-term Memory)        │   │
│  │           最近 N 轮对话                       │   │
│  │           容量: 最近 10-20 轮                │   │
│  └─────────────────────────────────────────────┘   │
│                       ↓                             │
│  ┌─────────────────────────────────────────────┐   │
│  │           长期记忆 (Long-term Memory)         │   │
│  │           向量数据库 / 知识库                 │   │
│  │           容量: 无限                         │   │
│  └─────────────────────────────────────────────┘   │
│                                                     │
└─────────────────────────────────────────────────────┘

实现示例

python
from dataclasses import dataclass
from datetime import datetime
from typing import Optional
import json

@dataclass
class Memory:
    content: str
    timestamp: datetime
    importance: float  # 0-1
    type: str  # "fact", "event", "preference", "decision"
    metadata: dict

class MemoryManager:
    def __init__(self, vector_store, summarizer, max_working_tokens=8000):
        self.vector_store = vector_store
        self.summarizer = summarizer
        self.max_working_tokens = max_working_tokens

        self.working_memory: list[Memory] = []
        self.short_term_memory: list[Memory] = []

    def add_memory(self, content: str, memory_type: str, importance: float = 0.5):
        """添加新记忆"""
        memory = Memory(
            content=content,
            timestamp=datetime.now(),
            importance=importance,
            type=memory_type,
            metadata={}
        )

        # 添加到工作记忆
        self.working_memory.append(memory)

        # 检查是否需要压缩
        if self._get_working_tokens() > self.max_working_tokens:
            self._compress_working_memory()

        # 存储到长期记忆
        self.vector_store.add(memory)

    def recall(self, query: str, top_k: int = 5) -> list[Memory]:
        """检索相关记忆"""
        # 从长期记忆检索
        long_term_results = self.vector_store.search(query, top_k)

        # 从短期记忆匹配
        short_term_results = [
            m for m in self.short_term_memory
            if query.lower() in m.content.lower()
        ]

        # 合并和去重
        all_results = long_term_results + short_term_results
        unique_results = self._deduplicate(all_results)

        # 按重要性和时间排序
        sorted_results = sorted(
            unique_results,
            key=lambda m: (m.importance, m.timestamp),
            reverse=True
        )

        return sorted_results[:top_k]

    def consolidate(self):
        """将工作记忆整合到短期记忆"""
        for memory in self.working_memory:
            if memory.importance > 0.7:
                self.short_term_memory.append(memory)

        # 限制短期记忆大小
        if len(self.short_term_memory) > 20:
            # 移除最不重要的
            self.short_term_memory.sort(key=lambda m: m.importance, reverse=True)
            self.short_term_memory = self.short_term_memory[:20]

        # 清空工作记忆
        self.working_memory = []

    def _compress_working_memory(self):
        """压缩工作记忆"""
        if not self.working_memory:
            return

        # 按重要性排序
        self.working_memory.sort(key=lambda m: m.importance, reverse=True)

        # 保留高重要性记忆
        high_importance = [m for m in self.working_memory if m.importance > 0.8]

        # 压缩低重要性记忆
        low_importance = [m for m in self.working_memory if m.importance <= 0.8]
        if low_importance:
            combined = "\n".join([m.content for m in low_importance])
            summary = self.summarizer.compress(combined, 0.3)
            summary_memory = Memory(
                content=summary,
                timestamp=datetime.now(),
                importance=0.7,
                type="summary",
                metadata={"original_count": len(low_importance)}
            )
            high_importance.append(summary_memory)

        self.working_memory = high_importance

    def _get_working_tokens(self) -> int:
        """计算工作记忆的 token 数"""
        total = 0
        for memory in self.working_memory:
            total += len(memory.content.split()) * 1.3  # 粗略估计
        return int(total)

    def _deduplicate(self, memories: list[Memory]) -> list[Memory]:
        """去重"""
        seen = set()
        result = []
        for memory in memories:
            key = memory.content[:100]  # 使用前 100 字符作为 key
            if key not in seen:
                seen.add(key)
                result.append(memory)
        return result

策略 4:滑动窗口

实现示例

python
class SlidingWindow:
    def __init__(self, max_messages: int = 20, overlap: int = 2):
        self.max_messages = max_messages
        self.overlap = overlap
        self.messages = []

    def add_message(self, role: str, content: str):
        """添加消息"""
        self.messages.append({"role": role, "content": content})

        # 检查是否需要滑动
        if len(self.messages) > self.max_messages:
            self._slide()

    def get_context(self) -> list[dict]:
        """获取当前上下文"""
        return self.messages.copy()

    def _slide(self):
        """滑动窗口"""
        # 保留最近的 max_messages - overlap 条消息
        # 以及 overlap 条旧消息的摘要
        keep_count = self.max_messages - self.overlap

        # 提取要压缩的旧消息
        old_messages = self.messages[:self.overlap]
        old_summary = self._summarize_old(old_messages)

        # 构建新的消息列表
        self.messages = [
            {"role": "system", "content": f"[历史摘要] {old_summary}"},
            *self.messages[keep_count:]
        ]

    def _summarize_old(self, messages: list[dict]) -> str:
        """摘要旧消息"""
        # 简化实现,实际应该调用 LLM
        topics = []
        for msg in messages:
            # 提取关键实体
            topics.append(msg["content"][:50])
        return " | ".join(topics)

滑动窗口变体

python
class SmartSlidingWindow:
    """智能滑动窗口:根据重要性滑动"""

    def __init__(self, max_tokens: int = 8000):
        self.max_tokens = max_tokens
        self.messages = []
        self.importance_scores = []

    def add_message(self, role: str, content: str, importance: float = 0.5):
        """添加消息"""
        self.messages.append({
            "role": role,
            "content": content,
            "tokens": self._estimate_tokens(content)
        })
        self.importance_scores.append(importance)

        # 检查是否需要压缩
        if self._get_total_tokens() > self.max_tokens:
            self._smart_compress()

    def _smart_compress(self):
        """智能压缩"""
        while self._get_total_tokens() > self.max_tokens * 0.8:
            # 找到最不重要的消息
            min_idx = self.importance_scores.index(min(self.importance_scores))

            # 压缩或删除
            if self.importance_scores[min_idx] < 0.3:
                # 直接删除
                del self.messages[min_idx]
                del self.importance_scores[min_idx]
            else:
                # 压缩
                self.messages[min_idx]["content"] = self._compress_content(
                    self.messages[min_idx]["content"]
                )
                self.messages[min_idx]["tokens"] = self._estimate_tokens(
                    self.messages[min_idx]["content"]
                )
                self.importance_scores[min_idx] *= 0.8  # 降低重要性

    def _estimate_tokens(self, text: str) -> int:
        """估算 token 数"""
        return int(len(text.split()) * 1.3)

    def _get_total_tokens(self) -> int:
        """计算总 token 数"""
        return sum(msg["tokens"] for msg in self.messages)

    def _compress_content(self, content: str) -> str:
        """压缩内容"""
        # 简化实现
        return content[:len(content) // 2] + "..."

策略 5:检索增强

检索策略

python
class RetrievalAugmentedContext:
    def __init__(self, retriever, reranker, max_context_tokens=6000):
        self.retriever = retriever
        self.reranker = reranker
        self.max_context_tokens = max_context_tokens

    def build_context(self, query: str, system_prompt: str) -> str:
        """构建检索增强的上下文"""
        # 1. 初始检索
        initial_results = self.retriever.search(query, top_k=20)

        # 2. 重排序
        reranked_results = self.reranker.rerank(query, initial_results, top_k=5)

        # 3. 构建上下文
        context_parts = [system_prompt]
        current_tokens = self._estimate_tokens(system_prompt)

        for doc in reranked_results:
            doc_tokens = self._estimate_tokens(doc["content"])
            if current_tokens + doc_tokens <= self.max_context_tokens:
                context_parts.append(f"\n[Reference]\n{doc['content']}")
                current_tokens += doc_tokens
            else:
                break

        context_parts.append(f"\n[Query]\n{query}")

        return "\n".join(context_parts)

    def _estimate_tokens(self, text: str) -> int:
        """估算 token 数"""
        return int(len(text.split()) * 1.3)

检索优先级

yaml
检索优先级:
  高优先级:
    - 精确匹配的实体
    - 最近的交互记录
    - 用户明确引用的内容
    - 关键定义和规则

  中优先级:
    - 语义相似的文档
    - 相关主题的内容
    - 历史决策记录

  低优先级:
    - 模糊匹配的内容
    - 过时的信息
    - 冗余的内容

检索配置:
  初始检索量: 20-50
  重排序后保留: 3-5
  最小相关分数: 0.7
  多样性权重: 0.2

策略 6:结构化上下文

上下文模板

python
class StructuredContextBuilder:
    def __init__(self, template: str):
        self.template = template

    def build(self, **kwargs) -> str:
        """构建结构化上下文"""
        return self.template.format(**kwargs)

# 系统指令模板
SYSTEM_TEMPLATE = """
# 角色定义
{role_definition}

# 核心能力
{capabilities}

# 约束条件
{constraints}

# 输出格式
{output_format}
"""

# 对话上下文模板
CONVERSATION_TEMPLATE = """
# 当前状态
任务: {current_task}
进度: {progress}

# 相关上下文
{relevant_context}

# 最近交互
{recent_messages}

# 当前请求
{current_request}
"""

# RAG 上下文模板
RAG_TEMPLATE = """
# 任务说明
{task_description}

# 检索结果
{retrieved_documents}

# 用户问题
{user_question}

# 回答要求
1. 基于检索结果回答
2. 标注信息来源
3. 不确定时明确说明
"""

使用示例

python
# 创建结构化上下文
builder = StructuredContextBuilder(RAG_TEMPLATE)

context = builder.build(
    task_description="根据提供的文档回答用户问题",
    retrieved_documents="""
    [文档1] 公司成立于 2020 年,主要业务是 AI 解决方案...
    [文档2] 公司的 AI 产品包括智能客服、智能推荐...
    [文档3] 公司在 2023 年获得 A 轮融资...
    """,
    user_question="公司的主要产品有哪些?",
    current_request="请详细回答用户的问题"
)

最佳实践总结

上下文管理清单

markdown
## 上下文管理检查清单

### 必须项

- [ ] 明确上下文窗口限制
- [ ] 实现优先级排序
- [ ] 设置压缩策略
- [ ] 定义保留规则

### 推荐项

- [ ] 实现记忆管理
- [ ] 使用滑动窗口
- [ ] 检索增强上下文
- [ ] 结构化上下文模板

### 可选项

- [ ] 动态上下文调整
- [ ] 多级缓存
- [ ] 上下文预热
- [ ] 上下文分析

### 避免事项

- [ ] 上下文溢出
- [ ] 无限增长的历史
- [ ] 无优先级的检索
- [ ] 无压缩策略

Token 预算分配

yaml
Token 预算分配建议:
  总预算: 100%

  系统指令: 10-15%
    - 角色定义
    - 核心规则
    - 输出格式

  检索上下文: 40-50%
    - 检索到的文档
    - 相关历史
    - 关键实体

  对话历史: 20-30%
    - 最近 N 轮对话
    - 历史摘要

  用户请求: 10-15%
    - 当前问题
    - 补充说明

  输出预留: 10-20%
    - 生成空间
    - 安全边际

不同场景配置

场景上下文预算检索量历史保留压缩策略
问答系统 10% + 检索 60% + 请求 15% + 输出 15%5-10 条最近 3 轮检索压缩
对话系统 10% + 历史 50% + 请求 20% + 输出 20%3-5 条最近 10 轮滑动窗口
写作系统 10% + 检索 30% + 请求 30% + 输出 30%3-5 条最近 5 轮关键点提取
分析系统 10% + 检索 50% + 请求 20% + 输出 20%10-20 条最近 3 轮分段摘要

参考来源