imprint-memory
Give Claude a memory that lasts. Everything you tell it stays — searchable across conversations, private, on your machine.
Your memory lives in a local database — not the cloud. Default embeddings use a free Google key; go fully local and offline with Ollama.
You: "Remember I'm allergic to shellfish"
↓ stored locally
... weeks later, new conversation ...
Claude: (recalls your allergy before suggesting a recipe)
Security update — 0.4.1 (Oct 2026). If you run HTTP mode (
imprint-memory --http), upgrade:pip install -U imprint-memory.
- Up to 0.4.0, requests from
127.0.0.1skipped the token check. Cloudflare Tunnel forwards the real client IP so it stayed locked, but plain TCP forwarders (frp, ssh -R, socat, many port-forward services) make every outside request look local, so the memory was open to anyone with the URL. 0.4.1 always requires the token and won't start HTTP mode without one.- 0.1.0 (and git installs from before 2026-05-17) let anyone in when no credentials were set and shipped a
cc_executetool that could drive your local Claude Code. If you ever exposed one of those, upgrade and assume the memory was readable.- 0.4.0 and earlier also broke on install once
mcp2.x came out; 0.4.1 pinsmcp<2.
What it does
- Captures every turn automatically — each surface feeds the same
store through its own entry point: a
Stophook for Claude Code, the Chrome sync extension for claude.ai, channel adapters for Telegram and the like. Every message lands in a searchable log, gets segmented into events, and multimodal-embedded (text + image). The LLM never needs to "decide to remember" — it's already stored. - Hybrid search out of the box — keyword (FTS5/BM25) + semantic (vector) + RRF rank fusion across memory / bank / chunk pools. "What did I say about that project last week?" → it finds it.
- Auto-surfaces context before you even ask — a
UserPromptSubmithook callssurfacing_searchon every prompt and injects a<recall>block with up to ~6 related events. - Multimodal — images sent in a conversation get text + image embeddings, so "the red screenshot I sent" lands on the right message even if the caption doesn't mention "red".
- Syncs your claude.ai chats — optional Chrome extension captures your web conversations into the same memory store.
- AI-flagged highlights (optional) — the LLM can call
memory_rememberto bump a specific fact higher in search results, but the system fully works without it. - Stays organised — facts can be pinned, tagged, and linked (graph edges between memories). Importance can be tuned per-entry.
- Speaks Chinese — full CJK search, time expressions like
昨天、上个月、去年冬天.
How it actually works
A common misconception: "the AI still has to choose what to
remember, right?" — No. The whole capture-and-recall pipeline
is automatic. Curation tools (memory_remember, pin, etc.) exist
and the LLM may use them for small quality boosts, but they sit
on top of an already-complete automatic system. Search, recall, and
auto-surfacing all work without any curation call ever being made.
Architecture at a glance
flowchart TB
subgraph CAPTURE["📥 Capture (automatic, every turn)"]
turn["any conversation turn<br/>(cc hook · claude.ai sync · telegram · app)"]
turn --> log["log_message → conversation_log"]
log --> mm["multimodal embed<br/>(text + image)"]
log -. scheduled .-> ch["chunker → events + summaries"]
ch --> ce["chunk_edges<br/>(Top-K similarity graph)"]
log --> fts["FTS5 + jieba CJK index"]
end
subgraph SEARCH["🔎 Search (automatic, on every memory_search call)"]
q["query"] --> rrf["vec cosine + BM25 + RRF fusion"]
mm --- rrf
ce --- rrf
fts --- rrf
rrf --> ranked["ranked results across memory/bank/chunk pools"]
end
subgraph SURFACE["✨ Surface (automatic, before LLM sees any prompt)"]
prompt["user prompt"] --> hook["UserPromptSubmit hook"]
hook --> ssearch["surfacing_search"]
ssearch --> recall["⟨recall⟩ block injected<br/>≈ 6 related events"]
end
subgraph CURATE["📝 Curate (optional — LLM or you call. System works fully without these)"]
rem["memory_remember(content)<br/>flag highlight fact"]
pn["memory_pin(id)<br/>no decay"]
tg["memory_add_tags / add_edge<br/>manual annotation"]
mt["find_stale / decay / delete<br/>bulk maintenance"]
end
rem -. boost ranking .-> rrf
pn -. boost ranking .-> rrf
tg -. boost ranking .-> rrf
Automatic pipeline — the whole system
Optional curation tools
The pipeline above is enough on its own. These extra MCP tools exist so the LLM (or you) can annotate memories for slightly better ranking — call them when useful, skip them entirely if you don't care; the system works fully either way.
If your LLM never invokes any of these, conversations are still
captured, chunked, embedded, indexed, and reachable through
memory_search — you just don't get the small "this fact is a
highlight" ranking nudge.
Quick Start
One command (recommended)
curl -fsSL https://raw.githubusercontent.com/Qizhan7/imprint-memory/main/scripts/setup.sh | bash
Installs imprint-memory and registers the MCP server. You'll then need a
GOOGLE_API_KEY for embeddings — see Models & Cost
below (5-min, free, no card). Data lives in ~/.imprint/.
Manual install
pip install imprint-memory
claude mcp add -s user imprint-memory -- imprint-memory
export GOOGLE_API_KEY=... # for embeddings, see below
Restart Claude Code. That's it.
Prefer fully-local (no API keys)?
Set EMBED_PROVIDER=ollama BEFORE the first write, then:
ollama pull bge-m3 && ollama serve
You lose multimodal (text+image) — bge-m3 is text-only. See the embedding consistency note about why this choice must be made up front.
Sync your claude.ai conversations (optional)
pip install 'imprint-memory[receiver]'
imprint-memory-receiver
Then install the imprint-chat-sync Chrome extension to capture your web chats.
Auto-capture hook — Claude Code (recommended)
This is what makes capture automatic for Claude Code. A Stop hook logs every
turn to your memory store. Add to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "imprint-memory-capture" }] }
]
}
}
imprint-memory-capture reads the just-finished turn's transcript and writes
each message to conversation_log. Without it the Claude Code channel won't
auto-capture (claude.ai sync and other channel adapters have their own paths).
Auto-surfacing hook (optional)
Makes Claude automatically recall relevant memories when you ask something:
mkdir -p ~/.claude/hooks
HOOK_PATH="$(python - <<'PY'
from importlib.resources import files
print(files("imprint_memory") / "hooks" / "memory-check.sh")
PY
)"
cp "$HOOK_PATH" ~/.claude/hooks/memory-check.sh
chmod +x ~/.claude/hooks/memory-check.sh
Add to ~/.claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "bash $HOME/.claude/hooks/memory-check.sh"
}
]
}
]
}
}
Models & Cost
imprint-memory does NOT replace your main chat model — Claude / ChatGPT / Gemini / whichever LLM you talk to keeps doing that job unchanged. What imprint-memory runs internally is a small handful of helper models that turn raw chat history into searchable memory: one for embedding (turning text and images into vectors), one for chunk summarization (the chunker's LLM that compresses each conversation segment into a one-paragraph summary), and an optional one for context compression. The helpers below are all that imprint-memory itself calls — your main chat LLM and your API bill for it are completely separate.
Default helper config is tuned to fit inside vendors' free tiers for normal personal use — no payment card required, no billing to enable, no surprise charges.
Helper models (what imprint-memory actually calls)
Bottom line: the only thing strictly required to get up and running
is one embedding provider. If you set up Google for embedding,
chunk summarization can reuse the same GOOGLE_API_KEY. Daily personal
use never gets close to free-tier limits, and "enabling" Workers AI
or Google AI Studio is a single click — neither provider can charge
you without you explicitly switching plans.
⚠️ Embedding consistency — DO NOT MIX
The vector dimension MUST stay constant across the lifetime of one DB.
Switching EMBED_PROVIDER mid-stream means new vectors get a different
shape than old ones. Cosine similarity then returns 0 across the
boundary and search silently goes half-blind — FTS still works, but
the semantic channel is dead for everything written before the switch.
Pick once, before the first write. If you absolutely must switch:
run imprint-memory-reindex to rebuild curated memory + bank vectors, then
DELETE FROM conversation_vectors; on the DB and let the receiver's backfill
re-embed conversations incrementally (a full cloud re-embed can exceed
free-tier quotas).
MCP Tools (27 total)
Memory CRUD
Graph Structure
Memory Maintenance
Conversation Search
Search Quality
Message Bus
Experience Bank
How Search Works
- Parse time expressions (
昨天,last week,after:2026-04-01) - Optionally expand the query with synonyms/variants
- Search all pools in parallel: memories, notes, conversations, summaries
- Fuse results by rank (best across all methods wins)
- Boost pinned memories, recent items, high-importance entries
- Expand hits: show linked memories and surrounding conversation context
- Update recall counters (frequently recalled memories stay relevant)
Recommended CLAUDE.md hint
Without a hint, the LLM tends to call memory_search once and stop. But
memory_search only sees the chunk-level index — and the chunker's LLM
summaries can drop proper nouns (e.g. system names, paper titles, library
names). When that happens, memory_search returns nothing even though
the raw conversation_log still has 20+ matching messages.
Two-step search rule — drop this into your CLAUDE.md so the model falls back automatically:
When you can't find past information, don't say "I don't remember"
until you've tried both steps:
1. memory_search(query) — unified entry, RRF across memory / bank / chunk pools
2. conversation_search(query) — raw conversation_log FTS5 fallback
(must hit if the word ever appeared in any message)
Configuration
All via environment variables. Defaults work out of the box for local use.
Data Layout
~/.imprint/
├── memory.db ← all memories + vectors
├── MEMORY.md ← human-readable memory guide
└── memory/
├── 2026-05-17.md ← today's daily log
└── bank/
└── experience.md ← technical notes
Development
git clone https://github.com/Qizhan7/imprint-memory.git
cd imprint-memory
pip install -e '.[all]'
pytest
Acknowledgements
Built on top of these open-source libraries:
- JioNLP by dongrixinyu (Apache 2.0) — powers the natural-language time parsing in
_extract_time_intent(昨天/上周/5月15日→ date ranges viajionlp.ner.extract_time). Without it, queries like "what we talked about yesterday" couldn't pin a real time window. - jieba (MIT) — Chinese tokenisation for FTS5 indexing and keyword extraction.
- NumPy (BSD) — vector-space matrix operations for the graph build (Top-K edges, MMR diversification).
- Embedding providers: Google Gemini Embedding 2 (multimodal), OpenAI
text-embedding-3-small, or local Ollamabge-m3— configured viaEMBED_PROVIDER.
License
AGPL-3.0 — free for personal use; commercial use requires open-sourcing your entire project under AGPL.
imprint-memory
让 Claude 记住你说过的话。下次聊天还在,搜得到,全部存在本地。
记忆存在本地数据库——不在云端。默认 embedding 用一把免费 Google key;想完全本地 + 离线就切到 Ollama。
你: "记住我对虾过敏"
↓ 本地存储
... 几周后,新对话 ...
Claude: (推荐菜谱前自动回忆起你的过敏)
安全更新 · 0.4.1(2026-10) 用 HTTP 模式(
imprint-memory --http)的请升级:pip install -U imprint-memory。
- 0.4.0 及以前,来自
127.0.0.1的请求不验 token。Cloudflare Tunnel 会带上真实来访 IP,所以照文档走是锁着的;但 frp、ssh -R、花生壳这类纯转发的内网穿透会让外面的请求全长得像本机,拿到地址的人都能读写记忆。0.4.1 起一律验 token,没配 token 不让起 HTTP 模式。- 0.1.0(以及 2026-05-17 前从 git 装的)没配凭据时谁都能进,还带一个能指挥本机 Claude Code 的
cc_execute。如果你曾把这些版本暴露出去,请升级,并当作记忆已经可能被读过。- mcp 出了 2.x 后旧版装完会直接报错,0.4.1 钉了
mcp<2。
能做什么
- 每个对话 turn 自动捕获 — 每个渠道用各自的入口喂进同一个库:
Claude Code 走
Stophook,claude.ai 走 Chrome 同步扩展,Telegram 等走 channel adapter。每条消息都自动写入可搜索的日志、切成事件、做 多模态 embedding(文字 + 图片)。LLM 不需要"决定要不要记住", 写进 DB 是默认发生的。 - 混合搜索开箱即用 — 关键词(FTS5/BM25)+ 语义(向量)+ RRF rank 融合,覆盖 memory / bank / chunk 三池。问"上周那个项目是 什么来着"也能找到。
- 提问前自动浮现相关上下文 —
UserPromptSubmithook 每次提问前 调surfacing_search,注入<recall>块(约 6 条相关事件)。 - 多模态 — 对话里发的图片会跟文字一起 embed,所以"我发的那张 红色截图"就算 caption 不提"红色"也能命中。
- 同步 claude.ai 对话 — 可选 Chrome 扩展,把网页端聊天也存进 同一套记忆库。
- AI 主动标"高亮" (可选) — LLM 可以调
memory_remember把 某条事实顶到搜索结果靠前,但系统跑起来不依赖这步。 - 可整理 — 记忆能 pin(不衰减)、加标签、加图谱边(记忆之间 的关系)。每条 importance 也可调。
- 中文友好 — 完整中文搜索,支持
昨天、上个月、去年冬天等时间表达。
真正的工作流程
常见误解:"是不是还是要 AI 自己决定要记什么?" —— 不是。整个
捕获-召回管线全自动。整理工具(memory_remember、pin 等)
确实存在,LLM 可以可选地调用它们做小幅 ranking 微调,但它们
叠在一套已经完整运转的自动系统之上。搜索、召回、自动浮现都
不依赖整理工具调用,一次不调也照样跑。
架构总览
flowchart TB
subgraph CAPTURE["📥 捕获(自动,每个 turn)"]
turn["任何对话 turn<br/>(cc hook · claude.ai 同步 · telegram · app)"]
turn --> log["log_message → conversation_log"]
log --> mm["多模态 embed<br/>(text + image)"]
log -. 定时 .-> ch["chunker → 事件 + 摘要"]
ch --> ce["chunk_edges<br/>(Top-K 相似度图谱)"]
log --> fts["FTS5 + jieba 中文分词索引"]
end
subgraph SEARCH["🔎 搜索(自动,每次 memory_search 调用)"]
q["query"] --> rrf["向量 cosine + BM25 + RRF 融合"]
mm --- rrf
ce --- rrf
fts --- rrf
rrf --> ranked["memory / bank / chunk 三池排序结果"]
end
subgraph SURFACE["✨ 浮现(自动,LLM 看到 prompt 前)"]
prompt["user prompt"] --> hook["UserPromptSubmit hook"]
hook --> ssearch["surfacing_search"]
ssearch --> recall["⟨recall⟩ 块注入<br/>≈ 6 条相关事件"]
end
subgraph CURATE["📝 整理(可选 — LLM 或你调。不调系统照常工作)"]
rem["memory_remember(content)<br/>把事实标为高亮"]
pn["memory_pin(id)<br/>不衰减"]
tg["memory_add_tags / add_edge<br/>手动标注"]
mt["find_stale / decay / delete<br/>批量维护"]
end
rem -. ranking 微调 .-> rrf
pn -. ranking 微调 .-> rrf
tg -. ranking 微调 .-> rrf
自动管线 — 这就是整个系统
可选整理工具
上面的自动管线已经够用。下面这些额外的 MCP 工具让 LLM(或你)给 记忆做标注、做小幅 ranking 微调 — 想用就用、不想用全跳过都行, 系统两种情况下都完整工作。
LLM 一次都不调这些工具,对话也照常被捕获、切块、embed、建索引,
能被 memory_search 搜到 —— 只是少了"这条是 highlight" 的那点
ranking 微调。
快速开始
一行搞定(推荐)
curl -fsSL https://raw.githubusercontent.com/Qizhan7/imprint-memory/main/scripts/setup.sh | bash
装好 imprint-memory + 注册 MCP。embedding 需要 GOOGLE_API_KEY ——
看下面 模型与费用 章节(5 分钟搞定,全程免费,无需信用卡)。
数据存在 ~/.imprint/。
手动安装
pip install imprint-memory
claude mcp add -s user imprint-memory -- imprint-memory
export GOOGLE_API_KEY=... # 嵌入用,见下方
重启 Claude Code 就能用。
想全本地不要 API key?
首次写入前设 EMBED_PROVIDER=ollama,然后:
ollama pull bge-m3 && ollama serve
代价:失去多模态(文字+图片)—— bge-m3 只支持文字。看下面 嵌入维度一致性 警告,这选择必须一开始就定下来。
同步 claude.ai 对话(可选)
pip install 'imprint-memory[receiver]'
imprint-memory-receiver
然后装 imprint-chat-sync Chrome 扩展,把网页端对话也收进来。
自动捕获 hook — Claude Code(推荐)
这就是让 Claude Code 自动捕获的关键。一个 Stop hook 把每个 turn 写进
记忆库。加到 ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "imprint-memory-capture" }] }
]
}
}
imprint-memory-capture 读刚结束那一轮的 transcript,把每条消息写进
conversation_log。不装它,Claude Code 渠道就不会自动捕获(claude.ai
同步和其它 channel adapter 有各自的捕获路径)。
自动浮现 hook(可选)
让 Claude 在你提问时自动想起相关的记忆:
mkdir -p ~/.claude/hooks
HOOK_PATH="$(python3 -c '
from importlib.resources import files
print(files("imprint_memory") / "hooks" / "memory-check.sh")
')"
cp "$HOOK_PATH" ~/.claude/hooks/memory-check.sh
chmod +x ~/.claude/hooks/memory-check.sh
加到 ~/.claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "bash $HOME/.claude/hooks/memory-check.sh"
}
]
}
]
}
}
中文输出设置:在 ~/.imprint/.env 里加 IMPRINT_LOCALE=zh 和 IMPRINT_HOOK_LANG=zh。
模型与费用
imprint-memory 不取代你的主对话模型 —— 你日常聊天用的 Claude / ChatGPT / Gemini 还是它自己干活。imprint-memory 内部跑的是几个 辅助模型,把对话历史转成可搜索的记忆:一个负责嵌入(把文字 和图片转成向量)、一个负责 chunk 摘要(chunker 用 LLM 把每段对话 压成一段总结)、还有一个可选的上下文压缩。下面列的就是 imprint-memory 自己会调用的全部模型 —— 你的主对话 LLM 和它的 API 账单跟这里完全无关。
默认配置都调成塞进各厂商免费档的水位 —— 不需要绑卡、不需要开 billing、不会冒出账单。
辅助模型清单(imprint-memory 真正调的)
底线:开起来绝对必需的只有一个嵌入服务。如果你用了
Google 做嵌入,chunk 摘要可以直接复用同一把 GOOGLE_API_KEY。日常
个人使用根本碰不到免费档上限,"启用" Workers AI 或 Google AI Studio
都是点一下(不要卡),两边都不可能在你没主动换套餐的情况下扣钱。
⚠️ 嵌入一致性 — 绝对不能混用
向量维度必须在同一个 DB 的整个生命周期里保持一致。
中途切 EMBED_PROVIDER = 新写入的 vec 跟旧的维度不一样。
跨边界的 cosine sim 一律返回 0,搜索悄悄变瞎子——FTS 还能用,但
语义通道对切换之前写入的所有数据都失效了。
写第一条之前就选好。如果非得切:先跑 imprint-memory-reindex
重建 curated memory + bank 向量,再对 DB 执行 DELETE FROM conversation_vectors;,让 receiver 的 backfill 渐进重嵌对话(云端全量
重嵌可能超免费档)。
MCP 工具(共 27 个)
记忆 CRUD
图谱结构
记忆维护
对话搜索
搜索质量
消息总线
经验库
搜索原理
- 解析时间表达(
昨天、上个月、after:2026-04-01) - 可选扩展查询(加同义词/口语变体)
- 所有池子并行搜索:记忆、笔记、对话、摘要
- 按排名融合结果(各种方法里排最高的赢)
- 加分:钉住的记忆、最近的、重要度高的
- 展开命中:显示关联记忆和前后对话上下文
- 更新 recall 计数(经常被想起的记忆保持相关性)
CLAUDE.md 搜索提示词建议
不写这条提示,LLM 倾向 memory_search 搜一次没结果就放弃。但
memory_search 只看 chunk 层索引——chunker 的 LLM 摘要可能把专有名词
扔掉(外部系统名、论文标题、库名)。这时 memory_search 返回空,
但 conversation_log 原文里其实还有 20+ 条相关消息。
两步搜索规则——把这段加到你的 CLAUDE.md,LLM 就会自动 fall back:
找不到过去的信息时,不要直接说"不记得",先走完这两步:
1. memory_search(query) — 总入口,跨 memory / bank / chunk 池 RRF 融合
2. conversation_search(query) — 原文 conversation_log FTS5 兜底
(只要这个词在历史消息里出现过,必中)
数据在哪
~/.imprint/
├── memory.db ← 所有记忆 + 向量
├── MEMORY.md ← 记忆使用指南
└── memory/
├── 2026-05-17.md ← 今天的日志
└── bank/
└── experience.md ← 技术笔记
开发
git clone https://github.com/Qizhan7/imprint-memory.git
cd imprint-memory
pip install -e '.[all]'
pytest
致谢
记忆系统建立在这些开源项目之上:
- JioNLP by dongrixinyu (Apache 2.0) — 提供自然语言时间解析(
_extract_time_intent),让"昨天/上周/5 月 15 日"能转成具体日期范围。没有它,"昨天我们聊了什么"这种 query 锚不到真正的时间窗。 - jieba (MIT) — 中文分词,用于 FTS5 索引和关键词提取。
- NumPy (BSD) — 图谱构建(Top-K 边、MMR 去重)的矩阵运算。
- Embedding 提供方:Google Gemini Embedding 2(多模态)、OpenAI
text-embedding-3-small、或本地 Ollamabge-m3—— 通过EMBED_PROVIDER配置。
许可证
AGPL-3.0 — 个人使用免费;商业使用须将整个项目以 AGPL 开源。