[!info]
Rag更像一个预置的prompt拼接技术,通过预切分的内容,构建出embedding向量,当输入prompt后,在向量数据库中查找并召回,将原文拼接到输入prompt,然后发送给LLM.其中的难点在于文档切割和召回率,也就是通俗意义上的检索

![[RAG流程.webp|562]]
![[RAG数据库结构.webp|527]]

主要解决的问题

第一是知识时效性,LLM 训练完知识就固定了,训练截止日期之后发生的事它一无所知;第二是私有知识覆盖,公司内部文档、行业专有数据根本没有机会进训练集,LLM 对这些内容是空白的;第三是幻觉问题,没有知识依据时 LLM 容易「自己发挥」编出一个听起来合理但实际错误的答案,给了它参考资料之后幻觉就少很多。

文档切割

  1. 最粗暴的方法就是以500个token为一个chunk,然后中间重叠100个token,防止语义的被迫切断
  2. 另外就是针对不同的文本内容做切割,对于普通文档可以使用段落\标点符号做多级切分, 对于代码文档,可以使用函数来进行切分,对于表格,需要将其转换成MD格式进行切分
策略 适用文档类型 优点 缺点
固定大小 + 重叠 纯文本、无明显结构 实现简单、chunk 大小可控 可能在语义中间截断
语义边界切割 段落分明的文章 语义完整,召回质量好 实现稍复杂,chunk 大小不均
标题层级切割 Markdown、HTML 文档 天然语义独立,带结构 metadata 依赖文档有清晰的标题结构
代码按函数切割 代码文件 保留代码逻辑完整性 需要 AST 解析,限定语言
父子切割(优) 各类文档(追求高质量) 检索精准 + 上下文完整两全 存储量翻倍,索引构建复杂
Late Chunking 各类文档 chunk 向量保留全文上下文 需要模型支持长输入
3. 另外还有很多更复杂的办法
方案 核心思路 适合场景 代价
重叠切割 相邻 chunk 有内容重叠 所有场景,基础兜底 存储轻微增加
语义边界切割 在句子/段落边界处切 段落结构清晰的文档 需要 NLP 工具
句子窗口检索 精细检索 + 扩展返回窗口 追求高召回精度 存储量大
父子切割 小块检索、大块返回 通用场景,效果均衡 存储翻倍、索引复杂
命题化切割 LLM 拆解为独立命题 高质量要求、重要知识库 LLM 调用成本高
Contextual Retrieval 向量化前为每个 chunk 补全背景上下文 文档语境强、chunk 孤立感严重的场景 LLM 调用成本(Prompt Caching 可大幅降低)

向量数据库

数据库 部署方式 适合规模 混合检索 主要优势 主要劣势
Chroma 本地/Client-Server/云 中小规模 是(支持 BM25/SPLADE) 零配置上手极快,生态集成好 超大规模稳定性待验证
Qdrant 自托管/云(支持分布式) 中大规模(亿级) 性能好,API 简洁,Rust 高性能 超大规模需调优
Milvus 自托管(分布式) 大规模(亿级) 可水平扩展 部署运维复杂
Pinecone 全托管云服务 中大规模 无需运维 费用高,数据出境
pgvector PostgreSQL 插件 中小规模 是(配合全文检索) 无需新组件,可 JOIN 业务数据 性能弱于专用向量库

RAG查询步骤

  1. query预处理:
    • 一是简单改写,把口语化问题改写成更正式、独立完整的检索句。
    • 二是 HyDE(Hypothetical Document Embeddings),让 LLM 先「假设」一个可能的答案,用这个假设答案的向量去检索。你可能会觉得奇怪,不用问题去搜反而用假设答案去搜?原因很简单:问题和答案的用词往往差异很大,但假设答案和真实答案的用词更接近,它们的语义空间天然更匹配,命中率往往更高。
    • 三是多角度扩写,把同一个问题扩展成 3-5 种不同表述,分别检索后合并结果,覆盖面更广。
  2. Query embedding: 必须用和离线建库时完全相同的 Embedding 模型。
  3. 向量检索(粗排)+多路召回
    • 向量检索+ BM25(Best Match 25)+RRF(互倒排名融合)算法融合
    • 一般使用向量检索和BM 25的混合检索,通过两路检索之后,再使用RRF对整体进行排序top-k
  4. Rerank 模型精排, 在这个步骤中对召回结果进行评分,如果分数较低,应该终止流程,防止LLM幻觉
  5. prompt 拼装
