6.1 信息密度与压缩原理

6.1.1 信息密度的概念

信息密度是指单位Token中包含的有效信息量.高密度意味着最少的token传达更多有价值的内容

1
信息密度= 有效信息量/token数量

信息密度对于上下文工程的影响:

  • 容量利用:更高上下文窗口的利用率
  • 成本效率:更低的token成本
  • 处理效率:更快的响应速度

6.1.2 信息的冗余与噪声

上下文中的低效内容主要来自于两个方面:

冗余信息

  • 重复表达相同含义
  • 过渡性词汇和填充语
  • 不必要的修饰和解释

噪声信息

  • 于任务无关的信息
  • 过时或错误的信息
  • 格式化符号和无意义标记

6.1.3 压缩的基本原则

image-20260915190423442

原则一:信息保真 不能损失关键信息,是提炼精华而非截断

原则二:任务导向 根据当前任务决定保留什么

原则三:可理解性 压缩结果应该把持连贯,让模型能够理解和使用

6.1.4 压缩技术分类

类型方法特点
抽取式选择原文中的关键句段保持原文表述
生成式生成新的精简表述更灵活、更精炼
结构化转换为结构化格式格式紧凑、易解析
层次化分级别保留细节按需展开

6.1.5 压缩与质量的权衡

压缩率 vs 信息完整度 高压缩率:节省空间但细节丢失 低压缩率:占用过多上下文

通用性 vs 针对性 通用压缩:一次压缩多次复用 针对性压缩:针对具体任务定制

成本 vs 效果 简单压缩(如截断):成本低但效果差 高级压缩(如LLM摘要):效果好但是需要额外成本

6.1.6 何时需要压缩

以下场景: 1.上下文接近容量上限 2.成本敏感场景 3.延迟敏感场景 4.对话历史过长 5.检索结果过多

6.1.7 压缩比与质量损失对比

压缩率越高,信息损失风险越大。下图展示了不同压缩比对问答准确率影响的 示意趋势

image-20260915191504456

6.1.8 压缩效果评估

评估压缩效果需要考虑:

直接指标

  • 压缩率:1 − 压缩后大小 / 原始大小,即削减掉的比例(压缩率越高=压得越狠)。注意不要写成「压缩后/原始」,那是保留率,方向相反
  • Token 减少量:压缩前后 Token 数量差异

间接指标:

  • 任务效果:压缩后任务完成质量
  • 信息保留率:关键信息的覆盖程度

6.2 摘要和信息提取

6.2.1 抽取式方法

从原文选择重要句子或者段落,保持原文描述

关键句提取:

根据句子的重要性选择关键句

TextRank算法: 借鉴 PageRank 思想,根据句子之间的相似度构建图,计算每个句子的中心性分数

基于嵌入的方法:

  1. 计算每个句子的嵌入向量
  2. 计算句子与文档整体的相似度
  3. 选择相似度最高的句子

关键片段提取

抽取包含关键信息的文本片段

  • 包含实体的句子
  • 包含数组和日期的句子
  • 定义行语句

抽取式方法的优缺点

1
2
3
4
5
6
7
8
9
优点:
- 保持原文表述,准确性高
- 计算成本相对较低
- 可追溯到原文

缺点:
- 可能不够连贯
- 无法重组或精简表达
- 受限于原文的表述质量

6.2.2 生成式方法

使用语言模型生成新的精简表述

大语言模型摘要

直接使用LLM生成摘要

1
2
请将以下内容总结为不超过 200 字的摘要,保留关键信息:
[原始内容]

技巧:

  • 明确指定输出长度
  • 指明需要保留的信息类型
  • 提供输出格式要求

递进式摘要

对于超长文档,采取递进压缩 image-20260915192330527

递进式摘要流程:

  1. 将文档分为多个段落
  2. 分别生成段落摘要
  3. 合并段落摘要
  4. 可选:对合并结果再次摘要

多级摘要

层级建议字数/词数主要用途
标题10–20 词快速索引
简述50–100 词内容概览
详情200–500 词深入了解

生成式方法的优缺点

1
2
3
4
5
6
7
8
9
优点:
- 表述更连贯流畅
- 能够重组和精简
- 可按需定制输出

缺点:
- 可能引入新的错误
- 需要额外的 LLM 调用成本
- 可追溯性较弱

6.2.3 信息提取

从文本中提取结构化信息

实体提取

提取关键实体:人名.地名,时间,数值等等

