engram-mem
用本地 engram 二进制给 Claude 提供跨会话、跨设备的持久记忆——工作和生活都能用。
做完有价值的事(改完 bug、定了方案,或记住一个偏好、计划、推荐)就保存结构化笔记,
以后再检索。存储是本地 SQLite,跨设备同步走 git。
完整的 engram 命令/参数见 references/engram-cli.md(需要查具体用法时再读;本文给出主流程)。
决定一切的前提:engram 只能搜英文
engram 用 SQLite FTS5 的默认 unicode61 分词器,不切分中文——一段连续中文会变成一个
无法匹配的整 token,中文查询即使内容存在也返回 0(已实测)。英文则切分良好。
因此策略:存一份英文用于检索,同时保留中文原文用于保真。 翻译是模型擅长的事,还顺带 带来跨语言召回——英文记忆用中文或英文查询都能命中(查询时也译成英文)。
注意这是关键词检索、非语义:查 "stuck" 匹配不到写成 "frozen" 的笔记。所以同一概念 存和搜都用同样的常用英文词,以对抗这种漂移(见下方「翻译原则」)。
前置条件
engram 必须在 PATH(brew install gentleman-programming/tap/engram)。若
engram --version 失败,直接让用户安装,不要猜。
硬性禁止:绝不让 engram 写入 agent 的 MCP / hook 配置
本方案的根基是"无 hook、纯 CLI":只装 engram 二进制,只经 engram 命令读写记忆。
engram 另有一条"插件/集成"安装路径会把 MCP server + lifecycle hooks 注入到 agent
(Claude Code / Codex / Gemini CLI 等)的配置里——这条路径在本方案中禁止使用,因为
hook 会带来后台自动捕获、常驻上下文成本和不可控行为,正是我们要避开的。
禁止执行(以下命令/操作都会写入 MCP 或 hook,一律不要跑):
engram setup <agent>(如engram setup claude-code/opencode/codex/gemini-cli/pi)claude plugin marketplace add Gentleman-Programming/engram及claude plugin install engram- 任何把
engram mcp写进~/.claude.json、项目.mcp.json、或 settingshooks的操作 engram connect .../--with-hooks之类的接线命令
允许:仅 brew install .../engram(或下载二进制),然后通过本 skill 用 CLI 调用。
自检(确认没被写入)
若怀疑被注册过,用下面命令检查并清理(发现 engram 条目应移除):
# Claude Code:全局与项目 MCP 配置里不应出现 engram
grep -i engram ~/.claude.json 2>/dev/null
grep -ri engram ./.mcp.json ./.claude/settings*.json 2>/dev/null
# 其它 agent 同理检查各自配置(~/.codex、~/.gemini、opencode.json 等)
预期:无任何 engram MCP/hook 输出。若有,说明误用了 setup/插件路径,需手动删掉相应条目。
激活策略(何时存、何时取)
前提:无 hook,激活靠"请求/判断"驱动,不会每会话自动跑。以下规则界定 Claude 应在何时 主动出手——既不漏掉值得记的,也不制造噪声。
存(save):显式 + 主动建议,点头才存,绝不静默自动存
- 用户显式要求时直接存:「记一下/记住/别忘了/存一下」。
- 到达明显节点时主动问一句"要记下这条吗?",得到确认再存。这些节点是:
- 解决了一个非平凡的 bug(有根因、有非显然的解法)
- 做了一个决策(架构/方案/选型)并有理由
- 用户陈述了一个长期偏好 / 计划 / 关于某人的事实(如"我习惯…""下周要…""我老婆对…过敏")
- 发现了不显然、以后还会用到的东西
- 不要为琐碎、一次性、马上过期的内容提议保存——主动是为了不漏重点,不是把什么都记。
- 永远不自动静默保存(那是被否掉的 hook 行为);保存前给用户一句话确认的机会。
取(recall):相关即主动搜,先搜再答
当任务/问题看起来可能有旧上下文时,先搜记忆再回答/动手:
- 在某项目里继续工作 → 用该项目搜近期相关记忆
- 用户问"我们怎么处理 X 的 / 之前定的方案 / 我之前记过吗 / 有没有类似的"
- 遇到一个像是以前解决过的问题
- 涉及可能存过的个人偏好 / 计划 / 某人信息(用
--scope personal搜)
保持轻量:搜一次,没命中换关键词再试一次,然后照常推进——不要因为没搜到就卡住。 对明显全新、与历史无关的问题,不必搜(避免无谓调用)。
触发词(供 description 匹配):存「记住/记忆/记一下/存一下/别忘了/下次记得」; 取「回忆/想起来/想不起来/之前/上次/笔记/我们怎么…/我之前…」。
保存记忆(save)
- 提炼:浓缩成简短结构化笔记,存决策/事实/心得及其原因或要点,别存原始对话。
- 翻译成英文:写英文标题 + 完整英文正文;技术词、人名、品牌、专有名词原样保留 (见翻译原则)。原文本就是英文则无需保留中文。
- 组装:按下面布局拼 title + content。
- 执行
engram save,并选好--type与--scope。
存储布局
title:简短、关键词丰富的英文标题(检索权重最高,把最值得搜的词放这)。
content:
[EN]块(完整英文,被检索)+[原文]块(中文原文,保真;不可检索也不误命中)。--type:从下面这固定 7 类里选(不要自造,避免过滤碎片化;实在不归类用
note):type 含义 常见 scope decision做了某选择 + 为什么 project / personal bugfix解决了某问题 + 根因 project / personal discovery学到/发现了什么 project / personal preference长期习惯/好恶(自己或他人) personal person关于某人的事实 personal plan未来的打算/行程/待办 personal note兜底:其它不归类的事实 任意 --scope:代码/项目相关 →
project(默认);日常生活相关 →personal(跨项目, 任何目录下都搜得到,不被项目边界切开)。两者的 sync 见下方「跨设备同步」——统一用--all。--topic(可选):会演化的主题给个稳定
topic_key,让同主题笔记聚合。
搭配习惯:代码记忆多为 decision/bugfix/discovery + --scope project;生活记忆多为
preference/person/plan/note + --scope personal。
命令模板
短的单行内容——直接传参:
engram save "Prefer pnpm over npm" \
"[EN] Use pnpm not npm; faster, cleaner hoisting in monorepos. [原文] 习惯用 pnpm 而非 npm:更快、monorepo 下依赖提升更干净。" \
--type preference --scope personal --topic package-manager
生活场景示例(用 personal scope):
engram save "Wife allergic to peanuts and shellfish" \
"[EN] Wife is allergic to peanuts and shellfish — avoid when booking restaurants or cooking. [原文] 老婆对花生和贝类过敏,订餐厅、做饭都要避开。" \
--type person --scope personal
较长或多行内容——先用带引号 heredoc 拼好 content,避免 shell 转义($、反引号、引号):
CONTENT="$(cat <<'EOF'
[EN]
Fixed login timeout: wrapped JWT validation in retry with exponential backoff.
Root cause was clock skew causing tokens to expire early.
[原文]
修复登录超时:把 JWT 校验包了重试和指数退避。根因是时钟偏移导致 token 提前失效。
EOF
)"
engram save "Fix login timeout via JWT retry and backoff" "$CONTENT" --type bugfix --topic auth-timeout
保存后用分配到的 id 加一句话摘要向用户确认。
检索记忆(recall)
检索是关键词精确匹配、无语义兜底,所以"召回靠多试、别过早过滤、别过早放弃"。 实测教训:一条明明存在的记忆,因第一次查询词不对 + 误加了 scope 过滤,前三次全返回 0, 第四次换词才命中——差点被误判成"没存过"。据此定下面的规则。
- 把查询译成英文(用与保存时一致的常用词);用户给英文则直接用。
- 第一次搜:宽,不要加
--scope过滤。 scope(project/personal)很难猜准,猜错会把 命中的记忆直接滤掉。先搜全量,拿到结果再按需收窄。 - 多轮换词,至少试 2-3 个角度再下"不存在"结论。换词思路:
- 用户的原词直译 + 明显近义词(分类→classify / categorize / lists / organize)
- 可能出现在记忆里的具体名词/专名(库名、API、项目名、功能名,如 "star lists"、"GraphQL")
- 放宽到单个核心词(如只搜 "star"、"stars")
- 可读地呈现;用户用中文时把
[原文]中文一并展示。 - 仅在确实需要收窄时才加过滤:
--type/--project/--scope/--limit。
engram search "github star lists" --limit 10 # 第一次:宽,不加 scope
engram search "star categorize organize" --limit 10 # 没中就换词再试
engram search "wife allergy food" --limit 10 # 确认是生活记忆后再 --scope personal 收窄
翻译原则
翻译用标准、惯用的英文术语(Claude 对常见词的译法本就稳定,不需要查表);只要同一概念 存与搜都用同样的常用词,就能对上。
绝不翻译代码标识符、库/工具名、报错串、API 名、人名、品牌——JWT、Redis、N+1、
useEffect、TypeError 等原样保留。它们本就是最好的检索键,也让纯中文内容的笔记在没有
英文块时也能被搜到。
跨设备同步(简版)
engram 以本地 SQLite 为权威源,通过 git 同步。完整命令见 references/engram-cli.md。
两条铁律:
engram sync把 chunk 写到「当前目录/.engram」(源码里是相对路径.engram,无法用 flag 改)。 所以必须在~/.engram里跑;在别处跑会把 chunk 写进那个目录的.engram/(如~/Code/某项目/.engram/),既不进数据目录也没进 git 仓——记忆看似同步了实际丢了。 (这是实测踩过的坑。)engram sync要带--all:默认只导出当前项目,会漏--scope personal的生活记忆;--all才导出全部(含 personal)。
为彻底免踩 cwd 坑,同步统一走 skill 自带脚本 scripts/engram-sync.sh(它内部用子 shell
固定到 ~/.engram,既保证 chunk 落对地方,又不改你当前目录):
# 路径相对本 skill 目录;<skill> = 本 SKILL.md 所在目录
<skill>/scripts/engram-sync.sh push # engram sync --all → git add/commit/push(默认)
<skill>/scripts/engram-sync.sh pull # git pull → engram sync --import
<skill>/scripts/engram-sync.sh status # engram 同步状态 + git 状态
首次每台机一次性初始化 git 远端(脚本不做这步,避免误操作):
( cd ~/.engram && git init && git remote add origin <私有仓> ) # 用私有仓!
务必用私有仓(记忆含敏感上下文)。检索文本已是英文,同步后各机表现一致;继续用同样的常用译词即可对齐。
注:
engram sync --status里的Remote chunks指的是 engram 自建 cloud 服务(未配置即为 0), 与"git 已推送成功"无关——本方案的云端走 git→GitHub,不是 engram cloud。 脚本可用ENGRAM_HOME覆盖数据目录(默认~/.engram)。
为什么这样设计(便于变通,而非死记)
- CLI 而非 MCP:不在上下文常驻、无后台 hook、每个动作都是可检视的 shell 命令。
- 先翻译再存胜过中文分词 hack:engram 纯 Go 无法加载 C 分词扩展(jieba/ICU),内置 trigram 会漏 2 字词还可能破坏更新;翻译绕开这一切且零依赖。
- 双存用一点冗余换信任:随时能读原始措辞、核对翻译没走样。
- 工作与生活通用:固定 7 类
type+--scope personal让它不止服务编码,也能记住 生活里的偏好、计划、人和事。