Metadata-Version: 2.5
Name: mem8-fs
Version: 0.3.1
Summary: Agent memory framework: md-based, sqlite-indexed, RRF-fused retrieval
Author: mem8 contributors
License: MIT License
        
        Copyright (c) 2026 mem8 contributors
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agent,ai,llm,memory,semantic-search,sqlite
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: anthropic-tokenizer>=0.1
Requires-Dist: anthropic>=0.40
Requires-Dist: click>=8.1
Requires-Dist: numpy>=1.26
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: pydantic>=2.6
Requires-Dist: sqlite-vec>=0.1.6
Provides-Extra: all-databases
Requires-Dist: chromadb<2.0,>=1.0; extra == 'all-databases'
Requires-Dist: lancedb<1.0,>=0.20; extra == 'all-databases'
Provides-Extra: chroma
Requires-Dist: chromadb<2.0,>=1.0; extra == 'chroma'
Provides-Extra: dev
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Provides-Extra: evals
Requires-Dist: pytest>=8.0; extra == 'evals'
Requires-Dist: pyyaml>=6; extra == 'evals'
Requires-Dist: rich>=13; extra == 'evals'
Requires-Dist: types-pyyaml>=6.0; extra == 'evals'
Provides-Extra: lancedb
Requires-Dist: lancedb<1.0,>=0.20; extra == 'lancedb'
Provides-Extra: local
Requires-Dist: onnxruntime>=1.17; extra == 'local'
Requires-Dist: sentence-transformers>=3.0; extra == 'local'
Provides-Extra: openai
Requires-Dist: openai>=1.40; extra == 'openai'
Provides-Extra: voyage
Requires-Dist: voyageai>=0.2; extra == 'voyage'
Description-Content-Type: text/markdown

# mem8

mem8 是面向 AI Agent 的本地、持久化长期记忆 Python 库。Markdown 是记忆内容的真实源，数据库负责元数据、全文检索和向量索引。它支持从对话中提取记忆，也支持接收外部 LLM 已总结好的知识，并通过 BM25、向量检索和 RRF 融合返回相关记忆。

mem8 对外提供稳定、通用的消息与知识模型，核心能力覆盖记忆提取、决策、持久化、检索和维护，各模块通过明确接口协作。

## 主要能力

- Markdown 文件持久化，数据目录可复制、挂载和备份。
- SQLite、LanceDB、Chroma 三种本地数据库 Backend。
- BM25 + Vector + RRF 混合检索。
- 按 ID 或相对路径查询记忆卡片。
- 支持 `text`、`thinking`、`tool_call`、`tool_result`、`attachment_text` 消息部件。
- 支持“从对话提取知识”和“直接写入已总结知识”两种更新模式。
- 支持幂等操作、乐观 generation 检查、只读打开和健康检查。
- 检查并修复 Markdown 记忆之间的链接。

## 环境要求

- Python 3.11 或更高版本。
- 默认 LLM 为 Anthropic，需要设置 `ANTHROPIC_API_KEY`。
- 至少安装一种 Embedding Provider：Local、OpenAI 或 Voyage。
- Local Embedding 首次运行可能需要下载模型。

当前内置 LLM 实现仅支持 Anthropic。`openai` extra 提供 OpenAI-compatible Embedding，不提供 OpenAI LLM。

## 安装

PyPI 分发名称为 `mem8-fs`，Python 包名仍为 `mem8`：

```bash
pip install mem8-fs
```

按需安装 Embedding Provider：

```bash
pip install "mem8-fs[local]"
pip install "mem8-fs[openai]"
pip install "mem8-fs[voyage]"
```

SQLite 是默认 Backend，不需要额外数据库依赖。其他 Backend 需安装对应 extra：

```bash
pip install "mem8-fs[lancedb]"
pip install "mem8-fs[chroma]"
pip install "mem8-fs[all-databases]"
```

源码开发环境：

```bash
uv sync --extra dev --extra evals --extra local
```

## 快速开始

### 初始化

```bash
mem8 init
```

默认记忆目录是当前工作目录下的 `.mem8`。也可以通过 CLI 的 `--memory-root PATH` 或 Python API 的 `Memory.open(memory_root=...)` 指定目录。

默认 SQLite 目录结构：

```text
<memory_root>/
├── core/                 # 只通过 inject_core_card 注入的核心记忆
├── notes/                # Agent 生成和维护的普通记忆
├── mem8.db               # SQLite 元数据、FTS5 和向量索引
└── mem8.toml             # 配置文件
```

`core/` 和 `notes/` 中的 Markdown 是记忆真实源，数据库保存可检查、可重建的索引与运行状态。