1
2
3
4
5
6
输入:2026年3月,张三在北京发布了新产品,售价1999元。
输出:
- 人物:张三
- 地点:北京
- 时间:2026年3月
- 价格:1999元

关系提取

1
2
3
4
输入:李四是A公司的CEO,该公司总部位于上海。
输出:
- (李四, 职位, A公司CEO)
- (A公司, 总部, 上海)

事实提取

提取关键事实点

1
2
3
4
5
输入:[长篇报告...]
输出:
1. 2025年营收增长15%
2. 新增用户100万
3. 计划明年进入欧洲市场

6.2.4 最佳实践

1. 根据场景选择方法

不同的压缩方法适合不同场景,选错方法会导致效果大打折扣。需要精确引用原文(如法律、医疗场景)时使用抽取式,保证信息准确;需要流畅表达(如给用户的摘要)时使用生成式;需要结构化处理(如数据分析)时使用信息提取。常见错误是不加区分地使用同一种方法,导致需要引用时找不到原文,需要连贯时读起来支离破碎。

2. 组合使用多种方法

单一方法往往难以兼顾多个目标,组合使用能发挥各自优势。典型模式是:先用抽取式选出关键段落,再用生成式整合为连贯摘要;或者生成摘要的同时,附上结构化的关键事实列表。这样既保证信息的可追溯性,又提供流畅的阅读体验。组合时注意控制总长度,避免压缩后反而变长。

3. 保留元信息以便追溯

压缩后的内容应该能追溯到原始来源。记录摘要来自哪份文档、第几页或第几段;标注使用的压缩方法(抽取/生成/提取)和生成时间。这些元信息在调试问题时非常有价值——当输出不准确时,可以快速定位是原始数据问题还是压缩过程引入的偏差。元信息的存储成本很低,但排查问题时能节省大量时间。

4. 建立质量验证机制

压缩必然带来信息损失,需要验证损失是否在可接受范围内。建立定期抽样检查机制,人工对比压缩前后的内容,评估关键信息是否保留。更重要的是对比压缩前后的任务效果——如果压缩后模型的回答质量明显下降,说明压缩过度或方法不当。用 A/B 测试量化压缩对最终效果的影响,找到压缩率与质量的最佳平衡点。

5. 多智能体场景下的上下文压缩与持久化执行

在企业级多智能体系统中,上下文累积特别容易超出限制。实践中常见的模式是 两部分编排(Two-Part Orchestration):

  • 初始化智能体(Initializer Agent):接收用户的原始需求,进行查询理解、计划分解,并生成对下游智能体的结构化指令和压缩后的上下文
  • 编码智能体(Coding/Execution Agent):专注于具体任务执行,工作上下文已被精心构建和压缩,避免冗余信息导致的上下文爆炸

持久化执行与状态恢复(Durable Execution Pattern):

为了在长时间运行的智能体流程中提高可靠性,应该采取:

  1. 步骤化执行:将长任务分解为原子性的步骤,每步生成可持久化的输出(Step Artifacts)
  2. 检查点保存:在完成关键步骤后保存完整的执行状态,包括输入、中间结果、输出和元数据
  3. 失败恢复:如果执行中断(网络问题、模型超时等),可以从最后一个成功的检查点重新开始,而不需要重新计算前面的步骤
  4. 可重放验证:使用保存的工件重新执行同样的操作,验证结果的确定性和一致性

这种模式特别适合批量处理、数据管道或需要跨多个 API 调用的复杂工作流,既提高了成功率,也降低了因重复计算导致的成本浪费。

6.3 对话历史管理

6.3.1 对话历史的挑战

  • 上下文膨胀
  • 信息稀释
  • 成本积累
  • 性能下降

6.3.2 对话历史管理策略

image-20260915210109623

滑动窗口

只保留最近N轮对话

**优点:**简单直接,容易实现 缺点:可能丢失重要的早期信息

参数调优:

  • 窗口的大小取决于任务特性
  • 简单问答2-5;复杂任务:5-10

选择性保留

根据重要性选择保留哪些历史

重要性判断依据:

  • 包含任务关键信息
  • 用户明确要求记住的内容
  • 定义了重要约束或者偏好
  • 最近的交互

实现方式: 规则标记:基于关键词或者模式 模型判断:让LLM判断

对话压缩

将冗长的对话历史压缩为摘要 即时压缩:每轮对话之后立即压缩

