LLM上下文的有效管理

使用说明:文中涉及的窗口占比阈值、预留比例、top_k、单次输出token数等,均为工程经验起点,不是跨模型、跨业务通用的标准答案。上线前应使用实际模型的tokenizer、真实对话分布和成本约束进行校准。

一、先分类:上下文超限的三大场景

大语言模型绝大多数上下文溢出问题,都可以先归入以下三类,再选择对应方案:

  1. 输入侧太长:历史对话轮次越来越多,旧对话持续占用token。典型场景:长对话聊天、多轮Agent。

  2. 输出侧太长:模型需要生成的答案体量很大,单次输出放不下。典型场景:长报告、代码库分析、万字文档改写。

  3. 长期记忆需求:跨会话、跨天、跨任务,需要记住远超单次窗口容量的信息。典型场景:个人助手、知识库、项目档案。

落地建议:上线前先建立基础监控,实时统计当前prompt总token,并为窗口上限预留安全余量。建议以约10%–15%作为初始安全余量起点,用于吸收不同分词器、消息格式和估算方式带来的偏差;对于极长prompt或对token极敏感的场景,应进一步校准。

二、场景1:输入历史过多,旧对话挤占上下文

方案1:固定窗口截断(滑动窗口)【最简单,优先基线方案】

原理:始终保留系统提示词(System Prompt)和最近N轮对话,更早的对话直接丢弃。例如窗口为8k时,可先设置保留最近6轮问答,更早对话删除。

适用:客服、简单聊天机器人、不需要回溯很早历史的业务。

优点:实现最简单、无额外LLM调用、开销低、延迟稳定。

缺点:早期重要信息会直接丢失;用户回头询问很早之前的内容时,模型容易“失忆”。

伪代码逻辑:

safe_margin = 0.15
max_token = model_context_window * (1 - safe_margin)
history = []
while count_token(system_prompt + history) > max_token:
    history.pop(0)  # 从最古老的一轮开始删除

实操注意:不要粗暴按“轮数”一刀切;优先按token数量动态截断,而不是固定保留N轮,避免单轮对话超长直接打满窗口。预留比例和保留轮数都应结合真实tokenizer校准。

方案2:滚动摘要Compaction(Rolling Summarization)【常用长对话方案】

原理:当历史接近超限时,把较古老的多轮对话合并成一段摘要放入上下文。上下文结构变为:

System Prompt + 【历史摘要】 + 最近若干轮原始对话 + 用户当前提问

适用:需要记住早期关键结论,但不需要逐字原文的场景,如项目讨论助手、代码评审多轮对话、顾问对话。

优点:保留关键信息,相比纯截断大幅缓解失忆问题;通常比全量RAG成本更低。

缺点:摘要可能产生幻觉、丢细节、扭曲信息;摘要本身也会持续变长,累积到一定程度仍需二次压缩。

可操作流程:

  1. 每次对话结束计算总token;例如可将“占用窗口约70%”作为触发摘要的起点,具体阈值需按业务校准。

  2. 单独调用一次LLM做摘要,可用更小更快的模型节省成本。摘要Prompt模板可参考:

下面是一段多轮对话,请精简摘要,只保留关键结论、用户明确要求、重要约束、数据,不要冗余描述;
禁止编造原文不存在的信息;摘要尽量简短。

对话内容:
{old_conversation}

输出格式:
【历史摘要】:xxx
  1. 替换:删除被摘要掉的原始对话,把生成的摘要插入对话头部。

  2. 兜底:为摘要设置最大长度;摘要过长时,对摘要再做一轮浓缩。

生产注意事项:摘要模型和主模型最好做校验;关键业务不要只依赖摘要。重要数字、需求最好单独抽取为键值对或结构化字段存储。

方案3:对话历史存入向量库,按需RAG检索【适合随时回溯久远细节】

