当对话历史或输入内容太长,超出了 LLM 的上下文窗口(Context Window)时,业界有一套非常成熟的工程组合拳来解决。
针对“上下文不够”这个痛点,我们需要分两个场景来看:一是“输入(历史对话)太多”,二是“输出(生成回答)太多”。
场景一:输入/历史对话太多(怎么塞进去?)
这就好比办公桌(窗口)已经堆满了文件,你需要处理新文件,可以采取以下策略
1. 滑动窗口与截断(Sliding Window & Truncation)
最直接粗暴但也最常用的方法。
- 做法:永远保留系统提示词(System Prompt) + 最近的 N 轮对话,直接丢弃最早的对话。
- 适用场景:日常闲聊、轻量级客服。用户通常只关注眼前的问题,不需要记住几十轮前的细节。
2. 滚动摘要(Rolling Summarization)
让模型自己当“秘书”,把废话精简。
- 做法:当历史对话快到窗口上限时,调用一次 LLM,把最早的 10 轮对话总结成 100 个字,然后把“摘要 + 最近对话”放进新的窗口里。
- 适用场景:超长对话助手(如陪伴类 AI、长线任务处理)。
- 缺点:摘要会丢失细节,且每次总结都需要额外算力。
3. RAG(检索增强生成)—— 外挂大脑
RAG。在长对话中,你可以把历史记录存进向量数据库。
- 做法:每次用户提问时,用当前问题去数据库里搜索“最相关的历史片段”,只把这几段拉出来塞进上下文,而不是把几百轮对话全塞进去。
- 适用场景:知识库问答、超长项目讨论。
4. 底层黑科技:KV Cache 驱逐(StreamingLLM)
这是针对开发者的底层优化。研究发现,模型对开头(系统提示词)和结尾(最新对话)的注意力最高。因此,可以直接在显存里把中间那些不重要 Token 的 KV Cache 清理掉,只保留“开头的 Attention Sink”和“最近的 Token”,从而让模型无限流式处理超长文本。
场景二:输出/回答太长(怎么生成完?)
当你让模型“写一部 5 万字的小说”或“总结一份极长的财报”时,模型的单次输出限制(通常是 4K 或 8K token)根本不够用。
1.分段生成与自动续写(Prompt Chaining)
- 做法:让模型只写第一章,写完后,在后台用一个脚本自动把第一章的结尾作为新 Prompt,发给模型说“请接着上一段,继续写第二章”。
- 关键点:很多 API 支持传入 continue_final_message: true,让模型直接顺着尾巴写,而不是重新开头。
2. 规划-执行框架(如 ReAct / Plan-and-Solve
- 做法:先让模型生成一个“目录”或“大纲”,然后拆解成多个小任务,再逐一让模型生成每个小节的回答。
- 适用场景:长代码生成、深度研报撰写。
3. 滚动输出并清理缓存
在流式输出(Streaming)时,边生成边把已经输出完毕且不再需要参考的早期 Token 从上下文里踢出去,保持窗口不爆。
场景三:进阶方案 —— 外部内存(Memory)
如果以上都不够,现在业界最前沿的方向是给 LLM 配备“外部内存”(如 MemGPT、Letta)。
这就像给模型买了一个无限大的硬盘。模型可以主动调用工具:
- “把这段对话存到长期记忆库”。
- “从长期记忆库里检索我上周告诉你的偏好”。模型自己管理自己的上下文窗口,实现了“无限上下文”的幻觉。
总结:工程上的最佳实践
在开发应用时,千万不要无脑把历史记录全发给大模型(既贵又慢还容易忘)。一个标准的长上下文处理流水线应该是:
- 永远保留:System Prompt(定义人设和规则)。
- 动态保留:最近 5-10 轮对话(保持连贯)。
- 滚动摘要:较早的对话压缩成大纲。
- 按需检索(RAG):遇到新问题,用向量搜索拉取历史相关的补充信息。
- 截断:如果还是超了,毫不留情地砍掉最久远的摘要。
一句话记住:内存(上下文窗口)不够,就用硬盘(向量数据库)来凑;硬盘太慢,就写摘要(压缩)。
浙公网安备 33010602011771号