# 萌芽 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-vec(rag.sqlite3) ``` 当前索引规模参考: ```text 笔记文件数: 495 分块数: ~1800 rag.sqlite3: ~10MB(chunk 表 + 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),主要方向: - 中文分词换 jieba(BM25 召回率最大瓶颈) - 放宽文件去重(当前每文件只取 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 ```