做智能体应用有一段时间的人,大概率都遇到过这样的尴尬:上一轮刚告诉助手“我叫XX”,下一轮它又礼貌地问你贵姓;让两个分头协作的智能体处理同一个项目,结果它们各自为政,重复整理了一堆相同信息。说白了,问题出在记忆管理——短期记忆和长期记忆怎么配合、多个智能体之间怎么共享和隔离记忆,这些没有想清楚,功能越复杂越容易翻车。
这篇文章想把我自己梳理的这套原理和落地经验讲清楚:短期/长期记忆协同的工作方式,多智能体场景下记忆共享与隔离该怎么设计,以及一套可以上手的轻量级参考方案。适合正在写智能体框架、做对话式产品、或者被上下文窗口逼疯的开发者;产品经理如果想搞懂“为什么智能体总是记不住”,也值得读一读。
1. 记忆不是缓存,而是智能体的行为基础
1.1 为什么大模型离不开记忆管理
先想一个问题:大模型本身有没有记忆?严格说没有。一个模型在每次调用时,只能看到你给它的上下文,上一次对话的内容并不会自动保留。你感觉它有记忆,是因为框架帮你把历史消息拼到了新的请求里。前端的对话列表、后端的会话数据、最近几轮的摘要,这些都属于“临时记忆”。
这种临时方案在小场景下够用,但一旦涉及到持续服务、个性化推荐、多角色协作,问题立刻暴露。比如用户两周前定制过一个报表格式,这周又来了,系统如果找不到这份偏好,就会重新问一遍;一个负责数据采集的智能体把结果存放在自己的会话里,另一个负责分析的智能体完全读不到,中间的沟通成本全落在开发者头上。更麻烦的是,当一个任务跨越多个会话时,如果没有记忆沉淀,每次开工都要从零开始,之前形成的判断和结论全部丢失。
所以记忆管理不是附加功能,而是智能体能不能在真实业务里存活的基础。它决定了三件事:记得住,也就是存储的完整度;找得到,也就是检索的准确度;用得好,也就是注入提示词的时机和格式。这三个能力环环相扣,任何一个环节缺失,都会让智能体表现出“间歇性失忆”。
1.2 短期与长期不是时间长短,而是职责不同
很多资料把短期记忆和长期记忆简单理解成“存多久”,我觉得不对。它们的本质差异是职责。
短期记忆对应“工作台”。它保存当前任务正在使用的信息,比如本次对话最近几轮、临时计算结果、当前步骤的中间状态。它的特点是易变、需要频繁读写、对响应速度敏感。你正在写一段文章,刚才改的标题、当前段落的大纲,这些都在工作台上,不需要立刻存进档案库。
长期记忆对应“档案柜”或者“经验笔记本”。它保存会跨会话复用的信息:用户偏好、项目事实、历史结论、领域规则。它的特点是稳定、更新频率低、需要被检索定位。比如“客户公司的报表必须用PDF格式”“这个项目的技术栈是Python”,这些信息今天用得到,下周仍然用得到。
在设计时,我会把两者看作一个分层存储系统:短期是高性能热区,长期是低成本冷区,中间靠写入和回填动作联动。只做短期记忆,智能体永远活在当下;只做长期记忆,智能体缺乏对当前进展的感知。两者协同,才像一个有经验的助手,而不是一台失忆的问答机。
2. 短期记忆的工程化:先解决“上下文窗口不够用”
2.1 短期记忆的三种常见实现
先讲短期记忆,因为它更接近工程,也更容易被理解。
第一种是“原样缓存”。把每次的对话消息按顺序存下来,新一轮请求时取出最近N条拼进去。实现简单、信息不失真,但会快速撑爆上下文窗口。如果用户的对话里夹杂着大段代码、长日志,往往五六轮之后就没法再塞新内容了。
第二种是“滑窗截断”。只保留最近K轮,更早的直接丢弃。很多框架的默认做法就是这样,但问题在于如果用户在第1轮说了关键需求,第10轮才用到,它早被踢出去了。这个方案适合一次性短任务,一旦对话变长,关键信息丢失几乎必然发生。
第三种是“滑动摘要”。窗口内保留最近几轮原文,窗口外更早的内容压缩成摘要挂在提示词开头。这种方式能保留更多历史脉络,但摘要会丢失细节,对摘要模型能力也有要求。摘要是“压缩损失”的过程,压缩得越狠,越容易丢失用户原话里的微妙措辞。
实际中我是组合用的:近3轮原文保留,更早的压缩成逐轮摘要,全局只保留一份用户画像级别的长期摘要。这相当于工作台上层是“当前能看到的纸”,下层是“几张写满要点的便签”,再下层才是档案柜。短期记忆不是越多越好,而是要在保留细节和节约空间之间找平衡。
2.2 短期记忆的容量怎么算
这里给一个可复用的估算方法。假设模型上下文窗口是8K token,你至少要留出3K token给系统提示词、工具定义和生成本轮回答,那给历史消息的预算就是5K token。如果单轮对话平均消耗0.6K token,那5K大约能容纳8轮。但真实情况还有长文本、工具调用结果,我不会卡满上限,会取60%-70%作为水位线,也就是保留最近5-6轮原文,超出部分的旧内容,要么摘要,要么转成长期记忆。
提示:短期记忆不能简单“越多越好”。上下文越长,模型对中段内容的注意力衰减越严重,有时候保留10轮啰嗦对话,效果反而比压缩成3条要点差。宁可让模型看到的少而精,也不要让它淹没在无关历史里。
2.3 短期记忆的写入与清除时机
短期记忆的写入时机通常是每一次模型输入前和输出后:输入前,加载当前任务的上下文;输出后,把新的消息追加到工作区。这个工作区可以在内存里,也可以在键值存储中,按会话ID或者任务ID作为键。还需要记录每一条消息的角色、时间和内容类型,方便后续决定哪些内容值得晋升到长期记忆。
清除时机有三个:任务完成、会话超时、窗口溢出。任务完成时把工作区标记为可归档,需要长期保留的内容落库到长期记忆,不需要的直接清空;会话超时可以根据业务定,比如30分钟没有新消息就释放内存;窗口溢出就是我们上面说的摘要转存。一定要给每个记忆条目打上时间戳和来源,否则后面做遗忘和排查都会很头痛。
3. 长期记忆的设计:从“能存”到“能想起来”
3.1 长期记忆里到底该存什么
长期记忆里不是所有信息都值得进。我的经验是只存三类:事实性信息、偏好性信息、经验性信息。
事实性信息:比如用户的名字、项目截止日期、某个系统的接口地址。这类信息准确、可校验,适合结构化存储。偏好性信息:比如用户习惯先看结论再看细节、周报喜欢表格、代码风格用空格不用tab。这类信息来自对行为的归纳,通常带不确定性,需要长期观察修正。经验性信息:比如上次处理类似故障时发现“重启前置服务比直接重启主服务更有效”。这类信息是智能体自己的“成长记录”,在多智能体场景下特别有价值。
分类的意义在于决定后续的检索策略和更新策略。事实类可以直接查表,偏好和经验类更适合向量化之后做相似度检索。如果你把所有内容塞进一个向量库,看起来省事,但大杂烩的检索质量会很差。我见过不少项目,长期记忆库里什么都有,结果检索时返回一堆风格相似但方向无关的内容,模型被带偏。
3.2 写入长期记忆的触发规则
长期记忆不是每轮对话都写,否则库里很快堆满垃圾。我通常会设置几个触发条件:
新事实出现:用户给出了关于自身或项目的新信息,比如“我们公司明年搬到南城”。明确偏好表达:用户说“以后这类邮件都抄送给财务”。重要结论达成:智能体帮助用户完成了一个多步骤推理,最终结论值得保留。反常行为:用户突然修改了既往设置,这往往说明偏好变了,需要更新而不是新增。
每个触发条件都对应一个“收益评估”:这条信息如果不存,下次会不会造成重复沟通?会不会导致决策错误?如果是,就值得写。如果没有触发条件,我会给每条候选记忆打一个重要性分,评分低于阈值的直接丢弃。宁愿漏存一些边缘信息,也不能让库里塞满噪声。
3.3 检索与注入:长期记忆怎么被用起来
存得好不如取得好。我常用的检索策略是“混合检索 + 重排”。
混合检索:先用关键词或结构化条件缩小范围,比如最近30天、类型是偏好、与当前会话角色相关,再做向量相似度检索,取Top K。这样可以避免纯向量检索在罕见词、编号、人名上失忆的问题。很多人以为向量检索是万能的,但在专有名词和数字面前,混合检索明显更稳。
重排:把混合检索召回的记忆条目按“相关性、时效性、来源可信度”综合打分。相关性解决“是不是这件事”,时效性解决“这个信息是否过时”,来源可信度解决“来自用户本人还是智能体猜测”。三者加权,比如0.6/0.3/0.1,实际权重可以按业务调。重排后取前3-5条,拼接到系统提示词里。
注入的时机也很关键。我的做法是偏好在系统提示词固定位置注入,事实类在对话开始前注入,经验类只在触发相关任务时注入。全部塞在最前面会让模型分不清主次,反而削弱了对当前指令的注意力。
4. 多智能体场景:记忆共享与隔离的博弈
4.1 单智能体方案为什么不能直接搬
单智能体场景下,记忆围绕着一个“自我”组织,所有信息都归同一个主人。多智能体场景完全不同:每个智能体有自己的角色、目标和知识边界。比如一个项目里,调研智能体负责搜集资料,写作智能体负责输出文章,审核智能体负责检查事实错误。如果它们共享同一份长期记忆,调研智能体写下的中间草稿会被写作智能体当作最终结论;如果完全隔离,写作智能体得不到调研结果,任务无法推进。
所以多智能体记忆设计的核心不是“让所有智能体记住一切”,而是“在正确的层次共享正确的信息”。我会把记忆分成三个层次:个体记忆、团队记忆、全局记忆。个体记忆只有自己读写,存放角色擅长的私有经验;团队记忆面向同一个任务组,组内可见;全局记忆是整个系统共享的常识和规则,任何智能体都能读。
4.2 三种共享模式怎么选
第一种是广播模式。写一条记忆,所有智能体都同步一份,适合播报系统级状态,比如“底层数据库已经切换到新环境”,所有人都应该知道。它的缺点是没有过滤,智能体会被无关信息干扰,时间一长,每个智能体的上下文里都堆满了和自己工作无关的状态信息。
第二种是订阅模式。每个智能体维护一个感兴趣的主题列表,比如财务智能体只订阅“预算”“报销”“合同金额”相关记忆;只有当写入的记忆命中了它的订阅规则,才会推送给它。这种模式适合在智能体数量多、职责区分明确的场景。订阅模式相当于让每个智能体自己决定“我要关心什么”,能把干扰降到最低。
第三种是黑板模式。所有任务相关的过程信息都写到一个共享区域,每个智能体只往自己的格子写,需要时再去读别人的格子。黑板模式适合复杂的阶段性任务,比如调研智能体把原始材料放到“inputs”区,写作智能体从中读取,再把草稿放到“drafts”区,审核智能体只看“drafts”。整个过程有空间结构,权限容易控制。
我实践下来最稳的组合是:全局广播只用于极少数关键状态,任务流程用黑板模式,长期经验用订阅模式。这样既保证关键信息不遗漏,又不至于让每个智能体都被无关信息淹没。
4.3 权限、命名空间与冲突处理
多智能体记忆一定要有权限意识和命名空间。最简单的做法是给每条记忆加读写标签:readers和writers。默认情况下,一个智能体创建的记忆,只有同任务组的智能体可以读,其他智能体必须显式授权。命名空间可以从粗到细:全局空间、团队空间、个体空间。每一层都有独立的读写规则,不能越权。
冲突处理是最容易踩坑的地方。两个智能体可能同时更新同一条事实,比如一个智能体从客户邮件推断出“交付日期是周五”,另一个智能体从项目计划里读到“交付日期是下周一”,两份记忆写入时会打架。我的方案是给每条可变记忆加version字段,写入时带上基于更新版本号的条件更新;如果版本冲突,不盲目覆盖,而是把两条都保留,交给冲突处理策略。可以保留最近的、保留来源优先级高的、或者生成需要人工确认的待办。具体选哪种,取决于业务对准确性的敏感度。
5. 我踩过的坑:常见问题排查实录
5.1 记忆污染:不该记的记了一大堆
记忆污染是我见过最多的问题。只要每轮对话都写入长期记忆,用不了几天库里全是“用户今天说了句早上好”“智能体回复了收到”这类垃圾。排查时先看长期记忆库里无关条目的比例,如果超过30%,基本是写入触发条件没设置。
解决思路很直接:写入前加一道重要性评分,给我用的一条规则是——如果这条信息在24小时内再次被用到,分数+1;如果被用到三次以上,才真正进入长期记忆。这就把一次性信息挡在了门外。同时给记忆条目标记来源类型,后续可以做统计和清洗。定期跑一次清理任务,把低重要性、长时间未命中的旧条目归档或删除,能让检索质量长期维持稳定。
5.2 检索命中率低:问题不一定在向量模型
很多人觉得检索差就是向量模型不好,其实我遇到的大部分检索问题出在前处理和后处理。前处理是查询意图没有拆解:用户问“上次那个表格后来怎么样了”,如果直接用整句去检索,不如先抽取关键词“表格”“上次”和实体“项目名”,再构造检索请求。后处理是召回后没有重排,向量召回的结果里混着时序不对、来源不对的内容,硬塞给模型当然效果差。
排查顺序我一般是这样:先看召回阶段有没有命中目标记忆,如果召回为空,检查文本切分长度、查询改写;如果召回了但模型没用,检查注入位置和格式是否清晰。两个环节分开排查,比盲目换模型有效得多。我见过一个项目,向量模型换了好几个都没解决问题,最后发现是旧记忆没有清理,库里全是过时信息。
5.3 多智能体重复记忆与互相覆盖
多智能体协作里,我碰到过调研智能体和写作智能体同时把“项目代号为Pilot”写入长期记忆,两条内容几乎一样。如果不做去重,检索时返回两个相似结果,白白占用上下文;如果直接按“最后写入覆盖”处理,又可能出现旧数据覆盖新数据的情况。
我的处理是写入时先做相似度检查,与已有条目的向量相似度超过0.92,就直接做更新而不是新增;低于0.92但语义相近,保留两条但用归组ID关联。更新时带上版本号,不满足版本条件的写入被拒。再配合命名空间隔离,能规避一大部分互相覆盖。
注意:相似度阈值不是拍脑袋定的。我在某个业务里用0.92,另一个细粒度标签多的场景用的是0.85。阈值需要拿测试集验证,建议准备几百条样本自己调。
6. 一套轻量级的记忆管理参考方案
6.1 最小可运行的结构设计
如果从零搭建,我不会一上来就引入重框架。一个最小的结构只需要三张表:短期记忆表存会话ID、角色、内容、时间戳;长期记忆表存记忆ID、智能体ID、类型、内容、向量、版本号、时间戳、重要性;共享记忆表存命名空间、读者列表、写者列表、内容。所有记忆都有智能体ID和命名空间,这样隔离和共享都能通过字段控制。
短期记忆直接用内存或文件缓存,长期记忆用嵌入式数据库加本地向量索引,完全够个人项目或小团队原型用。等规模上来之后,再把存储层替换成独立服务,但上层逻辑不需要大改。
6.2 核心流程的参考实现
写入长期记忆的主流程可以简化成下面这段伪代码:
async def write_long_term_memory(memory, agent_id): memory.namespace = memory.namespace or default_namespace(agent_id) if not should_persist(memory): # 重要性判断 return memory.embedding = embed(memory.content) similar = vector_search(memory.namespace, memory.embedding, top_k=1) if similar and sim_score(similar[0], memory.embedding) > 0.92: # 相似条目存在,走更新而不是新增 update_memory(similar[0].memory_id, memory, expect_version=similar[0].version) else: insert_memory(memory)读取长期记忆的流程也一样:
async def read_long_term_memory(query, agent_id, namespace=None): entities = extract_entities(query) candidates = hybrid_search(query, entities, namespace, top_k=20) ranked = rerank(candidates, query) # 相关性 0.6, 时效 0.3, 来源 0.1 return ranked[:5]这个实现没法直接跑,但结构能把写入、更新、检索、重排四个关键动作拆开,方便你替换成自己的存储和模型。
6.3 关键参数怎么定
有几个参数我建议先不追求完美,用经验值起步:
- 上下文窗口预留比例:60%-70%给历史会话,留足输出空间。
- 短期记忆原文保留轮数:新手先设5轮,验证效果后再调整。
- 长期记忆条数上限:初期可以设5000条,满了之后优先清理低重要度、超过90天未命中的条目。
- 向量相似度阈值:0.85-0.92之间起步,用自己的测试集调。
- 重排权重:相关性0.6,时效性0.3,来源可信度0.1,这个组合在大多数业务里表现稳。
这些参数都要能从配置中心调整,因为不同业务差异很大。我在一个法律咨询场景里,短期记忆只保留3轮就够,因为用户问题通常很短;但在长文档写作场景,保留10轮原文都嫌少。所以经验值只是起点,真正要做的是一边跑一边看数据调参。
6.4 扩展方向:从“会记住”到“会反思”
基础版本能用之后,可以往两个方向升级。
一是分层记忆:把记忆从原始消息、事实、偏好、经验四个层级组织,低层自动从高层沉淀,高层再反过来指导低层信息的取舍。这样记忆不再是一堆散落的点,而是一张有结构的网。
二是反思机制:定期让一个专门的角色回顾最近一段时间的记忆,提炼出新的经验和模式,形成“关于记忆的记忆”。比如它发现用户每周五都要汇总本周进展,于是自动生成一条偏好记忆“周五自动生成周报摘要”,这就是从数据到经验的跃迁。
这些扩展都会让系统更像一个真正的助手,但每一个都会带来新的成本和复杂度,建议等基础流程稳定后再动。
这套方案我完全基于开源组件和简单的表结构搭起来后,在一套模拟的多人协作智能体项目里跑了三个月。最大的体会是:记忆系统的90%问题出在写入端和检索端,而不是存储端。与其花大力气调数据库,不如先把“什么值得记”和“怎么找回来”想清楚。
最后分享一个小技巧:每次会话结束时,花几秒钟让智能体用两三句话总结这次会话的“可复用信息”,把总结写入长期记忆。这个动作成本极低,但在长期运行里会带来非常明显的效果提升,也是我最推荐先落地的一件事。