关于 Attestor
Attestor 是面向多 Agent 系统的可自托管记忆服务,适用于需要权限治理、来源追踪和历史状态重建的团队。它以确定性检索、时间回溯和审计式删除为核心,并可作为 Python 库、REST sidecar 或 MCP 服务运行。
主要功能
- 01基于角色的读写权限
- 02记忆来源记录与可选签名
- 03确定性检索与预算限制
- 04按历史时点回放记忆
- 05多租户存储隔离
- 06审计式删除与保留策略
适用场景
- 01管理共享记忆的多 Agent 产品
- 02重建系统过去某一时点的记忆状态
- 03执行可审计的用户数据删除
- 04隔离不同租户的记忆数据
MCP 相关
Attestor 可作为 MCP 服务运行,并向 MCP 客户端提供与其记忆服务相同的 API。该 MCP 接口直接调用存储,身份验证由接口自身负责;角色执行保证属于 AgentContext 层。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx attestorATTESTOR_PATHDirectory containing attestor's config.toml. Defaults to ~/.attestor.ATTESTOR_DISABLE_LOCAL_EMBEDSet to 1 to skip the local Ollama embedder probe (e.g., on hosts without Ollama). Defaults to unset (Ollama is the default embedder).OPENAI_API_KEY密钥OpenAI API key (used for the OpenAI text-embedding-3-large fallback embedder when Ollama is unavailable).OPENROUTER_API_KEY密钥OpenRouter API key (used for the federated LLM extraction / consolidation paths and for the LongMemEval benchmark runner).本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Attestor
面向多 Agent 生产网格的受治理记忆。
Attestor 已不再是“编码助手的记忆”。发生了三件事:编辑器厂商免费提供了记忆功能,基准测试竞赛变成了比拼哪个大模型能回答问题,而每次严肃讨论记忆层时,讨论的其实都是完全不同的事情:当许多 Agent 共享一份记忆时,谁有权写入,谁有权遗忘,事后又能否证明这些操作。因此,Attestor 现在的定位就是如此。
当许多 Agent 共享一份记忆时,检索是容易的部分。难的是谁可以写入、谁可以遗忘、答案基于什么构建,以及之后能否证明这一切。Attestor 是一项可自托管的记忆服务,它通过代码而非策略文档,让这些属性具有可强制执行性:在 AgentContext 层实施基于角色的访问控制;为每条记忆记录来源(可选择使用 Ed25519 签名);为每个 Agent 设置令牌预算和写入配额;采用不含大模型的确定性排序路径;支持按任意过去时刻查询的时序替代记录;在所有三个存储角色之间实现严格的租户隔离;提供可审计、以审计为先的 forget-user 路径,并采用声明式保留策略;以及通过持久化 Temporal 工作流运行治理任务:按策略重试、崩溃后恢复、在运维人员 UI 中可见,而且绝不处于读取路径上。它可以作为 Python 库、REST sidecar 或 MCP 服务运行,并使用相同的 API。角色强制执行是 AgentContext 的保证,并在进程内运行。REST 和 MCP 接口直接调用存储,并依赖各自的身份验证机制。
ctx = AgentContext(agent_id="reviewer-1", role=AgentRole.REVIEWER, namespace="acme-prod")
ctx.add_memory("...") # PermissionError: REVIEWER lacks 'write'
mem.recall("...", context=ctx, as_of=last_friday) # what the mesh believed then — nothing is deleted
pip install attestor
| 版本 | 4.1.11(稳定版;从零重建——没有 v3 迁移路径) |
| PyPI | attestor |
| 导入名称 | attestor |
| 在线站点 | https://attestor.dev/ |
| 代码仓库 | https://github.com/bolnet/attestor |
| 许可证 | MIT |
由 Surendra Singh 设计并构建——为多 Agent AI 打造可审计的基础设施,并将十五年的生产系统严谨经验带入记忆层。配套项目:
claude-finance(由 Claude 驱动的金融分析) ·private-equity(PE × AI 工作坊)。如果你正在招聘资深 IC 从事 AI 基础设施工作,欢迎联系。
它是什么
Attestor 是面向需要接受问责的 Agent 网格的记忆层——问责对象可能是安全审查人员、监管机构、客户,或凌晨 3 点被呼叫的工程师。每条记忆只在 Postgres 中存储一次(事实来源),然后派生到 Pinecone(向量)和 Neo4j(图)。每次读取和写入都会经过一个携带身份、角色、命名空间和预算的 AgentContext,而存储会保留足够的历史记录,以便重建任何过去的状态。
它强制执行的内容
以下每项保证都是代码的属性,而非路线图项目。文件引用指向实施强制执行的代码。
| 保证 | 代码的行为 | 位置 |
|---|---|---|
| 基于角色的访问控制,共六种角色 | ORCHESTRATOR = 读 + 写 + 遗忘;PLANNER / EXECUTOR / RESEARCHER = 读 + 写;REVIEWER / MONITOR = 只读。read_only=True 是独立的紧急开关,无论角色如何,都会禁用写入 + 遗忘。违规会在触及任何后端之前引发 PermissionError。 | attestor/context.py — ROLE_PERMISSIONS、_require_permission |
| 每条记忆都有来源信息 | 每次写入都会携带 agent_id、session_id、namespace 和角色;子上下文会累积 agent_trail,因此可以回答“这是谁说的,代表谁说的”。source_episode_id 由情节整合路径设置,而不是由每次写入设置。替代旧内容绝不会删除数据。只有通过显式调用 forget_user()(先审计)、保留策略中的 delete 策略(事后审计)或 compact()(不审计)才能移除行。可选用 Ed25519 对 id ‖ agent_id ‖ t_created ‖ content_hash 签名,并通过 verify_memory() 验证。 | attestor/context.py、attestor/identity/signing.py |
| 每个 Agent 的预算和写入配额 | token_budget 限制 recall() 为单个 AgentContext 打包的内容量;它永远不会限制写入。max_writes_per_agent 是上下文中的内存计数器,新建上下文时会重置。set_quota() 设置按用户计算的每日写入上限,并在 add() 时强制执行;配额针对用户,而非 Agent。 | attestor/context.py、attestor/core/quota_service.py、attestor/core/agent_memory.py |
| 确定性的读取路径 | 默认情况下,排序流程为向量 → BM25 → RRF → 图 → MMR → 预算适配,评分时不使用大模型。相同查询会得到相同顺序,且可通过单元测试验证并重放。查询扩展路径(HyDE、多查询)是可添加候选项的 YAML 标志;它们绝不会影响评分。可选的重排器(bge、cohere 或 llm 提供方)可以重新为候选项评分;默认关闭。 | attestor/retrieval/orchestrator/core.py、attestor/retrieval/scorer.py、attestor/retrieval/reranker.py |
时序替代,支持 as_of 重放 | 矛盾内容会使较旧事实失效;替代旧内容绝不会删除数据。每条事实都带有有效时间(valid_from、valid_until)和事务时间(t_created、t_expired)。recall(as_of=...) 会在文档加载和 BM25 路径中按事务时间筛选候选项;Pinecone 路径会忽略 as_of 并记录警告。 | attestor/temporal/manager.py、attestor/store/schema.sql、attestor/store/pinecone_backend.py |
| 严格的租户隔离,覆盖全部三种角色 | 租户即用户。Postgres:按 attestor.current_user_id 执行行级安全策略,并将 namespace 写入元数据。Pinecone:使用一个索引,每次调用都按 namespace= 分区。Neo4j:使用 namespace 属性和复合 (key, namespace) 约束,并按命名空间限定遍历;PageRank 投影为全局投影,仅用作排序信号。 | attestor/store/schema.sql、attestor/store/pinecone_backend.py、attestor/store/neo4j_backend.py |
| 可审计的用户遗忘与数据保留 | forget_user() 会在删除之前写入一条按约定只追加且不受 RLS 限制的 forget_audit 记录,然后按文档 → 状态 → 向量 → 图的顺序遍历,并报告各后端的计数,使部分成功可见而不会被静默忽略。声明式保留策略(archive / delete,按 namespace、类别、层、标签和数据年龄筛选)会先执行,之后再写入 forget_audit 记录;只有 forget_user() 会先审计。支持针对数据主体访问请求的完整导出。 | attestor/compliance/retention.py、attestor/core/agent_memory.py — export_user、purge_user、deletion_audit_log |
| 持久化治理任务 | 整合、派生状态修复(attestor durable rebuild)、先审计再执行的用户遗忘 Saga,以及保留策略 / 会话清理,都会作为 Temporal 工作流运行,并配有明确的重试策略和运维人员 UI。遗忘 Saga 会写入审计记录、删除文档行,然后并发执行向量、图和状态操作;它会分别重试各后端,并将部分状态报告为失败。可选择启用额外功能(attestor[durable]);读取路径和 Claude Code hooks 绝不会触及该功能。 | attestor/durable/、docs/INSTALL.md(第 04 章) |
如何评估
最接近的是时序知识图谱记忆层(Zep、Graphiti)。对于生产网格,有用的比较标准不是基准测试准确率,而是记忆层本身是否强制执行角色、预算、租户隔离、来源追踪和可审计删除,还是把这些都交给调用应用处理。上表是 Attestor 给出的答案;评估任何替代方案时,都应使用同一份检查清单,并附上文件引用。
适用场景
- 多 Agent 产品:多个由大模型驱动、信任级别各不相同的 Agent 会向同一记忆写入内容
- 受监管系统:需要按特定时间点重建状态,并提供有说服力的删除记录
- 可自托管的部署——使用你自己的 VPC、Postgres、Pinecone、Neo4j
不适用场景
- 单个编码助手的个人记忆——下方的 Claude Code 插件是同一引擎的入门演示,并非产品本身
- 通用向量数据库,或带有分块 / 重排序 / 编排功能的 RAG 框架
- LLM Agent 运行时——Attestor 是记忆后端;Agent 循环由你负责
用一条命令在本地试用。
pipx install attestor && attestor quickstart会在 Docker 中启动 Postgres + Pinecone Local + Neo4j,并配置 Claude Code MCP 服务 + 钩子,让你能在真实项目中观察治理层的运作。详情请见下方的快速入门和为 Claude Code 安装。
快速入门
1. 安装
pip install attestor # or: pipx install attestor
或者拉取容器镜像(支持内省的镜像,在 python:3.12-slim 之上仅增加一个层,目前支持 linux/amd64):
docker pull ghcr.io/bolnet/attestor:latest # recommended — anonymous pull, mirrored to all registries below
同一镜像也镜像发布于:
| Registry | 拉取地址 |
|---|---|
| GHCR | ghcr.io/bolnet/attestor:latest |
| Docker Hub | bolnet2025/attestor:latest |
| Quay | quay.io/bolnet/attestor:latest |
| AWS ECR Public | public.ecr.aws/m6h5j7o3/attestor:latest |
| GCP AR | us-central1-docker.pkg.dev/coral-marker-452616-n4/attestor/attestor:latest |
(内部 Azure ACR 镜像 memwright.azurecr.io/attestor 也已存在,但为私有镜像——Azure 客户应使用 az acr import,从上方任一公共 Registry 导入。)
镜像的默认入口点是 attestor mcp(通过 stdio 提供的 MCP 服务)。如需完整的生产环境用法,可通过环境变量将容器指向外部 Postgres + Neo4j(或使用 attestor/infra/local/docker-compose.yml 将它们组合运行);也可覆盖入口点来运行 attestor doctor、attestor api 等命令。
2. 启动本地堆栈——一条命令,无需回答问题
attestor quickstart
attestor quickstart 会以非交互方式完成整个本地安装,并打印每个步骤:它会写入 ~/.attestor/{config.toml,attestor.yaml,.env},在 Docker 中启动三角色本地堆栈,使用本地 Ollama bge-m3 嵌入器(无需云端密钥),接入 Claude Code MCP 服务(./.mcp.json)和生命周期钩子,并运行 attestor doctor。
前提条件: Docker 正在运行,且 Ollama 正在提供 bge-m3(ollama pull bge-m3)。quickstart 会运行预检,扫描端口和工具,并告知你是否缺少任何内容——它绝不会提示你输入信息。
| 容器 | 角色 | 端口 | 用途 |
|---|---|---|---|
| Postgres 16 | 文档 | 5432 | 事实来源——内容、标签、实体、时间戳、来源信息;按 user_id 通过 RLS 隔离 |
| Pinecone Local | 向量 | 5080-5089 | 稠密嵌入、按命名空间隔离、纯 gRPC(无 HTTPS) |
| Neo4j 5 + GDS | 图 | 7687 | 实体节点 + 带类型的边、PageRank / BFS / Leiden |
稍后如需撤销:运行 attestor teardown(无需回答问题;默认保留数据卷,--purge 也会清除数据卷,--dry-run 则预览操作)。
在 Claude Code 中, 你可以通过对话来执行相同的安装:/plugin marketplace add bolnet/attestor → /plugin install attestor(然后启用它),再运行 /attestor:install-attestor——它会替你运行 attestor quickstart。云端/托管后端(Neon / RDS / Cloud SQL、Pinecone Cloud、Neo4j AuraDB)和替代嵌入器(Pinecone Inference llama-text-embed-v2、Voyage voyage-4、OpenAI text-embedding-3)在 ~/.attestor/attestor.yaml(唯一事实来源)中配置——详见 docs/INSTALL.md。
attestor doctor(会在最后自动运行,也可以随时运行)会检查所有四个子系统:文档存储(Postgres)、向量存储(Pinecone)、图存储(Neo4j)、检索流水线。唯一绝不能停机的硬性依赖是文档存储(Postgres);临时的向量探测失败会显示在响应跟踪信息中,而不会被忽略(retrieval/orchestrator/debug.py——vector_error 字段)。
3. 使用
from attestor import AgentMemory, AgentContext, AgentRole
mem = AgentMemory() # picks up env / ~/.attestor.toml automatically
ctx = AgentContext(
agent_id="researcher-1",
role=AgentRole.RESEARCHER,
namespace="acme-prod",
)
mem.add(
content="Alice is the engineering manager",
entity="alice",
category="role",
context=ctx,
)
results = mem.recall(query="who runs engineering?", context=ctx)
for r in results:
print(r.score, r.memory.content)
SOLO 模式(零配置)。 在 v4 中,
AgentMemory().add('foo')会自动配置单例local用户、收件箱项目(metadata.is_inbox=true)和每日会话——因此,上面的代码片段在新数据库上无需配置身份即可运行(core.py:179-209)。在多租户生产环境中,请传入带有真实namespace的显式AgentContext。
4. 运行冒烟基准测试(可选)
使用 LongMemEval 的一个小型切片端到端验证安装。默认值来自 configs/attestor.yaml:Pinecone Inference llama-text-embed-v2(1024-D)嵌入器 + Pinecone 向量存储、openai/gpt-5.5 答题模型、双评审器(openai/gpt-5.5 + anthropic/claude-sonnet-4-6)、parallel=2。
set -a && source .env && set +a # OPENROUTER_API_KEY, PINECONE_API_KEY, NEO4J_PASSWORD
.venv/bin/python scripts/lme_smoke_local.py --n 2 --yes
所有模型和参数都来自 YAML——完整的基准测试框架详见下文的 § 基准测试。
基准测试
所有基准测试——冒烟测试、单个切片、完整扫描、合成取代测试——都从两个 YAML 文件读取参数:
| 文件 | 内容 |
|---|---|
configs/attestor.yaml | 堆栈——嵌入器、模型、检索功能、数据库、注册表、云服务 |
configs/bench.yaml | 仅用于基准测试的配置——变体、类别迭代顺序、目标分数、输出路径 |
这两个文件的键必须互不重叠。CI 测试 tests/test_config_no_duplicate_keys.py 会强制执行此要求;基准测试加载器(attestor.bench_config.get_bench)遇到键冲突时会崩溃。如果你想临时覆盖某项设置(例如某次基准测试使用不同模型),请使用环境变量或 CLI 标志——切勿在 bench.yaml 中重复定义该键。
LongMemEval 是什么
LongMemEval(Wu 等,2024 年——发表于 ICLR '25)是针对记忆增强型聊天助手的权威基准。它衡量 AI 系统能否正确回答需要从跨越多个会话的长对话历史中回忆事实的问题——这正是 Attestor 所针对的场景。
500 个问题、6 类推理、3 种干扰内容规模。 三种规模使用相同的问题;只有答案所在会话周围的干扰内容有所不同:
| 变体 | 每题 Token 数 | 会话数 | 测量内容 |
|---|---|---|---|
oracle | ~3-15k | 1-3 个黄金会话 | 推理上限——回答者在检索完美的情况下能做到什么。如果这里得分较低,说明提示词或 LLM 有问题(检索无济于事)。 |
s(Standard / Small) | ~115k | ~50 | 公开排行榜——权威比较基准。内容可放入单个 Claude/GPT 上下文窗口,因此 Attestor 的检索效果会与“把所有内容直接塞进长上下文”的基线进行比较。 |
m(Plus / Medium) | ~1M+ | ~500 | 纯检索——内容过多,无法放进任何上下文窗口。必须使用记忆层;无法走长上下文捷径。 |
LME-S 是最重要的待超越指标。 营销卖点是:在 Token 成本降低 30 倍的情况下,记忆层得分仍与长上下文基线相差不超过 5%。
6 类推理(清理后的 LME-S,共 500 个问题——注意:清理后的拆分中没有 abstention 子集,合成的 supersession 测试套件会覆盖此项):
| 类别 | 数量 | 测试内容 |
|---|---|---|
multi-session | 133 | 事实分散在多个会话中——必须追踪某个实体随时间的变化 |
temporal-reasoning | 133 | 日期计算(“两周前”“X 之前”)——Attestor 的双时态层专为此子集设计 |
knowledge-update | 78 | 事实取代——新事实与旧事实同时存在时,新事实必须优先 |
single-session-user | 70 | 单个会话,事实由用户陈述 |
single-session-assistant | 56 | 单个会话,事实由助手陈述 |
single-session-preference | 30 | 单个会话,用户偏好 |
为什么 Attestor 要使用此基准: temporal-reasoning 和 knowledge-update 子集直接测试了 Attestor 区别于普通 RAG 的功能:双时态回忆、矛盾时的事实取代,以及事件时间与事务时间的区分。这些子集的高分是面向受监管 AI、审计和合规场景的卖点。
已发布的 Attestor 指标见 docs/bench/——基准测试产物以 lme-{variant}-{category}-{date}.{report,summary}.json 的形式保存。下方的 Reporting 部分介绍如何将其渲染为表格。
下载 LongMemEval 数据集(一次性操作,在运行任何基准测试之前)
所有 lme_*.sh 脚本都使用 xiaowu0162/longmemeval-cleaned 在 HuggingFace 上发布的清理版 LongMemEval 拆分。首次使用时会自动下载,但你需要了解相关流程。
缓存位置(首次调用时创建):
~/.cache/attestor/longmemeval/
(如果设置了 XDG_CACHE_HOME,则位置为 $XDG_CACHE_HOME/attestor/longmemeval/。)
变体及其磁盘占用:
| 变体 | 文件名 | 大小 | 每题 Token 数 | 用途 |
|---|---|---|---|---|
oracle | longmemeval_oracle.json | ~5 MB | ~3-15k | 推理上限——成本最低的冒烟测试 |
s | longmemeval_s_cleaned.json | ~250 MB | ~115k | 公开排行榜(权威基准) |
m | longmemeval_m_cleaned.json | ~2 GB | ~1M+ | 强制使用检索(无法走长上下文捷径) |
选项 A — 自动下载(推荐)
运行任意基准测试命令即可。首次调用时会下载并缓存;之后每次调用都会从磁盘读取:
# Will download longmemeval_oracle.json (~5 MB) the first time
.venv/bin/python scripts/lme_smoke_local.py --n 2 --yes --variant oracle
# Will download longmemeval_s_cleaned.json (~250 MB) the first time
scripts/bench/lme_run.sh knowledge-update
每种变体只需支付一次下载成本。首次运行时网络不稳定?删除缓存目录中的不完整文件,然后重新运行。
选项 B — 预热缓存(离线/CI)
在基准测试日之前,预先获取计划使用的所有变体:
.venv/bin/python -c "
from attestor.longmemeval import load_or_download
for v in ('oracle', 's', 'm'):
samples = load_or_download(variant=v)
print(f'{v}: {len(samples)} samples')
"
预期输出:
oracle: 500 samples
s: 500 samples
m: 500 samples
选项 C — 手动下载(防火墙环境)
如果运行器无法访问 huggingface.co,请在可联网的机器上获取文件,然后手动将它们放入缓存目录:
mkdir -p ~/.cache/attestor/longmemeval
cd ~/.cache/attestor/longmemeval
# pick the variants you need
curl -L -o longmemeval_oracle.json \
https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_oracle.json
curl -L -o longmemeval_s_cleaned.json \
https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_s_cleaned.json
curl -L -o longmemeval_m_cleaned.json \
https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_m_cleaned.json
基准测试运行器会严格检查这些文件名,请勿重命名。
验证数据集是否可加载
下载后(自动或手动),确认加载器能正常加载数据集:
.venv/bin/python -c "
from attestor.longmemeval import load_or_download
from collections import Counter
samples = load_or_download(variant='s')
cnt = Counter(s.question_type for s in samples)
print(f'Loaded {len(samples)} samples')
for cat, n in sorted(cnt.items(), key=lambda x: -x[1]):
print(f' {cat}: {n}')
"
清理后的 s 变体的预期结果(500 个问题,6 个类别——注意:清理后的拆分中没有弃答子集):
Loaded 500 samples
multi-session: 133
temporal-reasoning: 133
knowledge-update: 78
single-session-user: 70
single-session-assistant: 56
single-session-preference: 30
如果数量不匹配,说明文件已被截断,请重新下载。
快速冒烟测试(≤ 1 分钟,≤ $0.10)
在提交或运行规模更大的测试之前,确认整个流程能够端到端运行:
.venv/bin/python scripts/lme_smoke_local.py --n 2 --yes --variant oracle
oracle 是成本最低的变体(仅包含黄金会话,没有干扰项堆)。每次运行都会自动重新应用架构;如果想在运行之间保留已填充数据的数据库,请传入 --skip-schema。
单个类别 — scripts/bench/lme_run.sh
# all 6 categories, current variant from bench.yaml (default: s)
scripts/bench/lme_run.sh
# one slice — full
scripts/bench/lme_run.sh knowledge-update
# one slice — capped at N samples (smoke)
scripts/bench/lme_run.sh knowledge-update 10
# one slice on a different variant (oracle = cheapest, m = ~1M tokens)
scripts/bench/lme_run.sh knowledge-update "" oracle
有效的 --category 值:single-session-user、single-session-assistant、single-session-preference、multi-session、temporal-reasoning、knowledge-update。有关样本数量以及每个类别的测试内容,请参阅上文什么是 LongMemEval。
每次运行会保存两个文件:
docs/bench/lme-{variant}-{category}-{YYYYMMDD}.report.json # full LMERunReport
docs/bench/lme-{variant}-{category}-{YYYYMMDD}.summary.json # BenchmarkSummary
全量测试 — scripts/bench/lme_all.sh
按顺序遍历 bench.yaml 中的 lme.categories 列表。添加或移除子集只需编辑 YAML,无需修改脚本:
# All 6 slices, current variant
scripts/bench/lme_all.sh
# All 6 slices, capped at 10 samples each (smoke)
scripts/bench/lme_all.sh 10
# All 6 slices on Oracle variant
scripts/bench/lme_all.sh "" oracle
如果某个子集运行失败,脚本会记录错误并继续运行下一个。
报告 — scripts/bench/lme_report.py
将所有 docs/bench/lme-*.summary.json 汇总成一个 Markdown 表格;每个 (variant, category) 组合选取最新文件:
.venv/bin/python scripts/bench/lme_report.py # latest-per-slice
.venv/bin/python scripts/bench/lme_report.py --variant s # filter to LME-S
.venv/bin/python scripts/bench/lme_report.py \
--markdown-out docs/bench/LME-S.md # also write file
.venv/bin/python scripts/bench/lme_report.py --trend # progression over time
默认模式(每个子集取最新结果):
| Variant | Category | Score | N | Date | Answer | Judges |
| ------- | -------- | -----:| -:| ---- | ------ | ------ |
| s | knowledge-update | 87.5% | 78 | 20260429 | openai/gpt-5.4-mini | openai/gpt-5.5, anthropic/claude-sonnet-4-6 |
趋势模式(--trend)会读取 docs/bench/trend.csv——每次基准测试运行都会追加一行(由 lme_run.sh 自动填充)——并通过 Δ 列展示变化趋势:
| Variant | Category | Date | N | Score | Δ | SHA | Features | Run |
| ------- | -------- | ---- | -:| -----:| -:| --- | -------- | --- |
| s | knowledge-update | 20260429 | 78 | 80.0% | | a126e7a | | bench |
| s | knowledge-update | 20260430 | 78 | 88.0% | +8.0 | badcf1b | multi_query | bench |
| s | knowledge-update | 20260501 | 78 | 91.5% | +3.5 | xxxxxxx | multi_query,hyde | bench |
Features 列会准确记录每次运行启用的检索器/回答器标志,因此你可以一目了然地看出哪个参数带来了哪些提升。
检索与回答器功能开关
通过 configs/attestor.yaml 中的布尔值开关启用五项彼此独立的功能。默认全部禁用——每次基准测试运行只选择一项,测量提升幅度,再决定是否启用后发布。
| Flag | 功能 | 提升幅度 | 成本开销 |
|---|---|---|---|
retrieval.multi_query | 将问题改写成 N 个释义版本,并对 N+1 个向量通道进行 RRF 合并 | +6-10%(文献数据);在 LME-S 时序 smoke 测试中退化 −10pp | 每次召回需 1 次小型 LLM 调用 + N 次额外向量搜索 |
retrieval.hyde | 生成事件描述型假设文档嵌入(temperature=0)——将其作为并行向量通道进行嵌入 | 在 LME-S 时序推理测试中实测提升 +10pp(30 个问题的 smoke 测试,从 70%→80%→96.7%,使用 BM25 混合检索) | 每次召回需 1 次小型 LLM 调用 + 1 次额外向量搜索 |
retrieval.temporal_prefilter | 用正则检测“两个星期前”等表述;在向量检索前缩小事件时间范围 | +1.5%(文献数据);在 LME-S 疑问锚点问题上为 0pp | 无成本(仅正则,不调用 LLM) |
self_consistency | 回答器以指定 temperature 采样 K=5 次,并选出共识答案 | +3-6%(文献数据) | 回答器成本的 5 倍 |
critique_revise | 回答 → 评议 → 有条件地修订 | +3-5%(文献数据) | 最坏情况下约为回答器成本的 3 倍 |
此版本中,multi_query 和 hyde 互斥(如果两个开关都开启,则以 multi_query 为准并记录警告)。self_consistency 和 critique_revise 在回答器端同样互斥。两侧功能可以组合(例如 hyde + self_consistency)。
HyDE v2 提示词(attestor/retrieval/hyde.py)——生成事件描述型片段,而非答案形式的响应,因此嵌入结果更接近源文本形式的对话轮次,而不是问题形式的查询。这正是 LME-S 时序推理测试实测提升 +10pp 的关键因素。固定使用 temperature=0,确保重复运行结果确定。
如实记录上述负面结果——multi_query 和 temporal_prefilter 在 LME-S 时序推理数据切片上的效果未达到文献报告的水平。multi_query 生成的释义仍保持问题形式,且 RRF 会稀释边际命中;temporal_prefilter 的启发式锚点对疑问形式的问题(“多少天前……”)没有帮助。HyDE 才是正确的工具。各功能的方法和诊断产物见 docs/bench/pinecone-lme-temporal-diagnostic-{baseline,mq3,hyde,hyde-bm25}-20260429.json。
跨向量数据库诊断工具——experiments/pinecone_lme_temporal_diagnostic.py 可使用 --baseline / --multi-query / --hyde / --bm25-hybrid / --temporal-prefilter / --category 标志,在 Pinecone Local 上运行仅检索的 LME-S 诊断。不包含回答器或评判器——只测召回@K 上限。--skip-ingest 可复用已填充的命名空间,快速迭代检索功能开关(30 个问题约需 60 秒;重新摄取则约需 50 分钟)。
**要对单项功能进行基准测试:**在 configs/attestor.yaml 中将其 enabled: true,运行基准测试数据切片,并与同一天、所有功能均关闭时的基准测试结果比较。趋势表将在 Δ 列显示差值。
合成版本替代测试集 — python -m evals.knowledge_updates
50 个人工整理的案例,涵盖 10 种矛盾类别,每类 5 个(数值、类别、时间、偏好、实体、位置、意图、关系、计数、二元状态)。每个案例会摄取两个会话(会话 1 陈述一个事实,会话 5 与之矛盾),并提出一个应以较新事实为准的问题。指标:检索结果中将新事实列为排名第一的案例百分比。
# All 50 cases — ~5 min, ~$0.50 worth of embedding calls
.venv/bin/python -m evals.knowledge_updates
# Smoke — first 5 cases
.venv/bin/python -m evals.knowledge_updates --limit 5
# Custom fixtures
.venv/bin/python -m evals.knowledge_updates --fixtures my_cases.json
输出:
docs/bench/knowledge-updates-{YYYYMMDD}.report.json # per-case verdicts (new_wins | stale_wins | miss | ambiguous)
docs/bench/knowledge-updates-{YYYYMMDD}.summary.json # aggregate score + per-category breakdown
目标分数(可在 bench.yaml 中配置):新事实胜出率 92%。低于该值时,需要调整 attestor/retrieval/scorer.py 中的版本替代置信度衰减权重。
成本与运行时间指南
近似值,采用 OpenRouter 定价,reasoning_effort=high,答题器和裁判均使用此设置,parallel=2:
| 运行 | N | 耗时 | 成本 |
|---|---|---|---|
| 快速冒烟测试 | 2 个 oracle | ~1 分钟 | < $0.10 |
knowledge-update 切片 | 78 | ~30-60 分钟 | ~$3-5 |
temporal-reasoning 切片 | 133 | ~50-100 分钟 | ~$5-8 |
| 完整 LME-S 500q | 500 | ~75-180 分钟 | ~$20-30 |
| 合成替代测试 | 50 | ~5 分钟 | ~$0.50(仅嵌入) |
如需降低成本,请将 configs/attestor.yaml 中的 models.reasoning_effort.{answerer,judge} 从 high 改为 medium 或 low。
配置速查表 — configs/bench.yaml
bench:
lme:
variant: s # s | m | oracle
cache_dir: ~/.cache/attestor/lme
output_dir: docs/bench
sample_limit: null # null = full dataset; int = truncate
category: null # null = all 7; or single slice name
categories: [...] # iteration order for lme_all.sh
variants_to_run: [...] # for full size matrix
knowledge_updates:
fixtures_path: evals/knowledge_updates/fixtures.json
n_cases: 50
target_score: 0.92
categories: [numeric, categorical, ...]
report:
headline_slice: abstention
trend_csv: docs/bench/trend.csv
markdown_path: docs/bench/LME-S.md
架构
双时态 — 重放任意过去状态
每条记忆都携带两个时间轴:
| 轴 | 列 | 含义 |
|---|---|---|
| 事件时间 | valid_from、valid_until | 事实在现实世界中成立的时间 |
| 事务时间 | t_created、t_expired | 该行写入存储中的时间 |
此外还有 superseded_by 链。旧事实从不删除,始终可查询(attestor/temporal/manager.py:30-66)。
# What did we believe on March 1?
mem.recall(query="who runs engineering?", as_of="2026-03-01T00:00:00Z", context=ctx)
# Show me everything we knew about Alice between Feb and Apr
mem.recall(query="alice", time_window=("2026-02-01", "2026-04-01"), context=ctx)
as_of 和 time_window 会在编排器与文档存储之间端到端传递。写入时的自动替代逻辑已接入 core.py:add()(core.py:762, 784-785):每次 add 时,时态管理器都会查找具有相同 (entity, category, namespace) 且内容不同的活动行,将其标记为 superseded,设置 valid_until=now,并将 superseded_by=<new_id> 链接到新行。目前的检测基于规则,使用字符串相等判断。
租户隔离 — Postgres 行级安全
每个租户表(users、projects、sessions、episodes、memories、user_quotas、deletion_audit)都带有 tenant_isolation_* 策略,该策略依据 attestor.current_user_id 会话变量执行。变量为空或未设置时将默认拒绝,不显示任何行(attestor/store/schema.sql:311-327)。
如实说明。 强制执行发生在 Postgres 中,而非 Python 中。
attestor/context.py:49-56中的AgentRole枚举是随记忆传递、用于溯源的元数据;它不会在 Python 中限制操作。真正控制访问的是 RLS。这是记忆后端的正确架构,但如果你只阅读 Python 代码,值得了解这一点。
检索流水线 — 语义优先,六个步骤
attestor/retrieval/orchestrator/core.py 会对每个查询执行相同的六个步骤:
- 向量 Top-K — Pinecone 余弦相似度,k=50(pgvector 仍可作为自包含部署的可选后备方案)
- 图谱收窄 — 从每个候选项的实体出发,以问题中的实体为目标进行深度 ≤ 2 的 Neo4j BFS;每跳提供亲和度加分(0 跳=+0.30,1 跳=+0.20,2 跳=+0.10;不可达=−0.05)。这是离散加分,而非“软性”加权。
- 注入三元组 — 将类型化边事实(
uses、authored-by、supersedes)作为合成记忆注入 - MMR 重排 — λ=0.7
- 置信度衰减 + 时态加权 — 越新权重越高;过时、置信度低的行权重降低
- 预算适配 — 按分数单调递减地贪心打包,纳入调用方的 token 预算
每次调用都会将 JSONL 跟踪记录写入 logs/attestor_trace.jsonl(通过 ATTESTOR_TRACE=0 禁用)。
异步检索 — 降低延迟,同时不削弱审计
独立的召回步骤通过 asyncio.gather 并发运行,但**八项审计不变量一项也不会放宽。**无需以信任换速度——两者兼得。
| 异步步骤 | 延迟收益 | 保持不变的审计不变量 |
|---|---|---|
| HyDE 大模型调用 ‖ 原始问题向量嵌入 | 启用 HyDE 的召回延迟降低 33%(模拟单元测试中约从 600 ms 降至 400 ms) | A7 — 生成器固定 temperature=0.0;相同提示词 + 相同模型 = 相同假设 = 相同 RRF 顺序。若 T > 0,异步会放大非确定性风险;我们明确固定 T=0。 |
| 各通道向量搜索并行运行(HyDE / 多查询) | 与 N 成比例(约从 N × 单通道耗时降至最长单通道耗时) | 输入相同时,各通道上的 RRF 结果具有确定性——gather 的顺序不会破坏排名位置(test_multi_query_async_preserves_RRF_order)。 |
| 自一致性 K 路并发采样(回答端) | K=5 采样时快 5 倍 | 投票共识不受顺序影响;此项改动在回答端,不涉及文档存储。 |
| 向量 ‖ BM25 ‖ 图候选项提取 | 基线召回延迟降低 20% | A2 recall_started_at 上限 — 每次跨存储读取都使用召回开始时捕获的相同单调时间戳。召回期间发生的并发写入不会被读取。 |
| 图 BFS ‖ Postgres 文档提取 | 通常快 50 ms | 同一上限。 |
写入端仍保持同步。所有 add()、update() 和 supersession 写入都明确不属于异步重构的目标——审计链依赖串行写入顺序,且双时态 t_created 顺序必须对每一行保持线性一致。异步改动仅限读取端。
**追踪仍可重建。**每个事件都携带 recall_id + 单调递增的 seq + 可选的 parent_event_id,因此审计仪表板会将并发召回呈现为事件树,而非事件流——(recall_id, seq) 可从 JSONL 日志重建因果顺序。
**保留相同的 recall(as_of=X) 重放保证。**过去的召回仍可根据双时态列 + deletion_audit + 追踪 JSONL 逐字节重现——异步并行不会改变读取内容,只会改变读取时机。关键测试(tests/test_as_of_replay.py)是每个异步 PR 回归门禁的一部分。
完整设计 + 审计不变量矩阵:docs/plans/async-retrieval/PLAN.md。约定:每个异步 PR 都必须附带审计保持说明,并在合并前通过相应的不变量测试(tests/async_retrieval/test_audit_invariants_under_async.py)。
三种存储角色
| 角色 | 用途 | 默认选项 | 替代方案 |
|---|---|---|---|
| 文档 | 事实来源(内容、标签、实体、ts、来源信息、置信度) | Postgres 16 | AlloyDB、ArangoDB、DynamoDB、Cosmos DB |
| 向量 | 每条记忆的稠密嵌入 | Pinecone(本地 Docker / 云端) | pgvector、AlloyDB ScaNN、ArangoDB、OpenSearch Serverless、Cosmos DiskANN |
| 图 | 实体节点 + 类型化边 | Neo4j 5 + GDS | AlloyDB 上的 Apache AGE、ArangoDB、Neptune、NetworkX(Azure) |
Postgres 是事实来源。Pinecone 向量和 Neo4j 图都是派生状态,均可从 Postgres 重建——但规范安装必须包含两者:向量余弦相似度计算是检索流水线的第 1 步,图扩展是第 2 步,且对话摄取会写入类型化边。唯一不能停机的是文档存储;编排器会在响应追踪中记录暂时性的向量探测故障(vector_error),而不是将其吞掉。
持久化治理作业 — 读取路径永不等待,作业永不停止
过去依赖手写队列、承诺会运行清理程序的文档字符串,或仅发出警告的写入操作的治理工作,现在都作为 Temporal 工作流运行(attestor/durable/)。可选择启用:默认情况下 durable.enabled: false;关闭该选项或服务不可达时,每项作业都会回退到它所取代的进程内路径。
| 触发条件 | 工作流 | 保证内容 |
|---|---|---|
| 每次写入时 | DeriveMemory | 向量和图谱派生操作具备幂等性并会重试,因此派生状态不会在 Postgres 中悄然漂移。 |
| 每个片段 | ConsolidateEpisode | 取代 SKIP LOCKED 队列和租约;卡住或失败的片段会清晰可见,不会丢失。 |
| 按计划执行 | RetentionSweep、SessionSweep | 声明式保留策略和空闲会话清理会通过只需应用一次的 Temporal Schedule 实际运行。 |
| 按需执行 | RebuildDerived、ForgetUser | 从 Postgres 重建 Pinecone 和 Neo4j。遗忘操作采用 Saga 模式:先写入审计行,再分别处理每个后端,并各自进行重试和记录结果。 |
tests/test_durable_isolation.py 强制执行两条硬性规则:召回绝不接触 Temporal,钩子绝不等待它(attestor/retrieval/** 或 attestor/hooks/** 下的任何内容都不得导入 attestor.durable 或 temporalio),并且 工作流负载只携带 ID,绝不携带记忆内容,因此 Postgres 始终是唯一真实数据来源,Temporal 只保存作业状态。驱动程序异常在进入工作流历史记录前会经过清理。
attestor quickstart --durable # Temporal server + UI (:8233) via compose profile `durable`
attestor worker # the governance worker; needs durable.enabled: true
attestor durable status # probe the server, list schedules, describe a workflow
attestor durable rebuild # rebuild derived state from Postgres
attestor durable schedules apply # idempotent create-or-update of the retention / session schedules
可选的 BM25 / FTS 通道
由触发器维护的 content_tsv tsvector 和 GIN 索引可提升嵌入召回不足的查询效果(首字母缩略词、ID、罕见专有名词)。检测到 v4 架构时启用;通过 Reciprocal Rank Fusion(RRF,k=60)与向量通道融合。对于不含该列的后端,会平稳地不执行任何操作(core.py:122-130)。
对话导入
将对话轮次转化为可审计记忆的重量级写入路径。core.py:ingest_round(turn) 会编排四个阶段:
turn → extract_user_facts(user_turn) ┐
extract_agent_facts(assistant_turn) ┘ → resolve_conflicts → apply
两轮发言者锁定提取
attestor/extraction/round_extractor.py:216, 258 — 为用户和助手轮次分别使用不同的提示词。用户轮次提取器只输出可归因于用户的事实;助手轮次提取器只输出由助手引入的事实。这样可以阻止交叉归因。我们的 LongMemEval 得分比 Mem0 高出 +53.6,正是源于这种拆分。
四种决策的冲突解决器
attestor/extraction/conflict_resolver.py:40, 98 — 对于每条新提取的事实,都会调用大模型,将其与现有的相似记忆进行比对,并返回以下决策之一:
| 决策 | 效果 |
|---|---|
ADD | 新信息,没有匹配项 — 写入新记忆 |
UPDATE | 同一实体和谓词,值经过细化 — 保留现有 ID |
INVALIDATE | 旧记忆被反驳 — 标记为已取代(时间线会重新回放) |
NOOP | 已有相关记录 — 跳过 |
每个 Decision 都携带 evidence_episode_id。每次取代操作都可审计。故障保护:单条事实解析失败时,默认采用 ADD — 宁可留下近似重复的记录,也不要静默丢弃。
两种写入路径,两种契约。
mem.add(...)会运行轻量级的基于规则的取代操作(§双时态)。mem.ingest_round(turn)会运行完整的四决策流程。处理对话数据时选择ingest_round;对于已完成冲突判断的结构化写入,选择add。
睡眠时整合
mem.consolidate()(core.py:526)会使用更强大的模型,从近期片段中重新提取并综合事实。目前只能通过 Python API 调用 — 没有 CLI 命令。如果希望获得比流式提取器更新的事实,可通过应用程序安排其运行(cron、systemd timer、ECS 定时任务)。
反思引擎
attestor/consolidation/reflection.py 会定期综合某位用户的 N 个情节。输出:
stable_preferences— 在 3 个或更多情节中出现的模式stable_constraints— 用户反复提及的规则changed_beliefs— 发生变化的偏好(旧值 → 新值,并明确标记为失效)contradictions_for_review— 标记为人工审核,不会自动解决
对于受监管的聊天系统,“不要自动解决”这一立场是关键所在。提示词中对此有明确说明(reflection.py:35-66):"不要自动解决矛盾。将其标记为待人工审核。"
Chain-of-Note 阅读
recall() 返回一个列表。recall_as_pack() 返回一个 Agent 能够实际推理的类型化检索封装——其中每个字段都是 Chain-of-Note 工作流在引用、弃答或记忆冲突时选择正确有效期所需的内容:
pack = mem.recall_as_pack(query="who runs engineering?", context=ctx)
for entry in pack.memories:
print(entry.id, # cite this in the answer
entry.confidence, # weight or abstain
entry.valid_from, # bi-temporal window for conflict resolution
entry.valid_until,
entry.source_episode_id) # provenance back to the round it came from
agent.send(pack.render_prompt()) # Chain-of-Note prompt, memories interpolated as JSON
ContextPack 设置了 frozen=True,可哈希、可序列化为 JSON。它可以直接用于工具调用。默认提示词中明确包含 ABSTAIN 和 CONFLICT 条款——否则,每个前沿模型都会默认编造内容。
多 Agent 原语
六种角色
AgentRole:ORCHESTRATOR、PLANNER、EXECUTOR、RESEARCHER、REVIEWER、MONITOR(attestor/context.py:49-56)。角色信息会写入每条记忆的元数据,以记录来源。访问权限通过两层机制强制执行:
- AgentContext 层 —
ROLE_PERMISSIONS矩阵按角色限制写入/遗忘操作。矩阵:ORCHESTRATOR = R+W+F;PLANNER/EXECUTOR/RESEARCHER=R+W;REVIEWER/MONITOR仅为R。read_only=True是独立的紧急开关。 - Postgres RLS 层 — 对
user_id进行行级过滤(参见 §租户隔离)。
AgentContext — 交接、暂存区、轨迹
orchestrator = AgentContext.from_env(agent_id="orchestrator", namespace="project:acme")
planner = orchestrator.as_agent("planner", role=AgentRole.PLANNER)
executor = planner.as_agent("executor", role=AgentRole.EXECUTOR)
# Each child carries parent_agent_id + accumulating agent_trail.
# All three share the same scratchpad: Dict[str, Any] for typed handoff data.
as_agent() 会创建一个子上下文,其中包含 parent_agent_id、完整的 agent_trail 和共享的 scratchpad。轨迹会不断累积,可用于证明“此答案来自 Agent X,而 Agent X 的信息来自 Agent Y”。
每个 Agent 的令牌预算
AgentContext.token_budget(默认值为 20 000)会受到强制执行——recall() 会贪心地打包结果,直到预算耗尽(scorer.py:fit_to_budget)。token_budget_used 会在一个会话中的多次调用间累积。
可选的写入配额
mem.set_quota(user_id, daily_writes=...) → 对 add 操作强制执行配额,使用 v4 的 user_quotas 表(core.py:592-621)。此项为可选;未设置时表示不限制。
安全与合规
行级安全
参见 §租户隔离。RLS 策略是访问控制层;Python 层信任这些策略。请为每个连接设置 attestor.current_user_id。
每条记忆的来源信息
每条记忆都包含 agent_id、session_id、source_episode_id。替代链(superseded_by)会永久保留。对话情节会原样存储,并与从中提取的记忆分开——这意味着你始终可以还原哪轮对话产生了哪条事实。
删除审计日志
硬删除(例如 GDPR 清除)会在级联删除之前向 deletion_audit 写入一行记录,注明删除内容、时间、原因和执行者。这是对其他不可变架构的例外。
GDPR — 导出与清除
mem.export_user(external_id="user-42") # full data export (memories + episodes + sessions + projects)
mem.purge_user(external_id="user-42", # cascading hard delete with audit trail
reason="GDPR right-to-erasure request 2026-04-27")
mem.deletion_audit_log(limit=100) # forensic readback
core.py:557-590。仅适用于 v4。返回/写入数据主体根据第 15 条/第 17 条所要求的全部内容。
可选:Ed25519 来源签名
通过配置启用(signing.enabled = true)。每次执行 add 时,attestor 都会使用 Ed25519 密钥对规范载荷 id || agent_id || t_created || content_hash 进行签名。mem.verify_memory(memory_id) 返回 bool(core.py:623-640)。此项为可选,默认关闭——对于需要具备密码学不可否认性的对抗性写入场景,可将其开启。
运行拓扑
三种模式使用相同的 API。只有配置不同。
| 模式 | 结构 | 适用场景 |
|---|---|---|
| A — 嵌入式库 | 进程内运行 AgentMemory(config);直接与 Postgres + Neo4j 通信 | 单进程 Agent、脚本、笔记本 |
| B — Sidecar | 在 localhost:8080 上运行 attestor api;语言无关的 HTTP 客户端共享同一套 Postgres + Neo4j | 同一台机器上的多语言 Agent(Python + TS + Go) |
| C — 共享服务 | 一个 Attestor 服务位于 Agent 网格前端(App Runner / Cloud Run / Container Apps),后端由托管 Postgres + Neo4j 提供支持 | 生产环境中的多 Agent 平台 |
attestor api --port 8080 # Mode B / C — Starlette ASGI REST (HTTP)
attestor mcp --path ~/.attestor # MCP stdio server (zero-config; for Claude Desktop / Cursor / Windsurf)
attestor serve ~/.attestor # MCP stdio server (positional-path variant; equivalent transport)
后端
| 后端 | 文档 | 向量 | 图 | 状态 |
|---|---|---|---|---|
| Postgres + Neo4j (默认) | ✓ | pgvector | Neo4j + GDS | 已可用于生产环境 |
| ArangoDB | ✓ | ✓ | ✓ | 已可用于生产环境(一个引擎承担全部 3 种角色) |
| AWS | DynamoDB | OpenSearch Serverless | Neptune | 已提供后端代码 + Terraform |
| Azure | Cosmos DB | Cosmos DiskANN | NetworkX(进程内) | 已提供后端代码,Terraform 即将推出 |
| GCP | AlloyDB | AlloyDB ScaNN | AlloyDB 上的 AGE | 已提供后端代码,Terraform 即将推出 |
通过配置覆盖默认设置:
# ~/.attestor.toml
backend = "postgres+neo4j" # or "arangodb" | "aws" | "azure" | "gcp"
参考 Terraform 位于 attestor/infra/ 下。
嵌入
提供程序会自动检测(attestor/store/embeddings.py:get_embedding_provider),顺序如下:
- 本地 Ollama
bge-m3— 1024-D,8K 上下文 — 当http://localhost:11434可访问时使用 - 云原生 — 如果存在相应 SDK + 凭据,则使用 Bedrock Titan / Vertex / Azure OpenAI
- OpenAI
text-embedding-3-large(原生 3072-D;为兼容架构,可固定设置OPENAI_EMBEDDING_DIMENSIONS=1024) - OpenRouter — 用于联邦运行
设计上优先使用本地服务。覆盖设置:
export ATTESTOR_DISABLE_LOCAL_EMBED=1 # skip the Ollama probe entirely
export ATTESTOR_EMBEDDING_PROVIDER=openai
export ATTESTOR_EMBEDDING_MODEL=text-embedding-3-large
CLI
attestor --help 会列出所有命令。最实用的命令:
| 命令 | 用途 |
|---|---|
attestor quickstart | 无需回答问题的本地安装 — 后端 + 配置 + MCP/hooks + doctor |
attestor teardown | 反向执行 quickstart(容器 + 配置 + MCP/hooks;--purge 会清除数据) |
attestor init | 创建入门存储配置(较底层;简单路径是 quickstart) |
attestor doctor | 对每个存储 + 检索管道进行健康检查 |
attestor add / recall / search / list | 类似 CRUD 的记忆操作 |
attestor timeline | 实体时间线(使用双时态管理器) |
attestor stats | 存储统计信息 |
attestor export / import | JSON 导出 / 恢复 |
attestor compact | 移除已归档的记忆 |
attestor update / forget | 修改 / 归档一条记忆 |
attestor inspect | 检查原始数据库状态 |
attestor api | 启动 Starlette REST API |
attestor serve <path> | 启动 MCP stdio 服务(位置参数路径形式) |
attestor mcp [--path …] | 启动 MCP stdio 服务(零配置;Claude Desktop / Cursor / Windsurf 的默认方式) |
attestor ui | 存储的只读浏览器 UI |
attestor hook {session-start, post-tool-use, stop} | 运行 Claude Code 生命周期钩子 |
attestor lme / locomo / mab | 内置基准测试运行器(参见 §Evaluation) |
MCP 服务
attestor mcp(或 attestor serve <path>)会启动一个包含八种工具的 MCP stdio 服务:
| 工具 | 用途 |
|---|---|
memory_add | 写入带有来源信息的记忆 |
memory_get | 按 ID 获取一条记忆 |
memory_recall | 运行完整的检索管道 |
memory_search | 筛选列表(实体 / 类别 / 时间 / 命名空间) |
memory_forget | 按 ID 归档一条记忆 |
memory_timeline | 实体的时间顺序 |
memory_stats | 存储统计信息 |
memory_health | 各角色的健康状态快照 — 集成时先调用此项 |
此外还提供 MCP 资源(记忆列表)和提示词(适用于 IDE 助手的预设召回提示词)。
Hooks(Claude Code)
attestor/hooks/ 中包含三个生命周期钩子:
session_start— 根据 cwd / repo 将相关记忆注入会话上下文post_tool_use— 自动从Write/Edit/Bash中捕获有用的内容stop— 退出时写入会话摘要
通过安装程序(下一节)配置它们,或手动在 ~/.claude/settings.json 中配置。
为 Claude Code 安装
一条命令即可。 执行 pipx install attestor,然后运行 attestor quickstart — 无需回答任何问题,使用一个默认配置。它会启动本地后端,使用本地 Ollama bge-m3 嵌入模型(无需云端密钥),配置 MCP 服务(./.mcp.json)和生命周期钩子,运行 attestor doctor,并打印每个步骤。随时运行 attestor teardown 即可撤销。
pipx install attestor && attestor quickstart # install (zero questions)
attestor teardown # uninstall (--purge also wipes data volumes)
先决条件: Docker 正在运行,且 Ollama 正在提供 bge-m3(ollama pull bge-m3)。quickstart 的预检会扫描这些条件并报告缺少的内容 — 它绝不会提示你输入信息。
在 Claude Code 内部驱动它(插件)。 只需安装一次插件,然后运行它提供的命令:
/plugin marketplace add bolnet/attestor # one-time
/plugin install attestor # then ENABLE it in the /plugin → Installed menu
/attestor:install-attestor # runs `attestor quickstart` for you
插件命令带有命名空间:命令是
/attestor:install-attestor(以及/attestor:uninstall-attestor),而不是不带命名空间的/install-attestor。新安装的插件可能处于禁用状态 — 请在/plugin→ 已安装菜单中启用它,并运行/reload-plugins,否则命令将无法解析。
记忆会自动按项目隔离 — 每个工作目录(git 根目录,否则为 cwd)都是一个严格隔离的租户,因此项目之间绝不会共享记忆。无需配置命名空间。
本地后端会以三个 Docker 容器的形式启动(由 quickstart 运行的捆绑文件 attestor/infra/local/docker-compose.yml):
| 容器 | 类型 | 存储用途 |
|---|---|---|
attestor_postgres_document_db | Postgres 16 + pgvector | 文档 — 事实来源 |
attestor_pinecone_vector_db | Pinecone Local | 向量 — 嵌入 |
attestor_neo4j_graph_db | Neo4j 5 + GDS | 图 — PageRank / BFS |
每个容器、卷以及 compose 网络 / 项目都以
attestor_…命名,因此运行docker ps -a \| grep attestor(以及docker volume ls \| grep attestor)即可列出 Attestor 拥有的所有资源。
云端 / 托管后端(Neon · RDS · Cloud SQL、Pinecone Cloud、Neo4j AuraDB)以及其他嵌入模型(Pinecone Inference llama-text-embed-v2、Voyage voyage-4、OpenAI text-embedding-3)可在 ~/.attestor/attestor.yaml 中配置 — 请参阅 docs/INSTALL.md。
作为技能安装(2026 年 Agent SDK)
Attestor 随附规范的 SKILL.md,路径为 skills/attestor-memory/SKILL.md。Anthropic(skills-2025-10-02)和 OpenAI 的 Responses API 都采用了这种格式 — 带有 YAML frontmatter 的 Markdown 文件 — 用于分发可复用的 Agent 专业知识。此 wheel 包含 SKILL.md,因此每种 2026 级别的运行框架只需执行一次 pip install attestor,即可自动发现它。
该技能会向 Agent 介绍六种核心原语(recall、add、timeline、current_facts、forget、audit),以及 v4 企业功能(双时态 as_of 回放、RBAC 角色、命名空间隔离、来源签名、GDPR 导出 / 清除)。每个代码示例引用的都是 attestor.AgentMemory 中实际存在的方法,CI 测试(tests/test_skill_md.py)则确保 SKILL.md 与当前 API 保持一致。
如需在自己的宿主中固定此约定:
pip install attestor
python -c "import attestor, importlib.resources as r; print(r.files('attestor'))" # confirm wheel installed
# Point your agent harness at the bundled SKILL.md or read it directly:
python -c "from pathlib import Path; import attestor; \
print((Path(attestor.__file__).parent.parent / 'skills' / 'attestor-memory' / 'SKILL.md').read_text())"
评估
边界说明。 双大模型裁判栈是一种基准测试机制,不是运行时约定。生产环境中的召回采用单流水线,且具有确定性。多个裁判只在评估中对答案打分,绝不会用于面向用户的读取。
| 运行器 | 来源 | 衡量内容 |
|---|---|---|
attestor lme | LongMemEval(Google 的长记忆基准) | 长历史下的答案准确率、蒸馏、跨模型系列双裁判评估 |
attestor locomo | LoCoMo | 对话式长记忆一致性 |
attestor mab | MultiAgentBench | 多 Agent 协调 |
| AbstentionBench(CI 门禁) | 内部 | 何时不该回答——已知的未知项 |
scripts/lme_smoke_local.py | 双大模型冒烟测试 | 快速验证安装(参见 Quick Start §6) |
冒烟测试驱动程序与已发布基准测试所采用的标准技术栈完全一致。有关完整的环境变量 / CLI 标志覆盖矩阵,请参阅 --help。
项目布局
attestor/
core.py -- AgentMemory (main public API)
client.py -- MemoryClient (HTTP drop-in for remote Attestor)
context.py -- AgentContext, AgentRole, Visibility
models.py -- Memory, RetrievalResult, ContextPack
cli.py -- attestor CLI entry point
api.py -- Starlette ASGI REST API
longmemeval.py -- LongMemEval benchmark runner (dual-judge)
locomo.py -- LoCoMo runner
doctor_v4.py -- v4 schema + invariant validator
init_wizard.py -- interactive install flow
store/
base.py -- DocumentStore / VectorStore / GraphStore protocols
registry.py -- backend selection
connection.py -- config layering / env resolution
embeddings.py -- provider auto-detect (Ollama / OpenAI / Bedrock / Vertex / Azure)
postgres_backend.py -- pgvector (document + vector roles)
neo4j_backend.py -- Neo4j + GDS (graph role)
arango_backend.py -- all 3 roles in one
aws_backend.py -- DynamoDB + OpenSearch Serverless + Neptune
azure_backend.py -- Cosmos DB DiskANN + NetworkX
gcp_backend.py -- AlloyDB pgvector + AGE + ScaNN
schema.sql -- v4 Postgres schema (RLS, bi-temporal columns, content_tsv)
conversation/
ingest.py -- ingest_round() pipeline
extraction/
round_extractor.py -- 2-pass speaker-locked extraction
conflict_resolver.py -- 4-decision contract (ADD/UPDATE/INVALIDATE/NOOP)
rule_based.py -- deterministic fact extraction (no LLM)
prompts.py -- shared prompt templates
consolidation/
consolidator.py -- sleep-time re-extraction
reflection.py -- cross-thread synthesis (stable patterns + flagged contradictions)
graph/
extractor.py -- entity / relation extraction
retrieval/
orchestrator/ -- 6-step semantic-first pipeline (core.py)
tag_matcher.py
scorer.py -- MMR, confidence decay, entity boost, fit-to-budget
trace.py -- JSONL trace writer
temporal/
manager.py -- timelines, supersession, contradiction detection, as_of replay
identity/
signing.py -- Ed25519 provenance signing (optional)
defaults.py -- SOLO mode auto-provisioning
mcp/
server.py -- MCP server (tools, resources, prompts)
hooks/
session_start.py
post_tool_use.py
stop.py
ui/
app.py -- Starlette read-only viewer
static/, templates/ -- Evidence Board UI
utils/
config.py, tokens.py
infra/
local/ -- Docker Compose (Postgres + Neo4j)
aws_arango/ -- Reference Terraform
tests/ -- Unit tests; live cloud tests env-gated
evals/ -- LongMemEval / LoCoMo / MultiAgentBench / AbstentionBench harnesses
docs/ -- Architecture notes, ADRs
commands/ -- /install-attestor, etc.
scripts/ -- lme_smoke_local.py, etc.
开发
poetry install
poetry run pytest tests/ -q # unit tests, no external services needed
ATTESTOR_LIVE_PG=1 poetry run pytest tests/live -q # live integration (env-gated)
风格:使用 black 格式化,使用 isort 整理导入,使用 ruff 检查代码,使用 mypy 检查类型。遵循 PEP 8,为函数签名添加类型注解,并使用 dataclass 表示 DTO。文件通常较小(200–400 行,最多 800 行)。
值得了解的约定:
- Postgres 是事实来源。Neo4j 是派生数据;如果两者不一致,请从 Postgres 重建 Neo4j。
- 向量 / 图路径中的非致命错误会被捕获并记录。文档路径绝不会静默失效。
- 配置层级:环境变量 →
~/.attestor.toml→ 代码内覆盖。 - 两种写入路径:结构化数据使用
add()(轻量级基于规则的替代机制),对话数据使用ingest_round()(完整的两阶段 + 四决策约定)。
健康检查
集成时请始终先调用此项:
attestor doctor # CLI
mem = AgentMemory()
print(mem.health()) # Python API
// MCP
{ "tool": "memory_health" }
该检查会探测文档存储(Postgres)、向量存储(pgvector)、图存储(Neo4j)和检索流水线。默认拓扑需要全部四项——图扩展是标准流水线的第 2 步,而不是可选的加速器。临时向量探测失败会显示在 recall() 跟踪信息的 vector_error 中,以便调用方区分结果降级与正常结果。
状态与版本管理
- 版本: 4.1.6(稳定版)——已发布到 PyPI 和 MCP Registry,名称为
io.github.bolnet/attestor。pip install attestor会安装最新的 4.1.x 版本(无需--pre标志)。 - v3 → v4: 基于原生 v4 的 Postgres 架构进行全新构建,支持严格的租户隔离、双时态事实,以及无大模型参与的检索关键路径。没有自动迁移工具。 v3 仅为 Alpha 版本,没有生产用户;请删除 v3 数据库并重新安装。
- 完整的逐版本更新日志请参阅
CHANGELOG.md。
许可证
MIT。请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。