### 检索和查询

```bash
mem8 core inject ./seed.md
mem8 retrieve "context overflow" --topk 5 --similarity 0.55
mem8 query --card-id 1
mem8 query --path notes/context.md
```

### 更新记忆

从 JSONL 历史对话提取知识，再执行决策和写回：

```bash
mem8 update --messages messages.jsonl
```

```json
{"role":"user","content":"I prefer concise answers."}
{"role":"assistant","content":"Understood."}
```

外部 LLM 已生成结构化知识时，可跳过提取阶段，直接复用去重、决策和写回链路：

```bash
mem8 update-knowledge --knowledge-file knowledges.json
```

```json
[
  {
    "claim": "The user prefers concise answers.",
    "scope": "communication preferences",
    "evidence": ["Please keep answers concise."],
    "confidence": 1.0,
    "suggested_tags": ["preference", "style"]
  }
]
```

检查和修复记忆链接：

```bash
mem8 check
```

## 数据库 Backend

### 选择建议

| Backend | 安装 | 向量存储 | 文本检索 | 适用场景 |
| --- | --- | --- | --- | --- |
| SQLite | `mem8-fs` | sqlite-vec | FTS5 | 默认、依赖最少、单机通用 |
| LanceDB | `mem8-fs[lancedb]` | LanceDB | SQLite FTS5 | 本地列式向量存储、数据量增长场景 |
| Chroma | `mem8-fs[chroma]` | Chroma | SQLite FTS5 | 已采用 Chroma 或偏好 Collection 模型的应用 |

三个 Backend 保持相同的 `Memory` API，并提供 `hybrid_rrf` 检索。当前 V2 为了最小改动和最大复用：

- SQLite 使用 `mem8.db` 保存元数据、FTS5 与 sqlite-vec 向量索引。
- LanceDB/Chroma 保存向量索引，并复用 `mem8-meta.db` 保存元数据、generation、操作回执和 FTS5 投影。
- Markdown 目录、写入约束、RRF 逻辑和上层服务不随 Backend 改变。
- Backend 不能通过修改配置原地切换；已有工作区切换前应显式迁移或重建索引。

LanceDB 目录：

```text
<memory_root>/
├── core/
├── notes/
├── mem8.toml
├── mem8-meta.db
├── lancedb/
└── .mem8-backend.json
```

Chroma 目录：

```text
<memory_root>/
├── core/
├── notes/
├── mem8.toml
├── mem8-meta.db
├── chroma/
└── .mem8-backend.json
```

### TOML 配置

`<memory_root>/mem8.toml`：

```toml
[database]
provider = "lancedb"       # sqlite | lancedb | chroma
path = "lancedb"           # memory_root 内的相对路径
distance = "l2"            # l2 | cosine
require_hybrid = true
collection = "mem8_cards"
```

SQLite 默认配置不需要写 `[database]`。`database.path` 不能是绝对路径，也不能通过 `..` 越过 `memory_root`。

环境变量配置：

```bash
MEM8_DATABASE__PROVIDER=lancedb
MEM8_DATABASE__PATH=lancedb
MEM8_DATABASE__DISTANCE=l2
MEM8_DATABASE__REQUIRE_HYBRID=true
MEM8_DATABASE__COLLECTION=mem8_cards
```

其他常用变量：

- `MEM8_LLM__MODEL`
- `MEM8_LLM__MAX_TOKENS`
- `MEM8_EMBEDDING__PROVIDER`
- `MEM8_EMBEDDING__MODEL`
- `MEM8_EMBEDDING__DIM`
- `MEM8_STORAGE__DB_FILENAME`

环境变量优先于 `mem8.toml`。LanceDB/Chroma 工作区的 Backend 标识文件会校验 provider、Embedding 维度和距离度量，避免用不兼容配置误开已有工作区。默认 SQLite 和历史工作区继续保持兼容，不强制要求该标识文件。

## Python API

### 检索与对话更新

```python
from mem8 import Memory
from mem8.domain.message import Message

with Memory.open(memory_root="/path/to/memory") as memory:
    results = memory.retrieve("user preferences", topk=5, similarity=0.55)
    card = memory.query(card_id=results[0].card_id) if results else None

    report = memory.update([
        Message(role="user", content="I prefer concise answers."),
        Message(role="assistant", content="Understood."),
    ])

    check_report = memory.check()
```

### 结构化消息

`Message.content` 继续兼容字符串，也支持厂商无关的结构化消息部件：

