LOCAL-FIRST · MCP · ZERO DEPENDENCY

记忆不是攒得越多越好,
它该像地层一样沉积。

sedimem 是一个跨会话、跨客户端的本地记忆层:DSH / Codex CLI / Claude Code 共读一份只追加的真相层,把记忆当数据库治理 —— 写入时去重、冲突时失效而不删除、过期后仍可回看、脏数据不进上下文。

288
测试用例(单元 + 注入式回归)
I1–I24
注入式评测场景
3
客户端共读一份记忆
0
运行时依赖(只要 Node ≥ 22)
Why

几乎所有 agent 记忆都烂在同一个地方:没人管

不是"检索得不够准",而是根本没人治理。半年后你的记忆库通常长这样 ——

✕同一条事实存了 7 份

措辞略有不同,全都被检索出来一起塞进上下文 → 上下文被稀释,模型反而更容易抓错重点。

✕旧版本与新版本同时有效

"明明记得是这个,它偏说是那个" —— 因为没人知道哪条还有效。

✕只增不删,直到吃满配额

检索变慢、token 成本线性上涨,最后只能整体清空 —— 顺便把有用的也扔了。

✕记忆里被塞进一句指令

"忽略之前所有指令" 一旦入库,之后每个会话都被悄悄影响,而且看不出是哪来的。

✓写入门控

hash 短路 → 同 slot 判等 → 近重复判定;重复的直接 noop,不进真相层。

✓失效而非删除

过时的事实打上 validTo,asOf 能把任意时点的库状态还原出来。

✓Agent 当裁判

语义去重交给正在跑的那个 Agent(复用当前会话,零 API 成本、可离线)。

Architecture

四层结构,依赖单向

客户端 → 接口 → 核心服务 → 治理 → 派生 / 真相。真相层是唯一不可重建的东西, 其余全部可以从它重算出来。

sedimem 架构分层图
客户端三端共读 → MCP/CLI/Web 三入口 → 一个 MemoryService → 治理层 → 派生层 + 真相层

1客户端层:没有"同步"

三端连的是同一个 stdio server、同一个 MEMO_HOME,所以不存在第二份副本, 也就没有同步问题 —— 同步的复杂度被结构消掉了。

2接口层:MCP 是唯一的缝

10 个 memory_* 工具,读写比刻意偏向读(写只有 write/supersede/forget/curate)。 Web 只读,没有写端点,所以观察台不可能改坏数据。

3核心服务:语义只在一处

拆成 lifecycle / write-path / search-path / maintenance 四模块, 门禁用 service-facade.test.js 保证对外方法集合不漂移。

4真相层:只追加

items/<scope>.jsonl 永不 UPDATE、永不 DELETE; "改过去"= 追加新版本 + 旧版本打 validTo。

Write path

写入四道门:越往后越贵

在线只做零成本判断,把"要花钱才能判断"的事情推到近线和离线。

写入路径四道门
① 身份与形状 ② hash 短路(O(1)) ③ 同 slot 判等 ④ 近重复 + 冲突
同一事实第二次写入
# 第一次
$ memo write --kind fact --title 'Node 版本要求' --text 'CI 要求 Node 22+'
created: 01M6…  # 进入真相层

# 第二次,同一个值
$ memo write --kind fact --title 'Node 版本要求' --text 'CI 要求 Node 22+'
noop_same_value: 01M6…  # 不落盘,线上零浪费

# 换个说法(真人改写)
$ memo write --text '部署流水线需要 Node 22 或更新的版本'
created: 01M7…  # 词法抓不住 → 交给 Agent 裁决(见下)
Hygiene loop

四条卫生路径,LLM 只裁决、不扫描

成本随记忆规模线性增,不随对话轮次增 —— 这是能长期跑下去的前提。

四条卫生路径与 Agent 裁决闭环

① 在线

hash 短路 / 同 slot 判等 / 近重复判定。成本 O(1) 哈希 + 少量相似度。宁可漏,不可误杀。