原理:不把全部历史塞进上下文。每轮对话结束后,将本轮问答切片、向量化并存入向量数据库;用户新提问时,用query检索相似历史片段,只把少量相关片段放入prompt。

适用:长期项目对话、知识库问答、需要回溯数小时或数天前对话细节的场景。

优点:理论上支持海量历史;不占用常驻上下文。

缺点:检索不准会导致信息缺失;召回无关内容会造成上下文污染;增加向量库维护和嵌入模型调用成本。

可操作配置:

  1. 分片策略:以单轮问答为最小单元,如{user: xxx, assistant: xxx}作为一条chunk;不要将几十轮揉成一大块。若单轮本身很长,可再按语义边界切分。

  2. 召回参数:top_k=3~5可作为常见起点,同时设置相似度阈值,低于阈值直接丢弃,防止垃圾召回。窗口越紧,越应减小k并提高阈值。

  3. Prompt拼接模板:

{system_prompt}

【相关历史信息(来自检索)】
{retrieved_history_chunks}

【最近对话】
{recent_raw_chat}

用户当前问题:{user_query}

选型提醒:简单业务可用轻量向量库(Chroma、FAISS本地);线上高并发可用Milvus、Qdrant。

方案4:StreamingLLM / Attention Sink KV-Cache驱逐优化【推理层底层优化】

原理:不修改发给模型的文本,而是在推理侧管理KV缓存:固定保留最开头少量token(attention sink锚点)和最新生成token,中间的KV缓存直接丢弃,从而实现超长流式持续输出。

适用:一次性超长流式任务,例如一次性读取几十页文档、超长转录文本摘要。

优点:上层业务几乎不用改对话逻辑;不需要额外调用LLM做摘要。

缺点:它不会让模型记住中间全部内容,中间内容的认知会衰减;属于推理优化,不会扩大模型原生上下文窗口能力;需要模型推理后端支持(如vLLM、Transformers配套实现),并非所有API服务商开放。

落地边界:适合离线批量超长文本处理;不适合多轮对话长期记忆,用户回头询问中间细节容易答错。商用API绝大多数不开放该能力,一般需自建推理集群。

三、场景2:输出太长,单次生成放不下

很多人只关注输入超限,却忽略输出token也占额度:任务本身需要生成超长内容时,单次可能出不完。

方案1:分段续写(Prompt Chaining)

原理:控制单次生成长度,模型生成到上限停止;程序拿到上一轮结尾文本,构造续写prompt,接力继续生成,直到完成。

注意:continue_final_message: true不是OpenAI标准参数,属于部分国内大模型厂商或推理框架的扩展字段,不可跨厂商通用,不要强依赖。

通用续写模板:

你上一轮输出在这里中断了,请接着上文继续完成,不要重复前面已经输出过的内容,保持文风、格式、逻辑一致。

上文:{{previous_generated_text}}

继续:

落地控制:设置单次最大输出token,例如限制单次输出1500 token作为示例起点;循环续写;最后程序拼接所有片段。具体数值应按模型输出上限和任务类型调整。

风险:分段处逻辑断裂、编号错乱、上下文漂移;长续写时,越往后模型越容易遗忘最开始的要求。

方案2:规划-执行框架(Plan & Solve / ReAct,先拆分任务)

原理:不让模型一次性输出全文。第一步让模型输出任务大纲和子任务清单;第二步逐个执行子任务,每个子任务单独调用模型并返回结果;最后汇总。

适用:万字报告、文档重构、代码库分析、复杂方案撰写。

规划阶段Prompt模板:

你需要完成任务:{{task}}

受上下文长度限制,不能一次性完成。请先输出结构化大纲,拆分成独立子任务,子任务之间尽量解耦;每个子任务描述清晰,不要输出正文内容。

输出格式:
1. 子任务1:xxx
2. 子任务2:xxx

然后循环遍历每个子任务,单独调用LLM并收集结果;最后汇总整合。进阶做法:汇总阶段再加一轮LLM润色合并,统一格式。

