# 上下文管理
## 一、为什么要进行上下文管理
在设计和开发大模型应用时，当模型可以获取到足够的上下文时，设计的重点不再是“获取”，而是“整理”。此时需要关注的是如何对上下文进行有效的管理，通过修剪，压缩，删除等方式，使保留下来的上下文既足够相关，又能被高效利用


有的开发者会停留在获取的阶段，因为模型的上下文窗口一直在持续的扩大，只要上下文窗口足够大，那么获取的信息就不用在整理了，直接将全部的上下文输入给大模型

但是这种方式一定会达到你想要的效果吗？

**实际上，更长的上下文并不会生成更好的响应，过度加载上下文会导致你的代理和应用以意识不到的方式失败。上下文可能会被污染，分散模型注意力，令模型困惑或者上下文内容是相互冲突的，这对于依赖上下文来收集信息、推理和协调行动的代理或应用来说是“致命的危险”**


接下来我们一起来看看上下文失控的各种情况

### 1.1、上下文污染
🌟 **定义：上下文污染是指幻觉或其他错误进入上下文，并被反复引用的情况**

当上下文被污染的时候，代理会发展出无意义的策略，并重复行为，以追求一个无法实现的目标


在**用户记忆的上下文**中，我们会借助用户输入的历史记录，对这些记录使用大模型进行整理，归纳总结出来用户记忆数据，并且将这些数据存储到记忆模块中，但是如果在整理的时候出现“误解”，那么这个“错误的用户记忆”就一直存储在用户记忆模块中，持续污染相关的上下文查询

> 例如用户只是一次情绪化的表达：“这节课烦死了，根本没用”
>
> 那么系统整理出来的记忆变为：“用户认为《课程 C》没有价值。
>

后续检索相关用户记忆的时候，这条记忆就可能一直伴随《课程 C》进行回答，就会在长期的交互中误导大模型进行回答

1. 有可能用户后面更换了态度，因为那次情绪表达不一定是因为课程的问题，也有可能因为在看这节课程之前，用户的心情已经很糟糕
2. 可能用户表达的”这节课“可能是这个知识点或者这章而已，不是整个课程体系


在**工具调用错误处理**中，我们会向 LLM 输入工具调用错误并且会触发重试机制，但是有些开发者没有用心设计工具调用的错误显示和输出，导致**LLM 接收到的工具调用错误信息，并不一定能准确反映工具出错的真正原因。**所以模型反复在“错误”中重试


📝：关于上下文污染的解决方案，其实更多的会从大模型应用的架构设计开始考虑，例如：

1. 在工具错误重试的机制中引入最大的重试次数，同时引入 LLM 反馈机制，当重试失败之后，模型可以将错误信息和原因反馈给用户，让用户输入新的内容来重新调整
2. 在记忆模块导致的“过时问题”，那在设计记忆模块的时候，应该有记忆更新和精简的机制

### 1.2、上下文干扰
**定义：上下文干扰是指当上下文变得过长时，模型过度关注上下文，忽视了在训练期间学到的内容。**

在代理工作流程中，随着上下文增长，模型会收集更多的信息并累积，累积的上下文可能会变得让模型过度关注上下文信息中的过去动作，而较少利用其训练得到的策略


这个对于在创造性的代理和应用中是非常危险，会让模型的输出结果“固化”并且是较低质量的结果固化


::: warning 我的理解关于场域共振：
+ 场域：在你和大模型之间建立一个联系，这个联系就是一个场域，场域是一个交流通道，一个允许你和大模型进行交流的空间
+ 共振：这个是结果，不是过程，能够触发这个结果的动作是“在这个场域中让大模型能理解我想做什么，并且它能够留出一定的发挥空间，它能够在这个空间中自由发挥”
:::


场域共振的结果并不满足理解，而是超越理解，例如：我说了一句话，你说了一句话，你这话很懂我，这个本质上还是理解，**场域共振最终是一种超越**

所以过多的上下文会干扰这个场域中的信号，留给大模型的空间会越来越小，最后的结果无论如何调整也只是“还行，不是太烂”


那如何有效的在提示词或者上下文中输入更多的信息给大模型呢？

**“极致压缩，并且在大模型内部发生连锁信息爆炸”**

> 压缩这个词在哲学领域最明显，哲学家提出一个单词的概念，往往代表极深的含义，需要用一章甚至一本书来诠释那个单词的包含的意义，这个在逻辑类的书籍中也是同样存在的，例如：磁场，生产力和生产关系，几何中的对顶角等
>

整个过程有点像小时候玩的“深水炸弹”，当把“深水炸弹”放入水中的时候，它在水中像“炸开”一样了，整杯水都变得有味道了