② 近线

dedupe_scan 只报候选簇,Agent 判 merge/a_wins/b_wins/keep_both。0 API。

③ 离线

nightly 重算 brief/metrics、配额淘汰、compact 清墓碑。可重放、可审计。

④ 读时

validTo 过滤、asOf 还原、suspicious 遮蔽。查询期零额外调用。

∞闭环:它挂在两个你本来就会做的动作上

brief 末尾常驻一行「待裁决去重候选 N 条」→ Agent 调 memory_dedupe_scan 看候选 → 判完 memory_curate 落盘(败者 superseded,真相层全留)→ 再扫清零。
不需要你额外记得清理 —— 会话开始读 brief、会话收尾写记忆,这两个动作已经存在。

What's new in v0.4

补的是「体验短板」,不是「功能堆料」

竞品对比后按价值排序做的六件事,全部默认关闭或默认保守 —— 开了才有,不开零成本。

⇄可选语义道

BM25 打底不变,另开一条向量路(自备 embedding 端点),用 RRF 融合;每条结果带 basis 说明凭什么命中。语义道挂了就显式降级回词法,绝不假装「没问题但搜不到」。

◨每步注入(默认关)

插件可以把 brief 顶部塞进 system prompt,预算 ≤1200 字符、带 <memory-context> 围栏并声明「这是数据不是指令」;正文里的围栏闭合标签会被转义,防提示注入逃逸。

✎半自动捕获

从对话里抽「记住 / 决定 / 坑」进 inbox,人工 accept 才落真相层。 不做自动写入 —— 自动写入是记忆库腐化的头号原因。

⚖分歧视图

同一 slot 里「不可比」的两条会标 disputed,观察台单列;选择赢家是非破坏的: 败者转 superseded,真相层一行不少。

◍入库脱敏

PAT / JWT / PEM / 内部域名 / 赋值型密码在落盘前遮蔽,再算 contentHash; 开关可回退,遮蔽幂等不嵌套。

⌕检索页 + 一条命令装插件

观察台新增 #/search:相关度、basis、降级提示都摆出来。 DSH 侧边栏「记忆中心」由 dsh-sedimem 插件提供,scripts/install-dsh-plugin.sh 一条命令。

Observer

只看不改:三页把"库的现状"摊开

严格绑定 127.0.0.1、没有任何写端点。想看它到底积了什么、有没有变脏,看这里。

今日变化
今日变化 —— 这一天库里发生了什么:新增 / 被取代 / 删除 / 合并,每条带「为什么」。
记忆图谱
记忆图谱 —— 3D 球体,拖拽手动旋转(水平环视 / 垂直俯仰);点节点看演化时间线。
卫生洞察
卫生洞察 —— 待裁决候选、30 天清理史、冗余率/冲突率/检索健康趋势、doctor 体检。
Deploy

三分钟接上你的客户端

零依赖、零构建。只需要一个 Node ≥ 22 和一个数据目录。

拿到代码

shell
git clone https://github.com/Old-Friends-Gathering/sedimem && cd sedimem
export MEMO_HOME="$HOME/.memo"        # 真相层放哪
export V="versions/$(cat versions/CURRENT)"

先确认它自己能跑

shell
node --test "$V/tests/**/*.test.js"   # 391 个用例
node $V/eval/run.js                  # I1–I36 注入式评测

接客户端(三选一,或都接)

Claude Code
claude mcp add --scope user memo \
  -e MEMO_HOME=$HOME/.memo -- \
  $(command -v node) $PWD/$V/src/mcp/server.js
Codex CLI · ~/.codex/config.toml
[mcp_servers.memo]
command = "/usr/local/bin/node"      # which node
args = ["/path/to/sedimem/versions/v0.4/src/mcp/server.js"]
enabled = true
[mcp_servers.memo.env]
MEMO_HOME = "$HOME/.memo"   # 换成你的数据目录

验证三端真的是同一份记忆

