Day3:RAG 深入详解

4 阅读8分钟

RAG 深入详解


一、RAG 完整流程


         ┌─────────── 离线阶段(提前准备)──────────┐

         │                                         │

         │  文档 → 分块 → Embedding → 存入向量数据库  │

         │                                         │

         └─────────────────────────────────────────┘

  


         ┌─────────── 在线阶段(用户提问时)─────────┐

         │                                         │

         │  用户提问 → Embedding → 向量检索           │

         │                │                        │

         │          取出 Top-K 相关文档               │

         │                │                        │

         │         (可选)重排 Reranking             │

         │                │                        │

         │          拼接到 Prompt 中                  │

         │                │                        │

         │          LLM 生成回答                     │

         │                                         │

         └─────────────────────────────────────────┘


二、分块(Chunking)

为什么要分块


黄帝内经全文约 20 万字

不分块 → 塞不进 Prompt,也找不到重点

分块后 → 只检索出最相关的 3-5 段,精准高效

四种分块策略

1. 固定大小分块

500 字切一刀,不管语义

优点:简单快速

缺点:可能切断句子

  "气虚者应当注意饮" | "食调理,多食补气之品"

**2. 固定大小 + 重叠(最常用) **


 500 字切一刀,相邻块重叠 100 

  chunk 1:  1-500 

  chunk 2:  400-900      重叠了 100 

优点:切断概率大幅降低

缺点:存储量增加约 20%

3. 按语义分块


根据标题、段落、句号来切

  ## 气虚证 → 一个 chunk

  ## 阳虚证 → 一个 chunk

优点:语义完整

缺点:chunk 大小不均匀

**4. 递归分块(LangChain 默认) **


