ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

caveman cavemem 实战详解:基于 SQLite + BM25 + 压缩引擎的跨会话持久化 Agent 记忆

2026/9/5 20:03:50 拓冰建站 浏览量
caveman cavemem 实战详解:基于 SQLite + BM25 + 压缩引擎的跨会话持久化 Agent 记忆 caveman cavemem 实战详解基于 SQLite BM25 压缩引擎的跨会话持久化 Agent 记忆【免费下载链接】caveman why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/cavemancavemem 是 caveman 项目mem/README.md中专为 AI Agent 设计的持久化记忆组件记忆以原始文本落入本地 SQLite召回时用确定性 BM25 排序并设置保守阈值每一条命中再经 Caveman 压缩引擎处理后注入上下文丢弃的细节可通过 CCR recovery handle 逐字节恢复。读完本文你将掌握 cavemem 的五个核心操作remember / recall / supersede / history / forget的完整 CLI 与 MCP 用法、token 预算机制的语义细节以及字节安全写入、有界召回等保证背后的源码级实现。一、定位与设计原则cavemem 解决的是 Agent 跨会话记忆问题把一条事实或笔记持久化下来后续会话能按语义查询召回同时注入上下文的 token 成本是诚实且可恢复的。其官方定位mem/README.md是Durable, compression-native agent memory:remember,recall,supersede,history,forget.三个设计支柱直接体现在包注释中mem/store.go#L1-L11SQLite 存储原始记忆raw text 是持久化真相源source of truthBM25 召回 保守阈值——宁可什么都不召回也不注入噪声召回时才压缩——压缩是瞬态行为transient只为让注入成本真实、丢弃细节可恢复。另一条贯穿全部接口的基础约定所有上报的数字tokens_added、score、basis都是inferred推断值组件从不声称verified节省。二、五个核心操作完整 CLI 工作流构建与基本用法继承自 mem/README.md并补充了源码中实际存在的recover子命令go build -o cavemem ./mem/cmd/cavemem ./cavemem remember the deploy key lives in vault under ops/deploy ./cavemem recall where is the deploy key # JSON: { hits: [...], basis: inferred } ./cavemem recall full migration context 5 0 # explicit 0 token budget unlimited ./cavemem supersede mem_xxxxxxxx deploy key moved to vault ops/deploy-v2 ./cavemem history mem_yyyyyyyy # oldest → current ./cavemem forget mem_xxxxxxxx ./cavemem # 无参数或显式 mcp运行 stdio MCP 服务器 ./cavemem recover recovery_handle # 将召回命中的原始字节写回 stdout子命令的分发逻辑在 mem/cmd/cavemem/main.go#L54-L87remember、recall、supersede、history、forget、recover属于 store-backed 动词只有这些动词才会打开数据目录help与未知子命令不会创建数据目录未知命令退出码 2。各命令的输出契约命令参数stdout 输出失败退出码remembertext或--stdin{id, created_at, basis}超长时65cave_memory_too_largerecallquery [limit] [token_budget]{hits: [...], basis: inferred}1参数必须是非负整数supersedeid text{id, supersedes, created_at, basis}1historyid{history: [...], basis: inferred}旧→新1forgetid{forgotten: bool}1recoverhandle原始文本的原始字节1无参数/mcp—stdio 上的 MCP 帧1几个值得注意的行为细节remember 支持--stdincavemem remember --stdin从标准输入读取且读取上限为MaxMemoryBytes1字节mem/cmd/cavemem/main.go#L239-L250超过 256 KiB 的输入直接被拒内容寻址 ID记忆 ID 是正文 SHA-256 的前 8 字节十六进制形如mem_xxxxxxxxxxxxmem/store.go#L709-L712因此重复 remember 相同文本是幂等的INSERT OR IGNORE保留原始created_at退出码 65POSIXEX_DATAERR是超长拒绝的契约两个薄客户端都导出MEMORY_TOO_LARGE_EXIT_CODE常量调用方无需解析 stderr 即可分支处理mem/cmd/cavemem/main.go#L32-L33。三、SQLite 存储层schema 与单写者并发纪律3.1 表结构存储 schema 定义在 mem/store.go#L34-L43CREATE TABLE IF NOT EXISTS memories ( id TEXT PRIMARY KEY, text TEXT NOT NULL, created_at TEXT NOT NULL, valid_from TEXT NOT NULL, valid_until TEXT, supersedes TEXT, superseded_by TEXT );valid_until/supersedes/superseded_by三列共同实现版本链supersession chainsupersede 时旧行不删除而是被打上valid_until并指向新版旧行永久保留用于审计。旧库会通过migrateMemorySchema就地迁移逐列ALTER TABLE 建索引mem/store.go#L657-L707。3.2 数据位置与并发数据目录默认~/.caveman/mem含mem.db记忆与ccr.db压缩恢复缓存可被环境变量CAVEMAN_HOME覆盖mem/store.go#L714-L724。单写者连接 WALOpen强制SetMaxOpenConns(1)DSN 携带busy_timeout(5000)与journal_mode(WAL)mem/store.go#L111-L121。注释解释了动机多个cavemem进程含 MCP 服务器共享同一个mem.dbJS 客户端还会Promise.all(facts.map(remember))并发写入没有这套纪律32 个并发写只会落地 1 个、静默丢弃 31 个SQLITE_BUSY。冷启动的多语句 DDL 迁移额外包在五秒预算内的ccr.RetryOnBusy重试里。3.3 supersede 与 forget 的事务语义Supersedemem/store.go#L201-L266是原子事务校验旧记忆仍是 current、替换文本必须不同、新 ID 不得已存在随后在同一事务里插入新行并给旧行打过期标记若RowsAffected ! 1则判定supersede 期间记忆被并发改动并回滚。已过期或未知的 ID 一律 fail closed。Forgetmem/store.go#L324-L363删除目标行后会原子修复相邻血缘指针删掉 current 版本意味着忘掉该事实绝不会静默复活更旧的过期版本。History沿supersedes向上、沿superseded_by向下展开整条链旧→新遇到断链或环直接报错而不是返回部分历史mem/store.go#L277-L319。四、BM25 召回分词、停用词与保守阈值打分实现全部在 mem/bm25.go完全确定性保证召回可复现分词mem/bm25.go#L18-L22小写化后按任何非字母数字字符切分。停用词mem/bm25.go#L27-L33the、is、where等 30 个无选择信号的虚词被同时从查询和文档中剔除。官方注释给了个例子查询 where is the deploy key 是靠deploy/key命中的而不是靠与无关笔记里偶然出现的 is/the 重叠。BM25 参数mem/bm25.go#L9-L13Robertson/Spärck Jones 默认值k1 1.5、b 0.75。非负分数IDF 使用 1 平滑形式log(1 (n-df0.5)/(df0.5))得分恒非负因此固定阈值才有意义mem/bm25.go#L48-L109。召回端的关键常量mem/store.go#L45-L56常量值语义DefaultThreshold0.1低于该分的命中直接丢弃无关查询召回空集而非猜测DefaultLimit5默认返回的命中数上限DefaultTokenBudget2000单次 Recall 所有命中的推断 token 总量上限UnlimitedTokenBudget-1内部哨兵关闭打包上限但保留LimitRecall的排序还带确定性平局处理分数相同时按记忆 ID 字典序mem/store.go#L444-L449保证同一数据集两次召回顺序一致。五、压缩原生召回token 预算、贪心打包与恢复句柄这是 cavemem 与存了就算型记忆方案的本质区别。Recall的完整流水线mem/store.go#L411-L535先加载、先打分拉取所有 current 记忆valid_until IS NULLBM25 过滤阈值、按分排序、截取limit条逐条压缩每条候选按排名顺序经engine.Compress压缩被注入的是压缩形态预算核算的也是压缩后的 token 数。引擎失败时保持 fail-closed 的原始字节与原始计数而不是记 0那会低估注入开销贪心打包预算内按 BM25 排名贪心装填实际委托给引擎的确定性打包器contextwindow.Pack与网关共享同一套预算实现单条超预算的特例若排名第一的记忆压缩后仍单独超过整个预算只返回一个预算尺寸的 head头部截断 CCR recovery handle绝不注入整段正文mem/store.go#L494-L503。截断用二分查找保证落在 UTF-8 rune 边界mem/store.go#L568-L591。如果引擎原样透传没有留下 handleheadHit会在此刻把原文存入 CCR确保丢掉的尾部必须可恢复这一不变量。token_budget的三态语义是 README 里explicit 0 unlimited注释背后的关键设计mem/cmd/cavemem/main.go#L299-L313 的externalTokenBudget省略或 Go 侧零值→ 安全默认 2000显式 0CLI/MCP/JS/Python 公共接口→ 映射到内部UnlimitedTokenBudget召回全部排名命中负值→ 公共接口一律报错内部哨兵-1不对外暴露。每条召回命中都是一个Hitmem/store.go#L385-L392{ id: mem_…, text: 压缩后的注入文本, score: 1.23, tokens_added: 187, basis: inferred, recovery_handle: … }配套的回归测试锁死了这套契约mem/recall_budget_test.goTestRecallRespectsTokenBudget12 条相似记忆 60 token 小预算断言总注入不超预算且必然丢弃部分命中TestRecallUnlimitedSentinelReturnsEveryRankedHit显式 unlimited 时 12 条全返回TestRecallRejectsUnknownNegativeTokenBudget负预算 fail closedTestRecallOversizedSingleHitReturnsHeadAndHandle复现单条 440,000 token 一次性召回的真实事故——超尺寸单条必须返回 head 有效 handle且Recover还原结果与原文逐字节一致TestRememberRejectsOversized超过 256 KiB 上限MaxMemoryBytesmem/store.go#L63-L71的remember必须携带cave_memory_too_large拒绝且不落库。六、MCP 服务器把记忆接进 Agentcavemem不带参数或显式mcp即在 stdio 上运行 MCP 服务器mem/cmd/cavemem/main.go#L98-L107通过项目共用的mcp.NewServer框架提供与 caveman-mcp 完全一致的帧格式。五个工具mem/cmd/cavemem/main.go#L111-L231MCP 工具参数说明cavemem_remembertext必填存储记忆相同文本幂等cavemem_recallquery必填、limit?、token_budget?token_budget默认 2000显式 0 解除上限cavemem_supersedeid、text替换 current 记忆历史保留cavemem_historyid返回该链路的旧→新版本cavemem_forgetid删除记忆所有错误都带cave_*惯用错误 IDcave_invalid_arguments、cave_memory_too_large等所有成功结果都标basis: inferred。客户端配置继承自 mem/README.md{ mcpServers: { cavemem: { command: cavemem } } }七、薄 JS / Python 客户端为什么把正文走 stdinmem/js/index.mjs 与 mem/py/cavemem.py 是镜像的薄客户端它们只把文本 shell 到 Go 二进制不重新实现任何存储、BM25 或压缩逻辑。README 给出的用法import { remember, recall, supersede, history, forget } from cavemem; // js/index.mjs await remember(…); await recall(…, 5, 2000); await recall(…, 5, 0); // unlimitedimport cavemem # py/cavemem.py cavemem.remember(…); cavemem.recall(…, limit5, token_budget2000)实现上有三个针对真实环境的取舍正文走remember --stdin而非 argv避开操作系统单参数大小限制二进制通过CAVEMEM_BIN环境变量解析否则走 PATHmem/js/index.mjs#L9-L11。JS 侧maxBuffer设为 32 MiB且刻意不把写入超长输入时收尾的EPIPE当作第二次失败——退出码 65 才是契约mem/js/index.mjs#L18-L25Python 双向固定 UTF-8textTrue单独使用会在 Windows 上落到 ANSI 代码页remember(café)可能在子进程见到之前就UnicodeEncodeError非 ASCII 召回则会乱码——因此显式encodingutf-8mem/py/cavemem.py#L29-L39契约常量两个客户端都导出MEMORY_TOO_LARGE_EXIT_CODE 65与 CLI 的EX_DATAERR退出码对齐mem/js/index.mjs#L6-L7、mem/py/cavemem.py#L16。八、保证清单Guarantees与许可证README 的五条保证mem/README.md逐条都有源码支撑字节安全写入——raw text 在任何压缩发生前就落盘 SQLite记忆永不丢失压缩只发生在召回时且是瞬态的mem/store.go#L7-L10无关查询召回空集而非猜测——BM25 平滑 IDF DefaultThreshold 0.1双重保守mem/bm25.go#L48-L50默认 2000 推断 token 上限——CLI/MCP/JS/Python 均可显式token_budget: 0请求无限制省略该参数绝不会解除上限mem/store.go#L52-L56召回默认排除被取代的事实——历史仅本地保留、可审计all()只查valid_until IS NULL的行mem/store.go#L599-L601每条压缩命中都携带recovery_handle——cavemem recover handle对应 Go 的Recovermem/store.go#L593-L596返回逐字节原文。注意它读的是 cavemem 自己的 CCR 库~/.caveman/mem/ccr.db不要与 caveman 网关的caveman retrieve混用mem/cmd/cavemem/main.go#L362-L366。许可证边界Go 核心与二进制遵循 BSL 1.1source-availableChange Date 前不属于 OSI 开源薄 JS/Python 客户端保持 MITmem/LICENSE、mem/js/LICENSE更完整的说明见 LICENSING.md。构建与测试约定为make product-build PRODUCTmem/make product-test PRODUCTmemmem/CLAUDE.md。九、小结适合什么场景cavemem 的设计取向非常一致失败时宁可什么都没有fail toward nothing、成本永远诚实inferred-only、丢掉的细节永远可找回reversible。它适合需要跨会话记住运维事实、配置位置、迁移上下文的本地 Agent 工作流通过 CLI、stdio MCP 或 JS/Python 薄客户端接入。局限同样明确召回是基于本地词法重叠的 BM25 而非语义向量检索同义不同词、非英语词表下召回能力受限记忆单条上限 256 KiB定位是事实与笔记不是文件转储。若你要在 Agent 系统中引入持久化记忆cavemem 提供的SQLite 真相源 确定性召回 压缩注入 恢复句柄四件套是一个可以直接对照实现的工程范本。【免费下载链接】caveman why use many token when few token do trick — Claude Code skill that cuts 65% of tokens by talking like caveman项目地址: https://gitcode.com/GitHub_Trending/caveman1/caveman创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考