4、上下文窗口管理:滑动窗口、摘要压缩、关键信息提取

上下文窗口,说白了就是大模型的「短期记忆」。

我刚开始做多轮对话时,总觉得模型记不住东西是它笨。后来才发现,是我没教它怎么「遗忘」。每个模型都有固定的上下文长度——GPT-4 是 128K,Claude 是 200K,但你以为真能塞满吗?我试过,塞到 80% 就开始胡言乱语了。

所以,管理上下文窗口,本质上是在做三件事:该留的留下,该扔的扔掉,该压缩的压缩

4.1 滑动窗口:最朴素的「断舍离」

滑动窗口的思路很简单:只保留最近 N 轮对话。

比如你设定了窗口大小为 10 轮。当对话进行到第 11 轮时,第 1 轮就被踢出去了。像排队买奶茶,前面的人走了,后面的人补上。

核心公式:

窗口大小 = min(模型最大上下文, 你设定的轮数阈值)
保留内容 = 对话列表[-窗口大小:]  # Python 切片思想

我在项目中遇到过一个问题:用户和客服聊了 50 轮,前 10 轮在确认订单号,中间 20 轮在讨论退款,最后 20 轮在催进度。如果用滑动窗口只保留最后 10 轮,模型就不知道订单号是多少了。

嗯,这里要注意——滑动窗口适合「短平快」的场景,比如即时客服、简单问答。不适合需要长期记忆的任务。

我的经验:窗口大小设成 8-12 轮最稳。太小了模型失忆,太大了浪费 token。你可以先设 10,然后看模型回答质量,再微调。

4.2 摘要压缩:把历史「榨成汁」

滑动窗口太粗暴了?那就试试摘要压缩。

每次对话到达窗口上限时,不是直接扔掉旧内容,而是让模型自己总结一下:「刚才聊了啥?」然后把总结塞回上下文里。

举个例子,你正在做一个项目管理的 AI 助手:

用户:帮我创建一个项目「双十一大促」
AI:好的,项目已创建,截止日期是?
用户:12月12日
AI:已设置。预算多少?
用户:50万
...(中间聊了20轮细节)
用户:现在帮我看看项目进度

# 此时上下文已经快满了
# 触发摘要压缩:
压缩后的记忆 = "项目名称:双十一大促,截止日期:12月12日,预算:50万,已完成:供应商确认、物料采购,待办:物流方案确认"

这样,模型虽然忘了中间那些「你一句我一句」的细节,但关键信息全在。

我曾经踩过的坑:摘要压缩不能每轮都做。那样 token 消耗反而更大。我建议每 5-8 轮做一次,或者当上下文使用率达到 70% 时触发。

为什么是 70%?你想想看,模型在接近满上下文时,推理速度会变慢,准确率也会下降。留出 30% 的余量,给新对话腾空间。

4.3 关键信息提取:只记住「重点」

这个方法更精细。不是压缩全部历史,而是只提取「对后续对话有用的信息」。

比如用户说:

  • 「我叫张三」→ 提取:用户姓名=张三
  • 「我住在北京朝阳区」→ 提取:用户地址=北京朝阳区
  • 「我的订单号是 20241001」→ 提取:订单号=20241001
  • 「今天天气不错」→ 忽略,不提取

说白了,就是让模型学会「什么该记,什么该忘」。

我习惯用 JSON 结构来存这些关键信息:

{
  "user_info": {
    "name": "张三",
    "address": "北京朝阳区"
  },
  "order_info": {
    "order_id": "20241001",
    "status": "待发货"
  },
  "current_intent": "查询订单进度"
}

每次新对话进来,先更新这个 JSON,然后把它放在系统提示词里。模型一看就知道:「哦,用户叫张三,在查订单,地址是北京。」

关键信息提取 vs 摘要压缩:

维度 摘要压缩 关键信息提取
信息密度 中等
实现难度 低(直接让模型总结) 高(需要定义提取规则)
适用场景 长对话、故事生成 客服、信息查询、表单填写
丢失风险 可能丢失细节 可能漏掉非结构化信息

4.4 三种策略怎么选?

别纠结,我直接给你一个决策树:

  1. 对话轮数 < 20 轮 → 直接用原始上下文,啥都不用管
  2. 对话轮数 20-50 轮 → 滑动窗口 + 关键信息提取(窗口设 15 轮,关键信息单独存)
  3. 对话轮数 > 50 轮 → 摘要压缩 + 关键信息提取(每 10 轮做一次摘要,关键信息持续更新)
  4. 超长对话(100+ 轮) → 分层管理:近 10 轮用原始上下文,10-50 轮用摘要,50 轮之前只保留关键信息

一个小技巧:我习惯在系统提示词里加一句:「如果用户提到新的个人信息或关键指令,请更新记忆库。」这样模型会自动帮你做关键信息提取,省得你写一堆解析逻辑。

4.5 实战中的「坑」与「解」

坑一:模型自己总结的摘要会「幻觉」

我曾经让模型总结一段对话,它把「用户说想退款」总结成了「用户已退款」。结果后面所有回答都基于「已退款」这个错误前提。嗯,后来我加了验证机制——每次摘要生成后,让模型再确认一遍:「以上摘要是否准确?如有不确定,请标注。」

坑二:关键信息提取会「漏」

用户说「我住在北京,但不是朝阳区,是海淀区」。如果提取逻辑只认「地址=北京朝阳区」,那就完蛋了。我现在的做法是:每次提取时,把旧信息和新的信息一起给模型,让它做「合并」而不是「覆盖」。

坑三:滑动窗口切断了「因果链」

用户在第 3 轮说「帮我查一下 A 订单」,第 30 轮说「那个订单怎么样了」。如果窗口只保留最后 10 轮,模型根本不知道「那个订单」是哪个。解决方案?把「当前活跃对象」作为关键信息单独提取出来,不依赖窗口。

4.6 总结一下

上下文窗口管理,说白了就是三个字:记、忘、省

  • :关键信息要牢牢记住,用结构化方式存
  • :无关细节果断扔掉,用滑动窗口或摘要
  • :能压缩就压缩,别让 token 浪费在废话上

我个人最常用的组合是:滑动窗口(15 轮)+ 关键信息提取(JSON 结构)。简单、稳定、好调试。等业务量上来了,再考虑加摘要压缩。

记住,没有银弹。每种方法都有 trade-off。你得多试,找到最适合你场景的那一套。

一句话送给你:上下文窗口管理,不是让模型记住一切,而是让模型在「该记住的时候记住,该忘记的时候忘记」。