先按标题(##)切 → 太大就按段落(\n\n)切 → 还大就按句子(。)切

逐级递归,优先保留语义完整性

分块大小选择


太大(2000+ 字)→ 检索不精准,浪费 Token

太小(50 字)  → 丢失上下文,全是碎片

  


经验值:

  一般文档:300-500 字/chunk

  代码:按函数/类切

  中医经典:按条文/方剂切(天然分块单位)

  overlap:chunk 大小的 10-20%


三、Embedding(向量化)

什么是 Embedding

把文字变成一组数字(向量),语义相近的文字数字也相近:


"气虚"      → [0.12, 0.85, 0.33, 0.67, ...]   ← 1536个数字

"气血不足"   → [0.13, 0.83, 0.35, 0.65, ...]   ← 跟"气虚"很接近

"Python"    → [0.91, 0.02, 0.78, 0.15, ...]   ← 跟"气虚"很远

关键区分:


Embedding 模型:文字 → 向量(数字)   用于检索

聊天模型(LLM):文字 → 文字          用于生成回答

这是两个不同的模型,不要搞混

Embedding 模型选型

| 模型 | 维度 | 特点 |

|------|------|------|

| text-embedding-v3 (阿里) | 1536 | 中文效果好 |

| text-embedding-3-small (OpenAI) | 1536 | 便宜,英文强 |

| text-embedding-3-large (OpenAI) | 3072 | 更准但更贵 |

| bge-large-zh (开源) | 1024 | 免费,中文专用 |

| jina-embeddings-v3 (开源) | 1024 | 多语言,免费 |

选型原则:


中文为主 → text-embedding-v3 或 bge-large-zh

英文为主 → OpenAI text-embedding-3

省钱     → 开源模型自己部署

图省事   → 用 API


四、向量存储与检索

三种相似度计算


余弦相似度(Cosine)← 最常用

  衡量方向是否一致,不管长度

  值域:-1 到 1(1 = 完全一样)

  


内积(Dot Product)

  同时考虑方向和长度

  适合:文档长度差异大的场景

  


欧氏距离(L2 Distance)

  衡量空间中两点的直线距离

  值越小越相似(跟前两个相反)

  


大部分场景用余弦相似度就够。Embedding 输出通常是归一化的,

余弦相似度和内积结果等价。

向量数据库对比

| 数据库 | 特点 | 适合场景 |

|--------|------|---------|

| pgvector | PostgreSQL 扩展,无需额外部署 | 数据量 < 100 万,已有 PG |

| Pinecone | 全托管云服务,开箱即用 | 不想运维,直接付费 |

| Weaviate | 开源,支持混合搜索 | 需要向量+关键词混合 |

| Milvus | 开源,高性能 | 数据量大,性能要求高 |

| Chroma | 轻量级 | 本地开发、小项目 |

| FAISS (Meta) | 库,不是数据库 | 嵌入到应用代码中 |

ivfflat vs HNSW 索引


ivfflat:

  原理:把向量分成 N 个桶,查询时只搜最近的几个桶

  优点:构建快,占内存少

  缺点:精度略低

  适合:数据更新频繁

  


HNSW:

  原理:构建多层图结构,跳跃式搜索

  优点:精度高,查询快

  缺点:构建慢,占内存大

  适合:数据相对稳定、要求高精度

  


一般推荐:

  数据 < 10 万条 → ivfflat

  数据 > 10 万条 + 要求高精度 → HNSW


五、重排(Reranking)

为什么需要重排


向量检索返回 Top-10(粗筛):

  1. "气虚者少气懒言"              ← 相关 ✅

  2. "补气方剂:四君子汤"           ← 相关 ✅

  3. "气功修炼方法"                ← ❌ "气"字相关但语义无关

  4. "气候变化对养生的影响"          ← ❌ 完全无关

  


问题:向量检索是粗筛,会混入不相关结果

重排流程


向量检索 Top-10(粗筛,快但粗)

    │

    ▼

Reranker 模型(精排)

  把问题和每个chunk拼在一起,让模型判断"到底相不相关"

    │

    ▼

重排后 Top-3(精准结果)


向量检索:分别编码,比方向 → 快但粗

Reranker:拼在一起,整体判断 → 慢但准

常用 Reranker


Cohere Rerank    → API 服务,效果好

bge-reranker     → 开源,可本地部署

Jina Reranker    → 开源,多语言

LLM 当 Reranker  → 直接问 LLM "这段话跟问题相关吗?"


六、拼接 Prompt 并生成

基础方式


System: "你是中医助手,根据参考资料回答。资料中没有答案就说不知道。"

  


User: "参考资料:

  1. 气虚者,少气懒言,肢体倦怠...

  2. 补气方剂:四君子汤,由人参、白术、茯苓、甘草组成...

  

  用户问题:气虚怎么调理?"

高级:带引用来源


User: "参考资料:

  [1] 《黄帝内经·素问》: 气虚者,少气懒言...

  [2] 《伤寒论》第34条: 补气方剂四君子汤...

  

  请根据以上资料回答,并标注引用来源编号。

  用户问题:气虚怎么调理?"

  


模型回答:

  "气虚调理建议:

   1. 可多食黄芪、山药、红枣[1]

   2. 方剂可参考四君子汤[2]"


七、高级 RAG 策略(面试加分)

1. 混合检索(Hybrid Search)


向量检索:语义相关("气血不足" 能匹配 "气虚")

关键词检索:精确匹配("四君子汤" 必须包含这四个字)

  


两种结合:

  results = 向量检索(query) + 关键词检索(query)

  合并去重,加权排序

  

弥补各自短板:

  向量检索找不到专有名词 → 关键词补上

  关键词搜不到同义词    → 向量补上

2. 查询改写(Query Rewriting)


用户原始问题:"我总是没力气"  ← 口语化,检索效果差

  


让 LLM 先改写:

  "我总是没力气""气虚 乏力 疲劳 调理方法"

  → 用改写后的关键词检索,效果好很多

  


类似中医项目中 analyzeSymptoms 的症状标准化:

  "容易疲劳""疲劳", "乏力"

3. 多跳检索(Multi-hop)


用户问:"气虚体质吃四君子汤要注意什么禁忌?"1跳:检索"四君子汤" → 成分:人参、白术、茯苓、甘草

第2跳:检索"人参禁忌" → 高血压慎用

第3跳:检索"气虚+高血压" → 替代方案

  


单次检索只能回答一部分,多跳才能回答完整

4. Self-RAG(自我判断)


普通 RAG:检索了就一定用

Self-RAG:检索后让 LLM 判断"这个结果有用吗?"

  


流程:

  检索 Top-5

    → LLM 判断:#1 相关 ✅ #2 相关 ✅ #3 不相关 ❌

    → 只用相关的生成回答

    → 生成后自检:"回答有没有超出参考资料范围?"


八、RAG vs Fine-tuning

| | RAG | Fine-tuning |

|---|------|------------|

| 做什么 | 给模型实时喂参考资料 | 把知识训练进模型参数 |

| 类比 | 开卷考试(带资料查) | 闭卷考试(全背下来) |

| 知识更新 | 改数据库就行,秒级 | 重新训练,小时/天级 |

| 成本 | 低(只需向量数据库) | 高(需要 GPU 训练) |

| 幻觉控制 | 好(有出处可查) | 差(可能编造) |

| 适合 | 知识会变、需要引用来源 | 改变模型风格/格式/行为 |

选型话术:

"中医项目选 RAG 原因有三:知识库需要持续更新,RAG 改数据库就行;

  RAG 能标注引用来源,用户可验证;微调成本高,不适合早期迭代。

  如果需要模型以特定中医话术风格回答,可以 RAG + 微调结合。"


九、面试高频问题

Q: 分块大小怎么选?

一般 300-500 字,overlap 10-20%。太大检索不精准浪费 Token,太小丢失上下文。中医经典按条文/方剂切是天然的最佳分块。

Q: 检索结果不相关怎么办?

四个方向:1. 加 Reranking 精排;2. 混合检索(向量+关键词);3. 查询改写让搜索词更精准;4. 优化分块策略和 Embedding 模型选型。

Q: 怎么减少幻觉?

  1. Prompt 里明确"资料中没有答案就说不知道";2. 要求标注引用来源;3. Self-RAG 让模型自检回答是否超出参考范围;4. 控制 temperature 参数。

Q: RAG 和微调怎么选?

知识会更新、需要引用来源 → RAG;改变模型风格和行为 → 微调;两者可以结合。

Q: 向量数据库怎么选?

已有 PostgreSQL 且数据量不大 → pgvector;需要高性能大规模 → Milvus;快速原型 → Chroma;不想运维 → Pinecone。

Q: 余弦相似度和内积的区别?

余弦只看方向,内积同时看方向和长度。Embedding 模型输出通常是归一化的,两者结果等价,选余弦即可。