面向 Agent 的分布式图记忆网络
Mebular 把记忆存成一张带签名事件的知识图谱,每条事实都记得自己什么时候有效、由谁写入。设备之间用向量时钟做增量同步,离线也能用,重连后自动收敛,改过什么都能查。
Agent 的记忆大多还躺在单个进程里:一个列表或键值存储,换台设备就断了,被谁改过也说不清,离线直接罢工。具体来说有四个问题。
| 问题 | 现在的做法 | Mebular 的做法 |
|---|---|---|
| 记忆是扁平队列 | 用列表或键值对存,没有实体和关系 | 图式记忆模型:Entity / Fact / Episode / Skill / Meta 五类节点,事实带 validFrom / validTo 有效期 |
| 写入不可验证 | 没有签名、没有内容寻址,也认不出作者 | 每次写入生成一条 Ed25519 签名、Blake3 寻址的事件,改了什么、谁改的都能查 |
| 同步依赖中心服务 | 必须在线,还要信任中间商 | 向量时钟做增量同步,冲突按「删除优先 > 时间窗 > LWW」裁决,离线可用 |
| 生态各自为政 | Hermes、mem0、Zep、Graphiti 之间不互通 | CMF v1 交换格式加适配器,Obsidian、日志型端、json-memo 都能接 |
除这四点之外,还带了 X25519 + AES-256-GCM 的加密信道、可用用户主密钥静态加密落盘(HKDF 派生 + AES-256-GCM,落盘密文、内存明文)、可选本地 embedding 语义召回(默认多语言 MiniLM,覆盖中文;缺包自动降级关键词并告警;真实模型能力由独立 CI job semantic-real 以 npm run verify:semantic:real 验证)、Hermes 的七方法 Provider 和幂等导入、可选的 libp2p 真实网络(含 circuit relay v2 跨网段寻址与手动 multiaddr 降级)、可切换的存储后端(JSONL 或 Node 内建 SQLite,均复用静态加密)与大图初始同步快照,以及一套带覆盖率门槛的测试。
还没发 npm 包,先从源码构建。运行时只依赖 bonjour 和 ulid,libp2p 是可选的。
git clone https://github.com/Windsander/Mebular.git
cd Mebular
npm install
npm run build # TypeScript strict → dist/
npm test # 47 套件 / 359 用例全绿首次初始化需要用户主密钥为本机签发设备证书。用 Mebular.generateUserMasterKey() 生成后,请自行持久化主私钥(信任根,示例见 examples/quickstart/index.mjs)。
import { Mebular, HermesMemoryProvider } from 'mebular';
const master = await Mebular.generateUserMasterKey();
const mebular = new Mebular({
storagePath: './store.jsonl',
deviceId: 'device-A',
encryption: {
userMasterKey: master.publicKey,
userMasterPrivateKey: master.privateKey,
},
network: { enabled: false }, // 单设备先从这里开始
});
await mebular.initialize();
const provider = new HermesMemoryProvider(mebular);
await provider.storeMemory({
type: 'preference',
content: '深色主题',
metadata: { preferenceType: 'theme', confidence: 0.9 },
});
const { memories } = await provider.retrieveMemory({ types: ['preference'] });
console.log(memories); // → [ { type: 'preference', content: '深色主题', ... } ]
await mebular.shutdown();把 network.enabled 改成 true 并配置传输,同一段代码就能跑在两台设备上,断线重连后自己收敛。
上面这段的可执行版本在 examples/quickstart/index.mjs(含主密钥持久化),npm run build 之后直接 node examples/quickstart/index.mjs 即可:
npm run build
node examples/quickstart/index.mjsexamples/ 下另有 obsidian-vault、log-journal、json-memo 三份带 README 的示例数据,供对应适配器导入,本身不是可执行脚本。
scripts/wan-sync.mjs 把「两主机增量同步 + 冲突收敛」脚本化,并接上 circuit relay 与手动 multiaddr 两条寻址路径。
# 本地编排自测(共享身份 + 两阶段 + relay 密文;non-evidence,退出码 0)
npm run verify:wan:cross:selftest
# 隔离自测:两个独立进程 + 各自独立存储 + 无共享路径(non-evidence)
npm run verify:wan:cross:selftest:isolated
# 本地回归(loopback,非证据)
npm run verify:wan # 手动 multiaddr 直连
npm run verify:wan:relay # 内嵌 circuit relay
# 跨机两阶段(异网段,无需共享文件系统):先分发用户主密钥,再 A=peer / B=cross
node scripts/wan-sync.mjs user-keygen --out key.json
node scripts/wan-sync.mjs relay --port 4000 --unlimited # 可选,异网段需要
node scripts/wan-sync.mjs peer --role a --user-master-key-file key.json \
--bind /ip4/0.0.0.0/tcp/4001 --relay <relay> # 固定地址+持久身份 → 地址稳定;打印 PEER_READY
node scripts/wan-sync.mjs cross --user-master-key-file key.json \
--peer <A-stable-multiaddr> --peer-id <A-deviceId> --relay <relay> --out B-evidence.json
# 判定:cross 退出码 0 且 B-evidence.json 的 stateMatches=true、differentPublicNetwork=true、identityShared=true- 用户主密钥:
user-keygen生成,A/B 用同一把(否则设备证书互验失败);也可用MEBULAR_USER_MASTER_KEY(内联 JSON)。 - 协调无需共享文件:B 用
--peer/--peer-id一次给定 A 的稳定地址,按图上阶段状态重试连接完成三阶段。 - 前置预检:
cross启动前先检查--peer/--peer-id/--relay是否齐全且 TCP 可达;缺失/不可达立即报错并打印补齐指引,不跑到中途才失败(超时可用MEBULAR_WAN_PREFLIGHT_TIMEOUT_MS调整,默认 3000ms)。 - 出口判据:
MEBULAR_WAN_IP_ECHO(缺省https://api.ipify.org?format=json)取公网出口 IP;任一私网/回环 → false,取不到 → 未知(绝不误判 true)。 - relay 默认限额;需显式
--unlimited(Libp2pProvider.relayUnlimited)才允许任意协议过 circuit,调用方承担开放 relay 的滥用风险;relay --capture <path>可捕获线上字节供「只见密文」取证。 - 诚实边界:上述本机命令都是 non-evidence;真实 G3-E 需两台不同公网主机 + 可达 relay,当前未达成(阻塞报告
docs.design/g3r-blocker-2026-09-13.md)。npm run verify:wan:cross无环境时退出码 1 并打印所需环境。
图分三层,层与层之间只靠接口耦合:
- Hermes 侧只依赖 Provider 和 Importer 接口,不碰核心实现。
- 核心层负责图存储、事件日志、同步和持久化,纯 TypeScript,不依赖网络。
- P2P 层管握手、加密信道和传输抽象,可以换成 libp2p、InMemoryHub 或自己实现。
矢量源文件在 assets/architecture.svg,独立页面在 assets/architecture.html,克隆到本地直接打开就能看。
Mebular 没走云端记忆 SaaS 那条路,也就有相应的代价。
| 更看重 | 代价 |
|---|---|
| 离线可用、数据自己拿着 | 不做 SaaS,节点要自己跑 |
| 写入可验证、抗篡改 | 每次写入多出签名和哈希的开销 |
| 图结构、能表达关系和时效 | 比扁平键值模型复杂,上手要花点时间 |
| 生态互通、方便迁移 | 功能还没成熟方案全 |
适合愿意自己管数据、要在多台设备或多端之间共享 Agent 记忆、也能接受早期项目的人。想开箱即用,或者要生产级 SLA 的,现在还不合适。
Mebular 还在早期设计阶段。Phase 0 到 6 的功能都能用了,但 API 还没稳定,也没发 npm 包,放到生产环境前请自己评估。
| 里程碑 | 状态 |
|---|---|
| 核心引擎(图存储 / 加密身份 / 事件日志) | 完成 |
| P2P 网络(握手 / 信道 / NAT / 发现) | 完成 |
| 图同步(增量同步 / 冲突收敛 / 离线恢复) | 完成 |
| Hermes 集成(门面 / Provider / 导入器) | 完成 |
| 跨端互通(证书链 / CMF / 适配器 / 故障注入) | 完成 |
| 质量收口、生态适配、广域网桥接 | 完成 |
| 静态加密、语义召回、真实广域网、SQLite 存储与快照同步 | 完成 |
| 信任模型 v2(证书吊销)、跨 NAT 实测回填 | 规划中 |
测试和质量方面:
| 项目 | 情况 |
|---|---|
| 测试 | 47 个套件、359 条用例全绿,覆盖单元、双设备端到端、四端互通和故障注入 |
| 覆盖率 | 行 90.3%、分支 77.1%,全库门槛 85/65,关键文件另有底线 |
| 类型检查 | tsc --noEmit,strict 加 noUncheckedIndexedAccess,零错误 |
| Lint | ESLint(typescript-eslint)零告警 |
| 质量门禁 | 每个阶段跑 verify 脚本加构建产物冒烟,src 里不留裸的 throw new Error |
分阶段验证脚本
# 每阶段:文件检查 + 编译 + 全量测试 + 实现点抽查 + 构建产物冒烟
node scripts/verify-phase6.mjs # 质量收口 · 生态适配 · 广域网桥接
node scripts/verify-phase5.mjs # 跨端互通
node scripts/verify-phase4.mjs # Hermes 集成
node scripts/verify-phase3.mjs # 图同步
node scripts/verify-phase2.mjs # P2P 网络| 方向 | 入口 |
|---|---|
| 核心 API | src/mebular.ts · src/types/ |
| 记忆模型和存储 | src/memory/ · src/core/ · src/storage/ |
| P2P 网络和同步 | src/p2p/ · src/sync/ · src/eventlog/ |
| CMF 交换和适配器 | src/exchange/ |
| Hermes 集成 | src/hermes/ |
| 可运行示例 | examples/ |
| 贡献指南 | CONTRIBUTING.md |
想参与就开个 Issue 先聊聊,再发 PR,具体约定见 CONTRIBUTING.md。
觉得有用的话,点个 star。