```python
from mem8 import (
    AttachmentTextPart,
    Memory,
    Message,
    TextPart,
    ThinkingPart,
    ToolCallPart,
    ToolResultPart,
)

messages = [Message(role="assistant", content=[
    ThinkingPart(text="private reasoning"),
    ToolCallPart(call_id="call-1", name="python_version"),
    ToolResultPart(call_id="call-1", content="Python 3.12"),
    AttachmentTextPart(
        text="project requires Python 3.12",
        filename="README.txt",
    ),
    TextPart(text="The project uses Python 3.12."),
])]

with Memory.open() as memory:
    memory.update(messages)
```

默认提取策略排除 `thinking`、`tool_call` 和失败的 `tool_result`；普通文本、成功的文本工具结果和附件提取文本可以作为证据。mem8 不接受二进制附件。

### 直接写入已总结知识

```python
from mem8 import Knowledge, Memory

knowledges = [Knowledge(
    claim="The user prefers concise answers.",
    scope="communication preferences",
    evidence=["Please keep answers concise."],
    confidence=1.0,
    suggested_tags=["preference", "style"],
)]

with Memory.open() as memory:
    report = memory.update_from_knowledge(
        knowledges,
        operation_key="memory-intent:example-1",
        expected_generation=memory.generation,
    )
```

- `Memory.update(messages)`：由 mem8 调用 LLM 提取知识，适合历史对话和后续 dream 流程。
- `Memory.update_from_knowledge(knowledges)`：接收外部 LLM 已总结的 1～10 条知识，跳过提取阶段。
- 两种更新共享决策、写回、文件锁、操作回执和 generation 冲突检查。
- 只读工作区允许检索和查询，但拒绝所有更新操作。

### Backend 状态

```python
with Memory.open(memory_root="/path/to/memory") as memory:
    print(memory.database_provider)
    print(memory.database_capabilities)
    print(memory.database_health())
```

以上是 V2 新增的只读接口；既有 API 的名称和必填参数保持不变。

### API 一览

```python
Memory.open(...)
Memory.retrieve(prompt, topk, similarity) -> list[RetrievalResult]
Memory.query(card_id=..., path=...) -> RetrievalResult
Memory.update(messages) -> UpdateReport
Memory.update_from_knowledge(knowledges, operation_key=..., expected_generation=...) -> UpdateReport
Memory.check() -> CheckReport
Memory.inject_core_card(source_md_path) -> int
Memory.record_eval_run(run) -> int
Memory.list_eval_runs(eval_name) -> list[EvalRun]
Memory.get_baseline_eval_run(eval_name) -> EvalRun | None
Memory.database_provider -> str
Memory.database_capabilities -> DatabaseCapabilities
Memory.database_health() -> DatabaseHealth
```

`Memory.open` 支持注入自定义 `llm`、`embedding`、`tokenizer` 和 `settings`。

## 数据与写入约束

- Agent 更新只写入 `notes/`。
- 核心记忆只能通过 `inject_core_card` 或 `core inject` 注入。
- Markdown 路径必须位于 `memory_root` 内；绝对路径和目录穿越会被拒绝。
- 单张卡片最多 4096 token、最多 8 个 Markdown 链接，链接按 1 token 计数。
- 修改卡片前会重新计算 token 和链接限制。
- Embedding 维度必须与现有向量索引一致；LanceDB/Chroma 会通过 Backend 标识提前校验。
- 生产环境应持久化整个 `memory_root`，而不是只持久化数据库子目录。

## 评测与开发

```bash
mem8 eval --suite retrieval
mem8 eval --suite update --record
mem8 eval --suite check --baseline
mem8 eval --suite all --record
```

源码验证：

```bash
uv run pytest
uv run ruff check mem8 evals tests
uv run mypy mem8 evals
```

可选 Backend 的真实集成测试仅在对应依赖已安装时运行。

## 发布到 PyPI

仓库使用 GitHub Actions 和 PyPI Trusted Publishing。发布前同时更新 `pyproject.toml` 与 `mem8/__init__.py` 中的版本号，合并到 `master` 后推送匹配的 Tag：

```bash
git tag vX.Y.Z
git push origin vX.Y.Z
```

工作流会校验 Tag 与版本声明、运行测试和静态检查、构建 wheel/sdist，并执行 `twine check`。发布 Job 使用 `pypi` GitHub Environment，仓库不保存 PyPI API Token。

## 文档

- [V1 产品需求](docs/v1/prd.md)
- [V1 开发设计](docs/v1/dev.md)
- [V2 多数据库产品需求](docs/v2/prd.md)
- [V2 多数据库开发设计](docs/v2/dev.md)

README 以当前仓库已实现能力为准。`sync`、`reindex`、`recover`、journal 恢复以及 OpenAI LLM 等仍属于后续能力。
