Doramagic 项目包 · 项目说明书
mnem 项目
生成时间:2026-05-15 02:27:44 UTC
项目介绍
Mnem 是一个用 Rust 编写的本地优先(local-first)知识图谱系统,旨在为 AI Agent 提供可靠、持久的记忆层。它将外部文档(PDF、Markdown、代码等)解析、分块、提取实体和关系,构建成结构化的有向图,支持版本化、冲突检测和密码学签名验证。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
Mnem 是一个用 Rust 编写的本地优先(local-first)知识图谱系统,旨在为 AI Agent 提供可靠、持久的记忆层。它将外部文档(PDF、Markdown、代码等)解析、分块、提取实体和关系,构建成结构化的有向图,支持版本化、冲突检测和密码学签名验证。
资料来源:crates/mnem-core/README.md
核心定位
Mnem 不是向量数据库,也不是传统的关系型知识库。它是一个本地优先的 DAG(有向无环图)存储,其设计目标包括:
| 设计目标 | 说明 |
|---|---|
| 本地优先 | 数据完全存储在本地,零云依赖 |
| 版本化 | 完整的操作日志,支持分支、回滚和合并 |
| 可验证 | 支持 Ed25519 签名和吊销列表验证 |
| AI 友好 | 专为 LLM/Agent 场景设计,支持上下文检索 |
| 多源解析 | 支持 PDF、Markdown、代码、会话记录等多种格式 |
资料来源:crates/mnem-core/src/lib.rs
系统架构
Mnem 采用分层架构,核心分为以下几个 crate:
graph TD
subgraph "表现层"
CLI[mnem-cli]
HTTP[mnem-http]
MCP[mnem-mcp]
end
subgraph "核心层 mnem-core"
OBJ[objects: Node/Edge/Commit]
PROLLY[prolly tree]
STORE[Blockstore trait]
RETRIEVE[Retriever]
SIGN[Signer/Verifier]
end
subgraph "数据接入层 mnem-ingest"
PDF[PDF 解析]
MD[Markdown 解析]
CODE[代码解析]
CONV[会话解析]
CHUNK[分块策略]
EXTRACT[实体/关系提取]
end
CLI --> HTTP
HTTP --> MCP
CLI --> mnem-core
HTTP --> mnem-core
MCP --> mnem-core
mnem-core --> mnem-ingest资料来源:crates/mnem-core/src/lib.rs
核心对象模型
资料来源:[crates/mnem-core/src/objects/node.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-core/src/objects/node.rs)
| 对象类型 | 用途 |
|---|---|
| Node | 图中的顶点,包含摘要、属性、上下文句子和可选内容 |
| Edge | 图中的边,连接两个 Node,支持关系类型标签 |
| Commit | 快照,包含一次操作后图的完整视图 |
| Operation | 操作记录,包含时间戳、作者、签名等元信息 |
| View | 读取视角,持有 heads 引用 |
数据接入流程
当用户向 Mnem 导入文档时,数据会经过以下处理流程:
graph LR
A[原始文档] --> B[格式检测]
B --> C{解析器选择}
C -->|Markdown| D[parse_markdown]
C -->|PDF| E[pdf 文本提取]
C -->|代码| F[tree-sitter 解析]
C -->|会话| G[JSON/JSONL 解析]
D --> H[Section 列表]
E --> H
F --> H
G --> H
H --> I[分块策略]
I --> J[Chunk 向量]
J --> K[实体提取]
K --> L[关系提取]
L --> M[Transaction 提交]资料来源:crates/mnem-ingest/src/lib.rs
支持的源格式
| 扩展名 | 源类型 | 默认分块策略 |
|---|---|---|
.md / .markdown | Markdown | Paragraph |
.txt | 纯文本 | SentenceRecursive (256 tokens) |
.pdf | SentenceRecursive (512 tokens) | |
.json / .jsonl | 会话记录 | Session (10 条消息) |
.rs / .py / .js / .ts 等 | 代码 | Structural |
资料来源:crates/mnem-ingest/src/pipeline.rs
分块策略详解
资料来源:[crates/mnem-ingest/src/chunk.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-ingest/src/chunk.rs)
| 策略 | 说明 | 适用场景 |
|---|---|---|
| Paragraph | 按双换行分割 | Markdown 文档 |
| Recursive | 词窗口滑动,固定 overlap | 通用场景(向后兼容) |
| SentenceRecursive | 基于 Unicode 句子边界(UAX #29)打包 | 纯文本、PDF |
| Session | 按对话轮次分组,边界在角色返回 user 时触发 | 会话记录 |
| Structural | 每 section 一个 chunk | 代码(函数/类级别) |
实体与关系提取
Mnem 提供了多层次的实体和关系提取能力:
资料来源:[crates/mnem-ingest/src/extract.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-ingest/src/extract.rs)
| 提取器 | 描述 |
|---|---|
| RuleExtractor | 基于规则的提取,使用正则和启发式方法 |
| KeyBertAdapter | 使用 KeyBERT 进行统计实体识别(需启用 keybert feature) |
| LLM Extractor | 使用 Ollama LLM 进行提取(需启用 ollama feature) |
Sidecar 提取器
支持外部提取服务:
# Cargo.toml
mnem-ingest = { features = ["sidecar-docling"] } # 或 ["sidecar-unstructured"]
资料来源:crates/mnem-ingest/src/lib.rs
检索系统
Mnem 提供了一套专为 Agent 设计的多阶段检索管道:
资料来源:[crates/mnem-core/src/retrieve/mod.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-core/src/retrieve/mod.rs)
graph TD
Q[用户查询] --> F[过滤阶段]
F --> V[向量检索]
V --> S[稀疏检索]
S --> R[重排序]
R --> P[Token 预算打包]
P --> R[最终结果]检索配置参数
| 参数 | 默认值 | 说明 |
|---|---|---|
retrieve.limit | - | 最大返回结果数 |
retrieve.budget | - | Token 预算上限 |
retrieve.vector_cap | - | 向量检索结果数上限 |
retrieve.graph_expand | - | 图扩展节点数 |
retrieve.graph_depth | - | 图扩展深度 |
retrieve.rerank_top_k | - | 重排序候选数 |
retrieve.hyde_max_tokens | - | HyDE 最大生成 Token 数 |
资料来源:crates/mnem-cli/src/config.rs
版本化与分支
Mnem 使用 DAG 结构存储版本历史,每次提交产生一个新的 Commit 节点:
graph LR
A[Commit A] --> B[Commit B]
B --> C[Commit C]
C --> D[Commit D]
B --> E[Commit E<br/>分支]
E --> F[Commit F]
D --> G[Commit G]| 概念 | 说明 |
|---|---|
| Head | 当前分支的最新提交 |
| Branch | 通过 refs 引用的命名指针 |
| Merge | 检测两个分支的冲突并合并 |
资料来源:crates/mnem-http/src/handlers.rs
冲突检测
资料来源:[crates/mnem-core/src/repo/conflict.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-core/src/repo/conflict.rs)
Mnem 支持基于策略的合并冲突检测:
| 策略 | 说明 |
|---|---|
ConflictPolicy::Default | 标准的冲突检测 |
ConflictPolicy::Manual | 手动解决冲突 |
密码学安全
资料来源:[crates/mnem-core/src/lib.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-core/src/lib.rs)
| 组件 | 功能 |
|---|---|
| Signer | 使用 Ed25519 对 Operation 进行签名 |
| Verifier | 验证签名有效性 |
| RevocationList | 支持吊销列表验证 |
CLI 命令
资料来源:[crates/mnem-cli/src/commands/ingest.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-cli/src/commands/ingest.rs)
| 命令 | 功能 |
|---|---|
mnem init | 初始化新仓库 |
mnem ingest <path> | 导入文档 |
mnem get <id> | 获取节点详情 |
mnem query <text> | 语义检索 |
mnem branch | 分支管理 |
mnem merge | 合并分支 |
mnem integrate | 集成到 AI 工具(Claude Code、Cursor 等) |
常用 ingest 选项
mnem ingest notes.md # 自动检测格式
mnem ingest --chunker recursive --max-tokens 1024 book.pdf
mnem ingest --recursive docs/ # 递归导入目录
mnem ingest --text "Hello world" # 导入纯文本
HTTP API
资料来源:[crates/mnem-http/src/handlers.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-http/src/handlers.rs)
| 端点 | 方法 | 说明 |
|---|---|---|
/v1/nodes | GET | 获取节点列表 |
/v1/nodes/{id} | GET | 获取指定节点 |
/v1/ingest | POST | 导入文档 |
/v1/branches | GET | 获取分支列表 |
/v1/branches | POST | 创建分支 |
/v1/retrieve | POST | 检索 |
MCP Server
Mnem 提供了 MCP(Model Context Protocol)集成,支持 AI 工具直接访问知识图谱:
graph LR
A[Claude Code / Cursor] <--> B[MCP Server]
B --> C[mnem-core]资料来源:crates/mnem-cli/src/integrate.rs
支持的集成平台:
| 平台 | 配置文件 |
|---|---|
| Claude Code | ~/.claude/CLAUDE.md |
| Cursor | ~/.cursor/rules/mnem.mdc |
| Continue | ~/.continue/config.json |
| Zed | ~/.config/zed/settings.json |
| Gemini CLI | ~/.gemini/GEMINI.md |
技术特性
资料来源:[crates/mnem-core/src/lib.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-core/src/lib.rs)
| 特性 | 说明 |
|---|---|
#![forbid(unsafe_code)] | 完全禁用 unsafe 代码 |
| 规范编码 | 所有对象支持字节级精确的编解码往返 |
| DAG-CBOR | 使用 DAG-CBOR 作为主要序列化格式 |
| Prolly Tree | 用于高效存储和查询的 Prolly Tree 实现 |
| Tree-sitter | 代码解析支持 10+ 主流编程语言 |
使用示例
初始化仓库
mnem init my-knowledge-base
cd my-knowledge-base
导入文档
# 导入 Markdown
mnem ingest README.md
# 导入 PDF
mnem ingest --chunker sentence_recursive document.pdf
# 递归导入目录
mnem ingest --recursive ./docs
# 导入代码文件
mnem ingest src/main.rs
检索知识
mnem query "Mnem 的检索算法是如何工作的?"
分支管理
mnem branch create feature-x
mnem branch switch feature-x
# ... 进行修改 ...
mnem commit -m "添加新功能"
mnem merge feature-x
总结
Mnem 是一个功能完整的本地优先知识图谱系统,特别适合需要持久化记忆的 AI Agent 场景。它通过:
- 多格式解析 - 支持 PDF、Markdown、代码、会话等
- 智能分块 - 针对不同内容类型优化分块策略
- 实体提取 - 规则、统计和 LLM 三种提取方式
- 版本控制 - DAG 结构支持分支和合并
- 可验证性 - Ed25519 签名确保数据完整性
- AI 友好 - 专为 Agent 设计的检索和渲染机制
为开发者提供了一个可靠、可信的知识管理基础设施。
资料来源:README.zh-CN.md
资料来源:[crates/mnem-core/README.md](https://github.com/Uranid/mnem/blob/main/crates/mnem-core/README.md)
安装指南
本文档详细说明 mnem 项目的各种安装方式、依赖要求以及初始化配置流程。mnem 是一个基于 Rust 构建的知识图谱管理工具,支持 CLI、HTTP 服务和 MCP(Model Context Protocol)三种接入方式。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
系统要求
硬件要求
| 组件 | 最低要求 | 推荐配置 |
|---|---|---|
| 内存 | 4 GB | 8 GB 或以上 |
| 磁盘空间 | 500 MB | 2 GB(用于本地数据库和向量索引) |
| 处理器 | x86-64 | 多核处理器 |
软件依赖
mnem 的核心由 Rust 语言编写,运行时依赖以下组件:
- Rust 工具链:建议使用最新稳定版(stable)
- OpenSSL:用于 HTTPS 通信和加密操作
- pkg-config:系统库链接管理
- protoc:Protocol Buffers 编译器(部分功能需要)
资料来源:crates/mnem-cli/src/main.rs
安装方式
mnem 提供多种安装途径,可根据使用场景选择合适的方式。
方式一:从源码编译安装
#### 前置准备
# 安装 Rust 工具链(如尚未安装)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# 安装系统依赖(Ubuntu/Debian)
sudo apt-get install -y build-essential pkg-config libssl-dev
# 安装系统依赖(macOS)
brew install openssl pkg-config
#### 编译步骤
# 克隆仓库
git clone https://github.com/Uranid/mnem.git
cd mnem
# 使用 Cargo 构建所有 crate
cargo build --release
# 安装二进制文件到 ~/.cargo/bin
cargo install --path crates/mnem-cli
编译完成后,可执行文件 mnem 将被安装到 Cargo 的 bin 目录。
方式二:使用安装脚本
项目提供了自动化安装脚本,支持 Linux、macOS 和 Windows。
#### Linux / macOS
curl -fsSL https://raw.githubusercontent.com/Uranid/mnem/main/scripts/install.sh | bash
脚本会自动检测系统平台,下载或编译对应版本的二进制文件。
#### Windows PowerShell
irm https://raw.githubusercontent.com/Uranid/mnem/main/scripts/install.ps1 | iex
脚本支持 PowerShell 5.1 及以上版本,会将 mnem.exe 添加到 PATH 环境变量。
方式三:Docker 部署
mnem 支持通过 Docker 容器运行,包含完整的 HTTP 服务和 MCP 服务器。
#### Dockerfile 结构
# 使用 Rust 官方镜像作为构建阶段
FROM rust:1.75 as builder
# 安装构建依赖
RUN apt-get update && apt-get install -y \
pkg-config libssl-dev
# 复制源码并编译
WORKDIR /app
COPY . .
RUN cargo build --release --bin mnem-http
# 运行阶段使用轻量级镜像
FROM debian:bookworm-slim
COPY --from=builder /app/target/release/mnem-http /usr/local/bin/
EXPOSE 8080
CMD ["mnem-http"]
资料来源:Dockerfile
#### docker-compose 编排
version: '3.8'
services:
mnem:
build: .
ports:
- "8080:8080"
volumes:
- mnem-data:/data
environment:
- MNEM_DATA_DIR=/data
- OLLAMA_BASE_URL=http://ollama:11434
restart: unless-stopped
ollama:
image: ollama/ollama:latest
volumes:
- ollama-data:/root/.ollama
restart: unless-stopped
volumes:
mnem-data:
ollama-data:
资料来源:docker-compose.yml
#### 启动 Docker 服务
# 构建并启动容器
docker-compose up -d
# 查看日志
docker-compose logs -f mnem
# 停止服务
docker-compose down
初始化配置
首次使用 mnem 前需要进行仓库初始化和基础配置。
仓库初始化
# 初始化新的 mnem 仓库
mnem init
# 指定自定义数据目录
mnem init --path ~/.my-mnem
初始化命令会在指定目录下创建 .mnem/ 子目录,包含以下结构:
.mnem/
├── repo.redb # 主数据库(Redb 键值存储)
├── config.toml # 用户配置文件
└── blocks/ # DAG-CBOR 数据块存储
资料来源:crates/mnem-cli/src/commands/init.rs
配置文件
初始化后生成的 config.toml 包含以下配置项:
[user]
# 用户标识(可选)
name = "Your Name"
email = "[email protected]"
[llm]
# Ollama 服务配置
base_url = "http://localhost:11434"
model = "llama3.2"
timeout_secs = 120
[ner]
# 命名实体识别提供者
provider = "rule" # 可选: "none", "rule"
[ingest]
# 默认分块策略
chunker = "auto" # 可选: "paragraph", "recursive", "session", "structural"
资料来源:crates/mnem-cli/src/config.rs
验证安装
# 检查 mnem 版本
mnem --version
# 查看当前仓库状态
mnem status
# 列出所有可用命令
mnem --help
成功安装后,mnem status 应显示仓库已初始化且无待提交操作。
接入方式
mnem 提供三种主要的接入方式,可根据使用场景选择。
CLI 模式
命令行模式适合日常笔记记录和知识管理:
# 添加节点
mnem add node -s "Alice 住在柏林"
# 添加关系
mnem add edge --from <src-uuid> --to <dst-uuid> --label 朋友
# 检索知识
mnem retrieve "Alice 住在哪里"
HTTP 服务模式
通过 HTTP API 提供服务,支持远程访问和程序化集成:
# 启动 HTTP 服务(默认端口 8080)
mnem http serve
# 指定端口
mnem http serve --port 9090
# 后台运行
mnem http serve --daemon
API 端点示例:
| 方法 | 端点 | 说明 |
|---|---|---|
| GET | /v1/status | 获取服务状态 |
| POST | /v1/nodes | 创建节点 |
| GET | /v1/nodes/{id} | 获取节点详情 |
| POST | /v1/ingest | 批量导入文档 |
资料来源:crates/mnem-http/src/handlers.rs
MCP 模式
MCP(Model Context Protocol)模式允许 AI 助手直接访问 mnem 知识图谱:
# 启动 MCP 服务器
mnem mcp serve
# 配置 Claude Desktop 使用 mnem MCP
# 编辑 ~/.claude/settings.json
资料来源:crates/mnem-mcp/src/lib.rs
依赖可选功能
mnem 的部分功能需要额外的依赖库支持。
文档解析依赖
| 功能 | 依赖 | 启用方式 |
|---|---|---|
| PDF 解析 | docling | cargo build --features sidecar-docling |
| 高级解析 | unstructured | cargo build --features sidecar-unstructured |
| KeyBERT 向量提取 | keybert | cargo build --features keybert |
| LLM 实体提取 | ollama | cargo build --features ollama |
资料来源:crates/mnem-ingest/src/lib.rs
向量检索依赖
mnem 的检索功能依赖嵌入模型生成向量表示,可通过以下方式配置:
[embedder]
# 本地 Ollama 嵌入模型
provider = "ollama"
model = "nomic-embed-text"
base_url = "http://localhost:11434"
安装流程图
graph TD
A[开始安装] --> B{选择安装方式}
B -->|源码编译| C[安装 Rust 工具链]
B -->|安装脚本| D[下载安装脚本]
B -->|Docker| E[安装 Docker]
C --> F[克隆仓库]
D --> G[执行安装脚本]
E --> H[配置 docker-compose]
F --> I[cargo build]
G --> J[验证二进制]
H --> K[docker-compose up]
I --> L[初始化仓库]
J --> L
K --> L
L --> M[配置 config.toml]
M --> N{选择接入模式}
N -->|CLI| O[使用 mnem 命令]
N -->|HTTP| P[启动 http serve]
N -->|MCP| Q[启动 mcp serve]
O --> R[安装完成]
P --> R
Q --> R卸载
二进制安装卸载
# 删除二进制文件
rm ~/.cargo/bin/mnem
# 删除配置文件(可选)
rm -rf ~/.config/mnem
Docker 卸载
# 停止并删除容器
docker-compose down -v
# 删除镜像
docker rmi mnem-mnem-http
# 删除数据卷
docker volume rm mnem_mnem-data
常见问题
编译失败
如果编译时出现 OpenSSL 相关错误,确保已安装开发库:
# Ubuntu/Debian
sudo apt install libssl-dev
# macOS
brew install openssl@3
权限问题
Linux/macOS 上如遇到权限错误:
# 修复 Cargo bin 目录权限
chmod +x ~/.cargo/bin/mnem
数据库损坏
如遇数据库异常,可尝试重建:
# 备份现有数据
mv ~/.mnem ~/.mnem.bak
# 重新初始化
mnem init
下一步
安装完成后,建议按以下顺序开始使用:
资料来源:[crates/mnem-cli/src/main.rs](https://github.com/Uranid/mnem/blob/main/crates/mnem-cli/src/main.rs)
系统架构
mnem 是一个基于 Rust 构建的本地优先知识图谱系统,采用内容寻址存储(Content-Addressed Storage)架构,通过 DAG-CBOR 规范实现数据的规范化编解码。该系统支持多端接入,包括命令行界面(CLI)、HTTP API 和 MCP(Model Context Protocol)协议,能够对 Markdown、PDF、代码文件、对话记录等多种格...
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
mnem 是一个基于 Rust 构建的本地优先知识图谱系统,采用内容寻址存储(Content-Addressed Storage)架构,通过 DAG-CBOR 规范实现数据的规范化编解码。该系统支持多端接入,包括命令行界面(CLI)、HTTP API 和 MCP(Model Context Protocol)协议,能够对 Markdown、PDF、代码文件、对话记录等多种格式的文档进行解析、分块、实体提取和语义检索。
核心设计原则
mnem 的核心架构遵循以下设计原则,这些原则贯穿整个系统的实现:
- 无 unsafe 代码:mnem-core crate 明确禁用所有 unsafe 代码,确保内存安全 资料来源:crates/mnem-core/src/lib.rs`。
- 字节级精确编解码:每个对象类型都保证字节级精确的规范化编码往返特性,即
decode(encode(x)) == x且encode(decode(b)) == b资料来源:crates/mnem-core/src/lib.rs`。 - 幽灵类型安全:
Link<T>通过泛型参数在编译时防止引用类型混淆,例如fn parents(&self) -> &[Link<Commit>]拒绝接受Link<Node>资料来源:crates/mnem-core/src/id/link.rs`。 - 多端统一解析:单一规范的解析器实现同时服务于 CLI、HTTP 和 MCP 接入面 资料来源:crates/mnem-ingest/src/lib.rs`。
系统分层架构
mnem 系统采用分层架构,各层职责明确,从底层数据存储到顶层用户接口逐层构建。
graph TD
subgraph 用户接入层
CLI[mnem-cli<br/>命令行工具]
HTTP[mnem-http<br/>HTTP API]
MCP[mnem-mcp<br/>MCP Server]
end
subgraph 核心库层
CORE[mnem-core<br/>核心数据模型]
INGEST[mnem-ingest<br/>文档解析与提取]
end
subgraph 存储层
STORE[Blockstore<br/>块存储接口]
VIEW[View<br/>视图快照]
COMMITS[Commit<br/>提交历史]
end
CLI --> CORE
CLI --> INGEST
HTTP --> CORE
HTTP --> INGEST
MCP --> CORE
MCP --> INGEST
INGEST --> CORE
CORE --> STORE
CORE --> COMMITSmnem-core 核心库
mnem-core 是系统的基础核心库,提供所有核心数据结构和算法实现。该库包含以下关键模块 资料来源:crates/mnem-core/src/lib.rs`:
| 模块 | 功能描述 |
|---|---|
id | CID(内容标识符)、ChangeId、OperationId 以及泛型链接类型 Link<T> |
codec | DAG-CBOR 编解码器和 DAG-JSON 调试导出 |
objects | Node、Edge、Commit、Operation、View、IndexSet 等核心对象类型 |
prolly | Prolly 树算法(分块器、构建器、查找、光标、diff、合并) |
store | Blockstore 和 OpHeadsStore trait 及内存实现 |
repo | ReadonlyRepo、Transaction 外观类 |
index | 辅助索引(Query、BruteForceVectorIndex) |
retrieve | Agent 面向的 Retriever,组合过滤、向量和稀疏排序 |
sign | Ed25519 签名和撤销列表验证 |
mnem-ingest 文档解析库
mnem-ingest 负责文档的解析、分块和实体提取工作。该库支持多种文档格式的分阶段处理 资料来源:crates/mnem-ingest/src/lib.rs`:
| 模块 | 功能 |
|---|---|
md | CommonMark + GFM Markdown 解析 |
pdf | 纯 Rust 实现的 PDF 文本层提取 |
code | 基于 tree-sitter 的代码解析 |
conversation | ChatGPT/Claude/通用对话导出格式处理 |
text | 纯文本处理 |
chunk | 分块策略(Paragraph、Recursive、SentenceRecursive、Session、Structural) |
extract | 实体和关系提取(RuleExtractor、KeyBertAdapter、LLMExtractor) |
sidecar | 外部工具集成(docling、unstructured) |
用户接入层
系统提供三种主要的用户接入方式:
- mnem-cli:命令行工具,支持本地文件的批量导入、检索和图谱管理
- mnem-http:基于 Actix-web 构建的 HTTP API 服务端
- mnem-mcp:Model Context Protocol 服务端,供 AI 代理调用
数据模型
mnem 采用 DAG(有向无环图)结构存储知识图谱,核心对象之间通过 CID(内容标识符)相互引用。
节点(Node)
Node 是图谱中的基本顶点单元,包含以下关键字段 资料来源:crates/mnem-core/src/objects/node.rs`:
classDiagram
class Node {
+id: NodeId
+ntype: String
+parents: Vec~Link~Commit~~
+context_sentence: Option~String~
+summary: Option~String~
+props: BTreeMap~String, Ipld~
+content: Option~Bytes~
+extra: BTreeMap~String, Ipld~
}| 字段 | 类型 | 说明 |
|---|---|---|
id | NodeId | 节点唯一标识符 |
ntype | String | 节点类型标签(如 "Doc"、"Section" 等) |
parents | Vec<Link<Commit>> | 父提交链接 |
context_sentence | Option<String> | 上下文句子(Anthropic 2024 contextual retrieval 设计) |
summary | Option<String> | LLM 可读的摘要文本,默认截断至 8192 字符 |
props | BTreeMap | 结构化属性键值对 |
content | Option<Bytes> | 不透明载荷(如文档体、文件内容) |
链接类型(Link)
Link<T> 是一个泛型封装的 CID,提供编译时类型安全保证。在传输层 Link 与普通 CID 字节相同,但泛型参数在 Rust 类型层面防止了错误的引用操作 资料来源:crates/mnem-core/src/id/link.rs`。
提交(Commit)与操作(Operation)
Commit 记录图谱的快照状态,Operation 记录具体的变更操作,两者共同构成版本控制的基础。
文档解析流程
当用户通过 CLI、HTTP 或 MCP 接入系统进行文档导入时,系统内部执行以下处理流程 资料来源:crates/mnem-ingest/src/pipeline.rs`:
flowchart LR
A[原始文件] --> B{文件扩展名检测}
B -->|md/markdown| C[Markdown解析器]
B -->|pdf| D[PDF解析器]
B -->|json/jsonl| E[对话解析器]
B -->|rs/py/js/ts...| F[Tree-sitter代码解析]
B -->|其他| G[纯文本解析器]
C --> H{分块策略选择}
D --> H
E --> H
F --> H
G --> H
H -->|Markdown| I[Paragraph分块]
H -->|PDF/Text| J[SentenceRecursive分块]
H -->|Conversation| K[Session分块]
H -->|Code| L[Structural分块]
I --> M[实体提取]
J --> M
K --> M
L --> M
M --> N[Transaction写入]
N --> O[Commit提交]源类型自动检测
系统根据文件扩展名自动推断源类型(SourceKind) 资料来源:crates/mnem-ingest/src/types.rs`:
| 扩展名 | SourceKind | 分块策略 |
|---|---|---|
.md .markdown | Markdown | Paragraph |
.pdf | SentenceRecursive (512 tokens, 64 overlap) | |
.txt | Text | SentenceRecursive (256 tokens, 32 overlap) |
.json .jsonl | Conversation | Session (10 messages) |
.rs | Code(Rust) | Structural |
.py .pyi | Code(Python) | Structural |
.js .mjs .cjs | Code(JavaScript) | Structural |
.ts .tsx | Code(TypeScript) | Structural |
.go | Code(Go) | Structural |
.java | Code(Java) | Structural |
.c .h | Code(C) | Structural |
.cpp .cc .cxx | Code(Cpp) | Structural |
| 其他 | Text | SentenceRecursive |
分块策略详解
系统提供五种分块策略,适用于不同类型的文档内容 资料来源:crates/mnem-ingest/src/chunk.rs`:
- Paragraph(段落分块):适用于 Markdown 文档,按段落边界切分
- Recursive(递归分块):按最大 token 数递归拆分,可配置重叠
- SentenceRecursive(句子递归分块):先按句子边界对齐,再按 token 限制拆分,适合 PDF 和纯文本
- Session(会话分块):保留消息对话结构,按最大消息数切分
- Structural(结构分块):利用 AST 解析提取函数、类、结构体等代码单元
存储架构
mnem 采用内容寻址存储,所有数据通过 CID 唯一标识。存储层提供两个核心 trait 接口:
Blockstore Trait
Blockstore 是底层内容存储接口,负责键值对形式的数据读写。系统支持多种后端实现,包括内存实现(用于测试)和持久化实现。
OpHeadsStore Trait
OpHeadsStore 管理操作头的集合,用于追踪当前工作区的最新状态。
View 与 Commit
- View:视图快照,包含 refs(引用表)和 heads(头提交集)
- Commit:提交记录,指向操作链和视图
graph LR
A[Operation] -->|prev| B[Operation]
B -->|prev| C[Operation]
D[Commit] -->|ops| A
D -->|view| E[View]
F[View] -->|heads| G["Vec<Cid>"]
F -->|refs| H["BTreeMap<String, RefTarget>"]检索流程
检索(Retrieve)模块负责根据用户查询从图谱中召回最相关的节点 资料来源:crates/mnem-core/src/retrieve/mod.rs`:
- 过滤阶段:根据查询条件筛选候选节点
- 向量排序:使用嵌入向量计算语义相似度
- 稀疏排序:BM25 等稀疏检索方法辅助排序
- 重排序:可选的重排序模型优化结果顺序
- Token 预算打包:将结果打包至 LLM 上下文预算内
检索模块实现了 Anthropic 2024 年提出的 Contextual Retrieval 技术,通过在节点上存储 context_sentence 字段(上下文句子)来增强检索效果,该字段在嵌入前被添加到 summary 前缀中 资料来源:crates/mnem-core/src/objects/node.rs`。
特性门控
部分功能通过 Cargo feature 进行条件编译控制 资料来源:crates/mnem-ingest/src/lib.rs`:
| Feature | 功能 |
|---|---|
keybert | KeyBERT 统计嵌入器适配器 |
ollama | Ollama LLM 提取器 |
sidecar-docling | Docling CLI PDF 解析集成 |
sidecar-unstructured | Unstructured.io 解析集成 |
总结
mnem 的系统架构体现了现代本地优先应用的设计理念:通过内容寻址确保数据的不可变性和可验证性,采用分层模块化设计支持多种接入方式,利用 DAG 结构自然地表达知识图谱的复杂关系。核心库的纯 Rust 实现保证了类型安全和跨平台兼容性,而丰富的文档解析和分块策略则满足了多样化的知识管理需求。
来源:https://github.com/Uranid/mnem / 项目说明书
Crates结构详解
mnem 是一个基于 Rust 构建的语义记忆系统,采用 IPLD (InterPlanetary Linked Data) 数据模型和 DAG-CBOR 编解码实现内容寻址存储。项目采用工作区 (workspace) 结构,包含多个独立的 crate,每个 crate 负责特定的功能领域。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mnem 是一个基于 Rust 构建的语义记忆系统,采用 IPLD (InterPlanetary Linked Data) 数据模型和 DAG-CBOR 编解码实现内容寻址存储。项目采用工作区 (workspace) 结构,包含多个独立的 crate,每个 crate 负责特定的功能领域。
graph TD
subgraph mnem工作区
CLI[mnem-cli]
HTTP[mnem-http]
MCP[mnem-mcp]
CORE[mnem-core]
INGEST[mnem-ingest]
end
CLI --> CORE
HTTP --> CORE
MCP --> CORE
INGEST --> CORE
CLI --> INGEST
HTTP --> INGEST
MCP --> INGEST核心 Crate 架构
项目依赖关系总览
| Crate 名称 | 依赖关系 | 主要功能 |
|---|---|---|
mnem-core | 无内部依赖 | 核心数据模型、存储、检索 |
mnem-ingest | 依赖 mnem-core | 数据解析、分块、实体提取 |
mnem-cli | 依赖 mnem-core, mnem-ingest | 命令行工具 |
mnem-http | 依赖 mnem-core, mnem-ingest | HTTP API 服务 |
mnem-mcp | 依赖 mnem-core | MCP 协议集成 |
mnem-core 核心库
定位与职责
mnem-core 是整个项目的核心基础设施库,提供底层数据模型、存储抽象和检索能力。该库完全禁止使用 unsafe 代码 (#![forbid(unsafe_code)]),确保内存安全。
核心模块
资料来源:[crates/mnem-core/src/lib.rs](crates/mnem-core/src/lib.rs)
pub mod id; // 内容标识符
pub mod codec; // DAG-CBOR 编解码
pub mod objects; // 核心对象类型
pub mod prolly; // Prolly tree 算法
pub mod store; // 存储抽象
pub mod repo; // 仓库事务
pub mod index; // 索引实现
pub mod retrieve; // 检索器
pub mod sign; // 签名验证
核心对象类型
资料来源:crates/mnem-core/src/objects/node.rs
| 类型 | 说明 |
|---|---|
Node | 核心节点类型,包含摘要、属性、内容和上下文句子 |
Edge | 节点间的边关系 |
Commit | 提交记录 |
Operation | 操作记录 |
View | 视图快照 |
IndexSet | 索引集合 |
ID 系统
资料来源:crates/mnem-core/src/id/link.rs
pub struct Link<T: ?Sized> {
cid: Cid,
_target: PhantomData<fn() -> T>,
}
Link<T> 是类型化内容引用,基于 CID 但增加了类型安全保证:
- 编译时类型检查防止引用混淆
Link<Commit>、Link<Node>等泛型参数确保语义正确- 序列化格式与裸 CID 完全兼容
检索模块
资料来源:crates/mnem-core/src/retrieve/mod.rs
检索模块负责将节点渲染为 LLM 可消费的文本格式,采用 YAML-like 格式:
ntype: <ntype>
id: <uuid>
context: <context_sentence>
summary: <summary>
<prop_key>: <prop_value>
渲染规则:
ntype和id始终存在context在summary之前输出(遵循 Anthropic 2024 contextual-retrieval 配方)summary默认截断至 8192 字符,可通过MNEM_RENDER_SUMMARY_CAP_CHARS环境变量配置- 标量属性按字母序输出,复杂属性(Link、Map、List)跳过
mnem-ingest 数据摄取库
定位与职责
mnem-ingest 负责将各种格式的源数据(Markdown、PDF、代码、会话等)解析、分块并提取实体关系。
模块架构
graph LR
subgraph 解析器
MD[Markdown]
PDF[PDF]
CODE[Code]
CONV[Conversation]
TEXT[Text]
end
subgraph 分块器
PARA[Paragraph]
REC[Recursive]
SENT[SentenceRecursive]
SESS[Session]
STRUC[Structural]
end
subgraph 提取器
RULE[RuleExtractor]
KEYBERT[KeyBertAdapter]
LLM[LLMExtractor]
end
MD & PDF & CODE & CONV & TEXT --> PARA & REC & SENT & SESS & STRUC
PARA & REC & SENT & SESS & STRUC --> RULE & KEYBERT & LLM支持的源类型
资料来源:crates/mnem-ingest/src/types.rs
| 源类型 | 文件扩展名 | 默认分块策略 |
|---|---|---|
Markdown | .md, .markdown | Paragraph |
Pdf | .pdf | SentenceRecursive (512 tokens, 64 overlap) |
Text | 其他无扩展名文件 | SentenceRecursive (256 tokens, 32 overlap) |
Conversation | .json, .jsonl | Session (10 messages) |
Code(lang) | 各类代码文件 | Structural |
分块策略详解
资料来源:crates/mnem-ingest/src/chunk.rs
pub enum ChunkerKind {
Paragraph, // 段落级别
Recursive { max_tokens, overlap },
SentenceRecursive { max_tokens, overlap },
Session { max_messages }, // 会话消息分组
Structural, // 代码结构(函数/类等)
}
| 策略 | 适用场景 | 关键参数 |
|---|---|---|
Paragraph | Markdown 文档 | 无 |
Recursive | 通用文本递归切分 | max_tokens, overlap |
SentenceRecursive | 句子边界感知的递归切分 | max_tokens, overlap |
Session | 对话/聊天记录 | max_messages |
Structural | 源代码结构解析 | 无 |
代码语言支持
资料来源:crates/mnem-ingest/src/code.rs
| 语言 | 扩展名 | Tree-sitter 提取项 |
|---|---|---|
| Rust | .rs | function_item, struct_item, enum_item, trait_item |
| Python | .py, .pyi | function_definition, class_definition |
| JavaScript | .js, .mjs, .cjs | function_declaration, class_declaration |
| TypeScript | .ts, .tsx, .mts, .cts | function_declaration, class_declaration |
| Go | .go | function_declaration, type_declaration |
| Java | .java | method_declaration, class_declaration |
| C | .c, .h | function_definition, struct_specifier |
| C++ | .cpp, .cc, .cxx, .hpp | function_definition, class_specifier |
| Ruby | .rb, .gemspec, .rake | method_definition, class_module |
| C# | .cs, .csx | method_declaration, class_declaration |
提取器
资料来源:crates/mnem-ingest/src/lib.rs
pub mod extract; // 规则提取器
#[cfg(feature = "keybert")]
pub mod extract_keybert; // KeyBERT 统计适配器
#[cfg(feature = "ollama")]
pub mod extract_llm; // LLM 驱动提取器
提取器特性矩阵:
| 提取器 | Feature Flag | 依赖 |
|---|---|---|
RuleExtractor | 默认启用 | 无 |
KeyBertAdapter | keybert | embedding 模型 |
LLMExtractor | ollama | Ollama 服务 |
Sidecar 集成
资料来源:crates/mnem-ingest/src/sidecar.rs
提供高级 PDF 解析的后备方案:
| Sidecar | Feature Flag | 说明 |
|---|---|---|
DoclingSidecar | sidecar-docling | 调用 docling CLI |
UnstructuredSidecar | sidecar-unstructured | 调用 unstructured-ingest CLI |
Sidecar 仅在纯 Rust 解析(parse_pdf)结果质量不足时使用,需手动配置触发。
mnem-cli 命令行工具
定位与职责
mnem-cli 是项目的命令行客户端,提供用户交互接口和配置管理。
主要命令
#### ingest 命令
资料来源:crates/mnem-cli/src/commands/ingest.rs
| 参数 | 默认值 | 说明 |
|---|---|---|
--chunker | auto | 分块策略:auto, session, paragraph, recursive, sentence_recursive, structural |
--max-tokens | 512 | 目标分块大小(token) |
--overlap | 32 | 相邻分块重叠 token 数 |
--recursive | false | 递归遍历目录 |
--text | - | 直接摄取内联文本 |
--ntype | Doc | 根节点类型标签 |
配置系统
资料来源:crates/mnem-cli/src/config.rs
配置项分层:
[user]
name, email, agent_id
[llm]
provider: openai | ollama
model
api_key_env
base_url
timeout_secs
[rerank]
model, api_key_env, base_url
[ner]
provider: rule | none
[retrieve]
limit, budget, vector_cap
graph_expand, graph_decay, graph_depth
rerank_top_k, hyde_max_tokens
Agent 集成
资料来源:crates/mnem-cli/src/integrate.rs
支持将 mnem 系统提示集成到多种 AI 开发工具:
| 主机 | 路径 | 存储格式 |
|---|---|---|
| ClaudeCode | ~/.claude/CLAUDE.md | Markdown |
| GeminiCli | ~/.gemini/GEMINI.md | Markdown |
| Cursor | ~/.cursor/rules/mnem.mdc | Markdown |
| Continue | ~/.continue/config.json | JSON (systemMessage) |
| Zed | settings.json | JSON (assistant.system_prompt) |
mnem-http HTTP 服务
定位与职责
mnem-http 提供 RESTful HTTP API,支持外部服务集成。
主要端点
资料来源:crates/mnem-http/src/handlers.rs
#### 分支管理
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /v1/branches | 列出所有分支 |
| POST | /v1/branches | 创建新分支 |
响应格式示例:
{
"schema": "mnem.v1.branches",
"branches": [
{"name": "main", "head": "<commit-cid>", "is_current": true}
]
}
摄取接口
资料来源:crates/mnem-http/src/handlers_ingest.rs
| 参数 | 说明 |
|---|---|
chunker | 分块策略 (auto, paragraph, recursive, session) |
max_tokens | 目标 token 数 |
overlap | 重叠 token 数 |
author | 提交作者(必需) |
message | 提交消息 |
extractor | 提取器选择 (none, keybert) |
ner_provider | NER 提供者 (rule, none) |
mnem-mcp MCP 协议集成
mnem-mcp 提供 Model Context Protocol 协议支持,使 mnem 可作为 AI 代理的记忆后端。详情待补充。
版本与特性矩阵
| Crate | 最小 Rust 版本 | 关键依赖 |
|---|---|---|
| mnem-core | 1.75+ | serde, cid, dag-cbor, tokio |
| mnem-ingest | 1.75+ | tree-sitter-*, pdf-extract |
| mnem-cli | 1.75+ | clap, anyhow, dirs |
| mnem-http | 1.75+ | axum, tower, tokio |
| mnem-mcp | 1.75+ | sse, tokio |
总结
mnem 项目采用清晰的模块化架构:
- mnem-core 提供不可变的 DAG-CBOR 数据模型和检索基础设施
- mnem-ingest 负责多格式数据的多策略分块与实体提取
- mnem-cli/http/mcp 提供多渠道访问接口
各 crate 通过严谨的类型系统和内容寻址确保数据一致性和可验证性,完全禁用 unsafe 代码保证了内存安全。
资料来源:[crates/mnem-core/src/lib.rs](crates/mnem-core/src/lib.rs)
知识图谱模型
mnem 的知识图谱模型是一个基于内容寻址(Content-Addressing)的图数据库核心,它采用 IPLD(InterPlanetary Linked Data)规范来组织和管理数据。该模型的核心设计理念是:
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mnem 的知识图谱模型是一个基于内容寻址(Content-Addressing)的图数据库核心,它采用 IPLD(InterPlanetary Linked Data)规范来组织和管理数据。该模型的核心设计理念是:
- 不可变性优先:所有对象一旦写入便不可更改,通过新建版本实现更新
- 类型安全的链接:使用
Link<T>泛型确保编译期类型检查 - DAG 结构:节点和边构成有向无环图(DAG),支持高效遍历和查询
- 确定性编码:所有对象支持 DAG-CBOR 规范下的字节级精确编解码往返
资料来源:crates/mnem-core/src/lib.rs:1-50
核心对象类型
节点(Node)
节点是知识图谱的基本存储单元,用于表示文档、文本块、实体等各类知识条目。
classDiagram
class Node {
+NodeId id
+NodeType ntype
+Option~String~ name
+Option~String~ summary
+BTreeMap~String, Ipld~ props
+Option~Bytes~ content
+Option~String~ context_sentence
+BTreeMap~String, Ipld~ ext
}关键字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
id | NodeId | 节点的唯一标识符(UUID) |
ntype | NodeType | 节点类型(如 Doc、Chunk、Entity 等) |
name | Option<String> | 节点的友好名称 |
summary | Option<String> | 摘要文本,供 LLM 消费使用 |
props | BTreeMap<String, Ipld> | 属性映射表,值为任意 DAG-CBOR 兼容类型 |
content | Option<Bytes> | 可选的不透明载荷(如文档正文、文件内容) |
context_sentence | Option<String> | 上下文前缀句(Anthropic 2024 Contextual Retrieval 论文应用) |
属性设计原则:
- 标量属性(
String、Integer、Float、Bool)按 BTreeMap 顺序(字母序)输出 - 非标量属性(
Link、Map、List、Bytes、Null)在渲染时被跳过,避免占用 token 预算 summary字段默认截断至 8192 字符,可通过MNEM_RENDER_SUMMARY_CAP_CHARS环境变量覆盖
资料来源:crates/mnem-core/src/objects/node.rs:1-150
边(Edge)
边表示节点之间的有向关系,是知识图谱关联结构的载体。
classDiagram
class Edge {
+EdgeId id
+Link~Node~ src
+Link~Node~ dst
+String label
+Option~Ipld~ props
}关键特性:
| 特性 | 说明 |
|---|---|
| 类型安全 | src 和 dst 均为 Link<Node> 类型,编译期确保指向节点 |
| 关系标签 | label 字段描述关系类型(如 knows、authored、part_of) |
| 属性扩展 | props 可选地附加关系级别的元数据 |
图遍历语义:
- 边是单向的:从
src指向dst - 支持按标签过滤遍历
- 支持按源节点或目标节点建立索引
资料来源:crates/mnem-core/src/objects/edge.rs:1-80
提交(Commit)
Commit 记录图谱的原子变更快照,包含一次或多次操作的集合。
classDiagram
class Commit {
+ChangeId change_id
+OperationId operation_id
+Link~Commit~ parent
+Author author
+Timestamp timestamp
+String message
+Vec~Operation~ operations
}变更原子性:
- 每个 Commit 包含完整的
operations向量 - 操作类型包括:
AddNode、AddEdge、UpdateNode、DeleteNode、DeleteEdge - 支持批量原子提交,确保要么全部成功,要么全部回滚
操作类型:
| 操作类型 | 说明 |
|---|---|
AddNode | 向图中添加新节点 |
AddEdge | 在两节点间建立关系 |
UpdateNode | 更新现有节点的属性(通过 Replace 实现) |
DeleteNode | 删除节点(软删除,生成 Tombstone) |
DeleteEdge | 删除边关系 |
资料来源:crates/mnem-core/src/objects/commit.rs:1-120
墓碑(Tombstone)
Tombstone 是已删除节点的占位符,用于在内容寻址结构中维护图的完整性。
classDiagram
class Tombstone {
+NodeId id
+Timestamp deleted_at
+Option~Author~ deleted_by
}设计目的:
- 在 IPLD 内容寻址系统中,被删除内容的 CID 仍然可能存在于历史记录或其他节点引用中
- Tombstone 确保遍历时不会因缺失内容而失败
- 支持后续的垃圾回收机制识别可清理的孤立对象
资料来源:crates/mnem-core/src/objects/tombstone.rs:1-60
链接系统(Link)
Link<T> 泛型类型
Link<T> 是 mnem 的类型安全内容引用封装。
classDiagram
class Link~T~ {
-Cid cid
-PhantomData _target
}
Link ..> Cid : contains核心特性:
| 特性 | 说明 |
|---|---|
| phantom type | 泛型参数 T 在运行时不占空间,仅用于编译期类型检查 |
| CID 等价 | 底层存储与裸 Cid 完全相同(相同字节、相同 CBOR 标签) |
| 编译期安全 | fn parents(&self) -> &[Link<Commit>] 会在传入 Link<Node> 时编译失败 |
使用示例:
// 正确的用法:编译通过
let commit_link: Link<Commit> = Link::new(cid);
fn get_parents(link: &Link<Commit>) -> &[Link<Commit>] { ... }
// 错误的用法:编译失败
let node_link: Link<Node> = Link::new(cid);
get_parents(&node_link); // 编译错误:类型不匹配
资料来源:crates/mnem-core/src/id/link.rs:1-80
索引系统
邻接表索引(Adjacency Index)
邻接表是图遍历的核心数据结构,支持高效的关系查询。
graph TD
A[Node A] -->|knows| B[Node B]
A -->|authored| C[Node C]
B -->|knows| D[Node D]
B -->|part_of| A
C -->|part_of| A
E[Adjacency Index] -->|out_edges| F[HashMap<NodeId, Vec<Edge>>]
E -->|in_edges| G[HashMap<NodeId, Vec<Edge>>]索引结构:
| 索引方向 | 用途 |
|---|---|
出边索引 (out_edges) | 给定节点,快速获取其所有出向关系 |
入边索引 (in_edges) | 给定节点,快速获取指向它的所有关系 |
查询能力:
- 按源节点查询所有出边
- 按目标节点查询所有入边
- 按边标签过滤
- 支持多跳路径查询(通过组合调用实现)
资料来源:crates/mnem-core/src/index/adjacency.rs:1-100
数据流与操作语义
写入流程
sequenceDiagram
participant Client
participant Transaction
participant Operation
participant Commit
participant Blockstore
Client->>Transaction: begin()
Client->>Transaction: add_node(node)
Transaction->>Operation: Create AddNode operation
Client->>Transaction: add_edge(src, dst, label)
Transaction->>Operation: Create AddEdge operation
Client->>Transaction: commit(author, message)
Transaction->>Operation: Finalize operations
Transaction->>Commit: Create Commit with operations
Commit->>Blockstore: Store Commit (CID)
Transaction->>Blockstore: Store Node/Edge objects
Blockstore-->>Commit: Store CIDs
Commit-->>Transaction: Commit CID
Transaction-->>Client: ReadonlyRepo图遍历流程
graph LR
A[输入: 起始节点] --> B[查询邻接表出边]
B --> C{边过滤条件}
C -->|无过滤| D[返回所有相邻节点]
C -->|按标签| E[返回匹配标签的节点]
D --> F[可迭代结果集]
E --> F
F --> G[继续遍历下一跳]检索与渲染
LLM 友好的节点渲染
检索系统在将节点返回给 LLM 使用前,会将节点渲染为确定性的文本表示。
渲染格式:
ntype: <ntype>
id: <uuid>
context: <context_sentence>
summary: <summary>
<prop_key>: <prop_value>
...
渲染规则:
| 字段 | 存在条件 | 位置 |
|---|---|---|
ntype | 始终 | 第1行 |
id | 始终 | 第2行 |
context | context_sentence 非空时 | summary 之前 |
summary | summary 非空时 | 第3或4行 |
| 属性 | 仅标量属性 | summary 之后 |
资料来源:crates/mnem-core/src/retrieve/mod.rs:1-100
节点类型枚举
| 节点类型 | 说明 | 典型属性 |
|---|---|---|
Doc | 文档节点 | mnem:source_kind、title、mime_type |
Chunk | 文本块节点 | section_path、tokens_estimate |
Entity | 实体节点 | NER 提取的实体 |
Conversation | 对话会话节点 | 消息序列 |
Ref | 引用/指针节点 | 指向其他节点的 Link |
约束与不变量
核心约束:
#![forbid(unsafe_code)]- mnem-core 禁止使用unsafe代码- 字节级精确编解码:每个对象类型必须满足
decode(encode(x)) == x和encode(decode(b)) == b - 不可变性:已有对象不可原地修改,只能创建新版本
- 类型化链接:所有节点间引用必须通过
Link<T>类型化
资料来源:crates/mnem-core/src/lib.rs:30-50
与外部系统的集成
摄入管道集成
graph TD
A[源文件] --> B[解析器 Parser]
B --> C[分块器 Chunker]
C --> D[提取器 Extractor]
D --> E[Transaction]
E --> F[Node/Edge 对象]
F --> G[Blockstore]
G --> H[Commit 提交]摄入管道将外部文档转换为图谱节点:
- Doc 节点:原始文档元数据
- Chunk 节点:分块后的文本段落
- Entity 节点:NER 提取的命名实体
- Edge 边:节点间关系(如
Chunk -[part_of]-> Doc、Entity -[extracted_from]-> Chunk)
资料来源:crates/mnem-ingest/src/pipeline.rs:1-80
全局知识图谱
mnem 支持跨仓库的全局知识图谱(~/.mnemglobal),检索时同时搜索当前仓库和全局图谱。
跨图谱链接策略:
- 不在 Edge 中添加
dst_repo: PathBuf(保持文件系统无关性) - Phase 1:检索时同时搜索全局图谱
- Phase 2(未来):在节点属性中添加
_global_anchor指向全局 CID
资料来源:crates/mnem-cli/src/global.rs:1-50
总结
mnem 的知识图谱模型是一个精心设计的图数据库核心,它通过:
- 类型安全的链接系统 确保编译期正确性
- 不可变的内容寻址 提供版本历史和完整性保证
- 确定性编码 确保跨平台一致性
- 上下文感知检索 支持 LLM 高效消费图谱数据
该模型为知识管理、AI 代理和语义搜索提供了坚实的数据基础。
资料来源:[crates/mnem-core/src/lib.rs:1-50]()
版本控制机制
mnem 的版本控制机制是一个基于内容寻址存储(Content-Addressable Storage, CAS)的分布式版本控制系统,采用 DAG(有向无环图)结构管理文档变更历史。该系统以操作为核心抽象,支持多分支并行开发、冲突检测与合并、以及加密签名验证等企业级功能。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mnem 的版本控制机制是一个基于内容寻址存储(Content-Addressable Storage, CAS)的分布式版本控制系统,采用 DAG(有向无环图)结构管理文档变更历史。该系统以操作为核心抽象,支持多分支并行开发、冲突检测与合并、以及加密签名验证等企业级功能。
mnem 的版本控制设计遵循以下核心原则:
- 无冲突写入:通过事务机制实现原子性操作
- 去中心化架构:每个仓库独立运行,无中心服务器依赖
- 可验证完整性:所有操作支持 Ed25519 数字签名
- 语义保留:变更记录包含 author、task_id、agent_id 等元数据
资料来源:crates/mnem-core/src/lib.rs:10-30
核心数据结构
Operation(操作)
Operation 是版本控制的基本单元,对应传统 VCS 中的 "commit" 概念。
pub struct Operation {
pub parents: Vec<ChangeId>, // 父操作列表(支持多父节点)
pub view: Cid, // 操作后的视图快照
pub predecessors: Option<BTreeMap<String, Vec<ChangeId>>>, // 前驱操作
pub author: String, // 作者标识
pub agent_id: Option<String>, // AI 代理标识
pub task_id: Option<String>, // 任务标识
pub host: Option<String>, // 主机标识
pub time: u64, // Unix 时间戳(微秒)
pub description: String, // 操作描述
pub signature: Option<Signature>, // 加密签名
pub extra: BTreeMap<String, Ipld>, // 扩展字段
}
关键特性说明:
| 字段 | 类型 | 用途 |
|---|---|---|
parents | Vec<ChangeId> | 支持多父节点,实现 merge 操作的完整历史记录 |
view | Cid | 指向操作后仓库状态的指针,保证内容可寻址 |
signature | Option<Signature> | Ed25519 签名,用于操作真实性和完整性验证 |
time | u64 | 微秒级时间戳,支持高精度排序 |
资料来源:crates/mnem-core/src/objects/operation.rs:30-70
ChangeId(变更标识)
ChangeId 是操作的唯一标识符,用于在 refs 和 parents 中引用操作:
pub struct ChangeId(bytes::Bytes);
ChangeId 通过内容哈希生成,确保相同操作的标识全局一致。
View(视图)
View 记录特定时间点的仓库完整状态,包含:
- 所有 refs(分支、标签)的当前指向
- 活动节点集合
- 墓碑集(tombstones):已删除节点的记录,用于冲突检测
资料来源:crates/mnem-core/src/objects/mod.rs
事务机制
Transaction 事务
Transaction 是 mnem 的核心写操作接口,提供原子性的变更构建与提交能力。
graph TD
A[创建 Transaction] --> B[add_node 添加节点]
A --> C[add_edge 添加关系]
B --> D[resolve_chunker 解析分块器]
C --> E[extract::RuleExtractor 实体提取]
D --> F[commit 提交事务]
E --> F
G[返回 IngestResult] --> H[ReadonlyRepo]主要方法:
| 方法 | 签名 | 功能 |
|---|---|---|
add_node | add_node(ntype, props, content) -> Result<ChangeId> | 添加节点到当前事务 |
add_edge | add_edge(src, dst, etype) -> Result<()> | 添加节点间关系 |
commit | commit(author, message) -> Result<ReadonlyRepo> | 原子提交事务 |
ingest | ingest(tx, bytes, kind) -> Result<IngestResult> | 端到端导入流程 |
资料来源:crates/mnem-core/src/repo/transaction.rs:60-120
IngestResult 结果
ingest 方法返回的 IngestResult 包含执行统计信息:
| 字段 | 类型 | 说明 |
|---|---|---|
nodes_added | u32 | 新增节点数 |
chunks_created | u32 | 创建的文本块数 |
entities_extracted | u32 | 提取的实体数 |
elapsed_ms | u64 | 执行耗时(毫秒) |
分支与引用管理
Refs 结构
mnem 使用 refs 管理命名引用:
refs/heads/<branch_name> # 分支头指针
refs/tags/<tag_name> # 标签引用
HEAD # 当前分支指针
分支操作
CLI 提供完整的分支管理能力:
| 命令 | 功能 | 关键参数 |
|---|---|---|
mnem branch | 列出所有分支 | -v 详细输出 |
mnem branch create <name> | 创建新分支 | --from <commit> 指定起点 |
mnem branch delete <name> | 删除分支 | --force 强制删除 |
分支创建流程:
- 解析目标 commit(支持 CID、ref 名称、分支短名、HEAD)
- 在 View 中创建
refs/heads/<name>指针 - 生成新的 Operation 记录变更
- 更新 HEAD 指针(如为当前分支)
资料来源:crates/mnem-cli/src/commands/branch.rs:1-80
标签操作
标签是只读的命名引用,常用于版本标记:
pub enum TagCmd {
List, // 列出所有标签
Create { name: String, target: Option<String> }, // 创建标签
Delete { name: String }, // 删除标签
}
约束条件:
- 标签名长度不超过 255 字符
- 不允许包含空格、制表符、换行符、
~、^、:、?、*、[等特殊字符 - 创建时必须指定有效的 author
资料来源:crates/mnem-cli/src/commands/tag.rs:1-60
差异比较
Diff 命令
mnem diff 命令用于比较两个提交之间的变更:
mnem diff [options] [<left>] [<right>]
| 参数 | 说明 |
|---|---|
<left> | 左侧 commit(基准),默认 HEAD |
<right> | 右侧 commit(比较目标),默认工作区 |
输出内容包括:
- 节点变更(新增/修改/删除)
- 边的变更(关系增删)
- 元数据变更(props、content)
合并机制
Merge 策略
mnem 支持三方合并(three-way merge),通过检测冲突实现:
graph TD
A[开始合并] --> B[加载 left commit]
A --> C[加载 right commit]
A --> D[计算 LCA]
B --> E[detect_conflicts]
C --> E
D --> E
E --> F{检测到冲突?}
F -->|是| G[返回 MergeConflicts]
F -->|否| H[自动合并成功]
G --> I[返回冲突列表]
H --> J[创建 merge commit]
J --> K[更新 View]冲突检测
pub fn detect_conflicts_with_policy(
repo: &ReadonlyRepo,
left: Cid, // 左侧 commit CID
right: Cid, // 右侧 commit CID
lca: Option<Cid>, // 最近公共祖先
policy: ConflictPolicy,
) -> Result<MergeConflicts, Error>
冲突类型:
| 类型 | 说明 |
|---|---|
ContentConflict | 内容修改冲突 |
StructuralConflict | 结构变更冲突(节点增删) |
PropertyConflict | 属性值冲突 |
ConflictPolicy 策略
冲突策略控制合并行为:
pub struct ConflictPolicy {
pub ours_priority: bool, // true: 优先我们的变更
pub theirs_priority: bool, // true: 优先他们的变更
pub auto_resolve: Vec<ConflictType>, // 可自动解决的冲突类型
}
资料来源:crates/mnem-core/src/repo/merge.rs:1-100
提交流程
Commit 命令
mnem commit 创建新的操作记录:
mnem commit [options] [--message <msg>]
| 选项 | 默认值 | 说明 |
|---|---|---|
--author | 配置值 | 作者信息 |
--message | 自动生成 | 提交消息 |
--amend | false | 修改上一个提交(追加操作) |
--no-edit | false | 跳过编辑器 |
提交流程:
sequenceDiagram
participant User
participant CLI
participant Transaction
participant Blockstore
participant Repo
User->>CLI: mnem commit -m "message"
CLI->>Repo: 创建 Transaction
Repo-->>Transaction: 返回 &mut Transaction
CLI->>Transaction: 加载待提交变更
CLI->>Transaction: commit(author, message)
Transaction->>Blockstore: 编码新 View
Transaction->>Blockstore: 创建 Operation
Blockstore-->>Transaction: 返回 View CID
Transaction->>Blockstore: 更新 HEAD ref
Transaction-->>Repo: 返回 ReadonlyRepo
Repo-->>CLI: 返回结果
CLI-->>User: 显示 commit 信息作者字符串格式:
- 完整格式:
"name <email>" - 仅名称:
"name" - 仅邮箱:
"<email>" - 仅 agent_id:
"mnem-cli"或配置的 agent_id
资料来源:crates/mnem-cli/src/commands/commit.rs:1-100
签名验证
操作签名基于 Ed25519 算法:
pub signature: Option<Signature>, // Ed25519 签名
签名流程:
- 序列化 Operation(排除 signature 字段)
- 使用私钥生成 Ed25519 签名
- 附加到 Operation 的 signature 字段
验证流程:
- 提取 signature 和公钥信息
- 使用公钥验证签名有效性
- 确保操作内容未被篡改
资料来源:crates/mnem-core/src/sign.rs
CLI 命令参考
命令总览
| 命令 | 模块 | 功能 |
|---|---|---|
mnem commit | commit.rs | 创建提交 |
mnem branch | branch.rs | 分支管理 |
mnem tag | tag.rs | 标签管理 |
mnem diff | diff.rs | 差异比较 |
mnem merge | merge.rs | 分支合并 |
mnem log | log.rs | 历史查看 |
仓库配置
配置文件位于 ~/.config/mnem/config.toml 或仓库根目录的 .mnem/config.toml:
[user]
name = "Your Name"
email = "[email protected]"
[llm]
provider = "ollama"
model = "qwen2.5"
base_url = "http://localhost:11434"
[ner]
provider = "rule" # rule | none
架构设计要点
DAG 持久化
mnem 使用 DAG-CBOR 编码存储 Operation 和 View,确保:
- 规范化:相同内容产生相同编码
- 可验证:编码后哈希作为 CID 可验证完整性
- 高效存储:内容去重通过 CID 实现
无头指针
mnem 不使用传统 Git 的 "head pointer" 链表模式,而是将所有历史直接编码在 Operation 的 parents 字段中。这简化了分支模型,但要求合并时必须显式指定 LCA。
事务隔离
Transaction 提供写隔离:
- 未提交的变更对其他读者不可见
- 提交是原子的(全部成功或全部回滚)
- 支持嵌套事务(通过 savepoint 实现)
资料来源:[crates/mnem-core/src/lib.rs:10-30]()
内容寻址系统
mnem 的内容寻址系统是整个知识图谱存储和检索的基础架构。该系统基于 IPFS 生态的标准规范构建,通过内容的加密哈希值作为内容的唯一标识符,实现了去重、版本追溯和分布式存储等核心能力。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mnem 的内容寻址系统是整个知识图谱存储和检索的基础架构。该系统基于 IPFS 生态的标准规范构建,通过内容的加密哈希值作为内容的唯一标识符,实现了去重、版本追溯和分布式存储等核心能力。
内容寻址的核心思想是:内容本身的哈希值即为该内容的地址。相同的内容必然产生相同的哈希值,这意味着任何节点可以对相同内容达成共识,无需依赖中心化的命名服务器。资料来源:crates/mnem-core/src/id/cid.rs:1-50
核心架构
mnem 的内容寻址系统由多个层次组成,每一层负责不同的职责:
graph TB
subgraph "内容寻址层次结构"
A["内容数据<br/>Bytes"] --> B["Multihash<br/>多哈希"]
B --> C["CIDv1<br/>内容标识符"]
C --> D["DAG-CBOR<br/>编码"]
C --> E["DAG-JSON<br/>编码"]
end
subgraph "上层应用"
D --> F["Node 节点"]
D --> G["Edge 边"]
D --> H["Commit 提交"]
F --> I["Transaction 事务"]
G --> I
H --> I
end
subgraph "类型系统"
J["Link<T><br/>类型化链接"]
K["ChangeId<br/>变更标识符"]
L["OperationId<br/>操作标识符"]
end核心不变性保证
mnem-core crate 在 crate 级别强制执行以下不变性:
#![forbid(unsafe_code)]- 整个核心模块禁止使用unsafe代码- 每个对象类型必须保持字节级精确的规范化编码往返属性:
decode(encode(x)) == x - 每个
put操作必须验证解码后的对象与原始内容一致 资料来源:crates/mnem-core/src/lib.rs:30-45
CID 内容标识符
CID 结构
CID(Content Identifier)是 IPLD 生态系统中用于标识有向无环图(DAG)中节点的标准格式。mnem 使用 CIDv1 格式,支持多种哈希算法。
// CID 结构示意(简化版)
pub struct Cid {
version: u64, // CID 版本,通常为 1
codec: u64, // 编解码器标识(如 dag-cbor = 0x71)
hash: Multihash, // 内容的哈希值
}
CID 的编解码
CID 的序列化采用 IPLD 规范定义的格式,支持二进制(DAG-CBOR)和字符串(base32)两种表示方式:
| 表示方式 | 用途 | 示例 |
|---|---|---|
| 二进制 | 内部存储、图遍历 | 存储在 Blockstore 中 |
| Base32 字符串 | 人类可读、URL 安全 | bafybeig... 格式 |
资料来源:crates/mnem-core/src/id/cid.rs:1-100
Multihash 多哈希
设计理念
Multihash 是一种自描述的哈希格式,它将哈希算法标识和哈希值封装在一起。这种设计允许系统在不破坏已有内容寻址的前提下,演进哈希算法。
┌─────────────┬─────────────┬──────────────────┐
│ 算法标识 │ 长度 (2B) │ 哈希值 │
│ (varint) │ │ │
└─────────────┴─────────────┴──────────────────┘
支持的哈希算法
mnem 通过 multihash crate 支持多种哈希算法,具体使用哪种算法取决于配置和用例。常见算法包括:
| 算法 | 标识符 | 输出长度 | 适用场景 |
|---|---|---|---|
| SHA-256 | 0x12 | 32 字节 | 默认、安全敏感场景 |
| Blake3 | 0xb201 | 32 字节 | 高性能需求 |
| Keccak-256 | 0x1b | 32 字节 | 以太坊兼容 |
资料来源:crates/mnem-core/src/id/multihash.rs:1-80
Link 类型系统
幽灵类型化链接
mnem 引入了 Link<T> 类型,这是一种phantom-typed(幽灵类型化)的链接类型,用于在编译期确保类型安全:
pub struct Link<T> {
cid: Cid,
_marker: PhantomData<T>,
}
这种设计实现了以下保证:
- 编译时类型检查:无法将
Link<Node>错误赋值给期望Link<Commit>的位置 - 运行时零开销:幽灵类型不占用实际存储空间
- 语义清晰:
Link<Node>表示指向节点,Link<Commit>表示指向提交
资料来源:crates/mnem-core/src/id/link.rs:1-60
相关的标识符类型
除 Link<T> 外,系统还定义了以下标识符类型:
| 类型 | 用途 | 说明 |
|---|---|---|
ChangeId | 变更标识 | 标识一次原子变更操作 |
OperationId | 操作标识 | 标识具体的操作实例 |
EdgeId | 边标识 | 标识图中的一条边 |
资料来源:crates/mnem-core/src/lib.rs:20-30
编解码器
DAG-CBOR 编解码器
DAG-CBOR 是 CBOR(Concise Binary Object Representation)的 DAG 扩展,是 IPLD 系统的默认编解码格式。它具有以下特点:
- 紧凑二进制表示:相比 JSON 节省 30-50% 空间
- 确定性编码:相同数据必然产生相同字节序列
- 丰富的类型支持:包括字节串、链接、标签等 IPLD 特有类型
graph LR
A["Rust Struct"] --> B["Serialize"]
B --> C["CBOR Bytes"]
C --> D["Multihash"]
D --> E["CID"]
F["CID"] --> G["Multihash"]
G --> H["CBOR Bytes"]
H --> I["Deserialize"]
I --> J["Rust Struct"]资料来源:crates/mnem-core/src/codec/dagcbor.rs:1-100
DAG-JSON 编解码器
DAG-JSON 用于人类可读的调试导出和日志记录。虽然不用于实际存储,但它对于开发和调试至关重要:
| 特性 | DAG-JSON | DAG-CBOR |
|---|---|---|
| 用途 | 调试、导出 | 实际存储 |
| 可读性 | 人类可读 | 二进制 |
| 效率 | 低 | 高 |
| 规范性 | 非严格 | 严格确定性 |
资料来源:crates/mnem-core/src/codec/dagjson.rs:1-80
对象模型
Node 节点
Node 是内容寻址存储的基本单元,每个节点由以下部分组成:
graph TB
A["Node"] --> B["id: NodeId"]
A --> C["ntype: String<br/>节点类型"]
A --> D["summary: Option<String><br/>摘要"]
A --> E["props: BTreeMap<br/>属性映射"]
A --> F["content: Option<Bytes><br/>内容负载"]
A --> G["context_sentence: Option<String><br/>上下文句子"]#### 字段语义
| 字段 | 类型 | 说明 |
|---|---|---|
id | NodeId | 节点的唯一标识符 |
ntype | String | 节点类型标签(如 "Fact", "Person") |
summary | Option<String> | LLM 可读的摘要文本 |
props | BTreeMap<String, Ipld> | 结构化属性,可包含 Link |
content | Option<Bytes> | 原始二进制负载 |
context_sentence | Option<String> | 上下文定位前缀 |
上下文句子(Context Sentence)是 Anthropic 2024 年上下文检索论文的实现。系统在嵌入前将此句子添加到摘要前,使密集和稀疏检索都能捕获位置和关系上下文。资料来源:crates/mnem-core/src/objects/node.rs:1-100
Edge 边
Edge 表示节点之间的有向关系:
pub struct Edge {
pub src: NodeId,
pub label: String,
pub dst: NodeId,
pub props: BTreeMap<String, Ipld>,
}
Commit 提交
Commit 封装了一组原子性的变更:
graph TB
A["Commit"] --> B["change_id: ChangeId"]
A --> C["parents: Vec<ChangeId>"]
A --> D["nodes: Vec<Node>"]
A --> E["edges: Vec<Edge>"]
A --> F["author: String"]
A --> G["time: u64"]
A --> H["message: String"]
A --> I["signature: Option<Signature>"]资料来源:crates/mnem-core/src/objects/commit.rs:1-100
内容寻址工作流
写入流程
sequenceDiagram
participant Client
participant Transaction
participant Blockstore
participant Codec
Client->>Transaction: add_node(node)
Transaction->>Codec: serialize(node)
Codec-->>Transaction: dag_cbor_bytes
Transaction->>Blockstore: put(cid, bytes)
Blockstore-->>Transaction: CID
Transaction-->>Client: Link<Node>
Client->>Transaction: add_edge(src, label, dst)
Transaction->>Transaction: 创建 Edge
Transaction->>Transaction: 创建 Commit
Transaction->>Codec: serialize(commit)
Blockstore->>Blockstore: 存储所有子节点读取流程
graph LR
A["Link<T>"] --> B["CID"]
B --> C["Blockstore.get"]
C --> D["DAG-CBOR Bytes"]
D --> E["Codec.deserialize"]
E --> F["T 类型对象"]源类型与分块
内容寻址系统与源类型处理紧密集成。SourceKind 定义了支持的源类型:
| 源类型 | 说明 | 默认分块策略 |
|---|---|---|
Markdown | Markdown 文档 | Paragraph |
Text | 纯文本 | SentenceRecursive (256 tokens, 32 overlap) |
Pdf | PDF 文档 | SentenceRecursive (512 tokens, 64 overlap) |
Conversation | 对话记录 | Session (10 messages) |
Code(lang) | 代码文件 | Structural |
CodeLanguage 枚举支持以下语言:Rust, Python, JavaScript, TypeScript, Go, Java, C, C++, Ruby, CSharp
资料来源:crates/mnem-ingest/src/types.rs:1-100
检索与渲染
节点渲染
检索系统需要将节点转换为适合 LLM 消费的文本格式。渲染格式如下:
ntype: <ntype>
id: <uuid>
context: <context_sentence>
summary: <summary>
<prop_key>: <prop_value>
...
渲染规则:
ntype和id始终存在context在summary之前(遵循 Anthropic 上下文检索方案)summary长度上限 8192 字符(可配置MNEM_RENDER_SUMMARY_CAP_CHARS)- 标量属性按 BTreeMap 顺序(字母序)输出
- 非标量属性(Link, Map, List, Bytes, Null)被跳过
- 不透明
content永不渲染
资料来源:crates/mnem-core/src/retrieve/mod.rs:50-100
配置与扩展
存储后端
mnem 通过 Blockstore trait 定义存储接口,支持多种实现:
pub trait Blockstore {
async fn get(&self, cid: &Cid) -> Result<Option<Vec<u8>>>;
async fn put(&self, cid: &Cid, block: &[u8]) -> Result<()>;
}
可扩展性设计
内容寻址系统的扩展点包括:
- 新的编解码器:实现
Codectrait 可添加新的序列化格式 - 新的哈希算法:通过
multihashcrate 注册新算法 - 新的对象类型:定义新结构体并实现序列化 trait
总结
mnem 的内容寻址系统建立在一个经过验证的标准之上(IPLD、CID、Multihash),同时通过以下设计决策提供了独特的价值:
- 类型安全:通过
Link<T>phantom 类型实现编译期检查 - 确定性编码:确保相同内容产生相同 CID,这是内容去重和一致性的基础
- 上下文感知:通过
context_sentence字段增强检索质量 - 规范化保证:crate 级别禁止
unsafe,所有编解码经过往返测试
这套系统使得 mnem 能够可靠地管理知识图谱的长期演进,同时保持高效的内容查询和去重能力。
资料来源:[crates/mnem-core/src/id/cid.rs:1-100]()
存储后端
mnem 的存储后端是整个系统的核心基础设施,负责持久化和检索内容寻址的数据块(blocks)。它定义了抽象的存储接口,并提供了多种后端实现,使得上层逻辑与具体存储技术解耦。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
架构概览
mnem 采用模块化的存储架构,将存储接口抽象为 trait(特征),支持可插拔的后端实现。当前系统包含两套核心存储抽象和两种后端实现。
graph TD
subgraph "应用层"
A["mnem-core<br/>(核心库)"]
B["mnem-cli<br/>(命令行工具)"]
C["mnem-http<br/>(HTTP服务)"]
end
subgraph "存储抽象层"
D["Blockstore trait"]
E["OpHeadsStore trait"]
end
subgraph "后端实现层"
F["InMemoryBlockstore<br/>(内存实现)"]
G["RedbBlockstore<br/>(redb持久化)"]
H["InMemoryOpHeadsStore"]
end
A --> D
A --> E
B --> A
C --> A
D --> F
D --> G
E --> HBlockstore 特征
Blockstore 是 mnem 系统中用于存储和检索内容寻址数据块的核心 trait。它定义了读写操作的抽象接口,使得无论底层使用何种存储技术,上层的编解码和 DAG 操作都能正常工作。
核心方法
| 方法 | 签名 | 功能描述 |
|---|---|---|
get | fn get(&self, cid: &Cid) -> Result<Option<Vec<u8>>, Error> | 根据 CID 检索数据块,返回 None 表示不存在 |
put | fn put(&self, block: &[u8]) -> Result<Cid, Error> | 存储数据块,返回其 CID |
has | fn has(&self, cid: &Cid) -> Result<bool, Error> | 检查指定 CID 的数据块是否存在 |
资料来源:crates/mnem-core/src/store/blockstore.rs:1-50
存储保证
Blockstore 的实现必须满足以下契约:
- 幂等性:
put操作对相同内容应产生相同的 CID(使用 DAG-CBOR 规范) - 一致性:读取已写入的数据必须返回完全相同的字节序列
- 原子性:单个
put操作应保持原子性
CID 与内容映射
mnem 使用 CIDv1 作为内容标识符,数据块采用 DAG-CBOR 编码格式。这种设计确保了:
- 相同内容始终映射到相同的 CID
- CID 包含足够的信息来验证内容完整性
- 存储系统无需维护独立的索引来追踪内容
OpHeadsStore 特征
OpHeadsStore 专门用于存储和管理操作头(operation heads)。在 mnem 的操作日志模型中,每个仓库(repo)在任意时刻都可能存在多个并发的操作头,这些头指针指向最新提交的操作序列。
核心方法
| 方法 | 签名 | 功能描述 |
|---|---|---|
get_op_heads | fn get_op_heads(&self) -> Result<Vec<OperationId>, Error> | 获取当前所有操作头 |
put_op_heads | fn put_op_heads(&self, heads: &[OperationId]) -> Result<(), Error> | 原子性地更新操作头集合 |
资料来源:crates/mnem-core/src/store/op_heads.rs:1-40
操作头语义
操作头集合代表了仓库的当前状态边界:
- 无头状态:空的
Vec表示仓库从未有过任何操作 - 单头状态:单一操作头表示线性历史
- 多头状态:多个操作头表示并发分支,需要合并
内存后端实现
mnem-core 包提供了一个内存中的参考实现,适用于测试、开发和单进程场景。
InMemoryBlockstore
基于 BTreeMap<Cid, Vec<u8>> 实现的内存块存储:
graph LR
A["put(cid, block)"] --> B["BTreeMap insert"]
C["get(cid)"] --> D["BTreeMap lookup"]
E["has(cid)"] --> F["BTreeMap contains_key"]特点:
- 无持久化能力,进程退出后数据丢失
- 查询性能为 O(log n),其中 n 为存储块数量
- 适合单元测试和快速原型开发
InMemoryOpHeadsStore
同样基于内存的 OpHeadsStore 实现,使用 Vec<OperationId> 存储操作头。
Redb 后端实现
mnem-backend-redb 提供基于 redb 的持久化存储实现。redb 是一个纯 Rust 编写的嵌入式事务型 KV 数据库,具有以下特性:
特性
| 特性 | 描述 |
|---|---|
| 纯 Rust | 无外部依赖,符合 mnem 的安全要求 |
| ACID 事务 | 支持完整的事务语义 |
| 嵌入式 | 无需独立的数据库服务器 |
| MVCC | 多版本并发控制,支持只读事务与读写事务并发 |
资料来源:crates/mnem-backend-redb/src/lib.rs:1-30
数据库结构
graph TD
subgraph "redb 数据库"
subgraph "元数据表"
M1["op_heads: Vec<OperationId>"]
end
subgraph "数据表"
D1["cid_1: Vec<u8>"]
D2["cid_2: Vec<u8>"]
D3["cid_n: Vec<u8>"]
end
end事务管理
RedbBlockstore 实现使用读写事务来保证数据一致性:
- 写操作:在读写事务中执行
put,事务提交后数据持久化 - 读操作:使用只读事务,避免阻塞写操作
- 批量操作:多个
put可以在同一事务中执行
配置选项
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path | &Path | 必需 | 数据库文件路径 |
max_db_size | u64 | 4GB | 数据库文件最大大小 |
read_transactions | bool | true | 是否启用并发只读事务 |
后端选择指南
| 场景 | 推荐后端 | 原因 |
|---|---|---|
| 单元测试 | InMemory | 快速、无副作用 |
| 开发调试 | InMemory | 便于检查状态 |
| CLI 工具 | Redb | 持久化 + 嵌入式 |
| HTTP 服务 | Redb | 事务保证 + 并发支持 |
| 内存受限环境 | InMemory | 零磁盘占用 |
与上层模块的关系
graph TD
subgraph "mnem-core"
R["Repo / ReadonlyRepo"]
T["Transaction"]
O["Operation"]
N["Node"]
end
subgraph "存储层"
BS["Blockstore"]
OHS["OpHeadsStore"]
end
R --> BS
R --> OHS
T --> BS
T --> OHS
O --> BS
N --> BSReadonlyRepo 和 Transaction 通过组合持有 Box<dyn Blockstore> 和 Box<dyn OpHeadsStore>,实现了存储后端的运行时多态。
错误处理
存储操作可能返回以下错误类型:
| 错误类型 | 触发条件 |
|---|---|
Error::BlockNotFound | get 操作的 CID 不存在 |
Error::Encoding | DAG-CBOR 编码/解码失败 |
Error::Store | 后端存储错误(如磁盘满、IO 错误) |
所有存储错误都会向上传播,最终由应用层决定如何处理(如重试、回滚或报告)。
扩展新的后端
要实现新的存储后端,需要完成以下步骤:
- 实现 Blockstore trait:提供
get、put、has方法 - 实现 OpHeadsStore trait:提供
get_op_heads、put_op_heads方法 - 实现 Send + Sync:确保线程安全
- 实现 Default:提供零参数构造能力
- 处理错误转换:将底层错误映射到
mnem_core::Error
建议参考 mnem-backend-redb 的目录结构:
crates/mnem-backend-redb/
├── src/
│ ├── lib.rs # 模块导出和公共 API
│ └── blockstore.rs # Blockstore trait 实现
├── Cargo.toml
└── README.md
安全考虑
mnem-core 整个包禁用了 unsafe_code(#![forbid(unsafe_code)]),这意味着:
- 存储后端实现也不能使用
unsafe - 数据完整性依赖 Rust 的内存安全保证
- 持久化数据的可靠性完全由后端实现负责
资料来源:[crates/mnem-core/src/store/blockstore.rs:1-50]()
数据流与摄取管道
摄取管道(Ingestion Pipeline)是 mnem 系统的核心入口,负责将外部源文件(Markdown、PDF、代码、对话记录等)解析、分割、提取并写入仓库图数据库。整个管道设计遵循确定性原则:相同输入无论何时处理,都会产生相同的输出内容与 CID 标识符。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
摄取管道(Ingestion Pipeline)是 mnem 系统的核心入口,负责将外部源文件(Markdown、PDF、代码、对话记录等)解析、分割、提取并写入仓库图数据库。整个管道设计遵循确定性原则:相同输入无论何时处理,都会产生相同的输出内容与 CID 标识符。
mnem 将每份摄入的文档建模为 Doc + Chunk + Entity 三层节点结构,并通过 relation edges 将实体与原文关联,形成可供检索的语义图谱。资料来源:crates/mnem-ingest/src/lib.rs:25
graph TD
A[外部源文件] --> B[SourceKind 检测]
B --> C[专用解析器]
C --> D[Section 列表]
D --> E[Chunker 分块策略]
E --> F[Chunk 列表]
F --> G[Entity Extractor]
G --> H[实体与关系]
H --> I[写入 Transaction]
I --> J[Commit 提交]
J --> K[图数据库]核心组件
模块架构
| 模块 | 职责 | 关键类型 |
|---|---|---|
pipeline | 端到端管道编排 | Ingester |
chunk | 分块策略选择与执行 | ChunkerKind, chunk(), auto_chunker() |
md | Markdown 解析 | parse_markdown() |
pdf | PDF 文本提取 | PdfExtractor |
code | 代码解析(tree-sitter) | parse_code() |
conversation | 对话记录解析 | parse_conversation() |
extract | 实体与关系提取 | RuleExtractor, Extractor trait |
text | 纯文本处理 | parse_text() |
types | 类型定义 | SourceKind, Section, Chunk |
资料来源:crates/mnem-ingest/src/lib.rs:10-32
数据类型体系
SourceKind 源类型枚举
管道根据文件扩展名自动检测源类型,不同类型采用不同的解析与分块策略。
| 扩展名 | SourceKind | 默认分块策略 |
|---|---|---|
.md, .markdown | Markdown | Paragraph |
.txt | Text | SentenceRecursive (256 tokens, 32 overlap) |
.pdf | Pdf | SentenceRecursive (512 tokens, 64 overlap) |
.json, .jsonl | Conversation | Session (max_messages: 10) |
.rs, .py, .js 等 | Code(lang) | Structural |
资料来源:crates/mnem-ingest/src/types.rs:58-75
Section 结构
Section 是解析后的中间表示,包含标题层级与正文内容:
pub struct Section {
pub heading: Option<String>, // 标题文本
pub level: u8, // 标题层级 (1-6)
pub body: String, // 正文内容
}
Chunk 结构
Chunk 是最终存储的基本单元:
pub struct Chunk {
pub tokens_estimate: usize, // token 数量估算
pub text: String, // 块文本
pub source_span: SourceSpan, // 源文件位置
}
资料来源:crates/mnem-ingest/src/chunk.rs:30-45
分块策略详解
ChunkerKind 枚举
管道支持五种分块策略,通过 ChunkerKind 枚举选择。
| 策略 | 描述 | 适用场景 |
|---|---|---|
Paragraph | 按双换行符分割 | Markdown 文档 |
Recursive | 滑动窗口 token 分块(向后兼容) | 通用文本 |
SentenceRecursive | 基于 Unicode UAX #29 句子边界感知的 token 分块 | PDF、纯文本 |
Session | 按消息数分组对话片段 | 对话记录 |
Structural | 每个 Section 一个 Chunk | 代码文件 |
资料来源:crates/mnem-ingest/src/chunk.rs:20-30
自动策略选择
auto_chunker() 函数根据源类型返回最佳分块策略:
pub fn auto_chunker(kind: SourceKind, heuristics: ChunkerAuto) -> ChunkerKind {
match kind {
SourceKind::Markdown => ChunkerKind::Paragraph,
SourceKind::Text => ChunkerKind::SentenceRecursive {
max_tokens: 256, overlap: 32
},
SourceKind::Pdf => ChunkerKind::SentenceRecursive {
max_tokens: 512, overlap: 64
},
SourceKind::Conversation => ChunkerKind::Session {
max_messages: 10
},
SourceKind::Code(_) => ChunkerKind::Structural,
}
}
资料来源:crates/mnem-ingest/src/chunk.rs:100-115
SentenceRecursive 策略特点
- 基于 Unicode 句子边界(UAX #29),确保块不在句子中间断开
- overlap 以句子为单位计算
- 平均块大小更均匀
- Token 计数采用空格分割估算(
tokens_estimate字段)
管道执行流程
Ingester 核心逻辑
sequenceDiagram
participant Client
participant Ingester
participant Parser
participant Chunker
participant Extractor
participant Transaction
Client->>Ingester: ingest(bytes, SourceKind)
Ingester->>Parser: parse(bytes, kind)
Parser-->>Ingester: Vec<Section>
Ingester->>Chunker: chunk(sections, ChunkerKind)
Chunker-->>Ingester: Vec<Chunk>
Ingester->>Extractor: extract(sections, chunks)
Extractor-->>Ingester: (Vec<Entity>, Vec<Relation>)
Ingester->>Transaction: add_node(Doc) + add_node(Chunk) + add_node(Entity)
Ingester->>Transaction: add_edge(relation edges)
Ingester-->>Client: IngestResult关键 API
Ingester::ingest() 方法:
pub fn ingest(
&self,
tx: &mut Transaction,
bytes: &[u8],
kind: SourceKind,
) -> Result<IngestResult, Error>
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
tx | &mut Transaction | 写入事务引用,不自动提交 |
bytes | &[u8] | 原始字节载荷 |
kind | SourceKind | 源类型标识 |
返回值:
| 字段 | 类型 | 说明 |
|---|---|---|
counts | Counts | 节点数量统计 |
elapsed | Duration | 执行耗时 |
commit_cid | Option<CommitId> | 留空,调用者需手动 tx.commit() |
资料来源:crates/mnem-ingest/src/pipeline.rs:80-110
专用解析器
Markdown 解析器
基于 CommonMark + GFM 规范,支持:
- ATX/Setex 标题(提取层级)
- 列表、代码块、表格
- 返回
Vec<Section>,每个顶级或次级块一个 Section
PDF 解析器
纯 Rust 实现,支持两种模式:
- 内置提取:解析 PDF 文本层
- Sidecar 扩展:调用外部
docling或unstructured-ingestCLI
#[cfg(feature = "sidecar-docling")]
pub struct DoclingSidecar;
#[cfg(feature = "sidecar-unstructured")]
pub struct UnstructuredSidecar;
资料来源:crates/mnem-ingest/src/sidecar.rs:30-50
代码解析器
使用 tree-sitter 解析代码文件,按函数/类级别提取 Section:
| 支持语言 | 扩展名 |
|---|---|
| Rust | .rs |
| Python | .py, .pyi |
| JavaScript | .js, .mjs, .cjs |
| TypeScript | .ts, .tsx, .mts, .cts |
| Go | .go |
| Java | .java |
| C/C++ | .c, .h, .cpp, .cc, .cxx, .hpp |
| Ruby | .rb, .gemspec, .rake, .erb |
| C# | .cs, .csx |
对话解析器
支持 ChatGPT、Claude 等导出的 JSON/JSONL 格式:
{
"messages": [
{"role": "user", "content": "..."},
{"role": "assistant", "content": "..."}
]
}
实体提取系统
Extractor Trait
pub trait Extractor: Send + Sync {
fn prepare(&self, sections: &[Section]) -> Result<(), Error>;
fn extract_entities(&self, sections: &[Section], text: &str) -> Vec<EntitySpan>;
fn extract_relations(&self, text: &str, entities: &[EntitySpan]) -> Vec<RelationSpan>;
}
资料来源:crates/mnem-ingest/src/extract.rs:50-70
RuleExtractor
默认提取器实现:
- 实体检测:委托给
NerProvider(默认:大写短语启发式) - 关系检测:基于动词窗口正则表达式
r"(?i)\b(?:joined|founded|acquired|owns|hired|married|...)
可选扩展
| 扩展 | 特性 | Cargo Feature |
|---|---|---|
KeyBertAdapter | 统计 NER + embedding | keybert |
OllamaExtractor | LLM 驱动 schema 约束 NER | ollama |
Ollama 提取器会对幻觉 span 重新验证,失败时降级为空 Vec 而非错误。
CLI 接口
基本用法
# 摄入单个文件
mnem ingest notes.md
# 指定分块策略
mnem ingest --chunker recursive --max-tokens 1024 book.pdf
# 递归目录
mnem ingest --recursive docs/
命令行参数
| 参数 | 默认值 | 说明 | ||||
|---|---|---|---|---|---|---|
--chunker | auto | 策略:`auto | paragraph | recursive | session | structural` |
--max-tokens | 512 | 目标 token 数 | ||||
--overlap | 32 | 重叠 token 数 | ||||
--recursive | false | 递归目录 | ||||
--ntype | Doc | 根节点标签 | ||||
--extractor | none | 提取器:`none | keybert` | |||
--ner | rule | NER 提供者:`rule | none` |
资料来源:crates/mnem-cli/src/commands/ingest.rs:50-80
HTTP API
POST /v1/ingest
请求体:
| 字段 | 类型 | 说明 | ||
|---|---|---|---|---|
file | multipart | 要摄入的文件 | ||
chunker | Option<String> | `auto\ | paragraph\ | recursive` |
max_tokens | Option<u32> | 目标 token | ||
overlap | Option<u32> | 重叠 token | ||
author | String | 提交作者(必需) | ||
message | Option<String> | 提交信息 | ||
extractor | Option<String> | `none\ | keybert` | |
ner_provider | Option<String> | `rule\ | none` |
资料来源:crates/mnem-http/src/handlers_ingest.rs:20-50
确定性保证
mnem 摄取管道的设计目标之一是确定性:
- 解析确定性:相同输入产生相同的
Vec<Section> - 分块确定性:
SentenceRecursive使用 Unicode 边界,不依赖运行时状态 - 编码确定性:所有对象类型保证字节级精确的编解码往返
- CID 确定性:相同内容产生相同 CID,支持去重
#![forbid(unsafe_code)] 确保管道中无未定义行为。
资料来源:crates/mnem-core/src/lib.rs:25-30
Node 输出结构
摄取完成后,仓库中包含以下节点:
| 节点类型 | ntype | 说明 |
|---|---|---|
| 文档节点 | Doc(可配置) | 根节点,记录来源 |
| 块节点 | Chunk | 分块后的内容单元 |
| 实体节点 | 实体类型 | 提取的命名实体 |
每个节点包含:
pub struct Node {
pub summary: Option<String>, // 摘要(供 LLM 使用)
pub props: BTreeMap<String, Ipld>, // 属性映射
pub content: Option<Bytes>, // 原始内容
pub context_sentence: Option<String>, // 上下文前缀句
}
context_sentence 字段实现 Anthropic 2024 Contextual Retrieval 方案,在嵌入前添加到摘要前缀。
错误处理
| 错误类型 | 触发条件 |
|---|---|
Error::ParseFailed | 解析器拒绝输入 |
Error::UnsupportedSource | 不支持的源类型 |
Error::Commit | 事务写入失败 |
Error::Sidecar | 外部工具缺失或失败 |
Sidecar 工具错误会包装为 Error::Sidecar { tool, detail }。
资料来源:[crates/mnem-ingest/src/lib.rs:10-32]()
混合检索系统
mnem 的混合检索系统(Hybrid Retrieval System)是核心检索引擎,负责在给定的 token 预算内高效地从知识库中召回最相关的内容。该系统通过组合向量检索(dense vector)、稀疏检索(sparse retrieval)和图扩展(graph expansion)等多种策略,为 Agent 提供高质量的上下文上下文。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
继续阅读本节完整说明和来源证据。
概述
mnem 的混合检索系统(Hybrid Retrieval System)是核心检索引擎,负责在给定的 token 预算内高效地从知识库中召回最相关的内容。该系统通过组合向量检索(dense vector)、稀疏检索(sparse retrieval)和图扩展(graph expansion)等多种策略,为 Agent 提供高质量的上下文上下文。
mnem 的检索架构遵循以下设计原则:
- 多策略融合:同时利用密集向量和稀疏向量的互补优势
- Token 预算感知:严格控制返回内容的 token 消耗
- 确定性渲染:为 LLM 生成稳定、可预测的输入格式
- 无 Unsafe 代码:整个检索管线保证内存安全
资料来源:crates/mnem-core/src/lib.rs
检索流程架构
混合检索系统的完整工作流程如下:
graph TD
A[用户查询 Query] --> B[查询理解层]
B --> C{检索策略选择}
C -->|混合模式| D[向量检索分支]
C -->|混合模式| E[稀疏检索分支]
C -->|混合模式| F[图扩展分支]
D --> G[结果融合]
E --> G
F --> G
G --> H[重排序 Rerank]
H --> I[Token 预算打包]
I --> J[渲染 Render]
J --> K[LLM 上下文输出]
L[配置参数] --> B
L --> D
L --> E
L --> F
L --> H核心组件
检索器(Retriever)
Retriever 是检索系统的主入口,负责协调各个检索分支的执行和结果融合。
| 组件 | 职责 |
|---|---|
Query | 查询结构体,包含查询文本和检索参数 |
Retriever | 主检索器,协调向量、稀疏、图扩展三种检索 |
BruteForceVectorIndex | 向量索引,支持暴力搜索和 ANN 近似搜索 |
QueryEncoder | 查询编码器,将自然语言查询转换为向量 |
资料来源:crates/mnem-core/src/retrieve/mod.rs 资料来源:crates/mnem-core/src/retrieve/retriever.rs
向量检索(Vector Retrieval)
向量检索使用密集向量表示来衡量语义相似性。mnem 采用 HNSW(Hierarchical Navigable Small World)算法实现近似最近邻搜索。
| 参数 | 说明 | 默认值 |
|---|---|---|
vector_cap | 向量检索返回的最大结果数 | - |
limit | 总体检索结果限制 | - |
HNSW 算法特点:
- 分层结构:构建多层跳表式图结构
- 贪心搜索:从顶层向下逐层搜索
- 参数控制:ef_search、ef_construction、m_max 等影响精度和性能
资料来源:crates/mnem-ann/src/hnsw.rs 资料来源:crates/mnem-core/src/index/vector.rs
稀疏检索(Sparse Retrieval)
稀疏检索基于词项精确匹配,适合处理专有名词、代码标识符等精确查询场景。
| 特性 | 说明 |
|---|---|
| BM25 算法 | 基于词频和文档频率的概率排名 |
| 倒排索引 | 高效的词项到文档映射 |
| 动态权重 | 根据查询词项在文档中的重要性调整 |
稀疏检索与向量检索形成互补:向量检索擅长语义相似性,稀疏检索擅长精确词项匹配。
资料来源:crates/mnem-core/src/index/sparse.rs
图扩展(Graph Expansion)
图扩展利用知识图谱的结构信息进行检索扩展。
| 参数 | 说明 |
|---|---|
graph_expand | 图扩展的邻居节点数量 |
graph_depth | 图遍历的最大深度 |
graph_decay | 距离衰减系数,控制远处节点的影响力 |
图扩展的工作原理:
- 从初始检索结果中的节点开始
- 沿着边扩展到相邻节点
- 根据
graph_decay衰减权重 - 重复直到达到
graph_depth
资料来源:crates/mnem-cli/src/config.rs
结果融合策略
当启用混合检索时,需要将多个检索分支的结果进行融合。mnem 采用基于分数的加权融合策略。
融合算法
graph LR
A[向量分数<br/>Vector Score] --> D[分数归一化]
B[稀疏分数<br/>Sparse Score] --> D
C[图扩展分数<br/>Graph Score] --> D
D --> E[权重加权]
E --> F[分数合并]
F --> G[排序输出]
H[配置权重] --> E融合分数计算公式:
final_score = w_vector × norm_vector + w_sparse × norm_sparse + w_graph × norm_graph
其中归一化方法采用 Min-Max 归一化:
norm(x) = (x - min) / (max - min)
资料来源:crates/mnem-core/src/retrieve/fusion.rs
重排序(Rerank)
融合后的候选结果会通过重排序模型进一步优化顺序。
| 配置项 | 说明 |
|---|---|
rerank.top_k | 重排序后保留的结果数 |
rerank.model | 重排序模型名称 |
rerank.base_url | 重排序 API 端点 |
rerank.api_key_env | API 密钥环境变量 |
支持的 Rerank 提供商:
- Cohere:Cohere Rerank API
- Voyage:Voyage AI Rerank API
- Jina:Jina AI Rerank API
资料来源:crates/mnem-cli/src/config.rs
Token 预算管理
检索系统的一个核心挑战是在严格的 token 预算内最大化信息价值。
预算控制参数
| 参数 | 说明 | 默认值 |
|---|---|---|
budget | 检索结果的总 token 预算上限 | 动态计算 |
预算打包算法
- 按分数排序:根据融合分数降序排列候选 chunks
- 贪心选择:依次添加 chunks 直到达到 budget 限制
- 上下文优先:优先保留高相关性、高信息密度的 chunks
资料来源:crates/mnem-core/src/retrieve/mod.rs
渲染与输出
检索结果最终需要渲染为适合 LLM 消费的格式。mnem 采用 YAML 风格的确定性渲染格式。
渲染格式规范
ntype: <ntype>
id: <uuid>
context: <context_sentence>
summary: <summary>
<prop_key>: <prop_value>
...
渲染规则:
ntype和id始终输出context(上下文句子)在summary之前输出,符合 Anthropic 2024 contextual-retrieval 最佳实践summary长度默认限制在 8192 字符内,可通过MNEM_RENDER_SUMMARY_CAP_CHARS环境变量覆盖- 标量属性(String、Integer、Float、Bool)按 BTreeMap 顺序输出
- 非标量属性(Link、Map、List、Bytes、Null)跳过,避免上下文膨胀
- 原始
content字节永不出现在渲染输出中
资料来源:crates/mnem-core/src/retrieve/mod.rs
配置参考
检索配置示例
[retrieve]
limit = 50 # 最大返回结果数
budget = 8192 # token 预算上限
vector_cap = 100 # 向量检索候选数
graph_expand = 5 # 图扩展邻居数
graph_decay = 0.5 # 图距离衰减系数
graph_depth = 2 # 图遍历深度
rerank_top_k = 10 # 重排序后保留数
hyde_max_tokens = 512 # HyDE 生成的最大 token 数
[rerank]
provider = "cohere" # rerank 提供商
model = "rerank-english-v2.0"
base_url = "https://api.cohere.com"
api_key_env = "COHERE_API_KEY"
[llm]
provider = "openai" # LLM 提供商(用于 HyDE 等功能)
model = "gpt-4"
环境变量
| 环境变量 | 说明 |
|---|---|
MNEM_RENDER_SUMMARY_CAP_CHARS | 渲染时 summary 的最大字符数,默认 8192 |
与其他模块的交互
数据依赖
graph TD
subgraph Ingest Pipeline
A[Markdown/PDF/Code] --> B[Chunker]
B --> C[Section/Chunk]
C --> D[Extractor]
D --> E[Index Builder]
end
subgraph Retrieval
E --> F[Vector Index]
E --> G[Sparse Index]
E --> H[Graph Index]
F --> I[Retriever]
G --> I
H --> I
I --> J[LLM Context]
end
subgraph Storage
E --> K[Blockstore]
I --> K
end检索系统依赖 Ingest Pipeline 构建的索引结构:
- 分块(Chunking):文档被分割为可检索的 chunks
- 索引构建:创建向量索引、稀疏索引和图索引
- 存储持久化:索引数据存储在 Blockstore 中
资料来源:crates/mnem-ingest/src/chunk.rs 资料来源:crates/mnem-core/src/index/mod.rs
Chunker 与检索的配合
不同类型的源文档采用不同的分块策略:
| 源类型 | Chunker 类型 | 参数 |
|---|---|---|
| Markdown | Paragraph | - |
| Text | SentenceRecursive | max_tokens: 256, overlap: 32 |
| SentenceRecursive | max_tokens: 512, overlap: 64 | |
| Conversation | Session | max_messages: 10 |
| Code | Structural | - |
资料来源:crates/mnem-ingest/src/chunk.rs
最佳实践
1. 混合检索配置建议
对于通用场景,推荐使用默认配置:
[retrieve]
limit = 50
budget = 8192
vector_cap = 100
2. 精确查询优化
当查询包含大量专有名词或代码标识符时,启用稀疏检索分支:
- 稀疏检索对精确词项匹配更有效
- 与向量检索组合使用可兼顾语义和精确性
3. 图感知查询
对于需要关系推理的查询,增加图扩展参数:
[retrieve]
graph_expand = 10
graph_depth = 3
graph_decay = 0.7
4. Token 预算规划
- 高频短查询:
budget可设置较低(如 4096) - 深度分析查询:
budget可提高至 16384 - 结合
rerank.top_k控制重排序计算量
总结
mnem 的混合检索系统通过融合向量检索、稀疏检索和图扩展三大策略,结合重排序和 token 预算管理,为 Agent 提供了灵活且高效的检索能力。系统设计遵循确定性原则,确保相同查询始终产生稳定的输出格式,便于 LLM 理解和处理。
检索系统的配置高度可定制,开发者可根据具体应用场景调整各分支的权重和参数,在检索精度、响应速度和 token 效率之间取得最佳平衡。
资料来源:[crates/mnem-core/src/lib.rs]()
失败模式与踩坑日记
保留 Doramagic 在发现、验证和编译中沉淀的项目专属风险,不把社区讨论只当作装饰信息。
可能影响授权、密钥配置或安全边界。
假设不成立时,用户拿不到承诺的能力。
可能增加新用户试用和生产接入成本。
新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
Pitfall Log / 踩坑日志
项目:Uranid/mnem
摘要:发现 8 个潜在踩坑项,其中 1 个为 high/blocking;最高优先级:安全/权限坑 - 来源证据:[feature] hermes support。
1. 安全/权限坑 · 来源证据:[feature] hermes support
- 严重度:high
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个安全/权限相关的待验证问题:[feature] hermes support
- 对用户的影响:可能影响授权、密钥配置或安全边界。
- 建议检查:来源问题仍为 open,Pack Agent 需要复核是否仍影响当前版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_c54919b2b8b340438a9e5aa17291b93a | https://github.com/Uranid/mnem/issues/27 | 来源类型 github_issue 暴露的待验证使用条件。
2. 能力坑 · 能力判断依赖假设
- 严重度:medium
- 证据强度:source_linked
- 发现:README/documentation is current enough for a first validation pass.
- 对用户的影响:假设不成立时,用户拿不到承诺的能力。
- 建议检查:将假设转成下游验证清单。
- 防护动作:假设必须转成验证项;没有验证结果前不能写成事实。
- 证据:capability.assumptions | github_repo:1221867246 | https://github.com/Uranid/mnem | README/documentation is current enough for a first validation pass.
3. 维护坑 · 来源证据:[bug] Broken docs links: SPEC.md, ROADMAP.md, and Architecture page
- 严重度:medium
- 证据强度:source_linked
- 发现:GitHub 社区证据显示该项目存在一个维护/版本相关的待验证问题:[bug] Broken docs links: SPEC.md, ROADMAP.md, and Architecture page
- 对用户的影响:可能增加新用户试用和生产接入成本。
- 建议检查:来源显示可能已有修复、规避或版本变化,说明书中必须标注适用版本。
- 防护动作:不得脱离来源链接放大为确定性结论;需要标注适用版本和复核状态。
- 证据:community_evidence:github | cevd_5c74e7a10f774af6b0460b5da009d1b4 | https://github.com/Uranid/mnem/issues/23 | 来源讨论提到 windows 相关条件,需在安装/试用前复核。
4. 维护坑 · 维护活跃度未知
- 严重度:medium
- 证据强度:source_linked
- 发现:未记录 last_activity_observed。
- 对用户的影响:新项目、停更项目和活跃项目会被混在一起,推荐信任度下降。
- 建议检查:补 GitHub 最近 commit、release、issue/PR 响应信号。
- 防护动作:维护活跃度未知时,推荐强度不能标为高信任。
- 证据:evidence.maintainer_signals | github_repo:1221867246 | https://github.com/Uranid/mnem | last_activity_observed missing
5. 安全/权限坑 · 下游验证发现风险项
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:下游已经要求复核,不能在页面中弱化。
- 建议检查:进入安全/权限治理复核队列。
- 防护动作:下游风险存在时必须保持 review/recommendation 降级。
- 证据:downstream_validation.risk_items | github_repo:1221867246 | https://github.com/Uranid/mnem | no_demo; severity=medium
6. 安全/权限坑 · 存在评分风险
- 严重度:medium
- 证据强度:source_linked
- 发现:no_demo
- 对用户的影响:风险会影响是否适合普通用户安装。
- 建议检查:把风险写入边界卡,并确认是否需要人工复核。
- 防护动作:评分风险必须进入边界卡,不能只作为内部分数。
- 证据:risks.scoring_risks | github_repo:1221867246 | https://github.com/Uranid/mnem | no_demo; severity=medium
7. 维护坑 · issue/PR 响应质量未知
- 严重度:low
- 证据强度:source_linked
- 发现:issue_or_pr_quality=unknown。
- 对用户的影响:用户无法判断遇到问题后是否有人维护。
- 建议检查:抽样最近 issue/PR,判断是否长期无人处理。
- 防护动作:issue/PR 响应未知时,必须提示维护风险。
- 证据:evidence.maintainer_signals | github_repo:1221867246 | https://github.com/Uranid/mnem | issue_or_pr_quality=unknown
8. 维护坑 · 发布节奏不明确
- 严重度:low
- 证据强度:source_linked
- 发现:release_recency=unknown。
- 对用户的影响:安装命令和文档可能落后于代码,用户踩坑概率升高。
- 建议检查:确认最近 release/tag 和 README 安装命令是否一致。
- 防护动作:发布节奏未知或过期时,安装说明必须标注可能漂移。
- 证据:evidence.maintainer_signals | github_repo:1221867246 | https://github.com/Uranid/mnem | release_recency=unknown
来源:Doramagic 发现、验证与编译记录