Skip to content

大模型 RAG 经验面

整理自:AiGC面试宝典 | 来源:知识星球 整理日期:2024年8月11日


一、RAG 基础面

1.1 为什么大模型需要外挂向量知识库?

  • 克服遗忘问题:大模型在训练后无法动态更新知识,外挂知识库可以实时补充新信息
  • 提升回答的准确性、权威性、时效性:避免模型生成过时或不准确的内容
  • 解决垂直领域知识不足:通用模型对小众领域涉猎有限,外挂知识库可以针对性补充
  • 提高可控性和可解释性:答案可追溯到具体来源,提高可信度和安全性

📝通俗解释:就像一个记忆力很好的学生,但不可能记住所有知识。考试时让他带一本专业参考资料(外挂知识库),既不会忘,又能准确回答各种问题。


1.2 RAG 思路是怎样的?

完整流程

  1. 文档处理流程(知识库构建)

    • 加载本地文档(txt/doc/pdf等)
    • 读取文本内容
    • 文本分割(切分成小块)
    • 文本向量化(embedding)
    • 存入向量数据库
  2. 查询与生成流程(用户提问)

    • 用户输入 query(问题)
    • 问题向量化
    • 在向量数据库中检索相似内容(Top K)
    • 匹配到的文档作为上下文
    • 组装 prompt 提交给 LLM
    • LLM 生成回答

流程图示

用户 Query → 向量化 → 向量相似度匹配 ← 文档向量库

         Top K 相关文档

    [上下文 Context] + [问题] → Prompt 模板

           大语言模型 LLM

           最终答案 Answer

📝通俗解释:RAG就像在图书馆找书。先把书籍分类整理(知识库构建),有人问问题时,根据问题关键词找到最相关的几本书(检索),然后翻阅这些书的相关章节来回答问题(生成)。


1.3 RAG 核心技术是什么?

  • 核心技术:Embedding(向量化)
  • 核心思路
    • 将用户知识库内容经过 embedding 存入向量知识库
    • 用户提问同样经过 embedding
    • 利用向量相关性算法(如余弦相似度)找到最匹配的文档片段
    • 将这些片段作为上下文,与问题一起提交给 LLM 回答

📝通俗解释:Embedding就是给文字"拍照",把文字转换成计算机能理解的数字向量。比如"猫"和"狗"的向量很相似(都是动物),而"猫"和"汽车"的向量差别很大。这样通过比较向量的"距离"就能找到相关内容。


1.4 RAG Prompt 模板如何构建?

text
已知信息:
{context}

根据上述已知信息,简洁且专业地回答用户的问题。如果无法从中得到答案,请说"根据已知信息无法回答该问题"或"没有提供足够的相关信息",不允许在答案中添加编造成分,答案请使用中文。
问题是:{question}

📝通俗解释:这个prompt模板就像给LLM一张"答题卡":先告诉它看哪些资料(已知信息),再问具体问题(question),最后还要提醒它不知道就说不知道,别瞎编。


1.5 如何评价 RAG 项目效果的好坏?

检索环节评估指标

指标说明
MMR 平均倒排率查询结果中正确信息的位置倒数
Hits Rate 命中率Top K 结果中包含正确信息的比例
NDCG标准化折损累计增益,综合考虑相关性和排名

生成环节评估指标

  • 非量化指标:完整性、正确性、相关性
  • 量化指标:Rouge-L(评估生成文本与参考文本的重叠程度)

📝通俗解释:检索就像考试找资料,要看找得全不全(命中率)、找得准不准(排名)。生成就像写答案,要看答案是否完整、正确、有没有答非所问。


1.6 RAG 的检索阶段,常见的向量检索模型有哪些?

ANN(近似最近邻)算法

  • 乘积量化(Product Quantization)
  • 暴力搜索(Brute Force)
  • HNSW(Hierarchical Navigable Small World)

其他方法

  • KD 树(K-Dimensional Tree)

📝通俗解释:想象在图书馆找书。暴力搜索就是一本一本翻(慢但准确);HNSW就像问图书管理员,他凭经验直接带你去大致区域(快但可能不是最优);KD树把书架分成区域管理,帮你快速定位。


