# ADR-007: BM25 RAG 知识检索 ## 修改记录 | 日期 | 变更 | |------|------| | 2026-07-18 | 初始创建 | ## ADR-007++ | 状态 | 项 | 说明 | 验证 | |------|-----|------|------| | ✅ | BM25 全文检索 | jieba 分词 + 停用词过滤 + 低分重试 | `rag.py` | | ✅ | RAG 上下文注入 | `build_chat_messages()` 自动检索 + 注入 system prompt | `chat.py` | | ⏸ | 多轮对话融合 | 支持历史消息中的隐含问题,不止单轮 query | 未实现 | | ⏸ | 索引热更新 | docs/ 文件变更后自动重建索引 | 未实现 | ## 状态 **✅ 已采纳**。 ## 背景 Agent 在对话中需要回答用户关于项目自身的问题(功能、技术栈、架构等)。纯靠 LLM 自身知识无法保证准确性和及时性,需要检索项目文档(`docs/*.md` + `README.md`)作为知识上下文。 ## 三方案对比 | 方案 | 优点 | 缺点 | |------|------|------| | **A: BM25 全文检索(采纳)** | 零外部依赖,首次检索即生效,项目文档量小 BM25 足够精准 | 无语义理解(同义词/近义词不匹配) | | **B: ChromaDB 向量检索** | 语义匹配强,自动处理同义词 | 需 embedding API 或本地模型(首次构建慢),依赖重 | | **C: API 嵌入**(LLM 直接回答) | 最省事 | 知识更新不及时,prompt 长度有限,无法精确引用 | BM25 被采纳的理由: - 项目文档不足 50 个切块,体量极小,BM25 的精确匹配已覆盖全部需求 - 零依赖 vs ChromaDB(chromadb + embedding 模型约 50MB+) - 首次构建即生效,无需等待 embedding 请求 - 后续若需语义增强,可叠加向量检索引擎(原生兼容,`search_rag()` 接口不变) ## 架构链路 ``` 用户: "这个项目可以做什么" → POST /api/chat { message } → build_chat_messages() 1. search_rag(query, k=5) → BM25 检索 docs/ 相关片段 2. 注入 system prompt: "以下片段来自项目文档,请优先使用这些信息回答..." [docs/structure/project.md#技术栈总览] [docs/structure/backend.md#HTTP 接口层] ... 3. 组装 [system, user] messages → LLM 推理(无 tool_call 时 SSE 直接流式输出) ``` ## 关键设计 ### 分词策略 中文用 `jieba.lcut()` 分词,英文按空白拆分,过滤 50+ 常见停用词(`的/了/是/可以/什么`等)。 对比效果: | 原始查询 | 旧方案(单字) | 新方案(jieba + 停用词) | |----------|--------------|------------------------| | `技术栈` | `技,术,栈` | `技术, 栈` | | `这个项目可以做什么` | `这,个,项,目,可,以,做,什,么` | `这个, 项目, 做` | | `后端用了什么框架` | `后,端,用,了,什,么,框,架` | `后端, 框架` | ### 文档切块 按 `##` 标题分割,每个块独立 BM25 索引。切块同时保留 `[source#heading]` 元信息供 LLM 引用。 ### 低分重试 当 BM25 最高分 ≤ 0.5 时,自动过滤单字词后用剩余关键词重检。解决全停用词查询(如"这个项目是做什么的")分数趋零问题。 ### 注入策略 - 检索量:`k=5`,覆盖宽泛查询 - 注入位置:system prompt 末尾,user 消息之前 - 指令措辞:`"以下片段来自项目文档,请优先使用这些信息回答用户关于项目本身的问题"` - `CHAT_SYSTEM` 第 6 条配合:允许在项目文档存在时跳出旅行伴侣角色 ## 影响范围 | 文件 | 变更 | 说明 | |------|------|------| | `backend/agent/tools/rag.py` | 新增 BM25 检索引擎 | jieba 分词 + 停用词 + 低分重试 | | `backend/agent/tools/prompts.py` | CHAT_SYSTEM 第 6 条加例外 | 项目文档存在时可跳出旅行伴侣角色 | | `backend/agent/chat.py` | RAG 检索注入 | k=5,强化引用指令 | | `tests/test_agent/test_tokenize.py` | 同步测试 | 适配 jieba 分词行为 | | `pyproject.toml` | 新增依赖 | jieba >=0.42.0 | ## 交叉引用 - 引擎实现:[`backend/agent/tools/rag.py`](../../backend/agent/tools/rag.py) - Agent 数据流:[`docs/structure/agent.md#rag-检索流程`](../structure/agent.md#rag-检索流程) - BM25 实现:参考 `backend/agent/tools/poi.py` 的 TOOL_REGISTRY 注册模式