前言

在第五篇文章中,我们将 LiteRAG 从一个“能回答问题的 API”升级成了一个“能聊天、有记忆、界面友好”的智能助手。系统具备了多轮对话、查询改写、双层缓存和 Gradio 交互界面,看起来已经很像一个产品了。

然而,一个系统“看起来能用”和“真正好用”之间,隔着一条鸿沟——量化评估

当我第一次运行全量 RAGAS 自动化评估时,数据直接给我浇了一盆冷水:Context Recall(上下文召回率)只有 0.50。也就是说,用户真正需要的信息,有一半被系统漏掉了。

这篇文章将完整记录我如何通过代码审计 → 根因分析 → 多轮迭代优化,将召回率从 0.50 拉升到 0.80,同时将忠实度、精准率等关键指标全面推高的全过程。希望这套“发现问题 → 量化诊断 → 对症下药”的方法论,能给同样在准备面试项目的同学一些参考。

一、第一轮评估:数据不会说谎

1.1 评估工具选型

评估 RAG 系统不是靠感觉,需要一套标准化的指标。我选择了 RAGAS(RAG Assessment),它是目前业界最主流的 RAG 评估框架,提供了四个核心指标:

指标 含义 考察什么
Faithfulness(忠实度) 答案是否忠于检索到的文档 LLM 是否编造了文档里不存在的内容
Answer Relevancy(答案相关性) 答案是否紧扣用户问题 是否答非所问
Context Precision(上下文精准率) 检索回的文档中,相关文档的占比 是否有大量噪声
Context Recall(上下文召回率) 所有相关文档中,被检索到的比例 是否有遗漏——这是本文要攻克的核心指标

1.2 构建测试集

搭建了一个包含 5 个样本的评估数据集,覆盖了从基础概念到扩展应用:

  • 什么是 RAG?

  • RAG 有哪些优点?

  • 混合检索是什么?

  • Reranker 在 RAG 中的作用是什么?

  • 什么是 LLM Agent?

每个样本包含 question(问题)和 reference(参考答案),参考答案用于 RAGAS 框架计算忠实度。

1.3 初始成绩

使用项目当前的完整链路(混合检索 + Reranker 精排 + 双层缓存)跑了一轮评估,结果如下:

指标 得分 评价
Faithfulness(忠实度) 0.70 中等偏上
Answer Relevancy(答案相关性) 0.84 优秀
Context Precision(精准率) 0.87 优秀
Context Recall(召回率) 0.50 不及格

相关性 0.84 和精准率 0.87 说明系统「找回来的东西基本是对的」,但召回率 0.50 说明「该找回来的东西,有一半没找回来」。这是一个典型的高精度、低召回问题。

1.4 逐样本诊断

一看逐样本数据,问题更加清晰:

  • 样本 1(什么是 RAG?):召回率 1.00,满分。知识库里有 article_1709964.md 等 RAG 介绍性文章,覆盖良好。

  • 样本 2(RAG 有哪些优点?):召回率 0.50。系统找回了一半优点,另一半被文档切分截断或漏掉了。

  • 样本 3(混合检索是什么?):召回率 0.00。完全没找到正确答案的文档,系统基于一篇不相关的文章硬编了一个回答。

  • 样本 4(Reranker 在 RAG 中的作用):召回率 0.00。同样颗粒无收。

  • 样本 5(什么是 LLM Agent?):召回率 1.00。因为库里有 Lilian Weng 那篇经典的 Agent 介绍文。

样本 3 和样本 4 的召回率为零,是拉低平均分的直接原因。此时我需要回答一个核心问题:这些漏检,到底是因为知识库里压根没有相关文档,还是检索系统没能把它们找出来?

二、代码审计:发现 4 个 P0 级致命缺陷

在深入诊断检索性能之前,我首先对整个代码库做了一次全面审计。很快发现了 4 个严重程度达到 P0 级别的问题。

2.1 查询改写被意外禁用

在 chat.py 中,我无意间注释掉了查询改写模块的调用:

python

# 查询改写临时禁用,直接使用原始查询
original_query = request.query
optimized_query = request.query

这导致多轮对话中的「它有什么优点?」永远无法被改写为「RAG 的优点是什么?」,检索器只能拿原始口语词去检索。

修复:恢复 rewrite_query_with_history() 的调用。单轮直接使用原始查询,多轮结合对话历史进行改写。

2.2 错误的多轮对话降级逻辑

