Files
NotesAgentic/docs/Knowledge与Retrieval-Core开发说明.md
T

231 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Knowledge Core 与 Retrieval Core 开发说明
> 本文档用于团队开发和模块联调,记录 Knowledge Core / Retrieval Core 已经落地的
> 模块边界、数据模型、接口与使用方式,对应分工表中的杨星萱。
## 当前实现
当前已经建立第一条可运行的检索链路:
```text
Markdown Vault
→ Markdown ParserBlock 切分 / heading_path / offset
→ SQLitenotes / blocks / FTS5+ sqlite-vecvec_blocks
→ FTS5(BM25) + Vector(cosine) 双路召回
→ RRF 融合 → Reranker 精排 → Metadata Filter → 分页
→ Citation + Snippet
```
对应代码:
```text
backend/app/
├── constants.py EMBEDDING_DIM = 128
├── config.py Settingsdata_dir / db_path / vault_path
├── textutils.py 分词、FTS 查询串、摘要片段
├── repository.py notes / blocks / blocks_fts 读写(领域记录层)
├── database/
│ ├── db.py SQLite 连接 + 事务 + 加载 sqlite-vec
│ └── migrations.py 轻量迁移(建 notes / blocks / blocks_fts / vec_blocks
├── knowledge/
│ └── parser.py Markdown → ParsedNote / NoteBlock
├── retrieval/
│ ├── embedding.py EmbeddingProvider 接口 + HashEmbeddingProvider
│ ├── reranker.py RerankerProvider 接口 + LexicalReranker
│ ├── vectorstore.py VectorStore 接口 + SqliteVecStore
│ ├── hybrid.py RRF 融合、分数归一化
│ └── engine.py RetrievalEngine(编排检索全流程)
└── services/
├── note_service.py Note CRUD + 索引编排
└── index_service.py 全量重建、索引状态、任务查询
```
Router`backend/app/routes.py`)只负责 HTTP 与错误转换;`/api/notes``/api/search`
`/api/index/*` 已接入上述服务,其余端点仍由对应模块负责。
## 模块边界
本模块负责(杨星萱):
- Markdown Vault、Note / NoteBlock 数据模型与解析;
- SQLite 元数据、FTS5 全文检索、sqlite-vec 向量检索;
- Embedding、Hybrid RAG、RRF、Reranker、Metadata Filter
- Citation 与笔记定位;
- 检索测试数据。
以下内容保持接口,不在本模块实现:
- Agent Runtime、Tool Registry、Permission:由 Agent Core 提供;
- Provider Adapter、多模型协议:由 Model Core 提供;
- Skill / Plugin 生命周期:由 Extension Core 提供;
- 文件系统与 API Key 明文读取:由 Rust Host 提供。
## 数据模型与稳定 ID
- 笔记元数据存 `notes` 表;正文切成 Block 存 `blocks` 表;`blocks_fts` 是 FTS5 虚拟表;
`vec_blocks` 是 sqlite-vec 的 `vec0` 虚拟表。
- 稳定 ID(内容/路径不变则 ID 不变):
```text
note_id = "note_" + sha256(rel_path)[:16]
block_id = "blk_" + sha256(note_id | heading_path | content)[:16]
citation_id = "cit_" + block_id
```
> 注意:MVP 阶段 `note_id` 由相对路径派生,移动文件会改变 ID;后续 `move` 流程会保留原 ID。
每个 Block 记录 `heading_path`(章节路径)、`start_offset` / `end_offset`(相对原文的
字符偏移,用于前端跳转高亮)、`content_hash``token_count`
## 分层依赖
按团队约定,依赖方向为:
```text
RouterHTTP/错误转换)
→ Servicenote_service / index_service 编排)
→ Repositorynotes/blocks/FTS5 访问) ← 只在此层访问 SQLite
→ Retrieval Infraembedding/reranker/vectorstore ← vec0 只在 vectorstore 层访问
```
- 检索 Executor 调用 `RetrievalEngine`,不直接拼接 FTS5 或 sqlite-vec SQL
- 向量实现只在 `retrieval/vectorstore.py`DB 访问只在 `repository.py`
## 分词与中文检索
FTS5 默认 `unicode61` 不切分中文,因此统一预分词:ASCII 单词 + CJK 单字 + CJK 相邻双字。
写入与查询走同一套拆分(`textutils.segment` / `textutils.match_query`),实现中文子串/词级召回。
## Embedding / Reranker(轻量实现,接口可替换)
当前是「统一接口 + 轻量实现」,后续接入真实模型时替换实例即可,不改变上层调用:
- `EmbeddingProvider``embed_documents` / `embed_query`)→ `HashEmbeddingProvider`
确定性特征哈希 + L2 归一化,`dim = 128``model_id = "hash-v1"`
- `RerankerProvider``rerank`)→ `LexicalReranker`:分数归一化 + 词重叠加权,
`model_id = "lexical-v1"`
- `VectorStore``upsert` / `delete` / `search` / `clear`)→ `SqliteVecStore`
sqlite-vec `vec0`,相似度取余弦 `score = 1 - distance² / 2`
## 检索流程
`RetrievalEngine.search(request)`
1.`mode` 收集候选:`fts` / `vector` 各取 Top `CANDIDATE_POOL = 50`
2. `hybrid` 用 RRF`k = 60`)融合两路排序;
3. Metadata Filter`folders` / `note_ids` / `tags` / 时间范围;
4. `hybrid` 再经 Reranker 精排,其余模式按分数排序;
5. 分数归一化 → 分页 → 组装 `Citation``Snippet`
模块级单例 `engine = RetrievalEngine(HashEmbeddingProvider(), LexicalReranker(), SqliteVecStore())`
检索入口统一为 `engine.search(request)`
## 接口清单
### Note
```text
GET /api/notes?limit=&offset=&folder=&tag=
POST /api/notes
GET /api/notes/{note_id}
PATCH /api/notes/{note_id}
DELETE /api/notes/{note_id}
POST /api/notes/{note_id}/move 501,待定语义)
```
创建笔记:
```json
POST /api/notes
{"title": "Python 基础", "markdown": "# 变量\n\nPython 是动态类型语言。", "folder": "编程", "tags": ["python"]}
```
### Search
```text
POST /api/search
```
```json
{"query": "向量数据库", "mode": "hybrid", "limit": 10}
```
`mode``fts` / `vector` / `hybrid`;可选 `folders` / `note_ids` / `tags` / 时间范围 /
`include_snippet`。结果项含 `score``snippet``citation``citation_id``file_path`
`heading_path``start_offset``end_offset`)。
### Index
```text
GET /api/index/status
POST /api/index/rebuild
GET /api/index/jobs/{job_id}
```
重建(MVP 同步执行,直接返回 `completed`):
```json
POST /api/index/rebuild
{"scope": "all"}
```
## 检索测试数据
样例 Vault 位于 `backend/data/vault/`,覆盖中英文、多级标题、frontmatter、子目录与不同 tags
```text
项目说明.md
编程/Python 基础语法.md
编程/向量数据库与相似度检索.md
产品/RAG 检索增强与引用定位.md
日记/2026-08-27 周会.md
```
示例查询:
```text
POST /api/search {"query": "向量数据库", "mode": "hybrid"} → 命中《向量数据库与相似度检索》
POST /api/search {"query": "检索", "mode": "fts", "folders": ["产品"]} → 只返回 产品/ 下笔记
POST /api/search {"query": "向量", "mode": "hybrid", "tags": ["向量"]} → 按 tag 过滤
```
## 测试
```powershell
cd backend
uv run pytest -q
```
当前 26 个用例通过(单元 + 端到端)。测试通过 `tests/conftest.py` 的 autouse fixture 把
数据目录/DB/Vault 重定向到临时目录,不读写真实 `backend/data`,任何本机状态下结果确定。
## 配置
```text
APP_DATA_DIR 默认 backend/data
APP_DB_PATH 默认 backend/data/app.db
APP_VAULT_PATH 默认 backend/data/vault
```
运行期生成的 `backend/data/*.db*` 已被 `.gitignore` 忽略,vault 下的 Markdown 测试数据会提交。
## 接入约定(Agent / 其他模块)
Agent 通过注册 Tool 接入本模块,不让 Agent Runtime 直接依赖具体实现:
```text
notes.search / notes.read / notes.create / notes.update / notes.list / notes.move
rag.search
```
- 写操作 Executor 调用 `note_service`,不直接访问 SQLite
- 检索 Executor 调用 `engine.search(request)`,不直接拼接 FTS5 或 sqlite-vec SQL。
## 当前限制与下一步
- `move` 接口未实现(需确认移动后 `note_id` 是否保持稳定)。
- Embedding / Reranker 为轻量实现,后续替换为真实模型(接口不变)。
- 小语料下 hybrid 检索召回偏宽(向量 Top-K 覆盖全部 block),可加相关性阈值收紧。
- 重建为同步 + 全量,后续接入增量索引与异步任务队列。
- 检索 Benchmark 待建立。