我虽然只是传递了几个单词而已在提示词中，几个概念而已，但是在大模型的内部，其借助训练时学到的内容，这些概念和词汇或许已经炸开成为好几本书了，无意中我传递了更多的信息给大模型


📝关于上下文的干扰的解决方案：

1. 上下文的修剪
2. 上下文的总结

这两个方案都是实际可行的

### 1.3、上下文混淆
**定义：上下文混淆是指模型使用上下文中多余的内容来生成低质量的响应**

当你把某些信息放入上下文中，模型就会关注它。这可能是不相关的信息或无用的工具定义，模型都会把这些考虑在内


在工具调用的 LLM 中，传入工具定义给 LLM 是必须的的，但是过多的无关工具和工具定义会导致模型变得混乱，最终选择出来的工具不是正确的

**所以在工具定义的上下文中，定义一定要清晰，有边界，同时当工具过多的时候，要适当使用 RAG 的方式去为用户输入检索相关的工具定义上下文给 LLM**


📝 关于上下文混淆的解决方案：

1. 使用 RAG 技术来检索相关的上下文
2. 增强工具模块的定义

### 1.4、上下文冲突
**定义：当你在上下文中加入新的信息或工具时，这些内容与上下文中已有的信息产生矛盾或冲突**

这些新的信息不是无用的，而是有效的，只不过和之前的信息发生了冲突


这个在多智能体架构的设计中是最可能出现的，因为多智能的上下文是彼此分开进行的，但是在使用的时候智能体之间会互相使用彼此输出的结果，A 智能体有可能会使用 B 智能体的结果，或者 A 智能体会使用 B 和 C 智能体的结果

例如：现在有一个查询检索的请求，A 智能体并行调用 B 和 C 智能体，B 和 C 智能体可能由于信息来源的问题导致得到的结果是冲突的，那么这个时候冲突的上下文组装输入给了 A 智能体，那么 A 智能体输出的结果就会变得不乐观


📝 关于上下文冲突的解决方案：

1. 可以使用上下文隔离的策略，将“大任务”拆分为不同方向的子任务，并且交给不同的 LLM
2. 在设计 代理的架构时，可以仔细选择“单智能体架构”还是“多智能体架构”根据应用场景选择

## 二、上下文管理的策略
在上一节中，我们介绍了上下文带来的问题，接下来我们来探讨使用哪些管理策略可以减少这些问题在大模型应用中产生

### 2.1、RAG
**检索增强生成（RAG）是指选择性的添加相关信息，以帮助 LLM 生成更好的回复**


关于 RAG 后续上下文工程里面会仔细讲解，在这里就不深入讨论了

每当上下文窗口上限增加的时候，就会诞生一场新的“RAG 已死”的争论，其实 RAG 也会改变，无论上下文的窗口上限如何增加，在大模型应用开发的过程中RAG 或许都会有”一席之地“

正如一句话：“如果你把你的上下文当作一个杂货抽屉，垃圾就会影响你的回复”

### 2.2、工具配置和定义
**在上下文的工具定义部分，应只加入与当前输入相关的工具定义，提供给 LLM 使用**


选择相关工具最简单的方法是将 RAG 应用在你的工具描述中，通过将工具描述存储到向量数据库中，RAG 能够根据输入来检索选择最相关的工具

> 在对 DeepSeek-v3 进行提示时，团队发现：当可用工具数量超过 30 个时，选择合适的工具就变得至关重要。超过 30 个之后，工具的描述开始互相重叠，导致混淆。而当工具数量超过 100 个时，模型几乎必然无法通过测试。利用 RAG 技术将工具数量筛选到 30 个以内，可以显著缩短提示长度，并将工具选择的准确率提升多达 3 倍
>


但是如果你的工具定义不多，并且边界清晰，那其实没有必要使用 RAG 来检索工具，完整的将你的工具定义提供给 LLM 效果会更好


### 2.3、上下文隔离
**上下文隔离（Context Quarantine）指的是将上下文隔离到各自专用的线程中，每个线程由一个或多个 LLM 独立使用**


当我们的上下文太长并且都是有效的时候，无法进行压缩裁剪，这个时候我们可以使用将任务分解成为更小、更隔离的工作给 LLM，每一个 LLM 都有自己的上下文

在 Anthropic 的博客文章中有详细的介绍

> 搜索的本质是压缩：从庞大的语料库中提炼见解。子代理通过并行操作自己的上下文窗口来促进压缩，在将最重要的标记压缩给主要研究代理之前，同时探索问题的不同方面。每个子代理还提供了关注点的分离——不同的工具、提示和探索轨迹，这减少了路径依赖性，并能够进行彻底的独立调查。
>

