内容
# Engram
> **适用于 AI 智能体的持久记忆**。默认多租户,支持本地或云部署。经过基准测试,而非概念验证。
[](https://github.com/jamjet-labs/engram/actions/workflows/ci.yml)
[](https://www.python.org)
[](LICENSE)
[](https://pypi.org/project/jamjet-engram)
```python
from engram import Engram
async with await Engram.open(":memory:") as memory:
await memory.record("Alice prefers espresso over drip.", user_id="alice", role="user")
await memory.record("Alice's brother lives in Tokyo.", user_id="alice", role="user")
results = await memory.recall(query="what does alice drink?", user_id="alice", top_k=1)
print(results[0].fact.text)
# Alice prefers espresso over drip.
```
这就是 Engram 的全部内容。记录用户(或智能体)所说的话;稍后提问;获取相关事实。具有每用户隔离、时间归因和推荐模式,超越简单召回。
---
## 为什么选择 Engram?
已经有多个适用于智能体的记忆库(例如 [Mem0](https://github.com/mem0ai/mem0)、[Letta](https://github.com/letta-ai/letta)、[Zep](https://github.com/getzep/zep) 和已发布的 [AgentMemory](https://arxiv.org/abs/2501.00309))。Engram 做出了一些特定的选择:
**1. 模式感知的读取**。大多数记忆层只做一件事:检索与查询匹配的事实。Engram 提供了两个不同的读取路径:`Reader(mode="recall")` 用于“用户对 X 说了什么?”和 `Reader(mode="synthesis")` 用于“根据用户的偏好推荐一些内容。”在 LongMemEval-S 上,这将 `single-session-preference` 的准确率从 29% 提升到 71%。
**2. 默认多租户**。每个 API 都需要 `user_id` + `org_id`(一个 `Scope`)。事实、向量和聊天消息在 SQL 和 HNSW 层上按分区 - 默认情况下不会发生跨租户泄漏。没有可以跨租户读取的“admin”范围。
**3. 本地优先或云部署**。适用于 LLM(Ollama / OpenAI / Anthropic)和嵌入(Ollama / OpenAI / 合成)的可插拔提供程序。使用 `nomic-embed-text` + `llama3.2:3b` 完全离线运行,或在需要吞吐量时切换到云提供程序。
**4. 活动事实版本控制**。`Engram.supersede(old_id, new_id)` 标记事实已被替换。召回默认过滤过时的事实。因此,当用户更改偏好时,旧偏好不会影响未来的答案。
**5. 基准测试,而非概念验证**。[`benchmarks/`](benchmarks/) 中有可重复的 LongMemEval-S 运行。我们发布整体和每个类别的数字(目前 **71% 整体**,见下文)。与 AgentMemory 的前沿 96.2% 相比,差距为 25 个百分点 - 这是一项公开的路线图项目。
**6. MCP 原生**。提供了一个 [MCP 服务器](src/engram/server/mcp.py),以便 Claude Code、Cursor 等智能体工具可以直接使用 Engram 内存。三个工具:`memory_record`、`memory_recall`、`memory_context`。
---
## 安装
```bash
uv add jamjet-engram # 或:pip install jamjet-engram
uv add 'jamjet-engram[rerank]' # 交叉编码器重新排序器(推荐)
```
需要 Python 3.12+。SQLite(标准库)和 `hnswlib`(自动安装)是唯一的持久性依赖项。
---
## 60 秒体验
### 记录 + 召回
```python
import asyncio
from engram import Engram
async def main():
async with await Engram.open(":memory:") as memory:
await memory.record("user prefers oat milk", user_id="alice")
await memory.record("user drinks coffee twice a day", user_id="alice")
await memory.record("user's dog is named Whiskey", user_id="alice")
results = await memory.recall(query="coffee", user_id="alice", top_k=3)
for r in results:
print(f" [{r.score:.2f}] {r.fact.text}")
asyncio.run(main())
```
### 为 LLM 构建上下文窗口
```python
ctx = await memory.context(
query="what should I order at the cafe?",
user_id="alice",
token_budget=2000,
)
# ctx 是一个换行连接的字符串,可以直接放入系统提示中
```
### 按类别路由(区分器)
对于偏好/推荐问题,路由到合成模式 - 读取器根据存储的用户偏好生成推荐,而不是简单地列出事实。
```python
from engram import Engram, Reader, RuleBasedClassifier, is_preference_question
from engram.llm.tier import ModelTier
tier = ModelTier.default() # gpt-4o-mini
classifier = RuleBasedClassifier()
async with await Engram.open(":memory:", tier=tier) as memory:
for turn in conversation_history:
await memory.record(text=turn["content"], role=turn["role"], user_id="alice")
question = "Recommend a movie for tonight"
qt = await classifier.classify(question)
if is_preference_question(question, qt):
ctx = await memory.context(query=question, user_id="alice", role_filter=("user",))
reader = Reader(tier.reader, mode="synthesis")
else:
ctx = await memory.context(query=question, user_id="alice", classifier=classifier)
reader = Reader(tier.reader)
res = await reader.read(question=question, context=ctx)
print(res.answer)
```
完整演练:[`docs/quickstart.md`](docs/quickstart.md)。工作示例:[`examples/05_preference_synthesis.py`](examples/05_preference_synthesis.py)。
### 作为 HTTP 服务器运行
```python
import uvicorn
from engram import Engram
from engram.server.http import build_http_app
memory = await Engram.open("./engram.db")
uvicorn.run(build_http_app(memory), host="127.0.0.1", port=19090)
```
```bash
curl -sX POST localhost:19090/v1/memory/record \
-H 'Content-Type: application/json' \
-d '{"text": "alice prefers espresso", "user_id": "alice"}'
```
端点:`record`、`extract`、`recall`、`search`、`context`、`raw_facts`、sessions、messages、healthz。
### 作为 MCP 服务器运行(适用于 Claude Code、Cursor 等)
```python
from engram.server.mcp import build_mcp_server
import mcp.server.stdio
server = build_mcp_server(memory)
async with mcp.server.stdio.stdio_server() as streams:
await server.run(streams[0], streams[1], server.create_initialization_options())
```
---
## 与 JamJet 结合使用
Engram 是构建在 **[JamJet](https://github.com/jamjet-labs/jamjet)** 上的智能体的内存层 - JamJet Labs 的持久执行运行时。两个项目共享设计 DNA:Apache 2.0、MCP 原生、默认多租户。Engram 处理“智能体记住什么”;JamJet 处理“进程在执行中途重启时会发生什么”。
三种组合模式:
- **Python 智能体 + Engram(此库)+ JamJet [Python SDK](https://pypi.org/project/jamjet)** - 独立 Python 堆栈。参见 [`examples/06_with_jamjet.py`](examples/06_with_jamjet.py)。
- **Java/Spring 智能体 + [`engram-spring-boot-starter`](https://central.sonatype.com/artifact/dev.jamjet/engram-spring-boot-starter) + [JamJet Java Runtime](https://github.com/jamjet-labs/jamjet-runtime-java)** - JVM 堆栈。Spring 启动器将 Engram 的 HTTP/MCP API 封装为 Spring AI `ChatMemoryRepository`。
- **任何支持 MCP 的智能体** - Claude Code、Cursor、自定义智能体 - 指向 Engram 的 [MCP 服务器](#run-as-an-mcp-server-for-claude-code-cursor-etc) 和 JamJet 的运行时分别。在协议层上组合。
如果您不使用 JamJet,Engram 单独使用效果良好。但是,如果您正在使用 JVM 或 Python 构建持久智能体,两个堆栈的组合将为您节省重新发明内存的时间。
> **快速区分**(生态系统已积累了一些名称重叠):
> - **`pip install jamjet-engram`**(此库)- 独立 Python 内存库。
> - **`pip install jamjet`** - JamJet 的 Python SDK,用于运行时;*不是*此库。
> - JamJet 中的 **“持久”** 指的是 *持久执行*(您的智能体进程可以崩溃并从检查点恢复)。
> - Engram 中的 **“持久”** 指的是 *持久内存*(您存储的事实在运行之间持续存在并干净地版本控制)。
> - 这两个是正交能力 - 两个都适用于同一个智能体。
---
## 核心概念
| 概念 | 它是什么 | 您关心的原因 |
|---|---|---|
| **`Scope`** | 附加到每个事实的 `(org_id, user_id)` 对 | 多租户。SQL 或向量索引层上没有跨租户泄漏。 |
| **`Fact`** | Pydantic 模型:文本、范围、有效性窗口、置信度、可选事件日期/会话 ID/角色/元数据 | 内存单位。通过 `supersede` 版本控制,在检索时评分,由每租户范围管理。 |
| **`Engram`** | 存储 + 嵌入 + 检索器 +(可选)提取器 +(可选)层上的异步外观 | 您实际调用的东西。`record()`、`recall()`、`context()`、`extract()`。 |
| **`Reader`** | 具有两种模式的 LLM 驱动答案生成器:“recall”(验证者门控事实召回)和“synthesis”(基于用户偏好的推荐) | 您选择的模式会改变整个管道 - 验证者开启或关闭、工具循环或不、召回提示或合成提示。 |
| **`RuleBasedClassifier`** | 将问题映射到 6 个 LongMemEval 问题类型之一 | 用于每类别 token 预算,并决定召回还是合成。覆盖具有自己的 `QuestionClassifier`(如果需要其他类别)。 |
| **`MemoryTier`** | 每个事实的 `WORKING` / `EPISODIC` / `SEMANTIC` 枚举 | 内存的可选结构,按时间范围组织。存储不强制执行;您的代码执行。 |
| **`Tool` + `ToolRegistry`** | 读取器的工具使用协议(文本标记器 `[TOOL_USE]{...}[/TOOL_USE]`) | 当 `mode="recall"` 时,读取器可以在生成中间调用工具。六个内置工具(搜索事实、搜索事件、解决时间、计数之间、添加天数、之间天数)。自带工具。 |
所有可从顶级包导入:`from engram import Engram, Scope, Fact, Reader, ...`。
---
## 架构(一段文字 + 图表)
`Engram` 外观下的三个层。**存储** = `SqliteStore`(FTS5 用于关键字)+ `HnswVectorStore`(余弦相似度)。**检索** = `HybridRetriever` 混合向量/关键字/时间评分,可选交叉编码器重新排序。**读取** = `Reader`(验证者门控召回)或合成模式(验证者关闭,推荐提示)。可选额外项:`ExtractionPipeline`(LLM 从聊天回合中提取事实)、`EventExtractor`(SVO 事件日历,用于时间求解器)、`QueryDecomposer`(拆分复合问题),以及读取器可以在生成中间调用的工具注册表。所有操作均为异步;所有操作均为范围隔离。
```
┌──────────────────────────────────────────────┐
│ Engram(异步外观) │
│ record • recall • context • extract │
│ supersede • record_message │
└───────────┬───────────┬──────────┬──────────┘
┌────────────────┘ │ └──────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ SqliteStore │ │ HnswVectorStore │ │ Reader │
│ 事实 + 消息 │ │ 每范围 HNSW │ │ 回忆模式 │
│ FTS5 关键字 │ │ 余弦 │ │ ↳ 验证者 │
│ 范围隔离 │ │ 确定性 │ │ ↳ 工具循环 │
└──────────────────┘ └──────────────────┘ │ ↳ 升级 │
│ │ │ 合成模式 │
└─────────────┬──────────────┘ │ ↳ 直接 LLM │
▼ └──────────────────┘
┌──────────────────┐
│ HybridRetriever │ 向量 + 关键字 + 时间评分
│ + 重新排序器 │ 交叉编码器重新排序(可选)
└──────────────────┘
```
开始探索的文件:[`src/engram/engram.py`](src/engram/engram.py) 是外观。[`src/engram/read/reader.py`](src/engram/read/reader.py) 是两种模式的所在地。[`benchmarks/smoke_runner.py`](benchmarks/smoke_runner.py) 是 LongMemEval 工具。
---
## 基准测试 - LongMemEval-S
Engram 使用 [LongMemEval-S](https://github.com/xiaowu0162/LongMemEval)(Wu 等人,2024 年)进行基准测试 - 500 个关于长合成聊天历史的问题,由 `gpt-4o-mini` 评判。
### 最新结果(v0.1.0)
**71.0%** 在 100 个问题的分层子集上(`gpt-4o-mini` 读取器,`--decompose --tools`,偏好感知路由)。
| 类别 | 分数 | n |
|---|---|---|
| `single-session-assistant` | 88% | 17 |
| `temporal-reasoning` | 75% | 16 |
| `single-session-preference` | 71% | 17 |
| `knowledge-update` | 71% | 17 |
| `single-session-user` | 69% | 16 |
| `multi-session` | 59% | 17 |
| **整体** | **71%** | **100** |
前沿比较:AgentMemory 报告 **96.2%** 在完整的 500 个问题上。Engram 目前落后 25 个百分点。[路线图](#roadmap) 中有文档记录的差距缩小工作。
### 复现
```bash
set -a && source /path/to/.env && set +a # 需要 OPENAI_API_KEY
export LONGMEMEVAL_ORACLE=/path/to/longmemeval_oracle.json
uv run python -m benchmarks.smoke_runner --n 100 --decompose --tools
```
运行程序会在 `benchmarks/reports/` 中写入每个问题的 JSONL 跟踪 + markdown 报告。设置 `PYTHONHASHSEED=42` 以实现完全确定的插入顺序。
### 我们尝试过、有效和无效的方法
基准测试历史(在 `CHANGELOG.md` 中可见)记录了两个消融程序 - 一个 **Tier 2-3 批次**(2026 年 4 月,64% → 68%,独立消融 8 种技术)和一个 **偏好提升**(2026 年 5 月,`single-session-preference` 从 29% → 71%,在一次失败尝试-1 后重新设计)。我们也会发布负面结果:`--ft-cross-encoder` 在 IR 指标上看起来很有前景,但在多会话任务结构中训练标签与实际不符,损失了 7 个百分点;`--reextract` 和 `--self-consistency` 没有帮助,因为验证者直接关闭了它们。诚实的记录使基准测试数字可信。
---
## 生产环境运行
**范围隔离。** 每个 API 调用都需要 `user_id` + `org_id`(合并为 `Scope`)。事实、向量和聊天消息存储在 SQL 和 HNSW 级别上按范围分区 - 默认情况下不会发生跨租户泄漏。没有可以读取所有数据的“admin”范围。
**嵌入提供商权衡。** `OllamaEmbedding` 完全本地运行(私有、免费、较慢)- 推荐用于敏感数据或离线使用。`OpenAIEmbedding` 更快、更准确,但会将每个记录的文本发送到 OpenAI。`SyntheticEmbedding` 仅用于测试。
**LLM API 密钥。** Engram 永远不会存储 API 密钥。它们由 `OpenAILLM` / `AnthropicLLM` / `OllamaLLM` 客户端从环境中读取。在生产环境中使用机密管理器。
**速率限制。** 默认提取管道对每个会话调用一个 LLM。大量导入大型聊天历史记录应分段并限速,以适应提供商的层级。`OpenAILLM` 和 `AnthropicLLM` 客户端使用官方 SDK,处理带有指数回退的重试。
**确定性。** `HnswVectorStore` 默认设置为 `random_seed=42` 以实现可复制的基准测试运行。设置 `PYTHONHASHSEED=42` 并使用相同的插入顺序以实现字节到字节的可复制性。
**按类别路由。** 对于偏好/推荐问题,路由到 `Reader(mode="synthesis")` 和 `Engram.context(role_filter=("user",))`。其他类别采用默认事实召回读取器。参见 [`docs/quickstart.md`](docs/quickstart.md) 中的规范模式。
---
## 主机 MCP 服务器
`jamjet-engram` 0.2.0+ 提供了一个通过 **Streamable HTTP** 公开 Engram MCP 工具的 `engram-server` 二进制文件。使用它为主机 JamJet Cloud 代理、ChatGPT 自定义连接器或任何 HTTP 感知 MCP 客户端托管远程 Engram。
### 快速入门
```bash
pip install jamjet-engram
export ENGRAM_AUTH_TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
engram-server --transport http --port 8765
```
或使用 Docker:
```bash
docker run --rm \
-e ENGRAM_AUTH_TOKEN="$ENGRAM_AUTH_TOKEN" \
-v engram-data:/data \
-p 8765:8765 \
ghcr.io/jamjet-labs/engram-py-server:0.2.0
```
### 连接客户端
```bash
curl -i -X POST http://localhost:8765/mcp \
-H "Authorization: Bearer $ENGRAM_AUTH_TOKEN" \
-H "Accept: application/json, text/event-stream" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize",
"params":{"protocolVersion":"2025-03-26","capabilities":{},
"clientInfo":{"name":"curl","version":"1"}}}'
```
服务器在响应头中返回 `MCP-Session-Id`;后续请求会回显它。
### 认证模型(v0.2)
- `/mcp` 需要 `Authorization: Bearer <token>` 匹配 `$ENGRAM_AUTH_TOKEN`。
- 服务器 **拒绝启动** 如果 `$ENGRAM_AUTH_TOKEN` 未设置(关闭失败)。唯一退出选项是 `--no-auth`,除非 `--host` 为 `127.0.0.1` 或 `::1`,否则拒绝。
- `/v1/memory/*` REST 路由保持未认证(与 Rust `engram-server` REST 行为匹配;在其后面部署反向代理,如果您需要认证)。
- OAuth2 动态客户端注册(Claude Desktop 远程服务器风格)正在路线图中。
### 标准输入传输
对于 Claude Desktop 和其他标准输入 MCP 客户端,请继续使用 Rust `ghcr.io/jamjet-labs/engram-server:0.5.0` 镜像 - 该路径不受此版本影响。
---
## 贡献
我们真诚欢迎贡献。仓库设置好后,新贡献者可以在几个小时内交付有意义的更改。
- 保持 **Markdown 格式和排版**
- **完全保留所有 HTML 标签**,包括标签的属性(如 style、href、src 等)
- **完全保留技术名词**,如 MCP Server、API、GitHub
- **翻译英文内容**,保留中文内容不变
### 快速入门
```bash
git clone https://github.com/jamjet-labs/engram.git
cd engram
uv sync --all-extras
uv run pytest # 309 个测试,大约 50 秒
uv run ruff check && uv run ruff format --check && uv run mypy src/engram
```
所有三个 CI 门控都对此有要求。覆盖率下限为 79%(`--cov-fail-under=79`),下游 `py.typed` 作业会构建轮包并验证 `mypy --strict` 是否适用于库用户。
完整的开发者指南请参见:[CONTRIBUTING.md](CONTRIBUTING.md)。安全策略请参见:[SECURITY.md](SECURITY.md)。
### 贡献最受欢迎的领域
| 领域 | 你需要做的事情 | 难度 |
|---|---|---|
| **新的嵌入提供商** | 为 Cohere / Voyage / local-sbert 等实现 `EmbeddingProvider`。参考 [`src/engram/embedding/`](src/engram/embedding/) 中的模式。 | 简单 - 单个文件 + 测试 |
| **新的 LLM 提供商** | 为 Vertex / Together 等实现 `LLMClient`。参考 [`src/engram/llm/`](src/engram/llm/) 中的模式。 | 简单 |
| **为阅读器添加新工具** | 为特定领域的工作(例如 `search_calendar`、`query_database`)实现 `Tool`。参考 [`src/engram/tools/`](src/engram/tools/) 中的模式。 | 简单 |
| **提高偏好谓词召回率** | 目前在 LongMemEval-S 上为 76% - 缺失 "你认为呢?" 决策帮助形状。大约有 5 个问题的提升空间。参见 [`src/engram/read/preference_gate.py`](src/engram/read/preference_gate.py)。 | 中等 - 需要基准验证 |
| **Postgres 后端** | 为 Postgres 实现 `EngramStore`。模式在 [`src/engram/store/`](src/engram/store/) 中。 | 中等 - 模式 + 测试 + CI 矩阵条目 |
| **性能基准** | 在规模上回忆延迟 / 吞吐量。目前还没有基线 - 开放领域。 | 中等 |
| **从 Rust v0.5.x 迁移指南** | 记录 Rust 运行时和 Python 重写之间的 API 映射。 | 简单,如果你使用过两者 |
| **将 LongMemEval 提升至 ≥80%** | 当前差距为 25pp,AgentMemory 为 96.2%。选择一个类别(多会话为 59%,是最低的),并找到 +5pp 的改进。 | 困难 - 但影响高 |
我们为前三类问题添加了 `good first issue` 标签。在开始大更改之前,请先创建一个问题,以便我们检查方法的合理性。
### 什么样的 PR 才算好
- 首先要有失败的测试(CI 检查强制执行 TDD)
- 对于影响基准的更改,需要有 n=100 的烟雾测试结果与已发布的基线比较(非目标类别的 ±2pp,目标类别有实际提升)
- 在 `[Unreleased]` 下的 `CHANGELOG.md` 条目
- 遵循约定提交消息:`feat(scope): description` 或 `fix(scope): description`
### 决策如何做出
架构变更会经过 头脑风暴 → 规格说明书 → 计划 → 实现 的周期。规格说明书和计划仅在本地保存(在 `docs/superpowers/` 下使用 gitignore);结果会记录在 CHANGELOG 条目中,并附上 PR 引用。我们还会记录负面结果 - 请参阅 "Preference uplift v1(已回滚)" 条目,了解一个未达到预期指标的策略示例以及我们学到了什么。
---
## 路线图
按优先级排序:
- **LongMemEval-S ≥85%** - 主要指标。目前为 71%;AgentMemory 前沿为 96.2%。多会话最低为 59%。
- **Postgres 后端** - 目前仅支持 SQLite。模式很直接;需要 `EngramStore` Postgres 实现 + CI 矩阵条目。
- **Sonnet / Haiku 阅读器兼容性** - v0.1.0 合成提示针对 `gpt-4o-mini` 进行校准。Sonnet/Haiku 在工具协议解析方面存在回归。原生Anthropic工具使用API路径将解锁 +5–10pp 的工具增强运行。
- **性能基准** - 回忆延迟、吞吐量、内存占用(在规模上)。目前还没有发布的基线。
- **非偏好类别的每类别合成模式** - 目前仅 `single-session-preference` 使用合成模式。多会话可能受益于不同的定制提示。
- **从 Rust v0.5.x 迁移指南** - Rust 运行时在 [jamjet-labs/jamjet](https://github.com/jamjet-labs/jamjet/tree/main/runtime/engram) 中保持维护模式;并行 API 映射将帮助用户迁移。
- **跨语言客户端 SDK** - Engram 目前是 Python。HTTP API 足够稳定,可以机械地编写 TypeScript / Go 客户端。
如果这些对你有吸引力,请创建一个问题,我们可以讨论。
---
## 项目元信息
- **许可证:** Apache 2.0 - 请参见 [LICENSE](LICENSE)。
- **安全:** 私密地报告给 security@jamjet.dev。请参见 [SECURITY.md](SECURITY.md)。
- **变更日志:** [CHANGELOG.md](CHANGELOG.md)。
- **其他发行版:** Engram 还作为 [Rust crate](https://crates.io/crates/jamjet-engram)(打包到 [JamJet 运行时](https://github.com/jamjet-labs/jamjet))、[MCP 服务器](https://registry.modelcontextprotocol.io/servers/io.github.jamjet-labs/engram-server)(Docker · GHCR)和 [Spring AI ChatMemoryRepository](https://central.sonatype.com/artifact/dev.jamjet/engram-spring-boot-starter) 发行。所有发行版都共享线协议;此 Python 库是最新的。
- **示例:** [`examples/`](examples/) - 从 `01_quickstart.py` 开始。
- **架构深入解析:** `docs/architecture.md`(即将推出)。
*Engram 由 [JamJet Labs](https://jamjet.dev) 构建。我们公开了基准目标和设计决策;失败(也不少)都记录在 CHANGELOG 中。如果你想帮助我们突破 71% - 请尽管参与。*
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器