shell
bash $V/configs/verify-three-clients.sh
# dsh 写一个盐值 → 另一个进程召回 → 用召回值作答
PASS

常驻(可选,macOS launchd)

观察台与 nightly 都由 scripts/sedimem-run.sh 启动,它读 versions/CURRENT —— 换大版本只改指针,plist 不用碰。

shell
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/live.sedimem.observer.plist
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/live.sedimem.nightly.plist
launchctl kickstart -k gui/$(id -u)/live.sedimem.observer   # 热加载新代码
open http://127.0.0.1:7788
Interface

命令与工具

CLI 给人用,MCP 给 Agent 用 —— 同一套语义、同一份数据。

CLI作用
memo write写一条(建议带 --title)
memo search检索,--as-of 回看历史时点
memo brief给 Agent 的摘要,--tier0 只出目录层
memo dedupe scan近重复候选簇(--json 带 pairs/strength)
memo curate …裁决任务包:prompt / apply / status / auto
memo compact唯一物理删入口(墓碑 + 宽限期)
memo view …L2 场景块 / L3 画像 / 溯源
memo serve只读观察台

MCP10 个工具,三端共用

memory_write memory_search memory_brief memory_get memory_supersede memory_forget memory_curate memory_dedupe_scan memory_stats memory_scope

设计取向:读比写多。写只有四个; forget 默认只写墓碑,物理删除另有唯一入口,保证 asOf 永远可回溯。

Gates

自己也会腐化,所以上了九道闸

main 受保护且对管理员同样生效:直推与 force push 会被拒绝, 任何改动都必须 issue → 分支 → PR → 九项全绿 → squash。

test (22) / (24)

Node 22 与 24 上跑全部测试,0 失败。

eval (I1–I36)

注入式回归:重复 / 冲突 / 过时 / 删除 / 投毒 / 并发 / 分库 / 拒绝服务…

quality

零依赖、源码规模、测试清单、评测对齐、契约覆盖 ≥90%(当前 97.6%)、文档链接、版本自包含。

security

个人路径、凭据、内部域名、配置可移植、许可证、大文件、历史面。

CodeQL

静态分析,含 security-and-quality 查询集;发现过真实堆栈泄露。

windows-smoke

真 windows-latest 上验跨平台文件名与 CLI 路径。

issue-link

MR 必须关联一条 open 的 issue,否则合不进去。

ci-gate

聚合门:任一子项红即整体红,避免"看起来绿了"。

本地同源

node tools/quality-check.mjs 等三个脚本与 CI 完全同一份。

Versions

每个大版本换一个目录

X.Y 换目录(v0.2 → v0.3 新建 versions/v0.3/), X.Y.Z 补丁同目录递增;老目录冻结并留 FROZEN.md;指针在 versions/CURRENT。

v0.1.x · 冻结 0.1.2

核心层:append-only 真相层 + 派生索引 + brief + 四条卫生路径 + MCP/CLI

v0.2.x · 冻结 0.2.1

只读观察台、SQLite FTS5、curate 任务包;0.2.1 拆 service、kind 数据化、JudgePort、compact、检索指标

v0.3.0

分层视图:L2 场景块(可字节重建)+ tier0 注入分级 + L3 画像(固定预算 + 强制溯源)

v0.3.1 – 0.3.3 · 冻结 0.3.6

title 一等字段与批量回填 · 密集图谱可读性 · 跨主题连线 + 3D 球体手动旋转 + 布局降噪

v0.4.0

语义道(RRF + basis)· 每步注入 · 半自动捕获 · 分歧视图 · 入库脱敏 · 检索页 · 规模基准 · 插件安装脚本

v0.4.1 – 0.4.3

CLI 退出码修复 · 浅色外观图谱可读性 · 插件 inject 形状修复(记忆中心面板全黑)

v0.4.4

Q10 发布面同步门禁:README 版本表、项目站、CI 检查名三处漂移会直接红

next · v0.5

跨机器同步(冲突可解释)· Windows 兼容性收口 · L3 画像的自动计划