关于 madeleine
madeleine 是面向 AI Agent 的本地优先记忆系统,能根据地点、时间、文字、图像或声音等线索自动唤起相关记忆,也支持主动检索。它以 SQLite 存储,并提供 MCP 服务和 Python 库。
主要功能
- 01根据多种感官线索匹配记忆
- 02支持主动检索与线索触发唤起
- 03通过 HTTP API 接收线索
- 04使用 SQLite 本地存储且无遥测
- 05提供 MCP 工具及 Python 库
适用场景
- 01在相关地点或时段自动呈现过往记忆
- 02根据传感器或媒体线索触发回忆
- 03让 Agent 在每轮提示时取得待处理回忆
MCP 相关
MCP 服务提供 remember、recall、sense、pending_recollections 等七个工具,以及 recollections://pending 资源。客户端可通过标准 stdio 接入;也支持 streamable HTTP 传输。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
7收录的工具列表 · 更新于 1 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| at | any | 否 | 发生时间,采用带用户本地时区偏移的 ISO 8601 格式,例如 '2026-06-01T21:30:00+02:00'。默认值:现在。 |
| lat | any | 否 | 纬度,单位为度(WGS84)。需要 lon。 |
| lon | any | 否 | 经度,单位为度(WGS84)。需要 lat。 |
| tags | any | 否 | 自由格式标签,例如 ['family']。 |
| content | string | 是 | 要记住的内容,按你希望日后回忆时呈现的方式填写(通常为一两句话)。其文本也会嵌入 'text' 通道。 |
| geohash | any | 否 | 地点,使用 geohash 表示(精度为 1-12)。优先于 lat/lon。 |
| signals | any | 否 | 额外的原始读数,每个通道最多一个(参见各字段)。 |
| metadata | any | 否 | 可选的字符串键/值注释,与记忆一同存储(不嵌入,也不用于匹配),例如 {"app": "notes"}。 |
| salience | any | 否 | 重要程度,范围为 0(无关紧要)到 1(改变人生)。默认值为 0.5。重要的记忆更容易浮现。 |
| media_ref | any | 否 | 相关媒体的可选指针(URI/路径);不会打开。 |
详细介绍

