给 14 个 AI Agent 装一个共享大脑:我们的 MemOS 接入始末
我们的 Agent 跑到了 15 个,每一个都是一座孤岛。 同一件事,不同 Agent 有的记了好几遍,有的根本没记。问其中一个上周发生了什么,它茫然地看着你。这个问题一直没断过——直到我们把 memos-local-openclaw-plugin 接了进来。 这是那次接入的完整复盘:三个 bug,没有一个简单。…

我们的 Agent 跑到了 15 个,每一个都是一座孤岛。
同一件事,不同 Agent 有的记了好几遍,有的根本没记。问其中一个上周发生了什么,它茫然地看着你。这个问题一直没断过——直到我们把 memos-local-openclaw-plugin 接了进来。
这是那次接入的完整复盘:三个 bug,没有一个简单。
为什么要共享记忆
单 Agent 的时候,记忆不是大问题,上下文窗口够用。但我们有 14 个 Agent,各管一摊——内容、代码、部署、设计、SEO、社区。它们不共享状态,每次调用都是从零开始。
真正逼我们动手的是一次具体的事。部署 Agent 在一台服务器上搭了个服务,什么都没记下来。两周后,另一个 Agent 被问起那台服务器的状况,它不知道。我们去问部署 Agent,它也忘了。
信息曾经存在过,只是没有地方可以安放。
memos-local-openclaw-plugin
我们选的这个方案:本地运行、SQLite 存储、通过 OpenClaw 的插件接口暴露给所有 Agent、不依赖外部服务。理论上很干净。
实际上一路三个 bug。
Bug 1:filterRelevant 出错时返回空列表
最先发现的问题:记忆搜索过程中,filterRelevant 这一步只要抛异常,返回的就是空列表。不报错,就是安静。
Agent 于是断定没有相关记忆。其实是有的,只是过滤那步崩了。
深挖下去,触发点是一个边界情况:候选列表为空时出现了除零。但更根本的问题是,filterRelevant 失败不该让整个搜索一起失败。
修法:filterRelevant 失败时,退回到返回全部候选,让 Agent 自己判断相关性。给多总比不给强。
Bug 2:Qwen3.5 思考模式返回空 content
我们的 MemOS 用 Qwen3.5 做语义相关性打分。思考模式下,Qwen3.5 的 content 字段可能返回空字符串——真正的推理过程跑到了 reasoning 字段里。
原来的代码直接读 content,拿到空字符串,就判定所有候选都不相关,全过滤掉了。
修法:加一个 fallback——content 为空就读 reasoning。一行代码。但找到它花了长得多的时间,因为症状和 Bug 1 一模一样:不管搜什么,都没有结果。
Bug 3:OpenClaw 的 JS 解析器不支持空值合并运算符
这个最丢人,修得也最快。
代码里用了 ?? 运算符。OpenClaw 内嵌的 JS 解析器不支持它,插件启动时直接抛解析错误,拒载。
把 ?? 全部换成 ||,插件起来了。
记一笔备查:OpenClaw 的 JS 解析器对 ES2020+ 支持不完整,写插件先查语法兼容性。
better-sqlite3 原生重编译
除了逻辑 bug,还有一个环境问题。better-sqlite3 自带预编译的原生绑定,只针对特定 Node.js 版本。我们跑 Node v25,npm 上的预编译二进制是按旧版本构建的,加载不了。
修法:npm rebuild better-sqlite3。机器上要有 Xcode Command Line Tools——Apple Silicon 的机器一般都有。
现在是什么样子
搜"部署服务器",相关的部署记录秒回。14 个 Agent 共享同一个知识库,一个存进去的,其他任何一个都能取出来。
效果比我预期的还大。之前每个 Agent 都是无状态的,每次调用都要重建上下文。有了共享记忆,就有了连续性。Agent 记得发生过什么,记得做过什么决定,也记得哪里出过错。
那是完全不同的一种系统。
Bug 汇总
| 问题 | 根因 | 修法 |
|---|---|---|
| 搜索返回空 | filterRelevant 异常没有 fallback | 失败时返回全部候选 |
| 搜索返回空 | Qwen3.5 思考模式 content 字段为空 | 回退读 reasoning 字段 |
| 插件起不来 | ?? 运算符不支持 | ?? 换成 or 形式绕过 |
| 原生绑定加载失败 | Node 版本不匹配 | npm rebuild better-sqlite3 |
共享记忆不是锦上添花,它是多 Agent 协作的地基。bug 不算多,但每一个都藏在你不会主动去翻的角落。