1500 行代码,召回率翻 3.4 倍:我用这套方法做了一个生产级 RAG 系统
不用 LangChain,不用 LlamaIndex,从零开始手写一个混合检索 RAG 系统,Recall@5 从 0.175 优化到 0.600。这篇文章把过程、选型、踩坑全讲透。
最近我基于 LLM WiKi 知识库搭了一套 RAG 检索服务。
第一版上线后,效果很糟糕:搜不到想要的东西。
跑了一轮评测,Recall@5 只有 0.175——给用户看 5 条结果,平均只能命中不到 1 条相关文档。
这不叫 RAG,这叫随机推荐。
接下来我对这个系统做了 7 轮优化(其中 3 轮是失败的),最终把 Recall@5 提升到 0.600,MRR 提升到 0.806,Hit Rate@5 达到 1.0。
这篇文章把整个过程拆开讲,包括:
- 技术选型时为什么没用 LangChain
- 哪些优化真正有效,哪些是坑
- 一个"反直觉"的设计:章节树数据模型
如果你正在做 RAG 系统,或者正在为检索效果发愁,这篇可能会帮你少走几个月弯路。
先看结果:一张表说清楚
| 指标 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| Recall@5 | 0.175 | 0.600 | +3.4x |
| MRR | 0.564 | 0.806 | +43% |
| Hit Rate@5 | 0.667 | 1.000 | +50% |
| 平均延迟 | — | 826ms | < 1 秒 |
| 代码量 | — | ~1500 行 | 无框架依赖 |
系统跑在 772 篇 Markdown 文档、8337 个章节上。每次搜索在 1 秒内返回结果。
第一个决策:为什么不用 LangChain?
这是被问最多的问题。
先说结论:当你能清楚描述每个组件在做什么时,直接实现比用框架更灵活。
检索流程是这样的:
查询 → 同义词扩展 → 向量检索 + BM25 → RRF 融合 → Rerank → 父子节点注入 → 返回每个步骤都有需要调的参数——RRF 的权重、Rerank 的候选集大小、父子注入的分数阈值。如果用 LangChain,这些参数会藏在框架的抽象层里,调优时你得先搞懂框架的 API 设计意图,再找到对应的配置项。
数据模型也不是标准的 chunk-based RAG。用的是基于 Markdown 标题层级的章节树——这个设计后面会详细讲。LangChain 的 RetrievalChain 不直接支持这种层级结构。
最终,整个系统只有 ~1500 行 Python 代码,依赖极少(FastAPI + ChromaDB + rank_bm25 + jieba),每个组件可独立调优。
原则:先搞懂原理,再决定是否需要框架。大多数 RAG 系统不需要。
数据模型:章节树 vs 纯文本切块
这是整个系统最关键的设计决策。
传统 RAG 把文档按固定 token 数切块(比如 512 tokens),然后索引每个 chunk。问题在于,切块后你丢失了三层信息:
它属于哪篇文档?
(
wiki/rag/chunking-strategy.md)它在文档中的什么位置?
(
RAG > 关键设计点 > 切块策略)它的父子章节是什么?
(子章节"重叠窗口策略"可能也是用户想要的)
做法是按 Markdown 标题(H1-H6)构建章节树:
Document: wiki/rag/overview.md├── H1: RAG 概述│ ├── H2: 什么是 RAG│ │ └── H3: RAG 的工作流程│ └── H2: RAG 的优势└── H2: RAG 的局限性每个节点携带:面包屑路径、LLM 增强摘要、源文件行号、父子关系。
这个设计带来三个直接好处:
精确溯源:搜索结果直接定位到源文件的具体行号,用户点击即跳转。
上下文扩展:检索到"切块策略"后,系统可以自动返回它的子章节"重叠窗口策略"。
更高质量的向量:Embedding 文本里拼了面包屑路径(RAG > 关键设计点 > 切块策略),比单纯的"切块策略"携带更多语义信息。
技术选型:6 个组件的选择理由
| 组件 | 选型 | 为什么选它 |
|---|---|---|
| Embedding | BGE-M3 | 中英混合首选,1024 维是精度/成本的甜点 |
| 向量数据库 | ChromaDB | 8337 条数据完全够用,零配置 |
| 关键词检索 | BM25 + jieba | 向量擅长语义匹配,BM25 擅长精确匹配,两者互补 |
| Rerank | Qwen3-Reranker-8B | 中文优化效果好,API 调用免 GPU |
| 摘要生成 | Qwen2.5-7B-Instruct | 统一 SiliconFlow API,7B 对摘要绰绰有余 |
| Web 框架 | FastAPI | 异步、类型安全、自带 API 文档 |
一个重要的选型教训:
Embedding 和 BM25 一定要同时用。向量检索擅长"理解意思"(“怎么切分文档” → “切块策略”),BM25 擅长"精确匹配"(搜"MCP"就是"MCP",不会被向量模型和"API"搞混)。两者通过 RRF 融合,效果远好于单通道。
7 轮优化:哪些有效,哪些是坑
优化 1:修复 Embedding 配置(+0.286)
最大的提升来自最基础的错误。
Embedding 模型名配置写错了,导致生成的向量质量极差。修复一行配置后,Recall@5 从 0.175 直接跳到 0.461。
教训:先检查基础配置。一行错误可能让你白调一个月参数。
优化 2:引入 Rerank(MRR +0.113)
加了 Qwen3-Reranker-8B 做 Cross-Encoder 精排。
Recall@5 几乎没变(-0.005),但 MRR 从 0.666 跳到 0.779。意思是:相关文档被排到了更前面。
Rerank 是 RAG 系统性价比最高的优化。一个好的 Cross-Encoder 可以把 MRR 提升 10-15%。
优化 3:Query Expansion(+0.060)
BM25 是纯词频匹配。用户搜"切块",无法匹配文档里写的"chunking"。
最初用手工同义词词典解决这个问题。后来升级为 Embedding 自动发现同义词——从知识库词汇表中,用 BGE-M3 向量相似度自动找到语义相近的词。
查询 "切块策略" → 自动发现 "chunking"(0.89), "分块"(0.85), "切分"(0.82) → 扩展为 "切块策略 chunking 分块 切分"换知识库只需 rebuild-index,零维护。
优化 4:文档增强 + 中文 Rerank Instruct(+0.040)
两个改动:
- 用 LLM 为每个章节生成 50-100 字的增强摘要,补充 Embedding 信号
- Reranker 的 instruct 从英文改为中文
# 英文(效果一般)"Given a web search query, retrieve relevant passages"# 中文(效果更好)"根据用户查询,检索与查询语义最相关的文档段落。"优化 5:保守父子节点注入(+0.028)
Rerank 后,对高分结果(> 0.8)的父节点,注入最多 2 个子节点,子节点分数打 5 折。
关键词是保守。后面会讲到,激进注入是失败的。
3 次失败实验:直觉越好,坑越深
失败 1:多阶段检索
直觉:在 Rerank 前注入子节点,让子节点也参与精排,应该更好。
结果:R@5 从 0.600 降到 0.589,延迟还多了 109ms。
原因:候选池太大(120 篇),Reranker 注意力被"稀释"了。
失败 2:增强 Reranker 上下文
直觉:给 Reranker 更丰富的文档信息(父节点标题、更多内容),它应该判断更准确。
结果:R@5 暴跌到 0.549(-0.051),某些查询直接腰斩。
原因:文本过长引入噪声,Reranker 关注了不相关的内容。
失败 3:激进父子注入
直觉:多注入几个子节点(5 个),分数不打折,应该召回更多。
结果:注入的子节点"挤占"了原本相关的结果位置,R@5 反而下降。
失败实验的共同教训
每个组件都有最优工作点,超过就会退化。
在 RAG 系统里,“更多"不等于"更好”。更精简的输入、更小的候选集、更保守的策略,往往效果更好。
一个反直觉的发现:Rerank 比你想的重要得多
优化过程中,我原以为最大的提升会来自更好的 Embedding 模型或更复杂的检索策略。
但数据告诉我:
| 优化 | Recall@5 提升 | MRR 提升 |
|---|---|---|
| 修复 Embedding | +0.286 | +0.102 |
| 引入 Rerank | -0.005 | +0.113 |
| Query Expansion | +0.060 | +0.032 |
| 文档增强 | +0.040 | +0.048 |
| 父子注入 | +0.028 | +0.011 |
Rerank 对 Recall 几乎没有贡献,但对 MRR 的贡献是最大的。它把"对的文档"从第 5 名提到了第 1 名。
如果你只能做一件事来改善 RAG 效果,加一个 Reranker。
当前架构全景
用户查询 │ ├──→ 查询扩展(Embedding 自动同义词发现) │ ├──→ Stage 1: 双通道并行召回 │ ├── 向量检索(BGE-M3 + ChromaDB)← 原始查询 │ └── BM25 检索(jieba 分词)← 扩展查询 │ ├──→ Stage 2: RRF 融合(Vector:2.0 / BM25:1.0) │ ├──→ Stage 3: Cross-Encoder Rerank(Qwen3-Reranker-8B) │ └──→ Stage 4: 保守父子注入(MIN_SCORE=0.8, MAX=2, DISCOUNT=0.5)整套系统只依赖一个外部 API(SiliconFlow),本地无需 GPU。
看一下实际使用情况:
给正在做 RAG 的你:7 条原则
这是我在这个过程中总结的经验:
1. 评测先行。没有评测数据,所有优化都是盲猜。先花 1 天标注 30 条评测数据,比花 1 周调参数更有效。
2. 从简单开始。先跑通最基础的向量检索,确认 pipeline 没问题,再逐步加 BM25、RRF、Rerank。
3. 保留结构信息。如果你的文档有标题层级,不要用纯文本切块。面包屑路径、父子关系都是有价值的语义信号。
4. 双通道互补。向量和 BM25 各有所长,RRF 融合效果远好于单通道。
5. Rerank 是性价比最高的优化。一个好的 Cross-Encoder 可以把 MRR 提升 10-15%。
6. 少即是多。更多候选、更多上下文、更多注入——直觉上"更好"的策略,往往引入噪声。
7. 记录每一次实验。失败实验的价值不亚于成功实验。知道什么不能做,和知道什么能做一样重要。
写在最后
这个系统不是什么前沿技术突破,用的都是成熟组件。但它证明了一件事:
RAG 效果不好,往往不是模型不行,而是工程设计没到位。
章节树数据模型、双通道融合、保守的上下文注入、评测驱动开发——这些看起来"不够酷"的工程决策,才是真正拉开差距的地方。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~