1.7 针对通用的 RAG,还有哪些改进点?

  • Query 侧优化:做 query 的纠错、改写、规范化扩展
  • 向量数据库优化:构建层次索引,提高检索效率和精度
  • LLM 微调:针对垂直领域引入知识库,提升回答的专业性、时效性和正确性
  • 后处理优化:对最终输出做处理,降低不合理答案的出现概率

📝通俗解释:改进RAG就像升级一个问答系统:问题输入要纠错(用户打错字能自动纠正);检索要更聪明(更快更准找到资料);回答要更专业(针对特定领域微调模型);最后还要把关(防止输出奇怪的内容)。


二、RAG 优化面

痛点1:文档切分粒度不好把控,既担心噪声太多又担心语义信息丢失

问题描述

问题1:如何让 LLM 简要、准确回答细粒度知识?

  • 举例:用户问"2023年我国上半年的国内生产总值是多少?"
  • 期望回答:"593034亿元"
  • 实际可能:模型啰嗦半天或者编造数字

问题2:如何让 LLM 回答出全面的粗粒度(跨段落)知识?

  • 举例:用户问"征信中心有几点声明?"
  • 期望回答:完整列出三点声明内容
  • 实际可能:只回答了两点,因为召回的文档不完整

需求分析

  • 需要实现语义级别的分割,而不是简单基于 html 或 pdf 的换行符分割
  • 痛点:文档分割不够准确,导致召回结果残缺
  • 切分粒度太大 → 噪声多,答案不精准
  • 切分粒度太小 → 语义丢失,回答不全面

解决方案

核心思想:基于 LLM 的文档对话架构 = 先检索,后推理

检索部分要满足三点:

  1. 尽可能提高召回率
  2. 尽可能减少无关信息
  3. 速度快

二级索引架构

第一级索引:关键信息(短摘要)→ 做embedding,参与相似度计算
第二级索引:原始文本(完整内容)→ 映射关系,交给LLM

架构图示

文章A                          文章B
├── 段落A                      ├── 段落D
│   ├── 句子a → 关键信息3       │   ├── 句子g → 关键信息9
│   └── 句子b → 关键信息4       │   └── 句子h → 关键信息10
├── 段落B                      └── 段落E
│   ├── 句子c → 关键信息5        └── 句子i → 关键信息11
│   └── 句子d → 关键信息6
└── 段落C
    ├── 句子e → 关键信息7
    └── 句子f → 关键信息8

检索示例:
问题1答案在句子c和句子h → 检索到关键信息5和10 → 返回原始句子
问题2答案在段落A、B、E → 检索到关键信息1、14、9 → 返回完整段落

如何构建关键信息?

方法1:NLP 篇章分析(discourse parsing)

  • 利用篇章分析工具提取段落间关系(如主从关系)
  • 把包含主从关系的段落合并,保证每段说同一件事

方法2:BERT 语义分割

  • 利用 BERT 的 NSP(Next Sentence Prediction)任务
  • 判断相邻段落是否有语义衔接关系
  • 设置相似度阈值,决定是否合并
python
def is_nextsent(sent, next_sent):
    encoding = tokenizer(sent, next_sent, return_tensors="pt", truncation=True, padding=False)
    with torch.no_grad():
        outputs = model(**encoding, labels=torch.LongTensor([1]))
        logits = outputs.logits
        probs = torch.softmax(logits/TEMPERATURE, dim=1)
        next_sentence_prob = probs[:, 0].item()
    return next_sentence_prob > MERGE_RATIO

方法3:关键信息抽取

  • 成分句法分析:提取核心名词短语、动词短语
  • 命名实体识别(NER):提取重要实体(人名、地名、机构名等)
  • 语义角色标注:提取"谁对谁做了什么"的信息

方法4:关键词提取工具

  • HanLP(中文效果好,但付费)
  • KeyBERT(英文效果好,中文效果一般)
  • 垂直领域建议:训练专门的关键词生成模型(如 ChatLaw 的 KeyLLM)

常见问题

Q:句子、语义段之间召回不会有包含关系吗,是否会造成冗余?

A:会造成冗余,但试验表明回答效果很好,无论是细粒度还是粗粒度知识准确度都比 LangChain 粗分效果好很多。可以优化但非必要。

📝通俗解释:文档切分就像切蛋糕——切太小了,每块都说不清完整的故事(语义丢失);切太大了,一块蛋糕里混了太多奶油和水果(噪声太多)。二级索引的思路是:先看每块的"简介"(关键信息)来快速匹配,找到相关的再给完整内容。这样既快又准。


痛点2:在垂直领域表现不佳

问题描述:通用 RAG 在垂直领域(如医疗、法律、金融)回答效果差

解决方法

  • Embedding 模型微调:基于垂直领域数据微调向量化模型
  • LLM 微调:基于垂直领域语料微调大语言模型

📝通俗解释:就像让一个通才医生做心脏手术——基础能力有,但不够专业。解决方案就是让他去专门进修(微调),变成专科医生。


痛点3:LangChain 内置问答分句效果不佳

问题描述:LangChain 默认的分句方式不够智能

解决方法

  1. 更好的文档拆分:使用语义识别模型(如达摩院的模型)进行拆分
  2. 改进填充方式:判断中心句上下文的相关性,仅添加相关度高的句子
  3. 分段总结:对每段分别进行总结,基于总结内容进行匹配

📝通俗解释:LangChain默认就像用尺子切文章——按固定长度切,但不管内容是否连贯。改进方法是要么用更智能的"语义切刀",要么切完后再"筛选一遍"去掉无关内容。


痛点4:如何尽可能召回与 Query 相关的 Document

问题描述:如何让与问题相关的文档尽可能多地被召回

解决方法

  1. 平衡 Document 长度、embedding 质量和召回数量

    • 文本较短时,embedding 质量可能更高
    • 各段落语义关联不宜过强
  2. 优化检索策略

    • 基于本地知识微调文本向量化工具
    • 结合 ES 搜索与 Faiss 结果

📝通俗解释:召回就像在大海里捞针。要么用更密的网(更多召回),要么让网更准(提高相关性)。短文本的embedding更准确,所以切分时不要贪多。


痛点5:如何让 LLM 基于 Query 和 Context 得到高质量的 Response

问题描述:如何让 LLM 生成高质量答案

解决方法

  1. Prompt 优化:尝试多个 prompt 模板,选择效果最好的
  2. LLM 微调:用本地知识问答相关的语料对 LLM 进行微调

📝通俗解释:同一个问题,问法不同,答案质量可能差很多。比如问"根据这段话,告诉我答案"和"请仔细阅读以下内容,然后用简洁准确的语言回答问题",效果可能完全不同。这就是prompt的学问。


痛点6:Embedding 模型在表示 Text Chunks 时偏差太大

问题描述

  1. 开源 embedding 模型效果一般,text chunk 很大时很难准确表示
  2. 多语言问题:文档是英文,问题是中文,跨语言检索困难

解决方法

  1. 用更小的 text chunk + 更大的 topk

    • smaller chunk → noise 更小,embedding 表示更准确
    • larger topk → 组合更丰富的 context
  2. 选择多语言 embedding 模型:使用支持多语言的 embedding 模型

📝通俗解释:Embedding就像给文章画"重点"。文章太长,重点就画不准(偏差大)。解决方案是:要么把文章写短一点(切分小一点),要么多找几本书一起看(topk大一点)。多语言问题就像用中文查英文书,需要找会双语的书架管理员。


痛点7:不同的 Prompt 可能产生完全不同的效果

问题描述:Prompt 是个神奇的东西,不同的提法可能产生完全不同的效果。尤其是希望生成特定格式时,需要反复尝试。

解决方法

  • 不断尝试和优化 prompt 设计
  • 针对特定格式要求进行专项调试

📝通俗解释:Prompt就像跟不同的人说话。有的人吃软不吃硬(喜欢鼓励式指令),有的人就喜欢直接命令(喜欢指令式)。LLM也是这样,不同的prompt写法效果天差地别,需要反复"调教"。


痛点8:LLM 生成效果问题

问题描述:LLM 本质上是"接茬"机器,各家 LLM 在理解 context 和生成方面相差较大。

实践经验

  • 付费 GPT 代理:生成内容可读性强,完全按格式生成
  • 本地开源模型(如 Chinese-LLaMA2-Alpaca、Baichuan2):
    • 量化后(Q6_K)性能接近 fp16
    • 在格式理解上不如 GPT 灵活
    • 生成时有点"轴"(过于遵循指令模板)

解决思路:选择开源模型,自己构造领域数据集进行微调,让 LLM 更听指挥