研究适合这种设计模式。当给定一个问题时，可以识别出几个子问题或探索领域，并使用多个代理分别提示。这不仅可以加快信息收集和提炼的速度（如果有计算资源的话），而且可以防止每个上下文积累过多信息或不相关的信息，从而提供更高质量的结果：

> 我们的内部评估显示，多智能体研究系统在涉及同时追求多个独立方向的前序查询中表现尤为出色。我们发现，以 Claude Opus 4 为主智能体、Claude Sonnet 4 为子智能体的多智能体系统，在我们的内部研究评估中比单智能体 Claude Opus 4 表现高出 90.2%。例如，在要求识别信息技术 S&P 500 公司所有董事会成员的任务中，多智能体系统通过将任务分解给子智能体来找到正确答案，而单智能体系统则因缓慢的顺序搜索而未能找到答案。
>


这种方法也有助于工具配置，当使用多智能体的架构设计时，可以为每一个智能体配置专属的工具定义

### 2.3、上下文修剪
**上下文修剪是从上下文中移除无关或不需要的信息的行为**


代理在触发工具和组装积累上下文的时候，在正式将上下文输入给 LLM 之前，进行上下文的内容评估并且移除冗余部分时值得的，因为这样会让上下文更加精简的同时发挥模型更大的推理能力


有一些实际开发策略时有效的：**Token 压缩策略**，根据不同的模型来决定压缩上下文不同的部分，并且会有一个压缩的策略，先压缩中间，如果还是不够，在压缩上或者下，主要目的还是根据不同场景保留最相关的上下文信息给 LLM，详细的 Token 压缩策略会在下文解释

### 2.3、上下文总结
**上下文总结是将累积的上下文浓缩成简要摘要的行为**


当上下文尤其是会话的历史记录超过最大的上下文长度时，这个时候可以针对历史记录生成一个简短的摘要，最好在这个简单的摘要中能够保留核心的信息

## 三、上下文管理的实现方案
### 3.1、上下文管理容器设计
![上下文管理容器设计](./image/image%20(9).png)

一个开源项目中的上下文管理容器的实现和设计思路：

1. 消息添加的处理流程：
    1. 添加用户｜助手｜工具的消息
    2. 调用增加消息的通用方法
    3. 对消息进行格式的验证
    4. 将消息存储到多后端设计的数据库中
    5. 更新消息上下文 Token 的总数
2. LLM 获取上下文的流程：
    1. 调用系统提示词管理容器获取**系统提示词**
    2. 调用多后端容器从数据库中获取**会话历史的消息**
    3. 消息的 Token 判断是否压缩
    4. 消息格式化为 LLM
    5. 传入 LLM 中执行
3. Token 压缩流程：
    1. 判断是否有必要需要压缩
    2. 执行压缩
    3. **保留每一次的压缩历史存储到数据库中**
4. 消息格式化的流程：**统一将内部的消息格式转换为 LLM 提供商需要的 API 消息格式**
    1. 获取 LLM 调用的提供商
    2. 调用特定 LLM 提供商的格式化的方法

### 3.2、上下文压缩步骤
在给Agent实现上下文压缩的时候，最关键的两个设计核心是：
1. **上下文压缩调度**：我们要压缩哪些东西，如何触发压缩流程
2. **上下文压缩指令**：我们如何压缩，压缩的时候保留什么关键信息

这两个核心在压缩节点中，是一前一后的关系，前表示的就是我们对于输入给大模型的上下文进行判断哪些需要压缩，后表示的是大模型如何压缩这些上下文

在设计上下文压缩调度的时候，一些重点设计是：
- 读取工具在读取大文件的内容的时候，要进行截断处理
- Agent所有的工具返回要进行限制，一般情况是限制工具返回的内容最多为2000字符，超过的部分就要进行压缩（可以使用快速低成本的模型），同时将完整工具返回内容写入某一个文件，将路径返回给大模型就可以
- 对于注入给大模型的上下文中，会话历史记录的上下文是可以压缩的，用户记忆，系统提示层是不能压缩的，Skill加载的上下文也不能压缩
- 触发上下文压缩的条件一般是：当上下文超过模型最大上下文的百分之八十的时候就要进行压缩处理

相关文章链接：[上下文压缩调度：工具裁剪与历史记录压缩](./上下文压缩调度：工具裁剪与历史记录压缩.md)

在设计上下文压缩指令的时候，一些重点设计是：
压缩指令的提示词设计，要留意不同的模型可能压缩指令是不同的，为了保证压缩结果高质量，要注意区分模型和指令的设计

相关文章链接：[上下文压缩指令：ClaudeCode与Gemini的压缩提示词解析](./上下文压缩指令：ClaudeCode与Gemini的压缩提示词解析.md)