大模型上下文如何科学管理:不要把聊天记录当数据库
很多人刚开始做大模型应用时,会把上下文理解成“聊天记录”。用户说过的话越多,全部拼接给模型,似乎模型就越了解用户。
但实际情况往往相反。历史消息越长,越容易混入过期信息、无关闲聊和互相冲突的指令。模型还可能因为上下文太长而超出限制,或者忽略真正重要的内容。
科学的上下文管理不是尽可能多地塞内容,而是:
每次只给模型完成当前任务所必需、可靠、最新的信息。
把上下文想成模型的工作台
可以把模型想象成一个临时办公的助手。它每次接到任务时,面前只有一张有限大小的工作台。
工作台上可以放任务说明、相关文件、当前状态和工具结果,但空间不是无限的。如果把过去几年所有聊天记录、全部业务文档和每一次工具返回结果都堆上去,助手不一定更聪明,反而可能找不到刚刚要处理的那张订单。
上下文窗口就是这张工作台的容量。一次请求中可能放入:
系统规则
+ 用户问题
+ 历史对话
+ 检索到的资料
+ 工具定义和工具结果
+ 本次模型输出
这些内容加起来不能超过模型的上下文窗口。而且,即使没有超过长度限制,内容太多也会降低模型找到重点的概率。
第一步:先给信息分类
不同信息应该由不同的系统负责保存,不要把所有东西都混在聊天记录里。
| 信息类型 | 适合放在哪里 | 例子 |
|---|---|---|
| 固定规则 | System Prompt 或配置 | 输出格式、安全规则、工具白名单 |
| 当前任务状态 | 数据库或状态表 | 当前步骤、订单号、任务进度 |
| 业务知识 | 知识库和检索系统 | 产品说明、制度、帮助文档 |
| 用户偏好 | 长期记忆 | 常用语言、展示习惯 |
| 最近对话 | 短期上下文 | 当前几轮问题和回答 |
| 工具结果 | 任务状态或结构化记录 | 查询结果、错误码、审批状态 |
例如用户说:“继续处理刚才那个订单。”这句话本身是不完整的。系统不能只依赖聊天记录猜测“刚才那个”是什么,而应该从任务状态中取出已经确认的订单号和当前步骤。
第二步:给信息设置优先级
上下文空间不够时,不能随机截断内容,而应该按优先级处理。
一个常见的优先级顺序是:
- 当前用户的最新指令;
- 安全、权限和业务硬规则;
- 当前任务状态和已确认事实;
- 与当前问题相关的检索结果;
- 最近几轮对话;
- 更早的历史闲聊。
例如,用户刚刚明确说“只查询,不要退款”,这条限制应该比几分钟前“帮我处理这个订单”的模糊表达优先。再比如,数据库确认的订单状态应该比模型自己总结的旧状态优先。
上下文超限时,优先压缩或删除低优先级内容,同时保留任务目标、硬性限制和最新事实。
第三步:保留结构化状态,不要只保留聊天记录
聊天记录适合帮助模型理解语气和意图,但不适合作为业务系统唯一的状态来源。
不要只保存这样一句话:
用户:继续处理刚才那个订单。
更可靠的做法是保存结构化任务状态:
{
"task": "查询订单",
"order_id": "12345",
"user_id": "u001",
"step": "等待返回订单明细",
"confirmed_facts": [
"订单存在",
"用户有查看权限"
]
}
结构化状态让程序知道当前任务走到哪一步,也让模型在下一次请求中获得一份清晰、短小、可验证的上下文。
这里要明确模型和系统的分工:
- 模型负责理解用户表达和提出下一步建议;
- 状态系统负责保存任务进度和真实事实;
- 数据库和权限系统负责确认数据存在以及用户能做什么;
- 上下文构建器负责把当前需要的信息整理给模型。
不要让模型自己成为唯一的状态管理器。
第四步:压缩时保留结论,不要简单截断
对话变长以后,直接删除最早的消息很容易丢掉任务目标。更好的办法是把旧对话压缩成结构化摘要。
例如,订单任务可以压缩成:
已确认:
- 用户要查询订单 12345
- 用户有查看权限
已完成:
- 已确认订单存在
待完成:
- 返回商品明细
限制:
- 不允许执行退款操作
这种摘要比几十轮原始对话更容易被模型使用,因为它保留的是当前任务真正需要的结论、进度和限制。
但摘要也不能直接当作最终事实。订单是否存在、权限是否有效,仍然应该由数据库和权限系统确认。模型摘要是上下文材料,不是事实来源。
第五步:检索只放相关内容
知识库不是上下文的仓库。用户问“如何申请退款”,不应该把整个公司制度库都塞给模型,而应该先检索与退款条件、流程和当前用户角色相关的内容。
一个基本的检索流程是:
理解用户问题
-> 生成检索条件
-> 召回相关文档
-> 按相关性和权限过滤
-> 必要时重新排序
-> 只把相关片段放入上下文
检索结果还要带上来源、更新时间和权限信息。过期文档、无权限文档和与问题无关的文档,都不应该因为“可能有用”而直接传给模型。
还要注意,外部文档和网页中的文字可能包含恶意指令。检索到的内容应该被当作参考资料,而不是可以覆盖系统规则的命令。
一个实用的上下文模板
每次请求都可以按照类似的结构组装上下文:
【任务目标】
用户现在要完成什么事情
【硬性约束】
权限、安全、输出格式和不可执行的动作
【当前状态】
任务 ID、当前步骤、待完成事项
【已确认事实】
经过数据库或业务服务确认的信息
【相关知识】
与当前问题有关的检索片段和来源
【最近交互】
最近几轮仍然有价值的对话
【工具结果】
后续步骤真正需要的字段和错误信息
【输出要求】
希望模型返回的格式和表达方式
这个模板的价值不在于格式固定,而在于把不同来源的信息分开,让模型知道哪些是规则、哪些是状态、哪些只是参考资料。
还要对上下文做监控和测试
上下文管理不是写完 Prompt 就结束了。生产系统至少应该记录:
- 输入 Token 数和输出 Token 数;
- 哪些内容被压缩或截断;
- 检索了哪些文档以及它们的版本;
- 工具返回了哪些字段;
- 模型是否引用了过期信息;
- 上下文变长后,任务成功率和延迟如何变化;
- 压缩前后,模型的关键判断是否一致。
还要专门测试三种情况:
- 很长的连续对话;
- 旧信息和新信息发生冲突;
- 检索内容中包含恶意指令或与系统规则相反的内容。
这些测试可以帮助我们判断问题到底来自模型能力、上下文拼装、检索质量,还是状态管理错误。
最后的判断
上下文管理的目标不是让模型记住一切,而是让它在当前任务中看到正确的信息。
可以把整个原则浓缩成三句话:
- 聊天记录不是数据库;
- 模型摘要不是事实来源;
- 真实状态、权限和业务数据必须由代码和存储系统管理。
大模型只是在一次请求中使用上下文进行判断。真正可靠的 Agent,需要在模型之外建立状态、检索、权限、压缩和监控机制。只有这样,上下文越长,系统才不会越混乱。
