Skip to content

[RFC] 记忆系统重构:LanceDB → tantivy-jieba 一体化全文索引 #158

@Bahtya

Description

@Bahtya

背景

基于对 Kestrel Agent (KA) 和 Hermes Agent 两套记忆系统的深入对比分析,提出将 KA 的 kestrel-memory crate 从 LanceDB 向量数据库全面转向 tantivy + tantivy-jieba 一体化全文倒排索引方案。

前序 issue #157 已关闭 — 原方案将 BM25 和 jieba 分开考虑,本 issue 改为采用 tantivy(内置 BM25 scorer)+ tantivy-jieba(内置 jieba-rs 分词)的一体式方案。

两大记忆系统对比分析

Kestrel Agent(当前)

组件 实现方式
L1 HotStore LRU 内存缓存 + JSONL 文件持久化
L2 WarmStore LanceDB 向量数据库 + Arrow 列式存储
搜索 KNN 余弦相似度(embedding)+ 词边界文本匹配
嵌入 HashEmbedding(随机投影哈希占位符,256-dim→1536-dim 不匹配)
分类 10 个 MemoryCategory(UserProfile, AgentNote, Fact, Preference, Environment, ProjectConvention, ToolDiscovery, ErrorLesson, WorkflowPattern, Critical)
安全 写入前安全扫描(prompt injection、XSS 检测)

已知的 LanceDB 问题:

  1. CPU 尖峰(Bug: CPU spike to ~175%% when processing Telegram messages due to LanceDB query blocking tokio workers #139):LanceDB 查询阻塞 tokio worker,175% CPU
  2. 零向量(fix(memory): warm store vectors are all zeros — semantic search broken #155):Learning insights 跳过嵌入生成,语义搜索失效
  3. 维度不匹配:HashEmbedding 默认 256-dim vs WarmStore 期望 1536-dim
  4. 重依赖:arrow-array、arrow-schema、lancedb、futures ≈ +50MB 二进制体积
  5. 谓词注入([Memory][P2] LanceDB predicate injection risk in warm_store.rs #127):字符串拼接 SQL-like 谓词需手动 ID 验证
  6. 并发写入竞态([Memory][P2] WarmStore store() race condition under concurrent writes #128):需 Mutex<()> 序列化

Hermes Agent(参考)

组件 实现方式
内置记忆 纯文件存储(MEMORY.md + USER.md),§分隔
外部记忆 MemoryProvider 插件架构(Honcho, Mem0 等)
搜索 SQLite FTS5 全文搜索 + BM25 排名
分类 双目标:user(用户画像)+ memory(Agent 笔记)
会话搜索 SQLite 跨会话历史检索
安全 内容注入/泄露检测 + Unicode 过滤
上下文管理 字符预算控制 + 上下文围栏(XML 标签隔离)

关键差异洞察

  1. Hermes 的双目标分类启发 KA:KA 的 10 个 MemoryCategory 过于细粒度,可归并为两大类(用户侧 vs Agent 侧),内部保留子分类用于过滤
  2. Hermes 证明纯文本搜索足够:不需要 embedding,不需要向量数据库,BM25 + CJK 分词即可满足记忆检索需求
  3. Hermes 的插件架构值得借鉴MemoryProvider 抽象允许热插拔后端
  4. Hermes 的上下文围栏<memory-context> XML 标签隔离记忆注入,防止模型混淆

提案:tantivy + tantivy-jieba

为什么选 tantivy?

  • 内置 BM25 scorer:无需自实现 BM25 算法
  • tantivy-jieba 适配器(v0.19.0):一行注册 jieba-rs 中文分词
  • 纯 Rust:无 protoc 依赖,编译快,二进制小
  • 成熟的持久化:Tantivy 自带磁盘索引格式,无需额外数据库
  • Block-Max WAND 加速:高效 Top-K 查询

架构设计

┌─────────────────────────────────────┐
│         MemoryStore trait           │
│  store / recall / search / delete   │
└──────────┬──────────────────────────┘
           │
    ┌──────┴──────┐
    │             │
┌───▼───┐   ┌────▼────────────────────┐
│HotStore│   │TantivyStore (新 L2)      │
│L1 LRU │   │tantivy index             │
│JSONL  │   │+ jieba CJK tokenizer     │
│       │   │+ BM25 scoring            │
│       │   │+ 文件持久化               │
└───────┘   └──────────────────────────┘
    │             │
    └──────┬──────┘
    ┌──────▼──────┐
    │TieredStore  │
    │write-through│
    │read-fallback│
    └─────────────┘

接口变更

  1. MemoryStore trait 不变:search/recall/store/delete/len/clear 保持一致
  2. 移除 embedding 相关接口
    • MemoryEntry.embedding 字段移除(或标记为 #[deprecated]
    • MemoryQuery.embedding 移除
    • EmbeddingGenerator trait 和 HashEmbedding 移除
    • MemoryConfig.embedding_dim 移除
  3. 新增 TantivyStore:替代 WarmStore,实现 MemoryStore trait
  4. MemoryError::LanceDbMemoryError::SearchEngine:泛化错误类型

依赖变更

# Cargo.toml (kestrel-memory)
- lancedb = "0.27"
- arrow-array = "57"
- arrow-schema = "57"
- futures = { workspace = true }
+ tantivy = "0.26"
+ tantivy-jieba = "0.19"
+ jieba-rs = "0.9"

实现计划

步骤 内容 影响文件
1 添加 tantivy/tantivy-jieba 依赖 Cargo.toml, workspace Cargo.toml
2 创建 TantivyStore 实现 MemoryStore 新文件 tantivy_store.rs
3 实现统一 tokenizer(Latin 词边界 + jieba CJK) tantivy_store.rs
4 实现 BM25 索引构建和搜索 tantivy_store.rs
5 更新 TieredMemoryStore:L2 从 WarmStore 切换到 TantivyStore tiered.rs
6 移除 WarmStore 和 LanceDB 相关代码 删除 warm_store.rs
7 移除 embedding 相关代码 删除 embedding.rs,清理 types.rs
8 更新 MemoryConfig:移除 embedding_dim,更新路径 config.rs
9 更新 MemoryError:LanceDb → SearchEngine error.rs
10 迁移所有 WarmStore 测试到 TantivyStore tantivy_store.rs tests
11 更新上层 crate 依赖(kestrel-tools, kestrel-learning) 上层 Cargo.toml
12 清理 workspace Cargo.toml:移除 lancedb/arrow workspace Cargo.toml

测试策略

  • 所有现有 WarmStore 测试迁移为 TantivyStore 测试
  • 新增中文分词搜索测试
  • 新增混合中英文搜索测试
  • 持久化跨重启测试
  • 并发写入安全测试
  • TieredMemoryStore 集成测试更新

风险与缓解

风险 缓解措施
tantivy-jieba 与 tantivy 版本兼容性 锁定 tantivy 0.26 + tantivy-jieba 0.19
上层 crate 依赖 embedding 类型 渐进式移除,先标记 deprecated
索引文件格式迁移 重新索引即可(数据量小)
搜索质量回归 BM25 对短文本已有成熟调优经验

署名: [CC-Main]

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions