这篇稿子不是我接的常规需求,是同事转来的一个内部项目复盘记录,说是某集团西南研发基地里,他们在跑“智能体指挥中枢”这个项目时,把记忆架构从玩具级重构成了能用级。我拿到资料后,把里面关于向量数据库选型、长短期记忆拆分和优化的工作整理成了下面这份实战笔记。里面所有的系统名、人名都已做脱敏处理,但技术细节和踩坑路径都是真实的。
1. 这个项目到底在解决什么问题:记忆不该是聊天记录
先说背景。去年下半年,我们接到某集团西南研发基地的一个需求,要做一个“智能体指挥中枢”,本质上就是一个能拆解任务、调度多个子智能体的平台。需求方给的验收标准很有意思:不是模型能答多准,而是“它能不能记得住事情”。
这里的“记得住”不是指上下文窗口里的会话记录,而是指跨任务的长期行为记忆。比如,某个业务模块的负责人上周让智能体把周报格式改成“先结论后数据”,那这个偏好这周、下个月、换了一个会话之后还要生效。再比如,智能体在处理退款流程时,不小心把某个用户的投诉等级判高了,这周用户又来了,它要能回忆起上次的判断,并主动修正。
需求方管这个叫“AI agent指挥官”的长短期记忆架构,但坦白说,项目刚开始的时候,我们团队对“记忆”这两个字的理解是分裂的。
一部分人认为,用向量数据库把所有对话切块存进去,检索到相关片段拼进提示词里就是长期记忆。另一部分人认为,这不就是扩大上下文吗,直接调模型的长上下文版本不就行了。
我们为此吵了两周。最后测试数据说话:纯靠长上下文窗口,4000 tokens以内的会话还能应付,一旦涉及多轮任务拆解、跨天跨周的记忆回填,模型的表现就是灾难级的——它不是忘了,而是把不同时间、不同任务的细节混淆到一起,产生严重的幻觉。
问题的本质在于,长上下文是“无差别记忆”,它把所有信息平权存储,模型无法区分哪些是稳定的用户偏好、哪些是临时任务状态、哪些是已经过期作废的中间结论。而智能体真正需要的记忆架构,应该像人的大脑一样,把记忆分成明显的两层:短期工作记忆和长期语义记忆。
于是我们定了这个改造方向:短期记忆负责当前任务链路上的状态跟踪,长期记忆负责沉淀跨会话的稳定偏好和业务规则,中间用一层向量检索和记忆压缩机制来衔接。
2. 向量数据库选型的真实决策过程:没有银弹,只有取舍
选型这件事,我们前后花了三周。一开始我们的想法很单纯:找一个开箱即用的向量数据库,把数据一插,检索接口一调,完事。但真正把需求铺开之后,发现坑很深。
先列一下我们在西南基地那套环境里的硬约束:数据规模不大,纯文本记忆片段,峰值也就几十万条量级;并发量不算高,但要求响应的稳定性;服务器资源有限,GPU和内存都不能放开用,因为同期还有其他AI服务在共享算力;团队没有专职的数据库运维。
基于这些约束,我们筛掉了重型的分布式向量数据库。不是说它不好,而是杀鸡用牛刀,后面的运维成本我们扛不住。我们最终圈定了四个候选:开源的FAISS、轻量的Annoy、功能完整的Milvus Lite,以及Postgres的向量扩展pgvector。
这里就是第一个经验教训:向量数据库的选型,千万别只看召回率榜单,要看你自己的数据规模、并发模型和团队运维能力。
我做了个对比表,方便大家看得清楚:
| 方案 | 部署难度 | 千条数据召回耗时(实测) | 内存占用 | 增量更新 | 混合过滤 | 运维成本 |
|---|---|---|---|---|---|---|
| FAISS (IVF+HNSW) | 低 | 6ms | 中等 | 需重建索引 | 需自己实现 | 低 |
| Annoy | 最低 | 10ms | 低,只读加载 | 不支持 | 需自己实现 | 低 |
| Milvus Lite | 中 | 8ms | 中等 | 支持 | 支持 | 中 |
| pgvector | 取决于已有PG | 18ms | 复用PG | 支持 | 原生SQL | 低 |
实测下来,单看耗时代数,FAISS的IVF+HNSW组合最好。但注意一个致命问题:FAISS的索引一旦构建,增量更新会很痛苦,要么重建,要么用ID映射批量追加,对于“每天持续写入新记忆”的场景,这个短板无法接受。
Annoy干脆不支持增量追加,只适合离线构建索引的检索场景,我们直接放弃。
Milvus Lite功能最全,支持标量过滤和向量检索混合,但它在低配服务器上吃内存还是偏多,我们测试时开到50万条数据,内存直奔8GB,会影响同机部署的其他服务。
最终我们选了pgvector。原因有三个,都很务实。
第一,我们本来就有一套Postgres在跑业务元数据,再加一个扩展,不用多维护一套服务。第二,记忆片段不只有文本向量,还有来源会话ID、任务类型、可信度评分、创建时间、遗忘状态这些结构化字段,pgvector天然支持SQL过滤,可以一边按业务条件过滤,一边做向量排序,这个能力其他方案都要自己拼。第三,团队对SQL太熟了,排查问题、改数据、写脚本,效率完全不在一个层级。
如果你手头也是中小规模RAG项目,我建议你认真考虑pgvector,它可能不是最酷的选项,但它是让你睡得着觉的选项。
3. 长期记忆的写入流程:不是存了就完事,得先“消化”
选定pgvector之后,真正的挑战才开始。因为“记忆”不是一拍脑袋把对话丢进数据库那么简单。我们见过太多失败案例:把用户问题、历史聊天记录全部切块入向量库,结果检索出来的上下文既不相关,又互相矛盾,模型被喂了大量噪音。
我们在项目里把长期记忆的写入拆成了五个步骤,每一步都有明确的目的,这里我用一个真实的业务场景串起来讲。
场景是这样的:智能体负责处理企业内部的报销流程咨询,用户某开发部的小王,他提出过一个特殊偏好:“报销单金额超过5000元的,先发到部门财务那里做预审,不要直接走系统自动审批。”
第一步:信息抽取。我们不能把小王说的这句话直接切块存起来,因为这句话里混合了偏好内容、触发条件、操作对象。我们用一个轻量级的抽取模型输出结构化字段,把这句自然语言拆成:主体=小王,条件=报销金额>5000元,动作=先送部门财务预审,优先级=高。
这一步的作用是让后续的检索不再依赖模糊的全文匹配,而是能按结构化条件精确找回来。
第二步:语义向量化。我们把上面那个结构化描述转成一段专用于记忆检索的规范化文本,再交给嵌入模型转成向量。注意,这里我们做了个关键选择:不用原始口语文本向量化,而是用结构化重写后的“记忆语义单元”向量化。
原因很简单,原始文本里小王的那句话是口语化的、带上下文的,单独切出来做成向量,检索时容易匹配到一堆不相关的碎片,比如其他报销问题的对话。而“记忆语义单元”是独立自洽的,检索时语义边界清晰。
第三步:冲突检测。存入之前要检查新的记忆是否与旧记忆冲突。假设小王的偏好上周已经存了一条:“报销金额超过3000元的需要部门预审”,现在新来的是5000元,两条例冲突。我们的冲突检测逻辑是:把新记忆向量与同主体下的现有记忆做相似度比对,相似度超过0.85就直接拦截,进入人工确认或规则覆盖流程。
第四步:可信度评分。这可能是最容易被忽略的一步。我们给每条长期记忆打一个可信度分,分值的计算规则是:初始分数0.5,用户明确确认一次加0.15,智能体推理后用行动成功触发并得到用户正面反馈,再加0.1,衰减规则是60天无复用,分数按0.9的系数逐周下调。
为什么要做可信度?因为在真实落地中,用户无意中的一句话和深思熟虑后设定的一条规则,对系统的价值是完全不同的。没有可信度分层,系统很容易记住一些随口说说其实并无效力的“伪偏好”。
第五步:持久化存储。最后才写入pgvector,一个结构化字段加一个向量字段,一条记录完整落库。
执行完这五步,记忆才算真正被“消化”完。很多团队把记忆系统做崩了,就是因为省掉了抽取、冲突检测和可信度这三步,导致数据越存越多,有效信息密度反而越来越低。
4. 短期记忆与工作记忆区:任务状态的临时跑马场
说完长期记忆,再来说短期记忆。这一层我们参考了Agent的上一个热门框架思路,但做了不少改造,因为我们发现框架默认的工作记忆设计有一个明显缺陷:它假设所有中间步骤都是平等的,实际上不是。
在我们的架构里,短期记忆区是一个TTL驱动的临时存储块,专门存放当前任务链路的运行状态。
举个例子,智能体在处理一个跨三天的报销流程异常跟踪任务时,短记区会记录:
- 当前进度:等待部门财务上传预审反馈
- 待处理事项:小王需要补充原始发票扫描件
- 中间产物:预审意见摘要、修订后的报销金额
- 步骤状态标记:已完成、进行中、阻塞
这些信息的共同点是“时效性极强、不需要沉淀到长期记忆”。三天后任务结束,这些状态就该被清理,否则它们会污染后续任务的检索结果。
我们为短期记忆设计了一套生命周期管理协议,我刚才说的TTL,就是每条短记都带一个过期时间,到期自动清理。同时用任务ID把碎片串起来,同任务内可以快进快出,任务结束后整体废弃。
这里有个很关键的心得:短期记忆根本不需要向量化。一开始我们尝试把短记也切成向量进向量库,检索时混合查询,结果发现完全是给自己添堵。因为短记是“点状”的状态信息,用SQL按任务ID取回即可,向量化只会带来额外交互延迟和噪音。所以代码里我们直接让短记区走Redis,支持结构化和TTL,根本不让它进入pgvector。
长短期记忆之间的搬运规则我们也定得死板但有效:只有当短记区里的某条状态出现三次以上重复,或者被用户明确确认为“以后都这样做”,它才会被送入信息抽取和可信度评分流程,随后写入长期记忆库。
这个“转存阈值得分判定”机制很管用。有一次测试任务里,用户连续三次让智能体用邮件附件方式发送报表,系统第三次时自动抓取了这条行为模式,写入长期记忆,后续所有报表发送请求都默认走邮件附件通道。什么叫智能体具备学习能力?在我看来,这种“从行为中提炼偏好、跨会话自动生效”的能力,比背一百页知识库都实在。
5. 向量库之上的记忆检索策略:相似度不是全部,RAG要点在重排
存储搞定之后,检索侧的工程细节才是体现功力的时候。我们的第一版检索特别朴素:把用户当前问题向量化,用pgvector检索top10记忆片段,塞进提示词上下文,完事。
结果翻车了。典型翻车现场长这样:用户问“报销流程是什么”,系统检索出来的top3记忆片段里,有一条是“某员工因为报销单造假被通报”,还有一条是“财务总监强调票据粘贴不得涂改”,这两条和“流程是什么”的问题都高度相似,但都不是当前真正需要的内容。原因我后面才想明白:向量相似度衡量的是语义层面的相关性,但相关不等于有用,更不等于排序正确。
我们花了两周时间搭建了一个六层的记忆检索策略,这里我把每一层的作用和踩坑点都写清楚。
第一层:快速过滤。先用结构化条件把候选集缩小。比如当前用户是小王,直接锁定主体ID,然后按时间范围过滤最近90天的记忆,按类型过滤掉标记为“临时”的记忆。这一步之后,候选集从全库变成几百条,压力骤减。
第二层:向量召回。在缩小后的集合里做向量检索,取top30。注意,这里取的数量后面会解释。
第三层:规则加权。给记忆片段做加权调和。规则包括:用户明确确认过的记忆加权1.2倍,最近7天刚写入的新记忆加权1.1倍,与当前任务的任务ID匹配的记忆加权1.5倍,遗忘次数多的降权0.8倍。加权结果作为最终召回排序的参考之一。
第四层:去重与冲突消解。如果检索结果里出现互相矛盾的记忆片段,比如一条说“假设你已经获得财务预审”,另一条说“现在要等预审结果才能行动”,这时候不能把两条都塞给模型让它自己判断。正确做法是:我们按可信度分值和写入时间的先后,只保留更可信的那条,另一条标注为“已废弃参考”。
第五层:重排器。这是提升精度的关键一步。我们把第三层、第四层产出的候选记忆片段交给一个rerank模型,让它结合当前问题,对每个片段打一个“对当前回答的贡献度”分数,按分数选top8作为最终的上下文记忆。
重排器看起来多了一次模型调用,但它真正解决了向量召回的排序缺陷。同一个话题,向量召回里排在第五名的片段,可能才是真正关键的约束条件,而因为语义相似度不够高,它被压在了后面。没有重排器,这类问题永远无法绕过去。
第六层:动态剪裁。最终选定的记忆片段还要根据当前上下文长度做剪裁。如果当前对话已经很长,记忆片段的数量就从8砍到4,优先保留与当前任务直接相关的部分。
这一套六层检索下来,整体召回耗时大概增加了一百多毫秒,但响应质量是不可同日而语的。项目在真实线上跑了两周,用户对“智能体更懂我了”这种感知的提升是明确且可感知的。
6. 遗忘机制与记忆卫生:存得多不是本事,忘得巧才见功力
每个做记忆系统的团队都必须直面一个问题:长期记忆库会无限膨胀,而且越是早期存的垃圾记忆,越会拖累后续检索。我们在项目里发现,很多记忆片段从入库那一刻起就注定是垃圾,比如那些用户随口说的、从未被重复引用的、时间一长就与当前业务规则完全脱节的。
所以我们在架构里专门定义了记忆遗忘机制,这算是我觉得整个项目里最值得分享的部分。
遗忘机制分三种策略,组合使用。
第一种是时间衰减遗忘。每条记忆根据可信度评分和最后被检索命中的时间计算“现时效用值”。现时效用值的公式是:
有效值 = 可信度系数 * 衰减系数^max(0, 当前日期 - 最后命中日期)/30
这里衰减系数我们默认设为0.85。也就是说,一条记忆如果90天内从没被检索命中过,它的有效值会降到初始的0.61倍,如果180天没被用过,降到0.37倍。有效值低于0.25的记忆标记为“休眠”状态,只保留元数据,不再参与检索。
第二种是冲突覆盖遗忘。当两条记忆的向量相似度超过0.9,且业务语义指向同一主体同一动作时,系统只保留可信度分更高或写入时间更新的那一条,旧记忆直接物理删除。这个机制保证了同样的偏好不会因为用户重复表达而积累出多个版本,把检索结果搞乱。
第三种是主动清理。每个季度我们会跑一次离线脚本,把已验证失效的记忆批量删除。什么叫“已验证失效”?比如某条记忆引用了已报废的内部流程编号,脚本会自动比对现有流程表的编号,发现对不上就自动将其标记为废弃。这部分工作也可以手工执行,避免误删。
这里有一个很重要的观点,我认为所有做记忆系统的人都应该记住:AI智能体的记忆不是收藏夹,不是积得越多越好,而是要在存储成本、检索精度和自适应能力之间找平衡。一个无限增长的记忆库,最终会导致系统越来越迟钝。
配置一个合理的遗忘策略,对生产环境来说不是可选项,而是必选项。
7. 回填与记忆内化:让记忆在实战中越用越准
最后一层设计,我称之为“记忆回填与内化”。这是整个架构里最体现“指挥官”气质的地方,因为系统不只是被动地存取,它能根据任务执行结果倒逼记忆更新。
具体机制是这样的:当一个任务执行完成后,系统会把当前任务的完整链路做一个复盘式的处理。举个例子,智能体在处理一次报销异常任务时,采用了“先联系部门财务确认发票真伪,再发起审批”这组操作,最终任务顺利完成,用户明确表示满意。
这一个“采用→成功→满意”的完整闭环,会被系统识别为一个高可信度的流程性记忆候选,随后它会走一遍前面说的信息抽取、向量化、可信度评分流程,被写入长期记忆库。
更关键的是,这个回填过程是一个自我强化的循环。假设后续又有类似报销异常任务,系统检索到这个高可信度的流程记忆,按它的套路执行再次成功,这条记忆的可信度分值又会再涨一截。
反过来,如果按这条记忆执行失败了,或者用户表示不认可,系统会触发“记忆降级”逻辑,在保留记录但不参与排序的休眠记忆区存一段时间,让新的可能性参与竞争。这样一来,可以说它是通过实战反馈在持续校准自己的记忆体系和决策路径,而不是靠人肉写死规则。
这里有一个很重要的工程细节:回填的记忆命中了旧记忆怎么办?我们做了去重检测,如果近一个月内已经存在同类的流程记忆,新回填的不直接覆盖,而是转入“同义审核待确认区”,等到人工复核或等到下一个月趋势稳定后再合并。这个设计极大避免了系统自我强化出偏移行为。
写到这,可能有人会问,这套架构跑起来效果到底如何?
我在项目里做了一组对比测试,同一批问题集,在无记忆、有长上下文记忆、有本架构记忆三种模式下跑了一遍。无记忆几乎是平均60分的水平,有长上下文记忆的能到75分,但容易出现不同时间信息互相打架,而我们的架构在稳定性和准确性上明显最强,最关键的是它对各种细碎、隐藏语义的把握很稳,不会答非所问。
从我们团队的经验来说,我最大的体会是:RAG项目不等于“堆得越快越多越好”,它更像是一个长期养成的过程,系统的价值很大程度取决于你对信息生命周期的管理做得好不好。这套长短期记忆架构,目前已经在我们内部的多个场景复用了。
如果你最近也在折腾智能体的记忆系统,希望这份踩坑笔记能帮上一点忙。如果你也正在做相关架构的选型,有更好的方案,非常欢迎交流,这个领域每天都在变化,我格外需要知道别的团队是怎么做的,好持续进行迭代。