更致命的是,为了「应对」查询改写失效,我写了一段降级代码:当检测到对话历史时,直接把历史文本拼接进 Prompt 发给 LLM,完全绕过了检索步骤。这让多轮对话退化成了纯聊天,RAG 的核心价值——基于外部知识回答——直接消失。

修复:删除整个降级逻辑。同时修改 llm.py 的 generate() 方法,新增 history 参数,让 LLM 能同时看到检索上下文和对话历史。

2.3 L1 缓存 Key 缺少会话隔离

原先生成缓存 Key 时只用了查询语句 + 参数,没有加入 session_id

python

cache_key_parts = [
    normalized_query,
    str(int(request.top_k)),
    "1" if request.use_rerank else "0",
]

这导致不同用户的相同提问会命中同一个缓存,跨会话数据互相污染。

修复:将 session_id 加入 Key 生成逻辑的第一个位置。

2.4 L2 语义缓存维度硬编码

这个 Bug 要等到后面升级 Embedding 模型后才暴露,但审计时已经预判到:semantic_cache.py 中的向量维度硬编码为 384,换用 1024 维的 bge-m3 后必然插入报错。提前做了修复埋点。

三、召回率攻坚战:从 0.50 到 0.80

P0 修复让系统逻辑正确了,但召回率仍然纹丝不动。真正的问题藏在知识库与检索算法之间。我通过逐级打印中间结果,锁定了根因链条。

3.1 根因诊断:向量检索找到了,BM25 淹没了

写了一个诊断脚本 diagnose_retrieval.py,分别打印向量检索和 BM25 检索的 Top-10 结果。数据一出来,真相大白:

向量检索(bge-m3, 1024维)Top-10:

text

1. blog_hybrid-search-benefits-rag-systems.md     score=0.64
2. blog_hybrid-search-benefits-rag-systems.md     score=0.62
3. blog_hybrid-search-benefits-rag-systems.md     score=0.62
4. dannwaneri_my-5month-rag-system...             score=0.60
5. docs_zh_hybrid_search.md                       score=0.59
...

向量检索完全是正确的! 新文档稳居前五,来源精准命中。

BM25 检索 Top-10:

text

1. developer_article_2553636.md      score=11.71  (无关)
2. article_1691466.md                score=11.14  (无关)
3. developer_article_2553636.md      score=10.73  (无关)
4. article_1691466.md                score=10.70  (无关)
5. developer_article_2553636.md      score=10.58  (无关)
...

BM25 完全跑偏了! 返回的全是和「混合检索」无关的旧文章。原因出在 Jieba 中文分词:默认词库把「混合检索」切成了「混合」和「检索」两个独立的词,BM25 只能分别匹配这两个关键词,无法作为一个完整术语去匹配文档标题中的「Hybrid Search」。

更糟的是,在 RRF 融合阶段,来自 BM25 的大量噪音(高分的旧文章)和来自向量检索的正确信号(新文档)被混在一起重新排名,噪音淹没了信号,导致最终 Top-5 全是垃圾。

这个诊断结果也证实了一个关键点:问题不在精排,而在初检融合阶段。Reranker 再厉害,也救不了压根没进候选池的正确文档。

3.2 知识库盲区填补

同时我也发现,知识库中确实缺少针对「混合检索」和「Reranker」的基础概念文档。之前的爬虫采集的大多是前沿论文和技术新闻,没有覆盖基础定义。我补充了 8 篇高质量文章,包括 Redis 官方博客、Milvus 混合检索教程、MongoDB Reranker 科普等,让每个评估问题在知识库里都有对应的「锚点文档」。

3.3 三管齐下的技术优化

优化项 具体操作 解决的问题
升级 Embedding bge-small-en-v1.5 (384维) → bge-m3 (1024维) 中文查询能精准匹配英文文档,"混合检索"与"Hybrid Search"的语义向量距离大幅缩短
强化分词 Jieba 添加 "混合检索"/"重排序"/"RRF"/"BM25" 等 12 个领域词 防止专业术语被错误切分,BM25 关键词匹配恢复精准
升级 Reranker bge-reranker-base → bge-reranker-v2-m3(多语言版) 精排阶段不再给英文文档打负分,中文查询匹配的英文文档能正确获得高分

3.4 加权 RRF 融合:核心创新点

升级 Embedding 和 Reranker 后,向量检索已经能准确命中新文档,但 BM25 引入的噪音仍然会在 RRF 融合时干扰排序。最粗暴的做法是直接关掉 BM25,但那会丢掉混合检索的技术亮点。