批量压缩:达到阈值之后批量压缩较早的历史

递进压缩:按照时间距离采取不同压缩级别: 最近(1-3轮):完整保留 近期(4-10轮):轻度压缩 早期(10轮以上):重度压缩

分层管理:

层次内容存储位置
工作层当前对话上下文上下文窗口
摘要层对话历史摘要上下文窗口
事实层提取的关键事实外部记忆
完整层完整对话历史持久存储

6.3.4 上下文拼接策略

整合不同来源的历史信息:

复制

1
2
3
4
5
[系统提示词]
[用户画像/偏好](从长期记忆)
[会话摘要](早期历史的压缩)
[最近对话](保留原文)
[当前问题]

6.3.5 最佳实践

1. 结合多种策略构建历史管理体系

单一策略难以应对复杂场景,推荐组合使用:滑动窗口保留最近的完整对话;压缩技术处理较早的历史;重要信息提取确保关键约束不丢失。例如:保留最近 5 轮原文 + 早期历史压缩摘要 + 提取的用户偏好列表。这种分层策略在控制上下文长度的同时,最大化保留有价值的信息。根据任务特点调整各层的比例。

2. 始终保留关键锚点信息

用户明确表达的约束和要求是“锚点”,必须始终保留在上下文中。例如:“请用简体中文回复”、“预算不超过 5000 元”、“不要推荐海鲜”。这些信息一旦丢失,模型会给出违背用户意愿的回答,严重影响体验。实现时可以用特殊标记标识锚点信息,确保压缩过程不会删除它们。宁可多保留一些锚点,也不要冒丢失关键约束的风险。

3. 确保压缩后内容的自然过渡

压缩后的摘要应该与当前对话自然衔接,避免风格突兀。使用第一人称还是第三人称?是口语化还是正式表达?压缩摘要的风格应与原始对话保持一致。突兀的风格切换会让模型“困惑”,影响后续生成质量。在压缩提示词中明确要求保持风格一致,必要时提供示例。

4. 保持完整历史的可恢复性

虽然在上下文中使用压缩版本,但完整的对话历史应该保存在持久化存储中。这样在需要时可以回溯——例如用户投诉某次回答不当,需要完整上下文来分析原因;或者用户要求“回到之前讨论的那个方案”,需要恢复详细信息。将完整历史与压缩版本关联存储,建立索引便于快速检索。

5. 持续监控压缩对对话质量的影响

压缩策略不是设置后就不管的“一次性配置”,需要持续监控效果。观察指标包括:用户是否需要重复说过的信息?模型是否遗忘了早期约束?对话连贯性如何?建立反馈机制,当检测到质量下降时自动调整压缩参数。定期审查压缩效果,随着对话模式变化优化策略。

6.3.6 实战案例:电商客服对话管理(示意)

以下示例用于说明一种对话历史管理的实现思路,生产环境需根据并发、权限、合规与成本要求进行改造。

场景描述

用户咨询退换货,对话可能持续 20+ 轮,涉及订单查询、问题确认、方案协商等多个阶段。

完整实现

复制

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from dataclasses import dataclass, asdict
from typing import List, Optional
import json
import sqlite3

@dataclass
class Message:
    role: str  # "user" 或 "assistant"
    content: str
    is_anchor: bool = False  # 是否为锚点信息

    def to_dict(self):
        return asdict(self)

    @staticmethod
    def from_dict(data):
        return Message(**data)

