
在实际的 Agent 开发或面试场景中当被问到“如何管理 Agent 的上下文”时很多人能答出“用向量数据库存储”或“用滑动窗口限制长度”。但一旦追问“当上下文远超模型限制除了简单截断还有什么更优的压缩策略”超过80%的候选人会卡壳。上下文管理远不止存储和检索其核心挑战在于如何在有限的计算窗口内保留对当前任务最关键的信息同时不丢失长期记忆和任务连贯性。这背后是一套系统的压缩、摘要、优先级排序和动态更新的逻辑。本文将深入拆解 Agent 上下文管理的核心压缩逻辑。我们将从为什么需要压缩讲起逐步分析几种主流压缩策略的原理、实现与取舍并提供一个可运行的示例展示如何为一个简单的问答 Agent 集成上下文压缩能力。无论你是正在准备 Agent 相关面试还是在实际项目中面临长上下文处理的难题理解这套逻辑都将帮助你设计出更高效、更智能的 Agent 系统。1. 为什么简单的“截断”在 Agent 场景中会失效在讨论压缩之前必须首先理解 Agent 与普通聊天应用在上下文管理上的根本区别。对于一个仅处理单轮问答的聊天机器人当对话历史超过模型限制例如GPT-4 的 128K 上下文最简单的做法是从最旧的消息开始丢弃。然而Agent 的工作流通常是多步骤、长周期且目标导向的。1.1 Agent 上下文的独特构成一个典型的 Agent 上下文可能包含以下部分系统指令定义 Agent 的角色、目标和行为约束。这部分通常需要全程保留。长期记忆从向量数据库或其他存储中检索出来的、与当前任务相关的历史信息。这部分是动态的、经过筛选的。工作记忆当前会话中产生的多轮对话、工具调用结果、中间推理步骤。这部分增长最快也最需要压缩。工具定义与模式Agent 可以调用的工具的描述。虽然重要但通常比较固定。问题在于工作记忆会随着 Agent 的“思考-行动-观察”循环迅速膨胀。例如一个数据分析 Agent 可能会先查询数据库然后对结果进行排序、过滤、计算统计量每一步都可能产生大量的文本输出。如果只是机械地从头部截断很可能丢失掉定义任务目标的早期指令或者关键性的中间结论。1.2 截断带来的典型问题目标遗忘Agent 忘记了最初的任务是什么导致行为偏离。逻辑断层丢失了关键的推理步骤使得后续的决策缺乏依据甚至出现矛盾。工具调用失效丢失了之前工具调用的输出导致无法进行链式操作。因此我们需要更智能的策略来压缩上下文其核心目标是在有限的 Token 预算内最大化保留对完成当前及后续步骤最有价值的信息。2. 核心压缩策略从基础到进阶上下文压缩的本质是信息筛选与再表征。以下是几种从简单到复杂的策略它们可以单独使用但更多时候是组合使用。2.1 策略一基于优先级的滑动窗口这是对简单截断的第一次改进。其思想是并非所有消息都同等重要。实现逻辑给消息打分为上下文中的每一条消息或片段赋予一个优先级分数。打分依据可以包括消息类型系统指令 用户最新问题 工具关键输出 普通中间结果。是否包含关键词如“目标”、“最终”、“总结”、“错误”。消息的新鲜度较新的消息通常权重更高但并非绝对。排序与保留当上下文长度接近限制时根据优先级分数对消息进行排序保留分数最高的一批消息直到填满 Token 预算。滑动窗口在优先级筛选的基础上可以设定一个基本窗口确保最近 N 条消息总是被保留以维持会话的即时连贯性。示例消息优先级评分表消息类型示例内容基础优先级说明系统指令“你是一个数据分析助手目标是生成报告。”10 (最高)定义 Agent 根本角色必须保留。用户最新查询“请根据上述数据计算环比增长率。”9直接驱动当前行动。关键工具输出SQL查询结果销售额 100万8任务的核心产出是后续步骤的基础。Agent 的重要推理“我认为应该先筛选出 A 类产品因为...”7体现了 Agent 的“思考过程”对理解其决策链很重要。普通的工具输出/中间结果排序完成。过滤条件已应用。5过程性信息在空间紧张时可被压缩或丢弃。早期的普通对话“你好。”3问候语等重要性最低。2.2 策略二增量式摘要与记忆点提取这是更高级的策略它不直接丢弃信息而是对其进行提炼。实现逻辑定期摘要每进行 K 轮交互或当工作记忆达到一定长度就触发一次摘要过程。调用 LLM 对最近一段上下文进行总结生成一段精炼的文本。替换原始内容用生成的摘要替换掉被总结的那部分原始详细对话。摘要本身作为一个新的、高优先级的消息插入上下文。记忆点提取在摘要的同时可以要求 LLM 提取出关键实体、数字、结论等“记忆点”这些结构化信息可以单独存储例如放入一个简易的键值对内存供后续快速引用。# 伪代码示例一个简单的增量摘要函数 def incremental_summarize(conversation_history, model_client): 对最近的对话历史进行摘要。 conversation_history: List[Dict] 格式如 [{role:user, content:...}, ...] model_client: 调用LLM的客户端 # 1. 选取需要摘要的片段例如最后10轮对话 recent_turns conversation_history[-10:] # 2. 构建摘要提示词 prompt f 请将以下对话内容浓缩成一个简洁的段落摘要保留所有关键事实、决策和结论。 对话内容 {format_conversation(recent_turns)} 摘要 # 3. 调用LLM生成摘要 summary model_client.generate(prompt) # 4. 用摘要替换原始片段 # 移除被摘要的原始消息 new_history conversation_history[:-10] # 插入摘要消息通常以系统或助理身份 new_history.append({role: system, content: f对话摘要{summary}}) return new_history, summary # 在实际压缩逻辑中调用 if len(calculate_tokens(conversation_history)) threshold: conversation_history, last_summary incremental_summarize(conversation_history, llm_client)2.3 策略三基于查询的上下文检索与重组这种策略常见于 RAG检索增强生成与 Agent 的结合中。它颠覆了“维护一个线性增长的历史”的观念。实现逻辑存储所有历史将完整的、未经压缩的对话历史保存到向量数据库或普通数据库中。动态检索当需要构造本次请求的上下文时不直接使用全部历史而是基于当前查询即用户的最新问题或 Agent 的当前目标去历史库中进行检索。重组上下文将检索到的、与当前最相关的历史片段可能来自很久以前与系统指令、工具定义等固定内容组合形成本次请求的上下文。这种方法确保了上下文始终与当前任务高度相关且长度可控。但它对检索质量要求极高如果检索失败会导致 Agent“失忆”。# 伪代码示例基于查询的上下文重组 class QueryBasedContextManager: def __init__(self, vector_store): self.vector_store vector_store # 存储历史片段的向量库 self.system_prompt 你是助手... # 系统指令 def build_context(self, current_query, tools_def): 根据当前查询构建上下文。 # 1. 从向量库检索相关历史 relevant_memories self.vector_store.similarity_search(current_query, k5) # 2. 构建上下文列表 context_messages [] # 加入系统指令 context_messages.append({role: system, content: self.system_prompt}) # 加入工具定义 context_messages.append({role: system, content: f可用工具{tools_def}}) # 加入检索到的相关记忆 for memory in relevant_memories: context_messages.append({role: user if memory.is_user else assistant, content: memory.content}) # 加入当前查询 context_messages.append({role: user, content: current_query}) return context_messages def store_interaction(self, user_input, agent_response): 将一轮新的交互存入历史库。 # 可以将单轮对话或一个小片段存入向量库 self.vector_store.add_texts([user_input, agent_response])3. 实战为简易任务型 Agent 集成混合压缩策略让我们设计一个简单的“旅行规划助手”Agent它需要记住用户的偏好、之前的讨论点并调用工具查询信息。我们将为其实现一个混合压缩管理器。3.1 环境与依赖准备假设使用 Python 和 OpenAI API。关键依赖openai调用大模型。tiktoken用于精确计算 Token 数量OpenAI 模型。langchain可选这里我们简化实现但其ConversationSummaryBufferMemory等组件正是此类思想的封装。# 安装依赖 pip install openai tiktoken3.2 定义上下文压缩管理器我们将结合优先级滑动窗口和增量摘要。import tiktoken from typing import List, Dict, Tuple import openai import json class HybridContextManager: def __init__(self, model_name: str gpt-3.5-turbo, max_tokens: int 4000, summary_trigger_length: int 3000): self.model_name model_name self.max_tokens max_tokens # 模型上下文总限制 self.summary_trigger_length summary_trigger_length # 触发摘要的Token数阈值 self.encoder tiktoken.encoding_for_model(model_name) self.messages: List[Dict] [] self.long_term_summary # 长期摘要累积多次摘要的结果 def _count_tokens(self, text: str) - int: 计算字符串的Token数 return len(self.encoder.encode(text)) def _calculate_messages_tokens(self, messages: List[Dict]) - int: 计算整个消息列表的预估Token数简化版 total 0 for msg in messages: # 简单估算内容 角色名 一些格式Token total self._count_tokens(msg[content]) 10 return total def _assign_priority(self, message: Dict) - int: 为单条消息分配优先级分数 role message[role] content message[content].lower() score 5 # 基础分 if role system: score 100 # 系统指令最高 elif role user: score 80 # 用户输入次高 if prefer in content or hate in content or budget in content: score 20 # 包含偏好的用户输入更重要 elif role assistant and tool_call in content: score 70 # 工具调用结果 elif role assistant and summary in content: score 90 # 摘要信息很重要 # 其他assistant消息普通回复分数较低 return score def _summarize_chunk(self, chunk_messages: List[Dict]) - str: 调用LLM对一部分消息进行摘要 prompt_messages [ {role: system, content: 你是一个高效的摘要助手请将以下对话片段提炼成简洁的要点保留关键决策、用户偏好和事实。}, {role: user, content: json.dumps(chunk_messages, ensure_asciiFalse)} ] try: response openai.ChatCompletion.create( modelself.model_name, messagesprompt_messages, max_tokens500, temperature0.2 ) return response.choices[0].message.content.strip() except Exception as e: print(f摘要生成失败: {e}) return [摘要生成失败保留原始片段] def compress_if_needed(self): 检查并执行压缩 current_tokens self._calculate_messages_tokens(self.messages) # 策略1如果超过摘要触发线进行增量摘要 if current_tokens self.summary_trigger_length: print(f上下文过长 ({current_tokens} tokens)触发增量摘要...) # 假设我们对中间部分非最新、非最旧进行摘要 # 这里简化处理摘要除头尾5条外的中间消息 chunk_to_summarize self.messages[5:-5] if len(chunk_to_summarize) 2: summary self._summarize_chunk(chunk_to_summarize) # 用一条摘要消息替换被摘要的块 self.messages self.messages[:5] [{role: system, content: f历史摘要{summary}}] self.messages[-5:] print(增量摘要完成。) # 策略2如果仍然超过最大限制进行优先级裁剪 current_tokens self._calculate_messages_tokens(self.messages) if current_tokens self.max_tokens: print(f摘要后仍超限 ({current_tokens} tokens)进行优先级裁剪...) # 为每条消息评分 scored_messages [(msg, self._assign_priority(msg)) for msg in self.messages] # 按分数降序排序 scored_messages.sort(keylambda x: x[1], reverseTrue) # 优先保留高分的直到Token数接近上限 new_messages [] token_count 0 for msg, score in scored_messages: msg_tokens self._count_tokens(msg[content]) 10 if token_count msg_tokens self.max_tokens * 0.9: # 保留10%缓冲给新请求 new_messages.append(msg) token_count msg_tokens else: print(f丢弃低优先级消息: {msg[content][:50]}...) # 裁剪后尽量保持消息顺序高分的在前但可以简单按原顺序重排被保留的消息ID # 这里简化直接使用new_messages已是按分数排序可能打乱时序 # 更优做法按原始顺序保留那些被选中的消息 self.messages [msg for msg in self.messages if msg in [m for m, _ in scored_messages[:len(new_messages)]]] print(f裁剪完成剩余 {len(self.messages)} 条消息。) def add_message(self, role: str, content: str): 添加新消息并触发压缩检查 self.messages.append({role: role, content: content}) self.compress_if_needed() def get_context(self) - List[Dict]: 获取当前压缩后的上下文 return self.messages.copy()3.3 在 Agent 循环中使用管理器# 模拟一个简单的Agent循环 def run_travel_agent_loop(): context_manager HybridContextManager(max_tokens4000, summary_trigger_length3000) # 1. 添加系统指令 context_manager.add_message(system, 你是一个旅行规划助手帮助用户制定旅行计划。请记住用户的偏好。) # 模拟多轮对话 user_inputs [ 我想去日本旅行喜欢文化和美食预算中等。, 请帮我看看东京有哪些推荐的博物馆, 我讨厌人多拥挤的地方。, 那么京都呢另外我对抹茶甜品很感兴趣。, 请根据我的喜好文化、美食、讨厌拥挤、喜欢抹茶总结一下东京和京都的行程建议。 ] for i, user_input in enumerate(user_inputs): print(f\n--- 第 {i1} 轮 ---) print(f用户: {user_input}) context_manager.add_message(user, user_input) # 模拟Agent“思考”和“行动”这里简化为直接调用LLM current_context context_manager.get_context() # 在实际中这里会调用LLM可能包含工具调用等 # 假设Agent的回复 if 博物馆 in user_input: agent_response 工具调用[查询东京博物馆]推荐东京国立博物馆、江户东京博物馆。 elif 京都 in user_input: agent_response 工具调用[查询京都景点]推荐伏见稻荷大社、金阁寺。工具调用[查询抹茶甜品]推荐中村藤吉、伊藤久右卫门。 elif 总结 in user_input: # 在总结时上下文管理器可能已经压缩过历史 agent_response 根据您的偏好建议东京侧重博物馆文化京都侧重寺庙与抹茶体验文化美食并避开高峰时段以应对拥挤问题。 else: agent_response 好的我已记录您的偏好喜欢文化美食预算中等讨厌拥挤。 print(f助手: {agent_response}) context_manager.add_message(assistant, agent_response) # 打印当前上下文状态演示用 print(f当前上下文消息数: {len(context_manager.messages)}) if len(context_manager.messages) 0 and 历史摘要 in context_manager.messages[-1][content]: print(f最新消息是摘要: {context_manager.messages[-1][content][:100]}...) if __name__ __main__: # 需要设置 OPENAI_API_KEY 环境变量 # import os; os.environ[OPENAI_API_KEY] your-key run_travel_agent_loop()运行上述模拟你会观察到当对话轮数增加上下文长度达到阈值后管理器会自动触发摘要和优先级裁剪而不是机械地丢弃最早的对话。4. 常见问题与排查路径在实际实现或面试中你可能会遇到以下问题4.1 压缩导致信息丢失或任务失败现象Agent 在长对话后突然忘记关键约束如预算或做出矛盾决策。排查检查优先级打分规则是否低估了系统指令和用户核心约束的分数确保它们不会被轻易裁剪。审查摘要质量调用 LLM 生成摘要时提示词是否明确要求保留“偏好”、“约束”、“目标”等关键信息摘要的温度参数是否过高导致信息扭曲验证压缩触发点summary_trigger_length是否设置得过低过早进行摘要可能丢失尚未形成完整逻辑链的细节。解决优化打分函数给包含特定关键词budget,must,not的消息额外加分。改进摘要提示词并考虑在摘要中显式附上关键结构化数据如用户偏好: [预算中等, 讨厌拥挤]。4.2 压缩操作本身消耗大量 Token 或时间现象每次压缩尤其是调用 LLM 摘要导致请求延迟显著增加或消耗的 Token 费用高昂。排查评估摘要频率是否每轮对话都尝试摘要summary_trigger_length是否太接近max_tokens导致频繁在边界触发分析摘要内容长度生成的摘要是否比原始内容缩短得不够多摘要的max_tokens参数是否设置过大解决采用懒惰压缩策略仅在准备发送给模型前检查并压缩上下文而不是每次添加消息后都压缩。调整阈值让摘要发生在上下文达到最大限制的 70%-80% 时为后续对话留出缓冲。对于摘要使用更小、更快的模型如gpt-3.5-turbo并严格限制生成 Token 数。4.3 混合策略下的逻辑错乱现象裁剪和摘要后的上下文消息顺序混乱导致模型理解出现时序错误。排查检查消息顺序优先级裁剪后是否粗暴地按分数排序完全打乱了对话的先后顺序LLM 对时序很敏感。摘要的定位摘要消息插入的位置是否合理它应该代表被它替换的那段历史的时间点。解决裁剪时优先保证消息的时序。可以按优先级筛选出要保留的消息 ID 集合然后按原始顺序从历史中提取这些消息。摘要消息的角色可以设为system并放在它所代表的历史时间段的大致位置或者统一放在上下文中部并注明“以下是之前对话的摘要”。5. 生产环境最佳实践与扩展方向5.1 最佳实践清单分层存储不要将所有希望寄托于单次上下文。结合工作记忆在对话中、短期记忆向量数据库存储近期会话片段、长期记忆外部数据库存储用户画像、重要结论。压缩可配置为不同的 Agent 角色或任务类型提供不同的压缩策略配置。一个创意写作 Agent 可能需要保留更多细节而一个数据查询 Agent 则可以更激进地压缩。保留原始日志无论上下文如何压缩完整的原始对话日志应持久化到数据库。这对于调试、分析和后续训练至关重要。监控与评估建立监控指标如压缩率、摘要后任务成功率、用户满意度。用 A/B 测试评估不同压缩策略的效果。用户显式控制对于高级用户可以提供“记住这一点”、“这不重要”等交互方式让用户参与优先级打分。5.2 扩展方向更智能的优先级学习利用强化学习根据任务完成情况自动调整不同类型消息的优先级权重。结构化记忆不只用文本摘要将上下文中的关键信息如日期、地点、决策、用户反馈提取成结构化对象如 JSON Schema存储和检索效率更高。图记忆网络将记忆单元构建成知识图谱通过节点和关系连接实现更复杂、更精准的关联回忆。与 LangChain/LlamaIndex 等框架集成这些框架提供了ConversationSummaryMemory、ConversationBufferWindowMemory、VectorStoreRetrieverMemory等高级抽象理解其源码是学习压缩逻辑的绝佳途径。回到面试场景当被问到“Agent 上下文管理”时你可以从存储讲到压缩从简单的滑动窗口讲到基于查询的动态重组并清晰地指出各种策略的优缺点和适用场景。核心是展现出你不仅知道“是什么”更理解“为什么”以及“如何根据实际情况做取舍”。这套压缩逻辑正是区分普通使用者和深度理解者的关键。