智能体用记忆层,已经不是新鲜事。把对话、偏好和长期事实存进一个共享的记忆服务,让多个智能体都能读能写,是不少团队正在落地的做法。mem0 在 9 月 18 日发布的 2.1.0 版本里,给记忆 SDK 的每次请求挂上了三层身份头。听着像个小改动,但它戳中的是多智能体共用记忆层长期没人认真补的盲区:一条记忆查询打过来,服务端其实并不知道是谁发的。

为什么记忆层需要「身份」

当只有一个智能体、一个应用的时候,记忆的归属不是问题——谁写的、谁读的,上下文里都清楚。可一旦多个编码智能体、多个插件、多个宿主应用共用同一个记忆实例,匿名请求就会出现。一条记忆被写进来,事后你想查「这是哪个产品、哪一层插件写的」,往往只能靠猜;一条涉及隐私的记忆被读走,你想审计「谁动了它」,也没有字段可追。这类问题在单智能体阶段被掩盖了,等到智能体数量涨上去,溯源就成了硬需求。

增量认知:记忆可观测性这几年被反复提起,但多数方案停留在「记了什么」,很少回答「谁记的、谁读的」。mem0 这一版的思路是把身份从应用层下放到每次请求的头里,让溯源变成服务端的默认能力,而不是各团队各写一套补丁。

三层头分别解决什么

2.1.0 引入了三个一次性声明的表面身份头。X-Mem0-Source 标明这是哪个产品面(比如某款 IDE 插件),X-Application 标明它跑在哪个宿主应用里,两者都是 set-once,外层已经声明过的身份会被保留。真正关键的是 X-Mem0-Client,它是一个追加式的调用链:按从外到内的顺序记录每一层的名字和版本,于是当一个插件通过 SDK 再去调记忆时,平台拿到的是整条链,而不是只剩最后一跳的调用方。

改前:一条裸记忆查询who / which app 全靠猜改后:三层身份头X-Mem0-Source = 产品X-Application = 宿主应用X-Mem0-Client = 调用链
图 1|同样的记忆写入,加身份头后服务端才知道是谁发起、来自哪个产品。

这套设计的用意很直接:一个记忆实例上面可能挂着六七个不同的智能体,如果每次请求只报出最里层那一个,平台看到的永远是「最后一个调用者」,真正发起请求的产品面反而被埋掉了。调用链把层级摊开,归属才能还原。环境变量的写法(MEM0_SOURCE、MEM0_APPLICATION、MEM0_CLIENT_STACK)也给那些没法传选项的封装层留了口子。

调用链为什么用追加而不是覆盖

这里有个容易踩的坑:如果调用链允许后一层直接覆盖前一层,那么任意一层都能伪装成更早的调用方,溯源就形同虚设。mem0 选择追加且不允许中间层改写,并规定 SDK 自己的那一项是保留字。实现上还顺手处理了一个细节——调用链做字符串拼接时,是按整条丢弃而不是按字符截断,因为截断可能在名字中间切断标识符,平台反而会把残缺字符串当成一个真实的客户端去解析。

它解决什么,不解决什么

边界提醒:身份头是「声明式信任」,不是密码学鉴权。它让服务端能区分来源、做归因和审计,但并不能阻止一个被攻陷的插件伪造自己的来源字段。真正的访问控制仍要落在密钥、权限策略和运行时隔离上,记忆层的身份头是审计线索,不是安全边界。

从落地价值看,这套头对三类场景最有用:一是多智能体调试,某条记忆被错误写入时,能快速定位是哪一层;二是成本与用量归因,不同产品、不同插件的记忆调用可以分开计量;三是合规留痕,谁在什么时候动过哪类记忆,有了可追的字段。它是把「记忆可观测」从口号落到具体字段的一步,而不是记忆安全的最终答案。

六个编码智能体共用一个 mem0 实例mem0Agent AAgent BAgent CAgent DAgent E...每个调用都带 Source/Application/Client 标签,服务端可逐条回溯归属
图 2|六个智能体打到同一个记忆实例,带标签后每一条读写都能归因到具体产品与插件层。

对正在搭建多智能体记忆层的团队,这一版值得跟进的点不在于功能多炫,而在于它把「谁在问」变成了一个默认就有的字段。等你的智能体数量涨到十几个,再回头补溯源,成本会比现在高得多。