方案3:流式输出 + 边生成边清理非必要KV缓存

配合vLLM等推理引擎的流式能力,生成后的中间KV缓存及时释放,控制显存占用。这更多是推理侧显存优化;业务层解决长输出问题,优先使用上面的任务拆分与分段续写方案。

四、场景3:长期外部记忆(跨会话记忆,MemGPT/Letta思路)

原理:把模型有限上下文看作高速工作内存,把向量库、数据库看作持久化硬盘。模型拥有读写记忆的工具函数:发现重要信息时主动调用工具写入长期记忆;需要时主动检索读取。这是MemGPT、Letta的核心思路。

适用:个性化Agent、私人助理、长期项目助手。

缺点:复杂度很高,属于Agent体系,不是简单对话应用;记忆读写本身也会出错,可能存储失败或检索不到;不存在真正“无限上下文”,只是把信息移出窗口。

极简可落地的最小记忆设计(不必上完整MemGPT)

  1. 定义工具save_memory(content, tag):存入向量库并打标签。

  2. 定义工具search_memory(query):检索历史记忆。

  3. 在system prompt中告诉模型:遇到用户重要偏好、长期需求、关键数据,主动调用save_memory;需要历史信息时调用search_memory。

  4. 记忆单独做老化策略:低频记忆可归档,避免记忆库无限膨胀。

五、推荐工业组合流水线(可直接作为架构参考)

推荐组合:

System Prompt
+ 【可选:历史摘要】
+ 最近N轮原始对话
+ RAG检索召回的相关历史片段
+ Token预算监控
+ 兜底截断

执行顺序:

  1. 每次提问进来,先组装候选prompt并估算token。

  2. 未到约70%阈值:直接放行。

  3. 约70%–85%:优先尝试摘要压缩古老对话。

  4. 约85%:摘要已压不动,启动强制截断,优先删除最早内容。

  5. 如果是长期记忆场景,叠加RAG检索历史。

  6. 如果任务输出体量巨大,走规划拆分+分段续写。

  7. 自建推理集群的超长文本批量场景,可叠加StreamingLLM优化KV缓存。

上述70%/85%为策略框架示例,实际阈值应结合摘要质量、调用成本和业务对“失忆”的容忍度校准。

六、必须重视的坑清单

  1. Lost-in-the-Middle中间遗忘问题:即使文本塞得进窗口,模型对上下文中部信息的识别能力也可能显著下降;重要信息尽量放在头部或末尾,不要埋在一大段中间。

  2. Token计算器有误差:不同库、服务商的分词器不完全一致,预留安全余量必不可少。

  3. RAG召回污染:无关片段进入prompt会误导模型;一定要加相似度过滤。

  4. 摘要幻觉风险:涉及生产业务、财务、需求文档时,摘要不能作为唯一可信来源;关键信息建议结构化存储(如JSON键值对)。

  5. 不要混用方案导致复杂度爆炸:普通聊天优先滑动窗口;顾问多轮对话优先滚动摘要;长期知识库才上RAG。

  6. 不同模型窗口规则差异:部分模型对System Prompt权重特殊,或对超长单块文本容忍度差。

七、选型速查表

方案开发成本额外开销适合场景核心短板
滑动窗口截断低无简单客服、闲聊久远历史直接丢失
滚动摘要Compaction中额外LLM调用中等长度多轮对话摘要幻觉、细节丢失
对话RAG检索中高向量库+嵌入模型需要回溯历史细节召回不准、污染prompt
StreamingLLM KV驱逐高(自建推理)显存优化一次性超长流式文档中间信息认知衰减,API很难用上
Plan任务拆分续写中多轮LLM调用超长输出(报告、文档)分段衔接容易断裂
MemGPT式长期记忆高工具+向量库个性化长期助手体系复杂,记忆读写不可靠
Logo

更多推荐