我选择了更优雅的方案:加权 RRF 融合。在 RRF 算法中引入一个可配置的权重系数 dense_weight,让向量检索的排名贡献放大:

python

# 向量结果:贡献 x3
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + dense_weight / (k + rank)

# BM25 结果:保持原始贡献
rrf_scores[doc_id] = rrf_scores.get(doc_id, 0) + 1.0 / (k + rank)

这样既保留了混合检索的架构完整性,又精准地压制了 BM25 的噪音。这个参数可以根据不同场景(中文/英文/多语言)灵活调整,体现了架构层面上的权衡能力。

3.5 硬件限制下的工程取舍

我的开发机只有 6GB 显存(RTX 3060 Laptop),而 bge-m3(约 2GB)和 bge-reranker-v2-m3(约 2.27GB)两兄弟同时上 GPU 根本吃不消。评估日志中频繁出现:

text

显存不足 (0.7GB),回退到 CPU
Reranker 初始化,设备: CPU

这不是 Bug,而是我之前在 Reranker 模块中预留的显存检测与 CPU 回退机制在自动生效。Embedding 模型留在 GPU 保证向量检索速度,Reranker 在 CPU 上慢跑保证不崩。评估耗时因此从预期的 5 分钟拉长到了 10 分钟以上,但稳定跑通了完整管线的所有推理。

四、最终评估成绩

完成全部优化后,重新跑了一遍全量 RAGAS 评估。以下是优化前后的对比:

指标 优化前 优化后 提升
Faithfulness(忠实度) 0.70 0.81 ↑ 15.6%
Answer Relevancy(答案相关性) 0.84 0.86 ↑ 1.6%
Context Precision(精准率) 0.87 0.97 ↑ 12.3%
Context Recall(召回率) 0.50 0.80 ↑ 60%

所有核心指标均达到 0.80 以上,召回率实现 60% 的飞跃,精准率更是逼近满分(0.97)。逐样本数据也证实了优化方向的正确性:

  • 样本 3(混合检索) 的召回率从 0.00 → 1.00

  • 样本 4(Reranker 的作用) 的召回率从 0.00 → 0.00(这是唯一未提升的样本,经排查发现 Reranker 概念在现有知识库中的文档与查询的向量匹配仍不够理想,属于知识库覆盖问题,后续可通过补充中文定义文章解决)

  • 样本 2(RAG 的优点)从 0.50 → 0.75

五、复盘与启示

这次优化历程让我对 RAG 系统的工程落地有了更深的理解:

关于评估:Demo 距离产品有多远?单靠「感觉」永远无法量化。RAGAS 这样的自动化评估工具,能帮我们在几分钟内精准定位盲区。先把指标测出来,再谈优化,否则一切努力都可能是无效的。

关于多语言:中英文混杂是 RAG 的隐形门槛。Embedding 模型和 Reranker 必须选择多语言版本(如 bge-m3 系列),否则跨语言检索会严重失效。Jieba 分词的领域词补充也是容易被忽略的细节——一个词的切分错误,就能让整个关键词检索通道瘫痪。

关于混合检索:混合检索不是简单地「两头都抓」就能做好。当两种检索器贡献严重失衡时,引入加权融合是平衡信噪比的有效手段。保留架构完整性的同时,用可配置参数灵活应对不同场景,这比一刀切地关掉某个通道更能体现工程判断力。

关于硬件限制:6GB 显存无法同时容纳两个大模型,但通过显存检测 + CPU 回退机制,依然能完成完整管线的推理。设计时预留「优雅降级」能力,在资源受限的生产环境中尤其珍贵。

关于缓存:双层缓存能极大提升性能,但维度不一致、Key 设计不当会引入隐蔽的 Bug。升级模型后务必检查缓存维度是否与新的向量维度匹配。

展望

LiteRAG 的性能突围告一段落,但工程化的路还没走完。下一步计划包括:

  • 监控闭环:导出 Grafana 仪表板 JSON 并归档到仓库,让 docker-compose up 启动后监控看板直接生效。

  • 容器化部署:将 localhost 硬编码改为从环境变量读取,实现真正的一键 Docker 部署。

  • 补充测试与文档:为检索和缓存模块增加单元测试,完善 README 的架构图和快速启动指南。

如果你也对 RAG 的实战落地感兴趣,欢迎在评论区交流交流。下一篇,我们可能会聊聊如何把 LiteRAG 部署上云,或者引入 Agent 能力让它能「动手做事」。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