📝通俗解释:LLM就像一个写作助手。付费的GPT就像经验丰富的老编辑,你说什么他都懂;开源模型像刚毕业的新手,你得慢慢教他规矩(微调),才能写出你想要的内容。


痛点9:如何更高质量地召回 Context 喂给 LLM

问题描述:初期使用 LangChain 默认配置时,召回的内容和 query 根本不相关

解决思路

  • 更细颗粒度地做 recall
  • 学术内容优化
    • 使用学术相关的 embedding 模型
    • 准备高质量的指令数据
    • 更细致的 PDF 解析

📝通俗解释:就像考试时找参考资料。如果随便翻几页就答题,肯定答不好。要更细致地"翻书"(细粒度召回),找真正相关的章节,才能写出好答案。


三、RAG 工程示例面

3.1 本地知识库问答系统(LangChain-ChatGLM)

3.1.1 避坑记录

问题1:解决持续网页 loading 的问题

  • 原因:高版本 gradio 检查 google 字体导致卡住
  • 解决:降低 gradio 版本
bash
pip install gradio==3.21.0

问题2:解决无法安装 detectron2

  • 解决:0517 后使用 paddleocr 代替
bash
# 1. 下载依赖包
git clone https://github.com/facebookresearch/detectron2
cd detectron2

# 2. 安装(注意 torch==1.8.0)
pip install -e .

# 3. 升级 torch,降至 protobuf 版本
pip install torch==2.0.0
pip install protobuf==3.20.0

问题3:解决 PDF 无法加载

bash
# 1. 更新 apt
sudo apt update

# 2. 安装依赖
sudo apt install libmagic-dev poppler-utils tesseract-ocr

# 3. 检查版本
tesseract --version
tesseract --list-langs

# 4. 如需中文识别,下载中文包放入:
# /usr/share/tesseract-ocr/4.00/tessdata/

问题4:解决错误 file0

bash
# 1. 下载 punkt.zip
# 解压放到 nltk_data/tokenizers 路径下

# 2. 下载 averaged_perceptron_tagger.zip
# 解压放到 nltk_data/taggers 路径下

# 查看 nltk_data 路径
python -c "import nltk; print(nltk.data.path)"

问题5:使用 PaddleOCR 出现错误

  • 错误ModuleNotFoundError: No module named 'tools.infer'
  • 原因:import 路径问题
  • 解决:修改 paddleocr 文件中的 import 语句
python
# 原代码
from tools.infer import ...

# 修改为
from paddleocr.tools.infer import ...

问题6:解决 MOSS 模型加载错误

  • 错误get_class_from_dynamic_module() missing 2 required positional arguments
  • 解决:修改 langchain-ChatGLM/models/moss_llm.py 第33行
python
# 原代码
cls = get_class_from_dynamic_module(
    class_reference="fnlp/moss-moon-003-sft--modeling_moss.MossForCausalLM",
    pretrained_model_name_or_path=llm_model_dict['moss']
)

# 修改为
cls = get_class_from_dynamic_module(
    pretrained_model_name_or_path="fnlp/moss-moon-003-sft",
    module_file="modeling_moss.py",
    class_name="MossForCausalLM"
)

# 模型文件放置路径:langchain-ChatGLM/fnlp/moss-moon-003-sft/

问题7:解决 MOSS 提问出现错误

  • 错误RuntimeError: probability tensor contains either inf, nan or element < 0
  • 解决:移除 do_sample=True 参数

📝通俗解释:这些坑都是实战中踩出来的。高版本gradio有bug要降级;detectron2安装麻烦不如用paddleocr;PDF加载需要系统级依赖;nltk数据要手动下载;模型加载参数要填对。这些都是"学费"啊!


总结

RAG(检索增强生成)是大模型应用的重要技术方向,核心在于:

  1. 构建高质量知识库:合理的文档切分、关键信息提取
  2. 优化检索效率:向量检索、层次索引、多路召回
  3. 提升生成质量:Prompt 工程、模型微调
  4. 工程实践:处理各种依赖和环境问题

📝通俗解释:RAG就像给大模型配了一个"知识外挂"。它让AI回答问题时有据可查,而不是凭空编造。做好RAG,关键在于:找资料要快(检索)、找得要准(相关性)、答案要好(生成)。这是一套系统工程,需要不断优化和实践。

基于 MIT 许可发布