1. 为什么上下文管理成了Agent落地的第一道坎
做过Agent项目的朋友大概率都经历过这种崩溃瞬间:明明逻辑写得没问题,工具调用也通了,结果跑到第十几轮对话的时候,模型开始胡言乱语,要么把前面确认过的参数忘了,要么把已经排除的方案又捡回来重新执行一遍。你去看日志,发现上下文窗口已经塞满了,模型在“记忆过载”的状态下做出的决策质量断崖式下跌。
这个问题的本质不是模型不行,而是上下文窗口是一种稀缺资源。当前主流模型的上下文窗口从8K到200K不等,看起来很大,但Agent场景下的消耗速度远超想象。一轮完整的Agent交互,包含系统提示词、工具定义、历史对话、工具返回结果、中间推理过程,轻松就能吃掉几千token。如果Agent需要执行一个多步骤任务,比如“帮我分析这份销售数据并生成报告”,中间涉及读文件、跑脚本、查数据库、调API,十几轮下来,上下文直接爆掉。
所以上下文压缩与记忆策略不是一个“锦上添花”的优化项,而是Agent能不能跑通长任务的生死线。这篇文章我会把我在实际项目中踩过的坑、试过的方案、以及最终沉淀下来的选型逻辑全部摊开讲。核心围绕三个东西:5种压缩手段、3类记忆形态、4条选型红线,中间会穿插对MemGPT、ACON、Context Editing这几个代表性方案的拆解分析。
适合谁看?如果你正在做Agent产品、正在被上下文溢出折磨、或者正在选型记忆方案,这篇内容应该能帮你省掉至少两周的试错时间。如果你还没开始做Agent但准备入坑,那更要看,因为这些东西等你踩坑了再补,成本会高很多。
2. 五种上下文压缩手段的底层逻辑与实操细节
压缩这个词听起来简单,但落到工程实现上,不同方案的差异巨大。有的压缩是有损的,压完信息就没了;有的是无损的,但压缩比有限;有的压缩需要额外调模型,成本上去了;有的压缩是结构化的,需要你提前设计好数据格式。下面这五种是我实际用过或者深度评估过的,按压缩力度从轻到重排列。
2.1 滑动窗口裁剪:最简单但也最容易翻车
滑动窗口的逻辑非常直白:只保留最近N轮对话,超出的直接丢掉。实现上就是一个队列,新消息进来,旧消息出队。代码大概长这样:
class SlidingWindowMemory: def __init__(self, max_turns=10): self.max_turns = max_turns self.history = [] def add(self, role, content): self.history.append({"role": role, "content": content}) if len(self.history) > self.max_turns * 2: self.history = self.history[-(self.max_turns * 2):] def get_context(self): return self.history这个方案的优势是零成本、零延迟、实现简单。但问题也很致命:它假设最近的信息最重要,但Agent场景下这个假设经常不成立。比如用户在第三轮说了一句“我的预算上限是5000”,到第十五轮你让他推荐方案,滑动窗口早就把预算信息冲掉了,模型只能瞎猜。
我踩过的坑:早期做一个客服Agent,用滑动窗口保留最近8轮,结果用户在前面提到的订单号被冲掉了,Agent反复问“请提供您的订单号”,用户体验极差。后来改成保留最近8轮加上一个“关键信息槽位”,才勉强能用。
注意:滑动窗口不是不能用,但一定要配合一个“关键信息提取”机制,把用户明确给出的约束条件、ID、参数单独存起来,不参与窗口淘汰。
2.2 摘要压缩:用一次模型调用换长期记忆
摘要压缩的思路是:当上下文快满的时候,调一次模型,把前面的对话压缩成一段摘要,然后用摘要替换原始对话。这样既保留了核心信息,又大幅降低了token占用。
实现上有两种粒度:
- 全量摘要:把整个历史对话一次性压缩成一段话。优点是简单,缺点是每次压缩都要调模型,而且随着对话变长,摘要本身也会变长。
- 增量摘要:只压缩最老的那部分对话,保留最近的原始对话。比如保留最近5轮原文,把5轮之前的压缩成摘要。这样摘要可以累积,但每次只需要压缩一小段。
def summarize_history(client, history, keep_recent=5): if len(history) <= keep_recent * 2: return history old_messages = history[:-keep_recent * 2] recent_messages = history[-keep_recent * 2:] summary_prompt = "请将以下对话压缩成一段简洁的摘要,保留所有关键信息、决策和约束条件:\n" for msg in old_messages: summary_prompt += f"{msg['role']}: {msg['content']}\n" response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": summary_prompt}] ) summary = response.choices[0].message.content return [{"role": "system", "content": f"历史对话摘要:{summary}"}] + recent_messages摘要压缩的关键在于摘要质量。我试过用不同模型做摘要,发现小模型(比如7B级别的)经常丢关键信息,尤其是数字、ID、否定条件(“不要用XX方案”这种)。后来固定用GPT-4o-mini做摘要,质量稳定很多,成本也可控。
实操心得:摘要prompt里一定要明确要求“保留所有数字、ID、否定条件和用户明确表达的偏好”,否则模型很容易把这些当成“细节”丢掉。
2.3 结构化提取:把对话变成数据库
结构化提取的思路和摘要不同,它不是把对话压成一段话,而是把对话中的关键信息抽取成结构化字段。比如:
{ "user_constraints": ["预算不超过5000", "必须支持中文"], "confirmed_decisions": ["方案A已确认", "方案B已排除"], "pending_items": ["等待用户提供订单号"], "key_entities": {"product": "X200", "order_id": "ORD-12345"} }这个方案的优点是信息密度极高,一个JSON对象可能只占100token,但包含了十几轮对话的核心信息。而且结构化数据可以被程序直接读取和操作,比如你可以写逻辑判断“如果pending_items不为空,就优先处理”。
缺点是提取本身需要模型调用,而且提取的准确性依赖prompt设计。我试过用function calling来做结构化提取,效果比纯文本prompt好很多,因为function calling会强制模型按照schema输出。
extract_function = { "name": "extract_context", "parameters": { "type": "object", "properties": { "user_constraints": {"type": "array", "items": {"type": "string"}}, "confirmed_decisions": {"type": "array", "items": {"type": "string"}}, "pending_items": {"type": "array", "items": {"type": "string"}}, "key_entities": {"type": "object"} } } }注意:结构化提取的schema设计很关键。字段太多,提取成本高且容易出错;字段太少,信息丢失严重。我的经验是控制在5-8个字段,每个字段的语义要清晰无歧义。
2.4 向量检索压缩:按需召回而非全量保留
向量检索的思路是:把所有历史对话存入向量数据库,每次需要的时候根据当前query检索最相关的几条,而不是把所有历史都塞进上下文。
这个方案在知识型Agent里特别好用。比如一个技术文档问答Agent,用户可能问了20个问题,但当前问题只和其中3个相关,那就只检索那3个相关的对话片段,其他的不占上下文。
import chromadb client = chromadb.Client() collection = client.create_collection("agent_memory") def store_message(msg_id, content, metadata): collection.add( documents=[content], metadatas=[metadata], ids=[msg_id] ) def retrieve_relevant(query, top_k=3): results = collection.query( query_texts=[query], n_results=top_k ) return results["documents"][0]但向量检索有个坑:它依赖语义相似度,但Agent场景下很多关键信息是“约束型”的,语义相似度不一定高。比如用户说“不要用红色”,当前query是“推荐一个配色方案”,语义上“不要用红色”和“配色方案”的相似度可能不高,但这条约束必须被召回。
我的解法是混合检索:向量检索 + 关键词检索 + 规则召回。规则召回就是硬编码一些模式,比如检测到“不要”“必须”“只能”这类词,就强制把相关消息召回。
2.5 分层压缩:把不同信息放在不同层级
分层压缩是我目前最推荐的方案,它的核心思想是不是所有信息都同等重要,所以不应该用同一种压缩策略。
我通常把上下文分成三层:
- 热层:最近3-5轮对话,保留原文,不压缩。
- 温层:5-20轮之前的对话,用摘要压缩,保留核心信息。
- 冷层:20轮之前的对话,用结构化提取,只保留关键字段。
这样既保证了最近交互的完整性,又保证了长期记忆不丢失,同时token占用可控。
class LayeredMemory: def __init__(self, hot_turns=5, warm_turns=20): self.hot = [] self.warm_summary = "" self.cold_facts = {} self.hot_turns = hot_turns self.warm_turns = warm_turns def add(self, role, content): self.hot.append({"role": role, "content": content}) if len(self.hot) > self.hot_turns * 2: self._demote_to_warm() def _demote_to_warm(self): old = self.hot[:2] self.hot = self.hot[2:] # 调模型更新warm_summary self.warm_summary = update_summary(self.warm_summary, old) def get_context(self): context = [] if self.cold_facts: context.append({"role": "system", "content": f"已知事实:{self.cold_facts}"}) if self.warm_summary: context.append({"role": "system", "content": f"历史摘要:{self.warm_summary}"}) context.extend(self.hot) return context分层压缩的难点在于层与层之间的信息传递。热层降到温层的时候,摘要要准确;温层降到冷层的时候,结构化提取要完整。我一般会在降层的时候多调一次模型做校验,确保关键信息没丢。
3. 三类记忆形态:从瞬时到长期的完整拼图
压缩解决的是“上下文太满”的问题,但记忆解决的是“信息该存哪里、存多久、怎么取”的问题。这两个是互补的,不是替代关系。我习惯把Agent记忆分成三类,每类的存储介质、生命周期、访问方式都不一样。
3.1 工作记忆:当前任务的临时黑板
工作记忆就是当前任务执行过程中的临时状态,任务结束就清掉。比如Agent正在执行“订机票”任务,工作记忆里存的是:出发地、目的地、日期、乘客信息、当前执行到哪一步了。
工作记忆的特点是读写频繁、生命周期短、结构灵活。我一般用一个字典或者JSON对象来存,不涉及数据库。
working_memory = { "task": "book_flight", "step": "select_seat", "params": { "from": "北京", "to": "上海", "date": "2025-03-15", "passenger": "张三" }, "intermediate_results": { "available_flights": ["CA1234", "MU5678"], "selected_flight": "CA1234" } }工作记忆的关键是step字段。Agent每执行一步就更新step,这样即使上下文被压缩了,Agent也能通过读取工作记忆知道当前进度。我见过很多Agent项目把进度信息只放在对话历史里,结果上下文一压缩,Agent就忘了自己执行到哪了,开始重复执行。
实操心得:工作记忆一定要有一个明确的“当前步骤”字段,并且每一步操作前都先读这个字段,确认自己该做什么。这个习惯能避免90%的重复执行问题。
3.2 会话记忆:跨轮次但限于单次会话
会话记忆是同一个会话内跨轮次保留的信息,会话结束就清掉。比如用户在一次会话中先问了产品价格,又问了售后政策,再问“那这个价格包含运费吗”,这里的“这个价格”就需要会话记忆来解析。
会话记忆的实现方式就是我前面讲的压缩策略的组合。我一般用分层压缩来管理会话记忆,热层保留最近几轮,温层用摘要,冷层用结构化提取。
会话记忆的一个关键设计是会话边界。什么时候算一次会话结束?我的经验是:用户明确说“再见”“谢谢”算结束;用户超过30分钟没说话算结束;用户主动点击“新会话”按钮算结束。会话结束后,会话记忆可以选择清空,也可以选择沉淀到长期记忆。
3.3 长期记忆:跨会话的知识沉淀
长期记忆是跨会话保留的,比如用户的偏好、历史订单、常用地址。长期记忆的存储介质一般是数据库或向量库,访问方式是检索。
长期记忆的设计要考虑三个问题:
- 存什么:不是所有信息都值得长期存。我一般只存三类:用户明确表达的偏好(“我喜欢靠窗座位”)、高频使用的实体(常用地址、常用联系人)、历史决策结果(上次选了方案A)。
- 怎么存:结构化字段存关系型数据库,非结构化文本存向量库。我一般用混合存储,结构化字段用于精确查询,向量库用于语义检索。
- 怎么取:长期记忆的检索要和当前query相关。我一般用“向量检索 + 规则过滤”的方式,先向量召回top-10,再用规则过滤掉过期的、不相关的。
def retrieve_long_term_memory(user_id, query): # 向量召回 candidates = vector_store.query(query, top_k=10, filter={"user_id": user_id}) # 规则过滤 filtered = [] for c in candidates: if c["timestamp"] > time.time() - 90 * 24 * 3600: # 90天内 if c["confidence"] > 0.7: # 置信度阈值 filtered.append(c) return filtered[:3]长期记忆最大的坑是信息过期。用户三个月前说“我住在北京”,现在可能已经搬到上海了。我的解法是给每条长期记忆加一个时间戳和置信度,检索的时候优先返回时间近的、置信度高的。如果新旧信息冲突,以新的为准。
4. 四个代表性方案的拆解:MemGPT、ACON、Context Editing及自研方案
前面讲的是通用方法论,这一节我拿四个具体方案来拆,看看它们分别是怎么解决上下文和记忆问题的,以及各自的适用场景和坑点。
4.1 MemGPT:把操作系统那套搬到了Agent上
MemGPT的核心思想非常有意思:它把LLM的上下文窗口类比成计算机的内存,把外部存储类比成硬盘,然后设计了一套“虚拟上下文管理”机制,让Agent可以在“内存”和“硬盘”之间换页。
具体来说,MemGPT把上下文分成两部分:
- 主上下文:就是实际送给LLM的prompt,包含系统指令、工作上下文、最近消息。
- 外部上下文:存在数据库里的历史消息、知识库、归档数据。
当主上下文快满的时候,MemGPT会触发一个“换页”操作:把不重要的消息从主上下文移到外部上下文,或者从外部上下文检索相关消息加载到主上下文。这个换页操作是通过function calling实现的,Agent自己决定什么时候换页、换什么。
# MemGPT风格的换页函数定义 functions = [ { "name": "archive_messages", "description": "将消息归档到外部存储", "parameters": { "type": "object", "properties": { "message_ids": {"type": "array", "items": {"type": "string"}}, "summary": {"type": "string"} } } }, { "name": "retrieve_messages", "description": "从外部存储检索消息", "parameters": { "type": "object", "properties": { "query": {"type": "string"}, "top_k": {"type": "integer"} } } } ]MemGPT的优点是架构清晰、扩展性强,理论上可以支持无限长的对话。但缺点也很明显:换页决策依赖模型判断,模型判断不准的时候会频繁换页或者该换不换。我实测下来,MemGPT在对话型Agent上表现不错,但在任务型Agent上,模型经常在关键步骤把重要信息归档了,导致后续步骤找不到参数。
注意:用MemGPT风格方案的时候,一定要给“归档”操作加约束,比如“当前任务未完成时,不允许归档与当前任务相关的消息”。
4.2 ACON:用压缩模型做端到端上下文压缩
ACON(Adaptive Context Compression)的思路和MemGPT不同,它不搞换页,而是训练一个专门的压缩模型,把长上下文压缩成短上下文。这个压缩模型是端到端训练的,目标是“压缩后的上下文能让下游任务模型表现最好”。
ACON的核心创新是自适应压缩率:不是所有上下文都压到同一个长度,而是根据内容的重要性动态决定压缩率。重要的内容少压,不重要的内容多压。
这个方案的优势是压缩质量高,因为是专门训练的模型,比通用摘要模型更懂“什么该保留”。但劣势也很明显:需要训练、需要额外部署一个模型、推理成本增加。对于大多数团队来说,训练一个压缩模型的门槛太高了,不太现实。
我的建议是:如果你是大厂或者有充足算力,可以考虑ACON思路,自己训一个压缩模型。如果是中小团队,直接用GPT-4o-mini做摘要,效果已经够用了,没必要为了10%的提升去搞一套训练 pipeline。
4.3 Context Editing:让模型自己编辑上下文
Context Editing是最近比较火的一个方向,核心思路是让模型自己决定保留哪些上下文、删除哪些上下文。具体实现上,一般是在prompt里加一个指令,让模型在生成回复之前,先输出一个“上下文编辑”操作,比如“删除第3-5轮对话”“保留第7轮的关键信息”。
editing_prompt = """ 在回复用户之前,请先分析当前上下文,决定是否需要编辑。 如果需要编辑,输出以下格式: <edit> <delete>消息ID列表</delete> <keep>消息ID列表</keep> <summarize>需要摘要的消息ID列表</summarize> </edit> 然后输出你的回复。 """Context Editing的优点是灵活、不需要额外模型,缺点是依赖模型的元认知能力,模型得知道自己“记得什么”“忘了什么”。我实测下来,GPT-4级别的模型做Context Editing效果还行,但小模型经常编辑错误,把该留的删了,该删的留了。
实操心得:用Context Editing的时候,一定要加一个“安全网”:编辑操作执行后,如果模型在后续回复中引用了被删除的信息,就自动回滚这次编辑。这个安全网能避免大部分编辑错误。
4.4 自研混合方案:我的最终选择
试过上面这些方案之后,我最终选择的是自研混合方案,核心思路是:
- 工作记忆用结构化字典,不参与压缩。
- 会话记忆用分层压缩:热层原文、温层摘要、冷层结构化。
- 长期记忆用向量库 + 关系型数据库混合存储。
- 压缩触发用规则 + 模型判断结合:token超过阈值触发,但具体压什么由模型决定。
- 安全网:每次压缩后,用一个小模型做校验,检查关键信息是否丢失。
这个方案的好处是可控、可调试、成本可控。每个环节我都能看到中间结果,出了问题能定位。MemGPT和Context Editing太“黑盒”了,出了问题很难排查。ACON又太重了,不适合快速迭代。
class HybridMemory: def __init__(self, token_threshold=3000): self.working = {} self.hot = [] self.warm = "" self.cold = {} self.long_term = VectorStore() self.token_threshold = token_threshold def before_llm_call(self, query): # 检查token数 if self._count_tokens() > self.token_threshold: self._compress() # 检索长期记忆 long_term_context = self.long_term.retrieve(query) return self._build_context(long_term_context) def _compress(self): # 热层降到温层 if len(self.hot) > 10: old = self.hot[:4] self.hot = self.hot[4:] self.warm = update_summary(self.warm, old) # 温层降到冷层 if len(self.warm) > 2000: self.cold = extract_facts(self.warm, self.cold) self.warm = "" # 校验 self._validate_compression()5. 四条选型红线:这些坑我替你踩过了
选型的时候,很多人只看“效果好不好”,但实际落地的时候,成本、延迟、可控性、可调试性往往比效果更重要。下面这四条红线,是我用真金白银的token费和无数个debug夜晚换来的。
5.1 红线一:压缩不能引入不可接受的延迟
压缩是要调模型的,调模型就有延迟。如果你的Agent要求响应时间在2秒以内,那摘要压缩基本不可行,因为一次摘要调用就要1-2秒。这种情况下只能用滑动窗口或者规则压缩。
我做过一个实时客服Agent,要求响应时间1.5秒以内。一开始用摘要压缩,结果每次压缩都超时。后来改成“滑动窗口 + 关键信息槽位”,响应时间降到800ms,虽然记忆能力弱了一点,但用户体验好很多。
实操心得:如果你的场景对延迟敏感,压缩策略一定要选“零模型调用”的方案,比如滑动窗口、规则提取、结构化槽位。摘要和向量检索可以作为异步操作,在后台慢慢做。
5.2 红线二:压缩不能丢失关键约束
Agent场景下,有些信息是“硬约束”,丢了就出大问题。比如“预算不超过5000”“不要用红色”“必须支持中文”。这些约束一旦在压缩中丢失,Agent就会给出错误建议。
我的解法是约束信息单独存储,不参与压缩。具体来说,在对话过程中,一旦检测到约束型语句(包含“不要”“必须”“只能”“不超过”等关键词),就立即提取出来存到一个专门的constraints列表里,这个列表永远保留在上下文中,不压缩。
CONSTRAINT_KEYWORDS = ["不要", "必须", "只能", "不超过", "至少", "禁止", "务必"] def extract_constraints(message): constraints = [] for sentence in message.split("。"): if any(kw in sentence for kw in CONSTRAINT_KEYWORDS): constraints.append(sentence.strip()) return constraints5.3 红线三:压缩策略要可调试、可回滚
压缩是个“有损操作”,有损操作就必须可调试。我见过太多项目,压缩逻辑写在一个黑盒函数里,出了问题根本不知道是哪一步压错了。我的做法是每次压缩都记录日志:压缩前的token数、压缩后的token数、压缩掉了哪些消息、压缩后的摘要内容。这样出了问题可以回溯。
def compress_with_logging(history): before_tokens = count_tokens(history) compressed = do_compress(history) after_tokens = count_tokens(compressed) log = { "timestamp": time.time(), "before_tokens": before_tokens, "after_tokens": after_tokens, "removed_messages": [m["id"] for m in history if m not in compressed], "summary": compressed[0]["content"] if compressed else "" } logger.info(json.dumps(log)) return compressed另外,压缩一定要支持回滚。如果压缩后模型表现异常,要能一键恢复到压缩前的状态。我一般会在压缩前存一个快照,压缩后如果连续两轮模型输出质量下降,就自动回滚。
5.4 红线四:不要为了压缩而压缩
最后这条红线最重要:压缩不是目的,让Agent表现好才是目的。我见过一些项目,为了追求“上下文利用率”或者“压缩比”,把压缩做得特别激进,结果Agent表现反而下降了。
压缩的收益是降低token成本、避免上下文溢出,但代价是信息损失、额外延迟、实现复杂度。如果当前上下文还没满,Agent表现也正常,那就不要压缩。压缩应该是“按需触发”的,不是“每轮都做”的。
我的触发策略是:token数超过模型窗口的70%才触发压缩。70%这个阈值是我试出来的,低于70%压缩收益不明显,高于70%容易溢出。当然不同模型不一样,你可以根据实际情况调整。
6. 常见问题排查与实战避坑速查
这一节我整理了一些实际项目中高频出现的问题和排查思路,做成速查表,方便你遇到问题的时候快速定位。
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Agent重复执行已完成的步骤 | 工作记忆的step字段没更新,或压缩时把step信息丢了 | 检查工作记忆的step字段是否每步都更新 | 确保step字段不参与压缩,每步操作前先读step |
| Agent忘记用户约束 | 约束信息被压缩掉了 | 检查压缩日志,看约束消息是否被删除 | 约束信息单独存储,不参与压缩 |
| 压缩后Agent表现下降 | 摘要质量差,关键信息丢失 | 对比压缩前后的模型输出,定位丢失的信息类型 | 优化摘要prompt,或改用结构化提取 |
| 压缩延迟太高 | 摘要调用太频繁或模型太大 | 统计每次压缩的耗时 | 降低压缩频率,或换更小的模型做摘要 |
| 长期记忆检索不准 | 向量检索语义相似度不够 | 检查检索结果和query的相关性 | 改用混合检索:向量+关键词+规则 |
| 上下文还是溢出 | 压缩比不够,或压缩触发太晚 | 检查压缩前后的token数 | 提高压缩力度,或提前触发阈值 |
| 模型在压缩后胡言乱语 | 压缩后的上下文格式混乱 | 检查压缩后的上下文结构 | 确保压缩后的上下文有清晰的角色标记和分隔符 |
除了表格里的问题,还有几个我踩过的坑值得单独说:
坑一:摘要prompt太笼统。一开始我用“请总结以下对话”做摘要,结果模型把很多关键细节都省了。后来改成“请保留所有数字、ID、否定条件、用户偏好和已确认的决策”,摘要质量明显提升。
坑二:向量检索的top_k设太大。top_k设成10,检索回来一堆不相关的,反而干扰模型。后来改成top_k=3,配合规则过滤,效果好很多。
坑三:压缩后没有重新计算token。有一次压缩完忘了重新计算token数,结果以为压缩成功了,实际上还是超的。后来在压缩函数里强制加了一步token重算。
坑四:长期记忆没有过期机制。用户三个月前说的偏好,三个月后还在用,结果推荐错了。后来加了时间戳和置信度,超过90天的记忆自动降权。
7. 我个人的选型建议与落地路线
如果你正在选型,我的建议是按这个顺序来:
- 先上滑动窗口 + 关键信息槽位。这是成本最低、实现最快的方案,能解决80%的上下文溢出问题。关键信息槽位用来存约束、ID、参数这些不能丢的东西。
- 再加分层压缩。热层原文、温层摘要、冷层结构化。这个方案能支撑大部分中长对话场景,成本也可控。
- 最后加长期记忆。用向量库 + 关系型数据库混合存储,配合时间戳和置信度做检索过滤。长期记忆不是必须的,如果你的Agent不需要跨会话记忆,可以跳过。
- MemGPT和Context Editing可以作为参考,但不建议直接上。这两个方案的黑盒程度太高,出了问题不好排查。除非你的团队有足够的工程能力做深度定制,否则自研混合方案更稳妥。
落地路线的话,我一般分三步走:
- 第一步:跑通基础流程。用滑动窗口 + 工作记忆,确保Agent能完成单轮和多轮任务。
- 第二步:加压缩和会话记忆。用分层压缩管理会话记忆,确保长对话不溢出。
- 第三步:加长期记忆和优化。根据实际需求加长期记忆,然后根据线上数据持续优化压缩策略和检索策略。
最后分享一个小技巧:压缩策略一定要做A/B测试。我一般会同时跑两套压缩策略,一套激进一套保守,然后对比Agent的任务完成率和token成本。数据说话,比拍脑袋靠谱。
这个方向后续还可以扩展的地方很多,比如用更小的模型做压缩、用强化学习优化压缩策略、做跨会话的记忆迁移等等。但核心逻辑不变:压缩是为了让Agent在有限的上下文里做更多的事,记忆是为了让Agent在更长的时间跨度里保持一致性。把这两个东西搞明白,Agent的上下文管理就通了。