[!info]
Rag更像一个预置的prompt拼接技术,通过预切分的内容,构建出embedding向量,当输入prompt后,在向量数据库中查找并召回,将原文拼接到输入prompt,然后发送给LLM.其中的难点在于文档切割和召回率,也就是通俗意义上的检索
![[RAG流程.webp|562]]
![[RAG数据库结构.webp|527]]
主要解决的问题
第一是知识时效性,LLM 训练完知识就固定了,训练截止日期之后发生的事它一无所知;第二是私有知识覆盖,公司内部文档、行业专有数据根本没有机会进训练集,LLM 对这些内容是空白的;第三是幻觉问题,没有知识依据时 LLM 容易「自己发挥」编出一个听起来合理但实际错误的答案,给了它参考资料之后幻觉就少很多。
文档切割
- 最粗暴的方法就是以500个token为一个chunk,然后中间重叠100个token,防止语义的被迫切断
- 另外就是针对不同的文本内容做切割,对于普通文档可以使用段落\标点符号做多级切分, 对于代码文档,可以使用函数来进行切分,对于表格,需要将其转换成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查询步骤
- query预处理:
- 一是简单改写,把口语化问题改写成更正式、独立完整的检索句。
- 二是 HyDE(Hypothetical Document Embeddings),让 LLM 先「假设」一个可能的答案,用这个假设答案的向量去检索。你可能会觉得奇怪,不用问题去搜反而用假设答案去搜?原因很简单:问题和答案的用词往往差异很大,但假设答案和真实答案的用词更接近,它们的语义空间天然更匹配,命中率往往更高。
- 三是多角度扩写,把同一个问题扩展成 3-5 种不同表述,分别检索后合并结果,覆盖面更广。
- Query embedding: 必须用和离线建库时完全相同的 Embedding 模型。
- 向量检索(粗排)+多路召回
- 向量检索+ BM25(Best Match 25)+RRF(互倒排名融合)算法融合
- 一般使用向量检索和BM 25的混合检索,通过两路检索之后,再使用RRF对整体进行排序top-k
- Rerank 模型精排, 在这个步骤中对召回结果进行评分,如果分数较低,应该终止流程,防止LLM幻觉
- prompt 拼装
1 | prompt = f""" |
- 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 关注「找到的里面相关的是不是排在前面」,两个配合能完整刻画检索质量。