feat(memory): add Mem0 as an optional external plugin memory provider - #546
Conversation
Mem0 (https://github.com/mem0ai/mem0) is a standalone memory service for LLM extraction, dedup, and vector recall. This PR plugs it in as a community-supported, opt-in plugin that stacks on top of the 4 builtin providers (Builtin / Structured / Session / Fact) — none of which are touched. The plugin is NOT in the default stack: mateclaw-server does not depend on the new module, users build the JAR and drop it into plugins/ themselves. Why: MateClaw is local-first with zero external dependencies. Mem0 requires a self-hosted service stack (FastAPI + pgvector + optional Neo4j). That's a reasonable trade-off for people who want a purpose-built semantic recall channel, but it stays opt-in. What's included: SPI extension (mateclaw-plugin-api): - PluginMemoryProvider.prefetch(Long, String, String ownerKey) default method — degrades to the two-arg variant. Without this, plugins can't receive the resolved ownerKey and per-owner recall is impossible. Bridge (mateclaw-server): - PluginMemoryBridge overrides the three-arg prefetch to forward ownerKey verbatim. Without the override, the interface default would silently drop ownerKey for every plugin provider. New module mateclaw-plugin-mem0: - Mem0Client: JDK HttpClient wrapper for POST /memories/ and POST /memories/search/. Zero external deps (Jackson + SLF4J only, both provided-scope). - Mem0Provider: implements PluginMemoryProvider. prefetch calls search for semantic recall; syncTurn pushes each turn asynchronously via a single-thread daemon executor; systemPromptBlock returns empty to avoid per-turn token bloat; getToolBeans returns empty (v1). - Mem0Plugin: entrypoint. Reads 6 config fields, wires the provider, registers via context.registerMemoryProvider. - Fault isolation: any HTTP error / exception is swallowed and logged — Mem0 being down never breaks the platform's local memory. Per-owner isolation mapping: - MateClaw ownerKey ("user:42", "feishu:sender_abc", ...) → Mem0 user_id (verbatim). - MateClaw agentId → Mem0 agent_id. Known limitations (documented in docs/{zh,en}/memory.md): - syncTurn's SPI signature has no ownerKey, so push falls back to agentId as user_id. Coarser than prefetch. Set syncEnabled=false to rely on prefetch-only recall. - The [Mem0 Recall] block returned by prefetch is NOT subject to the system-block-max-chars injection budget (that budget only governs user/feedback structured entries). maxResults is the only size knob. - v1 does not expose mem0_search / mem0_add style agent tools. Documentation: - docs/zh/memory.md and docs/en/memory.md each get a new section "Mem0 集成(可选)" / "Mem0 Integration (Optional)" covering positioning, behavior table, per-owner mapping, install steps, config reference, and known limitations. Tests (42 total, all green): - mateclaw-plugin-mem0 module (31 tests, runs on JDK 17+): Mem0ConfigTest (5), Mem0ClientTest (7, uses JDK HttpServer as a mock Mem0), Mem0ProviderTest (15), Mem0PluginTest (4). - mateclaw-server module (11 tests, requires JDK 21): PluginMemoryBridgeTest (6) — verifies ownerKey forwarding and graceful degradation when the plugin doesn't override three-arg. MemoryManagerPluginPrefetchTest (5) — verifies the full prefetchAll → bridge → plugin three-arg path, including fault isolation and unavailable-plugin filtering. Verified with: - mvn -pl mateclaw-plugin-mem0 -am test (JDK 17): 31/31 green. - mvn -pl mateclaw-server test -Dtest=PluginMemoryBridgeTest,MemoryManagerPluginPrefetchTest (JDK 21): 11/11 green.
|
@mateaix hello,已经过去好几天了,请问是否可以有继续推进的可能呢 |
|
@Lcos-000 感谢贡献,明天评估下合并。最近在主力更新agent team的一些基础功能。 |
|
好的非常感谢 |
|
太感谢这个 PR 了 🙏 这是 memory 插件生态方向上第一个完整的社区 provider 实现。 这个洞是真实存在的:平台内部 几个实现上写得特别好的地方:
这类"补 SPI 缺口 + 完整参考实现 + 双语文档"的工作平时很少有人愿意做全套 —— 单独提一个 SPI 改动很难说服人,单独提一个插件又跑不通,你把闭环做完了。 已合并,会随下个版本发布带出去。有几个小项我们会在 maintainer 侧 follow-up 处理,不需要你再动: 如果还有兴趣继续,这几个方向都是现成的下手点:
期待再次看到你的 PR ✨ 也欢迎来 issue 区聊聊 Mem0 v2 的工具设计。 |
|
Follow-up 已落地 ✅ 之前提到的
你的 |
摘要
将 Mem0 接入 MateClaw 作为可选的插件式 memory provider。Mem0 会叠加在现有 4 个内置 provider(Builtin / Structured / Session / Fact)之上运行,不改动、不替代任何内置实现,也不在默认栈中——
mateclaw-server不依赖新模块,需要用户自行构建 JAR 放到plugins/目录。设计定位
MateClaw 的定位是本地优先、零外部依赖。Mem0 需要自托管服务栈(FastAPI + pgvector + 可选 Neo4j),与这一定位存在权衡。因此本 PR 把 Mem0 定位为社区贡献的可选扩展:想获得独立语义召回能力的用户可以启用,默认安装不受影响。
主要改动
1. 补齐插件 SPI(
mateclaw-plugin-api)PluginMemoryProvider新增三参 default 方法:默认实现退化到两参版。这使得插件可以接收当前请求解析出的
ownerKey,实现 per-owner 隔离召回。没有该扩展,Mem0 这类需要按用户隔离的外部 provider 无法正确工作。2. Bridge 透传 ownerKey(
mateclaw-server)PluginMemoryBridgeoverride 三参prefetch,将ownerKey原样转发给插件。若缺失该 override,接口 default 会静默丢弃ownerKey,导致所有插件 provider 的 per-owner 召回失效。3. 新增
mateclaw-plugin-mem0模块Mem0ConfigMem0ClientHttpClient封装 Mem0 REST API(POST /memories/、POST /memories/search/)Mem0ProviderPluginMemoryProvider:prefetch 语义召回、异步 syncTurn、不注入 system prompt、v1 不暴露工具Mem0PluginPluginContextMem0Exception依赖策略:零额外依赖。Jackson、SLF4J 均为
provided作用域,HttpClient 使用 JDK 自带实现。故障隔离:recall / sync 过程中的任何异常都会被捕获并记日志,Mem0 服务不可用不会破坏平台本地 memory 链路。
4. per-owner 隔离映射
ownerKey(如user:42、feishu:sender_abc)user_idagentIdagent_id5. 文档
docs/zh/memory.md与docs/en/memory.md各新增一节:配置项
baseUrlapiKeysearchEnabledtruesyncEnabledtruemaxResults5timeoutMs3000测试
共 42 个测试,全部通过:
mateclaw-plugin-mem0Mem0ConfigTestmateclaw-plugin-mem0Mem0ClientTestHttpServermock Mem0,验证 payload、auth header、错误处理mateclaw-plugin-mem0Mem0ProviderTestmateclaw-plugin-mem0Mem0PluginTestmateclaw-serverPluginMemoryBridgeTestmateclaw-serverMemoryManagerPluginPrefetchTestprefetchAll → bridge → plugin 三参 prefetch端到端路径验证命令:
本地已在 JDK 17.0.19 与 JDK 21.0.11 下验证通过。
已知限制
syncTurn无ownerKey:当前 SPI 签名只有(agentId, conversationId, userMessage, assistantReply),推送 Mem0 时只能用agentId作为user_id降级。需要严格 per-owner 同步时,可设syncEnabled=false。[Mem0 Recall]块直接拼入上下文,不受system-block-max-chars约束;maxResults是当前唯一尺寸阀门。mem0_search/mem0_add等工具。以上限制均已写入中英文用户文档。
不包含的内容
prefetchAll加预算控制;onMemoryWrite钩子(该接口方法当前无调用方);改动文件
共 18 个文件,1755 行新增。
Review 关注点
PluginMemoryProvider的新三参 default 方法是否会对现有插件产生不兼容影响;PluginMemoryBridge三参 override 是否遗漏了异常处理或 null 处理;mateclaw-server是否意外引入了对mateclaw-plugin-mem0的依赖;Mem0Provider的异步 syncTurn 是否可能在应用关闭时泄漏线程;