把个人笔记文件夹变成 Agent 可查询的 RAG:本地优先方案
把个人笔记变成 Agent 可查询的知识库
这份方案回答一个具体问题:如何把本地笔记文件夹变成 RAG,让 Hanako/OpenHanako agent 能可靠查询信息。
Hanako Plugin + Local RAG Core + Optional MCP Bridge。检索层默认使用全文检索/BM25 + 向量语义检索 + rerank + 相邻 chunk 扩展,并且所有结果必须带来源引用。
为什么不是“直接上向量库”?
个人笔记不是普通网页语料。它里面有大量精确信号:文件名、课程名、项目名、人名、日期、标签、org heading、roam links。纯向量检索擅长语义相似,但对专名、缩写、路径和精确关键词并不稳定。
Haystack 的 hybrid retrieval 教程明确说明:BM25 这类 keyword-based retrieval 在特定领域常常优于 dense retrieval,而混合检索能同时利用关键词和语义表示的优势。Qdrant 的 hybrid search 教程也采用 dense embeddings、sparse BM25 和 late interaction reranking 的组合。
所以个人笔记 RAG 的默认检索流应该是:
query → FTS/BM25 recall → vector semantic recall → Reciprocal Rank Fusion → reranker → neighbor chunk expansion → cited context pack
推荐总体架构
用户笔记目录,例如 ~/Org/roam、Obsidian vault、课程笔记
↓ parser + watcher
RAG Core Service / Library
├─ parser: org / md / txt / pdf / docx
├─ metadata: path / title / tags / heading / links / mtime / hash
├─ index: SQLite FTS5 + sqlite-vec 或 Qdrant/Chroma
├─ retrieval: FTS + vector + RRF + rerank + neighbor expansion
├─ policy: roots whitelist / deny globs / rag_exclude
└─ API: local tool interface
├─ Hanako Plugin Tools
└─ optional MCP Bridge
Hanako plugin 应该是主入口,因为它能承载设置页、目录授权、索引状态、PathGuard、工具权限和桌面 UI。MCP Bridge 适合做跨客户端兼容层,让 Claude、Cursor 或其他 MCP host 查询同一个本地知识库。
笔记应该如何解析
不要把所有文件粗暴切成固定长度。笔记的结构本身就是重要信号。
| 格式 | 应提取的信息 | 作用 |
|---|---|---|
| Org-mode | title、ID、tags、properties、heading path、links、aliases | 保留 org-roam 语义和反链关系 |
| Markdown | frontmatter、heading、wikilinks、tags、代码块 | 适配 Obsidian/普通文档库 |
| PDF/DOCX | 转 Markdown、标题、段落、表格、页码 | 统一进入文本 chunk 管线 |
| 附件图片 | OCR 或 vision caption | 二期支持,不建议 MVP 先做 |
理想 chunk 是 heading/段落级,而不是固定字符窗口。每个 chunk 应保留前后邻居,避免 agent 拿到断裂上下文。
Agent 工具设计
Agent 不应该知道数据库细节,只调用稳定工具。
| 工具 | 作用 | 权限 | 说明 |
|---|---|---|---|
kb_search_notes | Hybrid 搜索笔记 chunk | 只读 | 返回 score、path、heading、摘要、chunk_id |
kb_read_note | 读取完整笔记或 heading | 只读 | path 必须在白名单内 |
kb_get_neighbors | 读取 chunk 前后文 | 只读 | 解决 chunk 截断问题 |
kb_build_context | 构造带引用上下文包 | 只读 | 搜索、邻居、反链、去重后组合 |
kb_index_status | 查看索引状态 | 只读 | 返回 last_indexed、pending、errors |
kb_request_reindex | 请求刷新索引 | 需确认 | 非只读,必须用户确认 |
MVP 不开放 write/delete/move。写笔记属于另一类高风险能力,必须有 diff 预览、备份和撤销。
技术路线对比
| 方案 | 优势 | 劣势 | 适用条件 |
|---|---|---|---|
| 纯向量 RAG | 语义召回好,教程多 | 专名和路径弱,调试难 | 非结构化长文本语义问答 |
| FTS/BM25 only | 轻量、可解释、精确词强 | 抽象语义弱 | MVP、文件名/标题/术语查询 |
| Hybrid + rerank | 质量上限高 | 工程复杂,需要调参 | 正式个人知识库 RAG |
| LlamaIndex | 文件夹读取和增量 API 成熟 | 长期产品化可控性一般 | 快速原型 |
| Haystack | Pipeline 和 hybrid retrieval 清晰 | 对小型本地库偏重 | 实验和工程 pipeline |
| SQLite FTS5 + sqlite-vec | 单文件、本地、易嵌入 | 需要自建 chunk/rerank/中文分词 | Hanako 内置 RAG Core |
| Qdrant | Hybrid、多向量、过滤强 | 需要独立服务或部署 | 大规模和复杂检索 |
| 现成 MCP RAG | 最快接入 MCP host | 权限/UI/工具面不一定适合 Hanako | PoC 或参考实现 |
安全与权限
个人知识库是高隐私资产,权限设计必须前置。
- 用户必须显式选择 roots。
- 所有 path 入参 canonicalize,解析 symlink 后仍必须在 roots 内。
- 默认 deny:
.git、node_modules、.env、private、财务、diary等。 - 支持 org property 或 frontmatter 排除:
RAG_EXCLUDE: t/rag: false。 - 搜索结果不能泄露被排除笔记的标题。
- 日志不记录完整 query 和正文,调试日志可一键清除。
- 云 embedding 必须明确提示笔记内容会发送给第三方,默认使用本地 embedding。
MCP roots 规范明确 roots 是服务器可访问的文件系统边界,并要求防 path traversal 和访问控制。MCP 安全文档也强调 scope minimization。
MVP 路线
第 0 周:离线分析
扫描目标笔记目录,统计格式、数量、总字数、最大文件、org/markdown 比例。做 parser spike:org heading、tags、links、property drawers。
第 1 阶段:只读本地搜索
建立 SQLite documents/chunks/FTS5,实现 kb_search_notes 和 kb_read_note。先不用向量,验证 agent 查询体验。
第 2 阶段:语义检索
加入 sqlite-vec 或 Chroma/Qdrant,增加 embedding cache、RRF hybrid fusion 和 kb_get_neighbors。
第 3 阶段:上下文构建
加入 reranker 和 kb_build_context,返回文件路径、标题、heading、chunk_id、修改时间等引用。
第 4 阶段:Hanako 插件化
设置页选择知识库目录和 deny globs,注册 agent tools,展示索引状态 widget,最后增加 MCP bridge。
补充:Hanako 插件落地接口
进一步读取 OpenHanako PLUGINS.md 后,可以把落地方案从“架构建议”推进到“插件开发入口”。OpenHanako 插件可以贡献 tools/、skills/、commands/、routes/、生命周期 index.js、配置 schema、page/widget,并分为 restricted 与 full-access 两级权限。
推荐插件形态
- 第一阶段:Tool-only restricted 插件,只暴露只读工具,降低权限面。
- 第二阶段:Full-access runtime 插件,加入后台 watcher、HTTP route、状态 widget 和动态工具注册。
notes-rag-plugin/
├── manifest.json
├── tools/
│ ├── search_notes.js
│ ├── read_note.js
│ ├── get_neighbors.js
│ ├── build_context.js
│ └── index_status.js
├── routes/ # 二期 full-access
│ └── status.js
├── index.js # 二期 full-access: watcher / background indexer
└── skills/
└── notes-rag/
└── SKILL.md
工具接口草案
最核心的工具是 search_notes。它应该接收自然语言 query、top_k 和 metadata filters,返回结构化结果,而不是只返回一段文本。
{
"query": "...",
"results": [
{
"chunk_id": "...",
"doc_id": "...",
"title": "...",
"path": "...",
"heading": "...",
"score": 0.87,
"snippet": "...",
"mtime": "2026-05-18T00:00:00+08:00"
}
]
}
配置 schema
插件配置应至少包含 roots、denyGlobs 和 embeddingProvider。roots 决定授权目录,denyGlobs 负责敏感路径排除,embeddingProvider 默认 local,避免默认把笔记发给第三方。
开发循环
OpenHanako 文档建议 Agent 开发插件时走 dev loop:源码放在工作区或 ${HANA_HOME}/plugin-dev-sources/,再通过 plugin_dev_install、plugin_dev_reload、plugin_dev_invoke_tool、plugin_dev_diagnostics 做安装、热重载、smoke test 和诊断。这让 Notes RAG 插件可以先从只读 search_notes 做起,再逐步加入 indexer、status 和 widget。
最终建议
推荐方案:先做 Hanako 原生只读 RAG 插件,底层使用 SQLite FTS5 + sqlite-vec 的轻量本地 RAG Core;后续再加 Qdrant 选项和 MCP Bridge。
- 先选一个目标目录,例如
~/Org/roam。 - 写只读 scanner,输出文件统计和解析成功率。
- 先用 SQLite FTS5 做可查版本。
- 再加 embedding 和 reranker。
- 所有答案必须带引用:文件路径、heading、chunk_id、mtime。