1
2
3
4
5
6
7
8
9
10
11
prompt = f"""
你是一个专业助手,请根据以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请回答「根据现有资料无法回答」,不要自行猜测。

参考资料:
[1] {chunk_1}
[2] {chunk_2}
[3] {chunk_3}

用户问题:{user_query}
"""
  1. LLM生产结果

优化策略

实际上所谓的优化策略,就是之前所提到的每个步骤的层面上的优化,例如索引预处理层面使用两份chunk数据库,其中小的chunck数据库中存一个parent ID, 索引大块的文档进行prompt拼接. 查询方面,使用Hyde的方法做query优化. 召回层面,使用多路召回、多个方法共同索引的方式. 最后使用re,rank精排对返回的top k结果重新做rerank. 还有图数据库的方案

评价指标

检索层

Hit@K 的直觉是:我把 Top-K 的检索结果摆在你面前,你要找的那个在里面吗?如果在,这次就算命中(Hit)。Hit@5 就是说,把前 5 个结果给你,能不能命中,最终统计命中率。这个指标回答的是「找到没」的问题,不关心找到的是第几名。一般 Hit@5 低于 0.7 就说明检索层有问题,需要考虑换 Embedding 模型或者优化 Chunking 策略;高于 0.8 说明检索层 OK,如果答案还不好,问题在生成层。

MRR(Mean Reciprocal Rank,平均倒数排名)更进一步,关心的是「你要找的东西排在第几名」。计算公式也很直观,就是对每个问题算 1 / 排名,然后对所有问题求平均。所以第一名找到得 1 分,第二名找到得 0.5 分,第三名得 0.33 分,第五名得 0.2 分,排名越靠后得分越低。MRR 越高,说明正确内容排名越靠前,用户越早看到它。这个指标回答的是「多快找到的」。MRR 低于 0.5 通常说明 Rerank 效果不够好,正确内容召回了但没排到前面。

生成层(RAGAs框架)

Faithfulness(忠实度):答案里说的每件事,在检索到的 chunk 里有没有出处?这个指标衡量的是幻觉程度。你可以把它理解为「LLM 裁判」在逐句问:「这句话你从哪条资料里找到的依据?」没有依据的句子越多,分越低。目标值是 > 0.8。

Answer Relevancy(答案相关性):答案有没有回答用户问的那个问题?注意这个指标和 Faithfulness 是两回事,很多人会把它们搞混。Faithfulness 是问「说的是不是真的」,Answer Relevancy 是问「说的是不是用户想要的」。一个答案可以字字有据、但完全跑题,Faithfulness 高、Answer Relevancy 低。

Context Recall(上下文召回率):要回答这个问题,所需要的信息有多少比例在检索结果里覆盖到了?这个指标需要有「标准答案」作为参照,衡量的是检索层有没有「漏掉该找到的内容」。目标值是 > 0.7。

Context Precision(上下文精确率):这个指标和 Context Recall 配对出现,衡量的是检索结果里「有用的内容」排名是否靠前。也就是说,如果你召回了 10 个 chunk,相关的那几个有没有被排在前面,而不是混在无关内容的后面。它同样需要 ground_truth 作为参照计算。Context Recall 关注「该找的有没有找全」,Context Precision 关注「找到的里面相关的是不是排在前面」,两个配合能完整刻画检索质量。


本站由 Edison.Chen 创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。