madeleine
AI Agent 的非自主记忆。 一个地点、一段时间、一种声音、一张照片或一条传感器读数, 都能自行唤起相关记忆,无需 Agent 主动询问。它的运作方式,就像玛德琳蛋糕的味道唤起了普鲁斯特的童年记忆。
本地优先(SQLite)· MCP 服务 + Python 库 · 无遥测
大多数 Agent 记忆都是主动提取的:Agent 决定搜索,编写查询,然后获取结果。madeleine 增添了另一种途径。每条记忆都会连同编码时的感官指纹一起存储:地点、时间,以及当时说过、看过或听过的内容。来自 Webhook、手机、智能家居和可穿戴设备的线索会持续与这些指纹进行匹配。当足够多的通道吻合时,记忆就会排入队列,成为一次非自主回忆,并在 Agent 收到下一个提示词时传递给它。这是将编码特异性应用于 Agent(概念)。
60 秒快速入门
# 1. See the whole loop, offline (nothing is downloaded, nothing leaves your machine)
uvx madeleine-mcp demo
演示会记住三个时刻。随后,一条线索到来,其中只包含地点和一天中的时间:距离奶奶家 30 米,周日下午。匹配的记忆会自行浮现,通过 MCP 工具 pending_recollections 传递,并且不会因重复线索而再次触发:
[madeleine 0.80 via geo+time] Sunday afternoons in grandmother's kitchen: lime-blossom tea and a madeleine (2019-04-14)
channel similarity geo=0.97, time=1.00; salience x1.16, age decay x0.70
# 2. Give Claude Code the tools
claude mcp add madeleine -- uvx madeleine-mcp serve
- 让非自主回忆进入每个提示词。将以下内容添加到
.claude/settings.json:
{"hooks": {"UserPromptSubmit": [{"hooks": [
{"type": "command", "command": "uvx madeleine-mcp hook claude-code", "timeout": 10}
]}]}}
即使客户端忽略 MCP 通知,这个钩子也能正常工作。有关 --sense-prompt 等选项,请参阅
examples/claude_code;该选项也会将你输入的内容视为线索。
其他 MCP 客户端
该服务是标准的 stdio MCP 服务(uvx madeleine-mcp serve),因此任何 MCP 客户端都能使用这七个工具。每次提示词都传递非自主回忆(上文第 3 步)是为
Claude Code 构建的;在其他客户端中,Agent 需要调用 pending_recollections 来接收回忆,而服务的说明会要求它在每轮开始时调用该工具。
Claude Desktop、Cursor、VS Code、OpenCode、Docker
Claude Desktop(claude_desktop_config.json)和 Cursor(~/.cursor/mcp.json):
{"mcpServers": {"madeleine": {"command": "uvx", "args": ["madeleine-mcp", "serve"]}}}
VS Code(.vscode/mcp.json):
{"servers": {"madeleine": {"type": "stdio", "command": "uvx", "args": ["madeleine-mcp", "serve"]}}}
OpenCode(opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {"madeleine": {"type": "local", "command": ["uvx", "madeleine-mcp", "serve"], "enabled": true}}
}
Docker(记忆会持久保存在 madeleine-data 卷中):
docker build -t madeleine-mcp https://github.com/fabianimv/madeleine.git
docker run -i --rm -v madeleine-data:/data madeleine-mcp # stdio MCP server
语义文本需要
[text]附加组件。 基础安装体积小巧,且可离线运行。若不安装附加组件,文本会通过哈希嵌入器进行词法匹配,因此 “car” 和 “automobile” 会被视为不相关。若要使用真正的句子嵌入(这会安装 PyTorch):claude mcp add madeleine -- uvx --from 'madeleine-mcp[text]' madeleine-mcp serve图像(
[image]、open_clip)和声音([audio]、LAION CLAP)也可用同样方式选择安装。[all]会安装全部内容。模型会在首次使用时按需下载,绝不会在导入时下载。
工作原理
flowchart LR
subgraph Sources
HA[Home Assistant] -->|webhook| HTTP
IOS[iOS Shortcuts] -->|webhook| HTTP
AG[Agent] -->|MCP tools| MCP
end
HTTP[HTTP API /cues] --> ENG
MCP[MCP server] --> ENG
subgraph ENG[engine]
direction TB
CH["channels<br/>adapter → embedder → similarity"] --> FU["fusion<br/>base · coverage · salience · decay"]
FU --> RF{"push?<br/>threshold + refractory"}
end
ENG <--> DB[(SQLite + sqlite-vec)]
RF -->|enqueue| Q[(recollection queue)]
Q -->|drain| HOOK[Claude Code hook]
Q -->|drain / notify| MCP
- 通道是可插拔的感知能力。每个通道都有一个 适配器(原始信号 → 规范化观测)、一个 嵌入器(观测 → 向量)和一个 相似度函数(→ [0, 1])。
内置通道:
text、time(小时、星期和季节,均按周期计算)、geo(球面距离)、biometric、image、audio。smell是有文档说明的占位项,为未来的电子鼻预留。 - 回忆会综合线索与记忆共有通道的各通道相似度,根据线索覆盖程度进行调整,增强显著记忆的权重,并随时间推移衰减至下限,因此几十年前的记忆仍然可以唤起。公式简洁且易于解释;每个结果都会说明匹配的通道 (ADR 0001)。
- 两种模式。 主动提取:Agent 发起请求(
recall)。被动触发:收到线索(sense、POST /cues),达到阈值的记忆会排入队列,无需任何人发起请求。 - 习惯化。 被触发的记忆会在 不应期 内保持静默(默认 6 h)。整个下午路过同一家咖啡馆,只会让你想起它一次。
- 上下文单独不会触发回忆。“现在是上午 9 点”会匹配所有在上午 9 点形成的记忆,因此
time(和biometric)可以增强匹配度,但永远不会单独触发回忆。
发送线索
madeleine serve --transport http # REST + MCP (streamable HTTP at /mcp/) on 127.0.0.1:8765
curl -s localhost:8765/cues -H 'content-type: application/json' \
-d '{"lat": 48.30195, "lon": 1.24650, "signals": [{"channel": "text", "value": "smell of lime-blossom tea"}]}'
| 路由 | 用途 |
|---|---|
POST /cues | 通用线索:signals、at、geohash 或 lat/lon、tags、source |
POST /cues/{generic,home_assistant,ios_shortcuts} | 直接来自这些工具的载荷(示例) |
POST /memories、POST /recall、GET/PATCH/DELETE /memories/{id} | 与 MCP 工具相同 |
GET /recollections/pending · POST /recollections/drain | 查看 / 取出排队的非自主回忆 |
现成集成:Home Assistant(到家、媒体开始播放、可穿戴设备传感器)、iOS Shortcuts(“到达地点”个人自动化)、curl。
MCP 接口:
- 工具:
remember、recall、sense、pending_recollections、get_memory、update_memory、forget。 - 资源:
recollections://pending;有内容浮现时会发送resources/updated通知。共享数据库的不同进程也可以收到这些通知。
隐私与安全默认设置
- 本地优先。 所有数据都存储在一个 SQLite 文件中(
~/.madeleine/madeleine.db,仅所有者可访问)。 不会收集遥测数据,也不使用云服务。 - 不保留原始媒体。 图片和音频字节会被嵌入,随后丢弃。除非传入
store_raw=True(按次调用或设置MADELEINE_STORE_RAW=1),否则只存储向量和元数据。 - 默认不访问本地文件。 客户端必须发送媒体字节。若要允许使用文件路径,需指定
madeleine serve --allow-file-uris。 - HTTP 绑定到 127.0.0.1。 Host 和 Origin 检查会阻止 DNS 重绑定。设置
MADELEINE_API_TOKEN可要求提供 bearer token。除非指定--insecure-no-auth,否则没有令牌时拒绝绑定到其他网络接口。 forget会彻底删除所有位置的数据:记忆、向量、原始字节、触发历史记录和排队的回忆,并启用 SQLitesecure_delete。- 回忆是数据,不是指令。 钩子会用分隔符将其包裹起来,并标明其数据属性。任何能够写入记忆的人仍然可以向 Agent 的上下文中添加文本,因此请将写入权限保留给自己。参见 SECURITY.md。
基准测试(及其不足之处)
madeleine bench 会运行合成基准测试:涵盖 3 个城市的 500 条记忆、300 条具有已知真实结果的线索、干扰项和困难负例。测试结果分别针对两种文本后端报告。完整表格、方法和盲点见 docs/benchmarks.md。
madeleine bench # offline hash text backend
madeleine bench --text-backend sentence-transformers # semantic, needs the [text] extra
| hash(离线,默认) | semantic([text],MiniLM) | |
|---|---|---|
| 提取 R@1 / R@10 | 0.69 / 0.81 | 0.74 / 0.82 |
| 非自主触发的精确率 | 0.69 | 0.70 |
| 能触发对应记忆的正向线索 | 0.53 | 0.67 |
| 负例误触发率 | 0.00 | 0.00 |
按线索类别划分(命中率 = 正确记忆自行浮现的频率):
| 线索类别 | hash:命中率 | semantic:命中率 | semantic:精确率 |
|---|---|---|---|
| 地点 + 时间 | 0.81 | 0.81 | 0.56 |
| 仅文本释义 | 0.21 | 0.67 | 1.00 |
| 文本 + 地点 + 时间 | 0.67 | 0.76 | 0.76 |
| 完全相同的照片 / 声音 | 1.00 | 1.00 | 1.00 |
| 略有改动的照片 / 声音 | 0.00 | 0.00 | – |
| 数年前的记忆 | 0.40 | 0.60 | 0.64 |
| 负例(干扰项、仅时间、同一地点 + 其他主题) | 触发 0 次 | 触发 0 次 |
在纯 Python 环境下,针对 500 条记忆的延迟:hash 后端的 sense p50 为 11 ms / p95 为 23 ms,semantic 后端的 p50 为 32 ms / p95 为 63 ms(CPU)。
坦诚说明局限性
- 非自主回忆经过保守调校:没有负向线索触发,但仍有三分之一(semantic)到一半(hash)的正向线索无法触发对应记忆。
- 两种文本后端都在基准测试生成器上进行了校准(种子为 1;结果使用种子 0,种子 2 和 3 的结果也一致)。未经校准时,hash 后端会低估释义,MiniLM 则会高估不相关的句子。数据是合成的英文文本,因此实际场景中的数字会有所不同。
- 在此基准测试中,改动过的媒体始终无法匹配,因为测试使用了图像和声音的 hash 嵌入器。要实现这一点,需要 CLIP 和 CLAP 扩展;其相似度重缩放目前仅针对合成输入进行了校准。
- 地点 + 时间线索的触发次数达到上限(每条线索触发 3 次),其中只有 56 % 符合严格的“同一地点、同一小时 ±1 h”真实结果。
- 融合使用的是人工设计的公式,而非学习得到的公式。没有多用户身份验证,也没有 UI。嗅觉通道只是一个存根。
- 一天中的小时数使用每个时间戳自身的 UTC 偏移。请传入本地偏移量(
+02:00),否则“傍晚”指的是 UTC 时间的傍晚。
配置
| 环境变量 | 默认值 | 含义 |
|---|---|---|
MADELEINE_DB | ~/.madeleine/madeleine.db | SQLite 路径(允许使用 :memory:) |
MADELEINE_TEXT_BACKEND | auto | auto(如果已安装,则使用 sentence-transformers)、sentence-transformers、hash |
MADELEINE_CHANNELS | 所有内置通道 | 以逗号分隔的已启用通道 |
MADELEINE_STORE_RAW | 关闭 | 持久化原始媒体字节 |
MADELEINE_ALLOW_LOCAL_FILES | 关闭 | 允许媒体信号引用本地文件 |
MADELEINE_API_TOKEN | 未设置 | HTTP API(以及 hook 的 --url 模式)的 Bearer 令牌 |
MADELEINE_ALLOWED_HOSTS | loopback | HTTP 服务接受的额外 Host/Origin 名称 |
CLI:madeleine serve | remember | sense | recall | pending | show | edit | forget | demo | bench | hook claude-code
(madeleine --help)。
作为 Python 库使用
from madeleine.config import load_config
from madeleine.core.serde import context_from_fields
from madeleine.engine import create_engine
engine = create_engine(load_config(db_path="memories.db", text_backend="hash"))
now = engine.now()
engine.remember("Ana proposed at the Eiffel Tower",
context=context_from_fields(now=now, lat=48.8584, lon=2.2945, salience=0.9))
cue = engine.build_cue(context=context_from_fields(now=now, lat=48.8585, lon=2.2946))
for rec in engine.sense(cue): # involuntary: fires on place (+ time) alone
print(rec.to_prompt_line())
使用 Adapter、Embedder 和相似度函数,编写自己的 sense(参见 docs/channels.md)。ImageBind 因其许可证为 CC-BY-NC 而被刻意排除在依赖项之外,但你可以自行将其接入作为 Embedder。
开发
请参阅 CONTRIBUTING.md。先运行一次 uv sync,之后每次提交前运行:
uv run pytest、uv run ruff check src tests、uv run mypy。架构说明见 ARCHITECTURE.md,设计决策见 docs/adr。
许可证
Apache-2.0。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。