class ConversationManager:
    def __init__(self, session_id: str, db_path="chat.db", max_recent=5):
        self.session_id = session_id
        self.db_path = db_path
        self.messages: List[Message] = []
        self.anchors: List[str] = []
        self.summary: str = ""
        self.max_recent = max_recent
        self._init_db()
        self.load_history()

    def _init_db(self):
        """初始化 SQLite 数据库表结构"""
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS sessions (
                    session_id TEXT PRIMARY KEY,
                    summary TEXT,
                    anchors TEXT,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            """)
            conn.execute("""
                CREATE TABLE IF NOT EXISTS messages (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    session_id TEXT,
                    role TEXT,
                    content TEXT,
                    is_anchor BOOLEAN,
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    FOREIGN KEY(session_id) REFERENCES sessions(session_id)
                )
            """)

    def load_history(self):
        """从数据库加载历史"""
        with sqlite3.connect(self.db_path) as conn:
            # 加载会话元数据(摘要和锚点)
            cursor = conn.execute(
                "SELECT summary, anchors FROM sessions WHERE session_id = ?",
                (self.session_id,)
            )
            row = cursor.fetchone()
            if row:
                self.summary = row[0]
                self.anchors = json.loads(row[1]) if row[1] else []

            # 加载最近的消息
            # 注意:实际生产中可能只需要加载最近 N 条,早期的已压缩
            cursor = conn.execute(
                """
                SELECT role, content, is_anchor FROM messages
                WHERE session_id = ?
                ORDER BY id ASC
                """,
                (self.session_id,)
            )
            self.messages = [Message(*r) for r in cursor.fetchall()]

    def save_snapshot(self):
        """保存当前状态到数据库"""
        with sqlite3.connect(self.db_path) as conn:
            # 更新会话元数据
            conn.execute(
                """
                INSERT OR REPLACE INTO sessions (session_id, summary, anchors)
                VALUES (?, ?, ?)
                """,
                (self.session_id, self.summary, json.dumps(self.anchors))
            )

            # 简单的全量保存(生产环境应改为增量保存)
            conn.execute("DELETE FROM messages WHERE session_id = ?", (self.session_id,))
            for msg in self.messages:
                conn.execute(
                    """
                    INSERT INTO messages (session_id, role, content, is_anchor)
                    VALUES (?, ?, ?, ?)
                    """,
                    (self.session_id, msg.role, msg.content, msg.is_anchor)
                )

    def add_message(self, role: str, content: str):
        is_anchor = self._detect_anchor(content)
        msg = Message(role, content, is_anchor)
        self.messages.append(msg)

        if is_anchor:
            self.anchors.append(content)

        # 实时保存(或采用异步批量保存)
        self.save_snapshot()

        # 触发压缩检查
        if len(self.messages) > self.max_recent * 2:
            self._compress_history()
            self.save_snapshot() # 压缩后再次保存更新状态

    def _detect_anchor(self, content: str) -> bool:
        anchor_patterns = ["订单号", "手机号", "必须", "预算"]
        return any(p in content for p in anchor_patterns)

    def _compress_history(self):
        """将较早的非锚点消息折叠进摘要,只保留最近消息"""
        old, recent = self.messages[:-self.max_recent], self.messages[-self.max_recent:]
        if not old:
            return
        # 教学示例用截断拼接;生产中应调用 LLM 生成语义摘要,
        # 并将旧消息归档到冷存储而非直接丢弃
        digest = ";".join(f"{m.role}: {m.content[:40]}" for m in old if not m.is_anchor)
        if digest:
            self.summary = f"{self.summary}\n{digest}" if self.summary else digest
        self.messages = recent

    def build_context(self) -> str:
        parts = []
        if self.anchors: parts.append("【关键约束】\n" + "\n".join(f"- {a}" for a in self.anchors))
        if self.summary: parts.append(f"【历史摘要】\n{self.summary}")
        recent = "\n".join([f"{m.role}: {m.content}" for m in self.messages[-self.max_recent:]])
        parts.append(f"【最近对话】\n{recent}")
        return "\n\n".join(parts)

实际效果

20 轮对话后的上下文示例:

复制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
【关键约束】
- 订单号:2024010512345
- 不接受换货,只要退款
- 必须今天处理完

【历史摘要】
用户购买的蓝牙耳机存在左耳无声问题,已确认在保修期内。
用户尝试过重置配对但未解决。客服已记录问题,准备发起退款。

【最近对话】
user: 退款多久到账?
assistant: 审核通过后3-5个工作日到账。
user: 太慢了,能加快吗?
assistant: 我帮您申请加急处理,预计1-2个工作日。
user: 好的,那就这样吧。

踩坑经验

  1. 订单号必须作为锚点 — 曾因压缩丢失订单号,导致后续无法查询,用户需要重复提供
  2. 用户情绪需要记录 — “用户表达了不满”这类信息应保留,影响后续安抚策略
  3. 压缩时机要对 — 不要在用户等待回复时压缩,会增加延迟;应在对话间隙异步执行
  4. 保留否定约束 — “不要推荐 X”比“推荐 Y”更容易被压缩丢失,需特殊标记

6.4 上下文窗口优化策略

6.4.1 Token估计和上下文预算管理

精确的Token估计是上下文预算管理的基础

Token计数策略:

  • 精确计数:使用上次API调用返回的input_token值作为基准
  • 粗估计: 使用"字符数/经验系数"进行估计,但是中文,代码,JSON和不同的tokenizer差别很大
  • 输出预留:按任务期望输出,模型Max+tkones和上下文窗口保留缓冲,避免填满窗口导致截断或者停止
  • 跨版本迁移注意:从旧版本切换到新版本,由于tokenzier的不同,需要重新估算

上下文窗口分辨率优先级

需要模型动态确定模式上下文窗口大小时,系统往往采取优先级链

  1. 模型能力查询:优先读取供应商模型元数据,token counting API等等配置
  2. 显式应用配置:按照模型ID,区域,账号层级和beta flag固化允许的上下文
  3. 请求/响应能力信号:某些API会通过beta header,错误码或者响应元数据暴露可用接口
  4. 模型名称标记:只能作为复制提示,不能把后缀当成权威来源
  5. 保守默认值:无法确定时应Fail closed,要求显式配置,而不是假设200k或者1m

预算分配

为不同的组成部分预先分配不同的token预算

组成部分典型预算占比说明
系统提示词5-15%尽量精简
知识/检索内容30-50%核心信息区
对话历史20-30%动态调整
输出预留15-25%确保足够空间

结合实际任务进行动态调整

动态调整

知识密集任务:增加检索内容预算 多轮对话任务:增加对话历史预算 长文生成任务:增加输出预留预算

6.4.2 系统提示词优化(可以看看O/最新的文章)

system prompt是最稳定的上下文部分,应该尽量的精简

精简策略

删除冗余:

  • 删除重复说明;合并相似指令;使用简介表达

结构化表达:

  • 使用列表代替段落;使用表格组织规则;使用编号易于引用

模板复用:

  • 将通用部分模板化;动态部分参数化;条件加载可选内容

提示词压缩技术

人工精简: 最直接最有效(存疑)

LLM辅助压缩: 让模型帮助精简

符号化表达: 用简洁符号代替冗长说明

1
2
原文:如果用户没有明确指定格式,请使用 Markdown 格式输出
精简:默认格式=Markdown

6.4.3 检索内容占用优化

精确检索

  • 提高精度;使用重排序过滤低相关结果;使用置信度阈值截断结果

检索后压缩

对检索结果进行二次压缩

  • 提取与查询相关的字段
  • 生成针对性摘要
  • 合并重复信息

渐进式检索

按需检索

  1. 先用少量关键信息回答
  2. 如果需要更多的关键信息再检索
  3. 避免预防性的大量检索(但是貌似有一个检索策略是预加载,比如滑动窗口会加载相邻的内容)

6.4.4 格式优化

格式选择影响token效率

结构化格式对比

格式Token 效率可读性适用场景
纯文本高一般连续内容
Markdown中好结构化文档
JSON低中数据交换
XML最低高明确边界

格式优化建议:

  • 非必要不使用嵌套结构
  • 属性名尽量简短
  • 避免冗长的key名称
  • 考虑自定义间接格式

6.4.5 KV缓存压缩技术

在不降低模型能力的情况下,压缩KV缓存体积是实现更长上下文的关键路径

多头隐式注意力(MLA: Multi-head Latent Attention)

DeepSeek-V2/V3采用的MLA将KV Cache压缩到了极致:

  • 原始 KV 缓存占用:以 Llama-3.1-405B 为例,单个 Token 需要约 516KB 的 KV 缓存
  • MLA 压缩后:每个 Token 的 KV 缓存仅需约 70KB,压缩比约 7 倍
  • 实现原理:将 KV 映射到低维隐向量,再按需扩展到多个查询头,既保留了多头注意力的表达能力,又大幅降低显存需求

分组查询注意力(CQA/MQA)

GQA(Grouped Query Attention) 和 MQA(Multi-Query Attention):多个查询头共享一个或几个 KV 头组,线性降低 KV 缓存体积。

  • GQA:8 个查询头共享 1 个 KV 头,KV 缓存减少约 8 倍
  • MQA(极端情况):所有查询头共享单个 KV 头,缓存最小化但精度可能下降
  • 权衡:在精度损失与缓存收益间找到最优点,通常 GQA 是最佳选择

KV缓存量化

将 KV 缓存从 FP32 或 BF16 量化到 INT4 或 FP8:

  • INT4 量化:降低位宽同时保留关键信息,从 BF16/FP16 量化到 INT4 理论上可将 KV 缓存显存需求降至约 1/4,有效上下文长度可放大约 4 倍
  • 实现策略:对 KV 进行分段量化,采用动态范围缩放,降低量化误差
  • 成本:推理时需额外的反量化开销,但总体 Token 吞吐仍能提升

6.4.6 缓存和复用

减少重复内容传输和计算

提示词缓存和基础设施级缓存

提示词缓存是优化成本和延迟的最有效的手段之一,而基础设施层面的KV缓存共享技术为系统级优化提供了新的可能性

经济学原理:

  • TTL(存活时间):缓存通常拥有较短的生命周期
  • 成本不对成性:写入缓存一般比常规输入token更贵,但是读取缓存读比input更便宜

基础设施级缓存:

Radix Attention 在系统级别实现全局 KV 缓存共享,超越单个会话的限制:

  • 全局基数树:维护所有历史 KV 缓存的全局 Radix 树,每个节点代表一个 Token 序列的 KV 缓存切片
  • 前缀匹配与重用:新请求到达时,系统自动检测其前缀是否在树中存在。若存在完全匹配的前缀(如相同的系统提示词 + RAG 文档),新请求可跳过该前缀部分的 Prefill 计算;新增的后缀(本轮用户输入)仍需 Prefill,之后才进入 Decode 阶段
  • 应用场景:系统提示词和 RAG 文档在多个请求间高度重复时,收益最大。客服、内部知识库等场景可能获得较高命中率,但具体比例必须用线上流量验证。

KV 缓存感知路由(KV-Cache-Aware Affinity Routing)

API 网关在接收请求时计算请求前缀的哈希,将其路由到缓存该前缀的 GPU 节点,避免冷启动:

  • 哈希映射:对请求的系统提示词 + 前 k 个用户消息计算哈希
  • 节点亲和性:根据哈希值选择已缓存该前缀的节点,若无则选择最近空闲的节点
  • 成本效益:对于高重复的前缀(RAG 文档、系统提示词),可显著降低 Prefill 计算;具体节省比例取决于请求重复度、缓存生命周期和调度实现。

架构级最佳实践:

为了最大化缓存命中率,系统设计需要严格保持"前缀一致性"

  • 使用消息而非修改系统提示词(非纯及坏)
  • 延迟加载工具:同样的道理,不要中途加载和删除tools
  • 跨模式保持工具稳定:如果系统拥有诸如“计划模式”与“执行模式”,不要在模式切换时尝试通过增删工具列表来加以限制。应保持全量的工具定义始终不变,仅仅通过添加一条普通的前置消息来指示模型改变其行为

各场景的缓存收益分析:

  • 复杂系统提示词:避免复杂system prompt的prefill
  • 代码补全和生成:避免对未修改的代码进行重复计算
  • 文档摘要与问答:最大化缓存复用
  • 多轮对话:收益随着对话轮次进行而递增

设计上下文时应始终考虑缓存特性:**将频繁变化的内容(如用户输入、最新检索结果)放在提示词末尾,将稳定内容(如系统提示词、工具定义)放在前部。**这一原则不仅适用于单轮请求,在多轮对话和智能体工作流中同样关键。

基于前缀匹配

预计算(prefill)

对于频繁使用的内容预先计算:

  • 预生成摘要并存储
  • 检索结果预处理
  • 通用模板预填充

6.4.7 监控和调优

监控指标

  • token使用分布:各部分占比(system prompt,tool call,tool result,user message)
  • 使用效率:有效部分/总token
  • 边界情况:接近上限的频率
  • 成本追踪

持续优化:

  1. 建立基准线
  2. 识别最大消耗点
  3. 针对性优化
  4. 验证效果
  5. 持续监控

6.4.8 长任务场景的高级压缩策略

紧凑化技术

Compaction:在上下文窗口接近限制时,对对话内容进行摘要并重新初始化上下文窗口 核心:高保真地提炼上下文窗口地内容,减少质量损失

实现要点:

  • 将消息历史传递给模型进行总结和压缩
  • 保留决策框架,未解决地问题和实现细节
  • 丢弃冗余地消息和工具输出
  • 用压缩的上下文加上最近的文件继续工作

分叉操作和缓存复用: 在执行压缩操作时的,一个关键的设计时将压缩请求作为当前对话的"分叉(Fork操作)",在压缩请求钟尽量复用原绘画稳定的系统提示词,知识库和工具定义作为前缀,仅将对话信息部分替换为原消息副本以及"请总结上述对话"的指令消息

压缩的艺术: 保留什么和丢弃什么 是一种上下文细节和上下文空间的trade-off

结构化笔记

定期将笔记持久化到上下文窗口外部存储的技术,后续在需要时被拉回上下文窗口

工作方式

  • 智能体创建待办事项或者维护NOTES.md文件
  • 用于追踪复杂任务的基站
  • 保持关键上下文和依赖关系,防止这些信息的在上下文中丢失

选择策略的指南

策略 / 架构适用场景说明
Compaction(压缩/精简)需要大量来回交互的任务缩减冗余上下文,释放 Token 空间
Structured Note-taking(结构化笔记)有明确里程碑的迭代开发实时记录进度与关键决定,确保上下文连续
子智能体架构需要并行探索的复杂研究分析拆分复杂任务,并行处理后再整合结果

6.4.9 压缩前的记忆刷写和检查点

为了降低信息损失,生产级系统通常在压缩前执行记忆刷写(Memory Flush)并建立压缩检查点(Compaction checkpoint)来支持任务恢复

记忆刷写:

当上下文占用接近窗口的某个阈值时,系统在执行压缩之前触发一次记忆刷写:将需要长期保留的关键信息(决策,偏好,待办事项)写入外部持久化存储,确保不会在压缩中丢失

image-20260916132723903

压缩检查点

压缩检查点在每次压缩时额外保存:

  • 压缩摘要
  • 结构化状态:任务进度,待办事项,已完成步骤
  • 活跃文件列表

工具输出的分层裁剪

按照时间衰减处理工具执行结果的输出

时间距离裁剪策略示例
最近 3 轮完整保留最新的命令输出全文
4-10 轮前软裁剪(保留首尾)前 1500 字符 + 后 1500 字符
10 轮以前硬清空[旧工具结果已清空] 占位符

6.4.10 智能体上下文缩减策略:

将工具执行结果分为“完整版”和“紧凑版”两种形式…

2. 陈旧结果压缩

随着对话进行,早期结果被替换为摘要…

3. 基于结构的轨迹摘要

即便使用摘要,也强制使用结构化 Schema…

6.5 压缩策略案例分析

6.5.1 案例一: 长文档会议纪要生成

场景描述

用户上传一份长达5万字得行业研报,希望生成一份结构化得摘要,包含关键数据,主要观点,未来趋势

挑战:

  • 长度限制:超出绝大多数模型上下文窗口
  • 信息分散
  • 结构要求:严格遵循特定格式

解决方法:递进式压碎+对话历史管理

  1. 分块(Chunking)
  2. 并行摘要(Map):对每个部分进行摘要,提取关键数据和观点
  3. 聚合(Reduce):对各部分合并
  4. 最终生成:整合信息,符合格式要求

6.5.2 案例二: 多轮对话中的长期记忆

挑战:

  • 上下文膨胀
  • 遗忘

解决方案:关键信息提取+摘要总结

1.短期记忆:保留最近10轮对话的完整内容

2.长期记忆:

  • 实体提取:每天结束对话扫描当天对话提取用户信息
  • 摘要归纳

3.检索增强:再次对话时,检索相关实体

6.5.3 案例三: 代码库理解与问答

场景描述

开发者询问关于整个项目架构的问题,项目包含数百个文件

挑战:代码块理解

  • 代码量巨大:无法一次放入Context Window
  • 依赖关系复杂

解决方法:结构化压缩(骨架提取)

  1. 生成骨架:
    • 保留:Class 定义、函数签名、关键注释、Import 语句
    • 移除:函数体具体实现、内部逻辑。
  2. 构建全景图:将所有文件的骨架连接,形成项目的高层地图
  3. 按需展开:
    1. LLM先阅读这些骨架,定位到负责相关逻辑的文件
    2. 工具调用读取这些文件的完整内容

6.5.4 案例四: 长时程变成助手渐进式上下文管理

场景描述:长时程编程助手

以 Claude Code 这类长时程编程助手为例,系统需要支持用户进行数小时甚至数天的开发任务,包括代码编写、文件修改、工具调用等。同时要在有限的上下文窗口内维持任务连续性,并控制成本。

挑战:长时程编程助手

  • 任务跨度长
  • 信息多层次
  • 成本敏感
  • 无缝体验

解决方法:递进式上下文策略

层级技术成本TTL用途
1工具结果持久化最低永久冷存储,2KB 预览
2微压缩(Micro-compaction)极低60 分钟清理临时工具输出
3会话记忆(Session Notes)零 API 成本永久结构化摘要,预构建
4完全压缩(Full Compaction)低永久9 部分总结,删除思考

第 1 层:工具结果持久化(Disk + 2KB Preview,冻结状态缓存命中)

每次工具执行(如 grep、read file)的结果不仅回传给模型,也同时写入磁盘。当上下文接近限制时,工具结果从上下文中移除,仅保留 2KB 的摘要预览。若模型在后续步骤中需要再次引用该结果,系统检测缓存命中情况:

  • 缓存命中:直接返回之前的完整结果,无需重新执行
  • 缓存未命中:重新执行工具并缓存

第 2 层:微压缩(Micro-compaction,60 分钟 TTL)

与提示词缓存 TTL 对齐,每 60 分钟自动对旧的工具输出进行一次轻量级压缩。不调用 LLM,而是采用启发式规则:

  • 删除重复的执行结果
  • 保留首次和最后一次执行的完整结果
  • 中间结果聚合为一句话摘要

第 3 层:会话记忆(Session Memory,结构化笔记,零 API 成本)

在每个关键里程碑(如完成一个功能、解决一个问题),系统引导用户(或自动)生成结构化笔记 MEMORY.md:

  • 容量限制:最多 200 行或 25KB
  • 条目格式:每条约 150 字符,包含类别标签(项目模式、用户偏好、工具配置、领域知识)
  • 成本:零 API 调用,完全本地存储
  • 效果:新会话加载时,这份预构建的摘要能快速恢复关键上下文

第 4 层:完全压缩(Full Compaction,9 部分总结)

当会话记忆仍不足以处理新的查询时,触发完全压缩。调用 LLM 对整个会话进行总结,生成 9 个部分:

  1. 任务目标与原始需求
  2. 整体架构与设计决策
  3. 已完成的步骤
  4. 当前进度与活跃文件
  5. 遇到的问题与解决方案
  6. 用户偏好与特殊约束
  7. 待解决的问题
  8. 下一步计划
  9. 关键代码片段与路径
  • 处理:去除详细的思考过程、冗余工具输出,保留结构化信息
  • 存储:压缩摘要保存为 COMPACTION.md,成为新会话的起点
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
会话开始
  ↓
加载 MEMORY.md(如存在)
  ↓
执行任务,记录工具结果到磁盘
  ↓
上下文接近 75%?
  ├─ 否:继续执行
  └─ 是:
      ├─ 距离上次压缩 < 60 分钟?
      │   ├─ 是:执行第 2 层微压缩
      │   └─ 否:检查 MEMORY.md 是否需要更新
      └─ 上下文仍接近限制?
          ├─ 是:执行第 4 层完全压缩,生成 COMPACTION.md
          └─ 否:继续工作

本章小结:

概念定义
信息密度单位 Token 中包含的有效信息量
抽取式压缩从原文中选择关键内容
生成式压缩使用 LLM 生成精简表述
递进式摘要分层次逐步压缩长文档
滑动窗口只保留最近 N 轮对话的策略
上下文预算为不同组成部分分配 Token 配额

核心观点

  1. 压缩是必要的优化:
    • 控制 Token 用量和成本
    • 提高上下文利用效率
    • 改善响应延迟
  2. 压缩方法各有适用场景:
    • 抽取式:需要精确引用
    • 生成式:需要流畅表达
    • 信息提取:需要结构化数据
  3. 对话历史需要专门管理:
    • 结合滑动窗口和压缩
    • 保留关键信息点
    • 分层次管理不同时间距离的内容
  4. 上下文优化是系统工程:
    • 预算分配
    • 格式优化
    • 缓存复用
    • 持续监控

常见误区

  • 误区一:压缩就是截断 正解:压缩是提炼精华,需要保留关键信息
  • 误区二:压缩率越高越好 正解:需要在压缩率和信息完整性之间平衡
  • 误区三:一次压缩永久使用 正解:不同任务可能需要不同的压缩版本

实践建议

  1. 建立 Token 预算:明确各部分的 Token 分配
  2. 优化最大消耗点:通常是检索内容和对话历史
  3. 组合多种策略:摘要 + 提取 + 窗口管理
  4. 监控优化效果:对比压缩前后的任务效果和成本
  5. 保留原始信息:压缩结果可能需要回溯验证