模型上下文窗口管理——长对话场景下的 Token 压缩与摘要策略一、问题的提出上下文窗口的硬约束大语言模型在处理长对话时面临一个根本性的物理限制——上下文窗口Context Window。无论是 GPT-4 的 128K、Claude 的 200K还是国产模型 Qwen 的 1M token 窗口当对话轮次积累到一定规模后输入 token 数量总会逼近甚至超出窗口上限。超出部分会被直接截断导致模型丢失早期对话中的关键信息回答质量断崖式下降。在实际生产环境中这个问题尤为突出。以智能客服系统为例用户可能在同一会话中连续追问数十轮涉及订单查询、退款流程、物流追踪等多个子任务。如果不做任何处理系统很容易在第 20 轮左右就超出窗口限制用户不得不重新描述问题体验极差。从成本角度看长上下文也意味着更高的 API 调用费用。按 token 计费的模型每次请求携带数万 token 的历史上下文其中大量是冗余或低价值信息这直接推高了运营成本。因此上下文窗口管理不仅是技术问题也是经济问题。二、核心策略压缩与摘要的双轮驱动解决长上下文问题的主流方案可以归纳为两个方向Token 压缩和对话摘要。两者并非互斥而是互补关系在实践中通常组合使用。Token 压缩的目标是在信息损失可控的前提下减少历史消息的 token 数量。常见手段包括截断早期消息只保留最近 N 轮对话但容易丢失跨轮次的关键上下文。消息精简移除对话中的客套话、重复表述、无意义的语气词。Embedding 检索将历史消息向量化存储按相关性动态召回最相关的片段而不是全量携带。对话摘要则是用模型自身的能力来生成历史对话的压缩表示。其核心思路是当对话积累到一定轮次后调用模型将历史内容总结为一段结构化摘要后续轮次只携带摘要而非原始消息。摘要的内容通常包括已完成的任务、当前待解决的问题、用户的关键偏好和约束条件。两种策略各有侧重。压缩策略侧重于量的减少适合对话信息密度较高的场景摘要策略侧重于质的提炼适合需要跨轮次记忆复杂上下文的长任务场景。三、架构设计分层上下文管理器在实际工程中我们设计了一个分层上下文管理器Layered Context Manager来统一处理上下文窗口问题。其整体架构如下graph TD A[用户输入] -- B[消息预处理] B -- C[短期记忆层br/最近 N 轮原始消息] B -- D[中期记忆层br/Embedding 检索] B -- E[长期记忆层br/结构化摘要] C -- F[上下文组装器] D -- F E -- F F -- G[Token 预算检查] G --|超预算| H[摘要生成触发器] H -- E G --|未超预算| I[LLM 调用] I -- J[响应输出] J -- K[历史归档与索引] K -- D短期记忆层保留最近 3-5 轮完整对话保证模型对当前话题的连续性理解。中期记忆层通过 Embedding 检索从全量历史中召回与当前问题相关的片段解决跨轮次信息关联问题。长期记忆层以结构化摘要的形式保存会话的全局状态包括用户意图演化轨迹、已确认的事实、未完成的任务队列等。Token 预算检查是关键的决策点。当组装后的总 token 数超出预设阈值例如窗口的 70%时系统自动触发摘要生成将历史内容压缩更新到长期记忆层释放短期记忆层的 token 空间。四、代码实现对话管理器核心逻辑以下是基于 Spring Boot 的对话上下文管理器的核心实现import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; /** * 分层上下文管理器——负责长对话场景下的 Token 预算控制与摘要生成 */ Service public class LayeredContextManager { /** 短期记忆最大轮次 */ private static final int SHORT_TERM_ROUNDS 5; /** Token 预算阈值窗口大小的 70% */ private static final double BUDGET_RATIO 0.7; /** 对话历史存储 */ private final DequeMessage shortTermMemory new ArrayDeque(); /** 结构化摘要 */ private String longTermSummary ; /** 当前 Token 预算上限 */ private int tokenBudget; public LayeredContextManager() { // 默认以 128K 窗口的 70% 为预算上限 this.tokenBudget (int) (128_000 * BUDGET_RATIO); } /** * 添加用户消息并返回组装后的上下文 */ public ListMessage addMessage(String role, String content) { Message msg new Message(role, content, System.currentTimeMillis()); shortTermMemory.addLast(msg); // 短期记忆滑动窗口超出上限时触发压缩 while (shortTermMemory.size() SHORT_TERM_ROUNDS) { Message oldest shortTermMemory.removeFirst(); // 将滑出的消息归档到中期记忆的索引中 archiveToEmbeddingIndex(oldest); } // Token 预算检查 if (estimateTokens() tokenBudget) { triggerSummaryGeneration(); } return assembleContext(); } /** * 估算当前上下文的 Token 数量 */ private int estimateTokens() { int total 0; // 中文场景下按 1.5 字符≈1 token 粗略估算 total longTermSummary.length() / 2; for (Message msg : shortTermMemory) { total msg.getContent().length() / 2; } return total; } /** * 触发摘要生成将历史内容压缩为结构化摘要 */ private void triggerSummaryGeneration() { try { String historyText shortTermMemory.stream() .map(m - m.getRole() : m.getContent()) .collect(Collectors.joining(\n)); // 调用 LLM 生成结构化摘要 String newSummary generateSummary(historyText); // 合并已有摘要与新摘要 this.longTermSummary mergeSummary(this.longTermSummary, newSummary); } catch (Exception e) { // 摘要生成失败时降级仅保留最近 3 轮消息 System.err.println(摘要生成失败执行降级策略: e.getMessage()); while (shortTermMemory.size() 3) { shortTermMemory.removeFirst(); } } } /** * 调用 LLM 生成对话摘要 */ private String generateSummary(String history) { // 实际场景中通过 LLMService 调用模型 // 提示词要求模型输出 JSON 格式的结构化摘要 String prompt 请将以下对话历史总结为结构化摘要包含 1. 已完成的操作列表 2. 当前待解决问题 3. 用户的关键偏好和约束 \n对话历史:\n history; return callLLM(prompt); } /** * 合并新旧摘要避免信息丢失 */ private String mergeSummary(String oldSummary, String newSummary) { if (oldSummary null || oldSummary.isEmpty()) { return newSummary; } // 通过 LLM 智能合并两份摘要 String prompt 请将以下两份对话摘要合并为一份去除重复信息保留所有关键事实 \n旧摘要:\n oldSummary \n新摘要:\n newSummary; return callLLM(prompt); } /** * 组装最终上下文长期摘要 中期检索结果 短期消息 */ private ListMessage assembleContext() { ListMessage context new ArrayList(); // 1. 加入长期摘要 if (!longTermSummary.isEmpty()) { context.add(new Message(system, 以下为历史对话的结构化摘要:\n longTermSummary, System.currentTimeMillis())); } // 2. 加入短期记忆 context.addAll(shortTermMemory); return context; } /** * 将消息归档到 Embedding 索引中期记忆 */ private void archiveToEmbeddingIndex(Message msg) { // 实际场景中调用向量数据库存储 // embeddingService.index(msg.getContent(), msg.getMetadata()); } private String callLLM(String prompt) { // 实际场景中通过 LLM 客户端调用 // 此处为示意返回模拟结果 return {\completed\:[],\pending\:[],\preferences\:[]}; } /** * 消息实体 */ public static class Message { private final String role; private final String content; private final long timestamp; public Message(String role, String content, long timestamp) { this.role role; this.content content; this.timestamp timestamp; } public String getRole() { return role; } public String getContent() { return content; } public long getTimestamp() { return timestamp; } } }上述代码实现了一个带有 Token 预算控制的对话管理器。其核心设计要点包括滑动窗口自动维护短期记忆规模、超预算时自动触发摘要生成、摘要失败时的降级策略回退到更短窗口、以及新旧摘要的智能合并避免信息孤岛。五、工程实践中的注意事项在生产环境中落地上下文管理还需要关注以下几点Token 估算精度。上文示例使用了简单的字符数估算实际场景应使用对应模型的 tokenizer 进行精确计数。对于中文场景建议使用 tiktoken 库或模型原生 tokenizer。摘要触发的时机选择。不应等到超出预算才触发摘要而是在接近预算阈值如 70%时主动触发为摘要生成和合并留出缓冲。同时要考虑摘要生成本身的 token 消耗。中期记忆的检索策略。Embedding 检索的效果高度依赖分块策略和相关性阈值。建议按照语义边界如自然段落、话题切换点进行分块并设置合理的最小相关性分数避免引入噪音。多模态上下文的处理。当对话中包含图片、代码块、结构化数据时Token 估算和摘要策略都需要相应调整。例如图片可先用视觉模型生成文字描述代码块保留关键片段而非全文。上下文窗口管理是一个需要持续打磨的工程问题。没有一劳永逸的方案需要在信息完整度和成本之间找到适合业务场景的平衡点。
模型上下文窗口管理——长对话场景下的 Token 压缩与摘要策略
发布时间:2026/7/11 22:31:55
模型上下文窗口管理——长对话场景下的 Token 压缩与摘要策略一、问题的提出上下文窗口的硬约束大语言模型在处理长对话时面临一个根本性的物理限制——上下文窗口Context Window。无论是 GPT-4 的 128K、Claude 的 200K还是国产模型 Qwen 的 1M token 窗口当对话轮次积累到一定规模后输入 token 数量总会逼近甚至超出窗口上限。超出部分会被直接截断导致模型丢失早期对话中的关键信息回答质量断崖式下降。在实际生产环境中这个问题尤为突出。以智能客服系统为例用户可能在同一会话中连续追问数十轮涉及订单查询、退款流程、物流追踪等多个子任务。如果不做任何处理系统很容易在第 20 轮左右就超出窗口限制用户不得不重新描述问题体验极差。从成本角度看长上下文也意味着更高的 API 调用费用。按 token 计费的模型每次请求携带数万 token 的历史上下文其中大量是冗余或低价值信息这直接推高了运营成本。因此上下文窗口管理不仅是技术问题也是经济问题。二、核心策略压缩与摘要的双轮驱动解决长上下文问题的主流方案可以归纳为两个方向Token 压缩和对话摘要。两者并非互斥而是互补关系在实践中通常组合使用。Token 压缩的目标是在信息损失可控的前提下减少历史消息的 token 数量。常见手段包括截断早期消息只保留最近 N 轮对话但容易丢失跨轮次的关键上下文。消息精简移除对话中的客套话、重复表述、无意义的语气词。Embedding 检索将历史消息向量化存储按相关性动态召回最相关的片段而不是全量携带。对话摘要则是用模型自身的能力来生成历史对话的压缩表示。其核心思路是当对话积累到一定轮次后调用模型将历史内容总结为一段结构化摘要后续轮次只携带摘要而非原始消息。摘要的内容通常包括已完成的任务、当前待解决的问题、用户的关键偏好和约束条件。两种策略各有侧重。压缩策略侧重于量的减少适合对话信息密度较高的场景摘要策略侧重于质的提炼适合需要跨轮次记忆复杂上下文的长任务场景。三、架构设计分层上下文管理器在实际工程中我们设计了一个分层上下文管理器Layered Context Manager来统一处理上下文窗口问题。其整体架构如下graph TD A[用户输入] -- B[消息预处理] B -- C[短期记忆层br/最近 N 轮原始消息] B -- D[中期记忆层br/Embedding 检索] B -- E[长期记忆层br/结构化摘要] C -- F[上下文组装器] D -- F E -- F F -- G[Token 预算检查] G --|超预算| H[摘要生成触发器] H -- E G --|未超预算| I[LLM 调用] I -- J[响应输出] J -- K[历史归档与索引] K -- D短期记忆层保留最近 3-5 轮完整对话保证模型对当前话题的连续性理解。中期记忆层通过 Embedding 检索从全量历史中召回与当前问题相关的片段解决跨轮次信息关联问题。长期记忆层以结构化摘要的形式保存会话的全局状态包括用户意图演化轨迹、已确认的事实、未完成的任务队列等。Token 预算检查是关键的决策点。当组装后的总 token 数超出预设阈值例如窗口的 70%时系统自动触发摘要生成将历史内容压缩更新到长期记忆层释放短期记忆层的 token 空间。四、代码实现对话管理器核心逻辑以下是基于 Spring Boot 的对话上下文管理器的核心实现import org.springframework.stereotype.Service; import java.util.*; import java.util.stream.Collectors; /** * 分层上下文管理器——负责长对话场景下的 Token 预算控制与摘要生成 */ Service public class LayeredContextManager { /** 短期记忆最大轮次 */ private static final int SHORT_TERM_ROUNDS 5; /** Token 预算阈值窗口大小的 70% */ private static final double BUDGET_RATIO 0.7; /** 对话历史存储 */ private final DequeMessage shortTermMemory new ArrayDeque(); /** 结构化摘要 */ private String longTermSummary ; /** 当前 Token 预算上限 */ private int tokenBudget; public LayeredContextManager() { // 默认以 128K 窗口的 70% 为预算上限 this.tokenBudget (int) (128_000 * BUDGET_RATIO); } /** * 添加用户消息并返回组装后的上下文 */ public ListMessage addMessage(String role, String content) { Message msg new Message(role, content, System.currentTimeMillis()); shortTermMemory.addLast(msg); // 短期记忆滑动窗口超出上限时触发压缩 while (shortTermMemory.size() SHORT_TERM_ROUNDS) { Message oldest shortTermMemory.removeFirst(); // 将滑出的消息归档到中期记忆的索引中 archiveToEmbeddingIndex(oldest); } // Token 预算检查 if (estimateTokens() tokenBudget) { triggerSummaryGeneration(); } return assembleContext(); } /** * 估算当前上下文的 Token 数量 */ private int estimateTokens() { int total 0; // 中文场景下按 1.5 字符≈1 token 粗略估算 total longTermSummary.length() / 2; for (Message msg : shortTermMemory) { total msg.getContent().length() / 2; } return total; } /** * 触发摘要生成将历史内容压缩为结构化摘要 */ private void triggerSummaryGeneration() { try { String historyText shortTermMemory.stream() .map(m - m.getRole() : m.getContent()) .collect(Collectors.joining(\n)); // 调用 LLM 生成结构化摘要 String newSummary generateSummary(historyText); // 合并已有摘要与新摘要 this.longTermSummary mergeSummary(this.longTermSummary, newSummary); } catch (Exception e) { // 摘要生成失败时降级仅保留最近 3 轮消息 System.err.println(摘要生成失败执行降级策略: e.getMessage()); while (shortTermMemory.size() 3) { shortTermMemory.removeFirst(); } } } /** * 调用 LLM 生成对话摘要 */ private String generateSummary(String history) { // 实际场景中通过 LLMService 调用模型 // 提示词要求模型输出 JSON 格式的结构化摘要 String prompt 请将以下对话历史总结为结构化摘要包含 1. 已完成的操作列表 2. 当前待解决问题 3. 用户的关键偏好和约束 \n对话历史:\n history; return callLLM(prompt); } /** * 合并新旧摘要避免信息丢失 */ private String mergeSummary(String oldSummary, String newSummary) { if (oldSummary null || oldSummary.isEmpty()) { return newSummary; } // 通过 LLM 智能合并两份摘要 String prompt 请将以下两份对话摘要合并为一份去除重复信息保留所有关键事实 \n旧摘要:\n oldSummary \n新摘要:\n newSummary; return callLLM(prompt); } /** * 组装最终上下文长期摘要 中期检索结果 短期消息 */ private ListMessage assembleContext() { ListMessage context new ArrayList(); // 1. 加入长期摘要 if (!longTermSummary.isEmpty()) { context.add(new Message(system, 以下为历史对话的结构化摘要:\n longTermSummary, System.currentTimeMillis())); } // 2. 加入短期记忆 context.addAll(shortTermMemory); return context; } /** * 将消息归档到 Embedding 索引中期记忆 */ private void archiveToEmbeddingIndex(Message msg) { // 实际场景中调用向量数据库存储 // embeddingService.index(msg.getContent(), msg.getMetadata()); } private String callLLM(String prompt) { // 实际场景中通过 LLM 客户端调用 // 此处为示意返回模拟结果 return {\completed\:[],\pending\:[],\preferences\:[]}; } /** * 消息实体 */ public static class Message { private final String role; private final String content; private final long timestamp; public Message(String role, String content, long timestamp) { this.role role; this.content content; this.timestamp timestamp; } public String getRole() { return role; } public String getContent() { return content; } public long getTimestamp() { return timestamp; } } }上述代码实现了一个带有 Token 预算控制的对话管理器。其核心设计要点包括滑动窗口自动维护短期记忆规模、超预算时自动触发摘要生成、摘要失败时的降级策略回退到更短窗口、以及新旧摘要的智能合并避免信息孤岛。五、工程实践中的注意事项在生产环境中落地上下文管理还需要关注以下几点Token 估算精度。上文示例使用了简单的字符数估算实际场景应使用对应模型的 tokenizer 进行精确计数。对于中文场景建议使用 tiktoken 库或模型原生 tokenizer。摘要触发的时机选择。不应等到超出预算才触发摘要而是在接近预算阈值如 70%时主动触发为摘要生成和合并留出缓冲。同时要考虑摘要生成本身的 token 消耗。中期记忆的检索策略。Embedding 检索的效果高度依赖分块策略和相关性阈值。建议按照语义边界如自然段落、话题切换点进行分块并设置合理的最小相关性分数避免引入噪音。多模态上下文的处理。当对话中包含图片、代码块、结构化数据时Token 估算和摘要策略都需要相应调整。例如图片可先用视觉模型生成文字描述代码块保留关键片段而非全文。上下文窗口管理是一个需要持续打磨的工程问题。没有一劳永逸的方案需要在信息完整度和成本之间找到适合业务场景的平衡点。