滑动窗口是什么
滑动窗口是最简单可控的对话记忆策略:每次请求只带最近 N 轮原文,窗口外的内容丢弃,或先压成摘要再进入上下文。它的好处是计算量恒定、行为可预期、出问题时容易复现,因此常被当作其他记忆策略的基线与兜底方案。真正的工程难点不在裁剪逻辑本身,而在判断哪些内容绝对不能裁、裁剪前应该先把什么压缩掉。
基本信息
| 窗口定义 | 按轮数或按 token 双约束更稳,如最近 10 轮且不超过 4000 token |
|---|---|
| 常驻内容 | 系统提示、任务目标、工具定义不参与淘汰,永远保留在窗口头部 |
| 淘汰顺序 | 从最老的对话开始丢,先丢工具回包再丢用户原话 |
| 回包处理 | 工具返回的长结果先折叠成摘要或截断,再进入窗口 |
| 窗口外内容 | 丢弃,或压缩成一段进度摘要挂在系统提示后面 |
| 观测指标 | 裁剪触发次数、上下文 token 分布、被丢弃后的追问率 |
典型使用场景
- 客服与售后机器人:对话轮次多,但用户当轮问题往往只需要局部上下文
- 代码助手:工具回包动辄上万字符,必须先折叠再进窗口,否则两轮就超限
- 长会话产品的成本控制:把上下文 token 稳定在固定区间,账单才可预测
- 作为摘要记忆策略的对照基线,验证摘要方案是否真的带来收益
- 多步任务流程中保留最近几次决策,让模型记住刚确认过的选择
窗口裁剪示例
窗口裁剪示例
def build_messages(system, history, keep_turns=10, max_tokens=4000):
recent = history[-keep_turns:] # 先按轮数粗裁,保留最近的对话
recent = [shrink_tool_output(m) for m in recent] # 折叠工具长回包,最省空间
while count_tokens(system, recent) > max_tokens and recent:
recent.pop(0) # 从最老的一条开始丢,直到装得下
if dropped(history, recent): # 有内容被裁掉时补一段摘要
system = system + summary_of(history[: -len(recent)])
return [system] + recent # 系统提示始终在第一位,不参与淘汰裁剪对象与处理
| 对象 | 能否裁剪 | 处理方式 |
|---|---|---|
| 系统提示与任务目标 | 不可裁 | 固定在窗口头部永久保留 |
| 工具调用长回包 | 可裁 | 先折叠成摘要或截断,再进窗口 |
| 早期已确认的决议 | 可裁 | 压缩成一句话进度摘要挂在系统提示后 |
| 历史附件正文 | 可裁 | 只留引用编号与要点,正文移出上下文 |
| 用户原始需求 | 谨慎 | 先写进摘要再丢,防止目标漂移 |
按 token 裁剪比按轮数更贴合实际:一轮可能是五十字的追问,也可能是两万字的代码粘贴,只按轮数算会让上下文长度剧烈波动。
裁剪前先折叠工具回包,通常能多留好几轮真实对话:很多「上下文超长」并不是聊天太长,而是回包堆出来的。