Files
mengyanote-rag/README.md
shumengya c410d69970 feat: 初始提交 mengya-rag 知识库项目
轻量级 Obsidian Markdown RAG 系统,包含:
- Markdown 结构感知分块(标题层级 + 代码块/表格整体保留)
- FastEmbed + BAAI/bge-small-zh-v1.5 本地向量化
- SQLite + sqlite-vec 向量库(无需外部服务)
- BM25 + 向量混合检索,RRF 融合
- 盘点模式(有哪些/列表/目录类问题)
- DeepSeek API 生成回答
- mengya-rag CLI 工具(ask/search/context/read/sync/index/status)
- docs/RAG优化方案.md 待实施优化计划

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:12:06 +08:00

347 lines
8.8 KiB
Markdown
Raw Permalink 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.
# 萌芽 RAG 知识库
轻量版 Obsidian 笔记 RAG 项目,基于 LlamaIndex 和 DeepSeek API。
检索使用 Markdown 结构感知分块 + 本地小 embedding + BM25 的混合检索,适合个人 Obsidian 笔记这种规模。
## 项目结构
```text
mengya-rag/
├── data/
│ ├── notes/ # 从 bigmengya 同步来的 Obsidian Markdown 笔记(不入库)
│ └── index/bm25/ # 本地索引目录(不入库)
│ └── rag.sqlite3 # SQLite + sqlite-vec 本地向量库
├── docs/
│ └── RAG优化方案.md # 下一步优化计划
├── scripts/
│ ├── sync_notes.sh # 同步 bigmengya 笔记
│ ├── sync_notes.py
│ ├── build_index.sh # 构建索引
│ ├── build_index.py
│ ├── ask.sh # 命令行问答
│ └── ask.py
├── src/mengya_rag/
│ ├── config.py # 配置读取
│ ├── markdown_chunker.py # Markdown 结构感知分块
│ ├── embeddings.py # Embedding 生成FastEmbed / SentenceTransformers
│ ├── vector_store.py # SQLite + sqlite-vec 向量存储
│ ├── indexing.py # 建索引(分块 + 向量写入)
│ ├── retrieval.py # 混合检索、RRF 融合、盘点模式
│ ├── qa.py # DeepSeek 生成回答
│ └── cli.py # CLI 命令入口
├── .env.example
├── pyproject.toml
└── README.md
```
## 技术栈
- RAG 基础框架LlamaIndex
- 本地 embedding`BAAI/bge-small-zh-v1.5`
- embedding 运行FastEmbed + ONNX
- 大模型回答DeepSeek API
- 当前推荐模型:`deepseek-v4-flash`
- 检索方式BM25 + 向量检索
- 融合方式RRF 类融合 + 原始相似度过滤 + 同文件去重
- 数据库:无
- 向量库SQLite + sqlite-vec
- 服务端:无,当前是命令行工具
## 初始化
```bash
cd /shumengya/project/agent/mengya-rag
cp .env.example .env
```
编辑 `.env`,填入:
```env
DEEPSEEK_API_KEY=你的 DeepSeek API Key
```
安装依赖:
```bash
uv sync
```
## 同步笔记
笔记源路径:
```text
bigmengya:/shumengya/docker/mengyanote-backend/data/mengyanote/
```
同步到本地:
```bash
./scripts/sync_notes.sh
# 或
uv run python scripts/sync_notes.py
```
## 构建索引
```bash
./scripts/build_index.sh
# 或
uv run python scripts/build_index.py
```
## 提问
```bash
./scripts/ask.sh "萌芽笔记是什么"
# 或
uv run python scripts/ask.py "萌芽笔记是什么"
```
也可以直接使用安装后的命令:
```bash
uv run mengya-ask "Docker 常用命令有哪些"
```
## Agent 调用推荐
统一入口是 `mengya-rag`。给大模型 Agent 或脚本调用时,推荐固定使用 `--json`,输出是一行 JSON便于解析。
```bash
# 查看配置、索引和笔记数量
uv run mengya-rag status --json
# 只检索,不调用大模型,返回命中文档和内容
uv run mengya-rag search "Docker 常用命令" -k 5 --json
# 返回可直接注入上游大模型的 context 字段
uv run mengya-rag context "WireGuard 怎么配置" -k 4 --json
# 按 search/context 返回的 source 读取完整笔记
uv run mengya-rag read "Docker/Docker常用命令总结.md" --json
# 本项目自己调用 DeepSeek 生成回答
uv run mengya-rag ask "Docker 常用命令有哪些" -k 4 --json
# 同步笔记并重建索引
uv run mengya-rag reindex --json
```
Agent 优先使用这几个命令:
- `status --json`:检查索引是否存在、笔记数、节点数、模型配置。
- `search --json`:拿结构化检索结果,包含 `results[].source``title``heading_path``score``content`
- `context --json`:拿拼好的 `context` 字符串,适合直接放进其他大模型提示词。
- `read --json`:按 `source` 读取完整 Markdown 原文,适合检索命中后补充上下文。
- `ask --json`:让本项目完成 RAG + DeepSeek 回答,返回 `answer``sources`
- `reindex --json`:一条命令完成同步笔记和重建索引。
常用参数:
```bash
--mode auto|hybrid|inventory # auto 默认hybrid 普通语义检索inventory 盘点/目录类检索
-k, --top-k 数字 # 控制返回条数
--env-file /path/.env # 指定配置文件
--notes-dir /path/notes # 覆盖笔记目录
--index-dir /path/index # 覆盖索引目录
```
## 查询模式
项目会自动区分两类问题:
- 普通问答例如“WireGuard 命令怎么用”,走 Markdown 分块 + 向量/BM25 混合检索。
- 盘点列表:例如“查一下我目前博客文章有哪些”“查一下安卓 Gradle 相关笔记”,走文件级目录检索,优先列出相关 Markdown 文件和内容预览。
## 技术原理
### Markdown 分块
项目不使用固定字符数暴力切割,而是使用结构感知分块:
```text
Markdown 文件
解析 frontmatter
识别 H1-H6 标题
按标题层级组织分块
代码块和表格整体保留
长块使用滑动窗口兜底
```
每个 chunk 前会附加上下文前缀:
```text
文件: AI/控制台Agent工具安装教程.md
标题: 控制台Agent工具安装教程
标题路径: H1 > H2 > H3
```
每个 chunk 会保存这些元数据:
```text
source_file
rel_path
file_name
folder_path
title
heading_path
h1
h2
h3
h4
h5
h6
chunk_index
tags
created_at
```
### 索引构建
执行:
```bash
./scripts/build_index.sh
```
构建流程:
```text
Markdown 文件
结构感知分块 → TextNode
bge-small-zh-v1.5 生成 embedding → 归一化
写入 SQLite + sqlite-vecrag.sqlite3
```
当前索引规模参考:
```text
笔记文件数: 495
分块数: ~1800
rag.sqlite3: ~10MBchunk 表 + sqlite-vec 向量表)
```
### 普通问答检索
普通问答适合“怎么做”“是什么”“原理是什么”这类问题。
```text
用户问题
query 清洗
BM25 关键词检索
本地向量语义检索
RRF 融合
同文件去重
相关性过滤
top-k chunk 注入 DeepSeek
生成回答和来源
```
### 盘点列表检索
盘点列表适合“有哪些”“查一下”“相关笔记”“目录”“清单”这类问题。
```text
用户问题
识别为盘点类问题
构建文件级摘要节点
文件级 BM25 检索
关键词覆盖过滤
输出 Markdown 文件列表、路径和内容预览
```
## 重新更新知识库
笔记变更后按顺序执行:
```bash
./scripts/sync_notes.sh
./scripts/build_index.sh
```
## 设计取舍
- Markdown 笔记按结构感知分块:识别 frontmatter、H1-H6、代码块、表格。
- H1-H6 标题会写入 `h1``h6` 元数据,同时保存 `heading_path` 面包屑。
- 代码块和表格整体保留,不跨块切割;长块再用滑动窗口兜底。
- 每个 chunk 都带 `source_file``heading_path``chunk_index``tags``created_at`
- 本地 embedding 使用 `BAAI/bge-small-zh-v1.5`,通过 FastEmbed/ONNX 运行,不依赖 PyTorch 和 GPU。
- 检索采用 BM25 + 向量召回,再用 RRF 融合和同文件去重,避免只靠关键词或只靠语义。
- 使用 DeepSeek 只负责生成回答:减少 API 调用,只在提问时调用大模型。
- 本地向量库使用 SQLite + sqlite-vec无需 Qdrant、Milvus、Postgres 等额外服务。
## 当前效果
目前比较适合处理:
- 某类笔记有哪些
- 某个教程在哪里
- 根据笔记总结某个主题
- 列出博客文章
- 列出 Agent 安装教程
- 查 Gradle 构建相关内容
示例:
```bash
./scripts/ask.sh '查一下我目前博客文章有哪些'
./scripts/ask.sh '查一下我的安卓gradle构建相关笔记'
./scripts/ask.sh '查一下我的Agent安装教程'
```
## 当前限制
- 当前是 SQLite + sqlite-vec 单机向量库,数据量很大后可以换 Qdrant 或 Chroma。
- 没有 reranker复杂问题可能还会有弱相关内容混入。
- embedding 模型是轻量中文模型,效果够用但不是最强。
- `build_index` 每次是全量重建,还没有增量索引。
- frontmatter 解析是轻量手写版,不是完整 YAML 解析。
- 当前是 CLI 工具,没有 Web 页面和 API 服务。
## 后续优化方向
详见 [docs/RAG优化方案.md](docs/RAG优化方案.md),主要方向:
- 中文分词换 jiebaBM25 召回率最大瓶颈)
- 放宽文件去重(当前每文件只取 1 chunk长文章不友好
- QA Prompt 加强引用约束,降低幻觉
- 注入上下文加 token 预算(节省费用 + 提升精度)
- 可选:换 `BAAI/bge-m3` 嵌入模型(中英混合效果更好)
- 增量索引,只重建变更文件
- 加 reranker例如 `bge-reranker-v2-m3`
- 数据量变大后接 Qdrant
## 调参
`.env` 中可调整:
```env
TOP_K=6
EMBED_MODEL=BAAI/bge-small-zh-v1.5
EMBED_BATCH_SIZE=32
VECTOR_WEIGHT=0.65
BM25_WEIGHT=0.35
```