【大模型(LLMs)RAG 检索增强生成 面经】
1 RAG 基础面
1.1 为什么大模型需要外挂 (向量) 知识库?
如何将外部知识注入大模型,最直接的方法:利用外部知识对大模型进行微调。
思路: 构建几十万量级的数据,然后利用这些数据 对大模型进行微调,以将 额外知识注入大模型
优点: 简单粗暴
缺点:
这几十万量级的数据 并不能很好的将额外知识注入大模型;
训练成本昂贵。不仅需要 多卡并行,还需要 训练很多天;
既然大模型微调不是将外部知识注入大模型的最优方案,那是否有其它可行方案?
1.2. RAG 思路是怎么样?
⚫ 加载文件
⚫ 读取文本
⚫ 文本分割
⚫ 文本向量化
⚫ 问句向量化
⚫ 在文本向量中匹配出与问句向量最相似的 top k 个
⚫ 匹配出的文本作为上下文和问题一起添加到 prompt 中
⚫ 提交给 LLM 生成回答

1.3 RAG 核心技术
RAG 核心技术:embedding
**思路:**将用户知识库内容经过 embedding 存入向量知识库,然后用户每一次提问也会经过embedding,利用向量相关性算法(例如余弦算法)找到最匹配的几个知识库片段,将这些知识库片段作为上下文,与用户问题一起作为 promt 提交给 LLM 回答。
RAG prompt 模板构建
已知信息:
{context}
根据上述已知信息,简洁和专业的来回答用户的问题。如果无法从中得到答案,请说 “根据已知信息无法回答该问题” 或 “没有提供足够的相关信息”,不允许在答案中添加编造成分,答案请使用中文。
问题是:{question}
2. RAG 优化面
痛点 1:文档切分粒度不好把控,既担心噪声太多又担心语义信息丢失
问题描述
问题 1:如何让 LLM 简要、准确回答细粒度知识?
用户:2023 年我国上半年的国内生产总值是多少?
LLM:根据文档,2023 年的国民生产总值是 593034 亿元。
需求分析: 一是简要,不要有其他废话。二是准确,而不是随意编造。
问题 2:如何让 LLM 回答出全面的粗粒度(跨段落)知识?
用户:根据文档内容,征信中心有几点声明?
LLM:根据文档内容,有三点声明,分别是:一、……;二……;三……。
需求分析:
要实现语义级别的分割,而不是简单基于 html 或者 pdf 的换行符分割。
笔者发现目前的痛点是文档分割不够准确,导致模型有可能只回答了两点,而实际上是因为向量相似度召回的结果是残缺的。
有人可能会问,那完全可以把切割粒度大一点,比如每 10 个段落一分。但这样显然不是最优的,因为召回片段太大,噪声也就越多。LLM 本来就有幻觉问题,回答得不会很精准(笔者实测也发现如此)。
所以说,我们的文档切片最好是按照语义切割。
解决方案:
思想(原则)
基于 LLM 的文档对话架构分为两部分,先检索,后推理。重心在检索(推荐系统),推理交给 LLM 整合即可。
而检索部分要满足三点 ①尽可能提高召回率,②尽可能减少无关信息;③速度快。
将所有的文本组织成二级索引,第一级索引是 [关键信息],第二级是 [原始文本],二者一一映射。
检索部分只对关键信息做 embedding,参与相似度计算,把召回结果映射的 原始文本交给LLM。主要架构图如下:

如何构建关键信息
首先从架构图可以看到,句子、段落、文章都要关键信息,如果为了效率考虑,可以不用对句子构建关键信息。
文章的切分及关键信息抽取
关键信息为各语义段的关键信息集合,或者是各个子标题语义扩充之后的集合(pdf 多级标题识别及提取见下一篇文章)。
语义切分方法 1
利用 NLP 的篇章分析(discourse parsing)工具,提取出段落之间的主要关系,譬如上述极端情况 2 展示的段落之间就有从属关系。把所有包含主从关系的段落合并成一段。这样对文章切分完之后保证每一段在说同一件事情。
语义切分方法 2
除了 discourse parsing 的工具外,还可以写一个简单算法利用 BERT 等模型来实现语义分割。BERT 等模型在预训练的时候采用了 NSP(next sentence prediction)的训练任务,因此 BERT 完全可以判断两个句子(段落)是否具有语义衔接关系。这里我们可以设置相似度阈值 t,从前往后依次判断相邻两个段落的相似度分数是否大于 t,如果大于则合并,否则断开。当然算法为了效率,可以采用二分法并行判定,模型也不用很大,笔者用 BERT-base-Chinese 在中文场景中就取得了不错的效果。
语义段的切分及段落(句子)关键信息抽取
如果向量检索效率很高,获取语义段之后完全可以按照真实段落及句号切分,以缓解细粒度知识点检索时大语块噪声多的场景。当然,关键信息抽取笔者还有其他思路。
方法 1
利用 NLP 中的成分句法分析(constituency parsing)工具和命名实体识别(NER)工具提取。成分句法分析(constituency parsing)工具可以提取核心部分(名词短语、动词短语……);命名实体识别(NER)工具可以提取重要实体(货币名、人名、企业名……)。譬如说:原始文本 “MM 团队的成员都是精英,核心成员是前谷歌高级产品经理张三,前 meta 首席技术官李四……”,关键信息为(MM 团队,核心成员,张三,李四)。
方法 2
可以用语义角色标注(Semantic Role Labeling)来分析句子的谓词论元结构,提取 “谁对谁做了什么” 的信息作为关键信息。
方法 3
直接法。其实 NLP 的研究中本来就有关键词提取工作(Keyphrase Extraction),也有一个成熟工具可以使用。一个工具是 HanLP,中文效果好,但是付费,免费版调用次数有限。还有一个开源工具是 KeyBERT,英文效果好,但是中文效果差。
方法 4
垂直领域建议的方法。以上两个方法在垂直领域都有准确度低的缺陷,垂直领域可以仿照 ChatLaw 的做法,即:训练一个生成关键词的模型。ChatLaw 就是训练了一个 KeyLLM。
常见问题
句子、语义段、之间召回不会有包含关系吗,是否会造成冗余?
会造成冗余,但是笔者试验之后回答效果很好,无论是细粒度知识还是粗粒度(跨段落)知识准确度都比 Longchain 粗分效果好很多,对这个问题笔者认为可以优化但没必要。
痛点 2:在基于垂直领域表现不佳
模型微调:一个是对 embedding 模型的基于垂直领域的数据进行微调;一个是对 LLM 模型的基于垂直领域的数据进行微调。
痛点 3:langchain 内置问答分句效果不佳问题
**文档加工:**使用更好的文档拆分的方式(如项目中已经集成的达摩院的语义识别的模型及进行拆分)。
改进填充的方式,判断中心句上下文的句子是否和中心句相关,仅添加相关度高的句子。
文本分段后,对每段分别及进行总结,基于总结内容语义及进行匹配。
痛点 4:如何尽可能召回与 query 相关的 Document 问题
**问题描述:**如何通过得到 query 相关性高的 context,即与 query 相关的 Document 尽可能多的能被召回。
解决方法:
将本地知识切分成 Document 的时候,需要考虑 Document 的长度、Document embedding 质量和被召回 Document 数量这三者之间的相互影响。在文本切分算法还没那么智能的情况下,本地知识的内容最好是已经结构化比较好了,各个段落之间语义关联没那么强。Document 较短的情况下,得到的 Document embedding 的质量可能会高一些,通过 Faiss 得到的 Document 与 query 相关度会高一些。
使用 Faiss 做搜索,前提条件是有高质量的文本向量化工具。因此最好是能基于本地知识对文本向量化工具进行 Finetune。另外也可以考虑将 ES 搜索结果与 Faiss 结果相结合。
痛点 5:如何让 LLM 基于 query 和 context 得到高质量的 response
**问题描述:**如何让 LLM 基于 query 和 context 得到高质量的 response。
解决方法:
尝试多个的 prompt 模版,选择一个合适的,但是这个可能有点玄学。
用与本地知识问答相关的语料,对 LLM 进行 Finetune。
痛点 6:embedding 模型在表示 text chunks 时偏差太大问题
问题描述:
一些开源的 embedding 模型本身效果一般,尤其是当 text chunk 很大的时候,强行变成一个简单的 vector 是很难准确表示的,开源的模型在效果上确实不如 openai Embeddings。
多语言问题,paper 的内容是英文的,用户的 query 和生成的内容都是中文的,这里有个语言之间的对齐问题,尤其是可以用中文的 query embedding 来从英文的 text chunking embedding 中找到更加相似的 top-k 是个具有挑战的问题。
解决方法:
用更小的 text chunk 配合更大的 topk 来提升表现,毕竟 smaller text chunk 用 embedding 表示起来 noise 更小,更大的 topk 可以组合更丰富的 context 来生成质量更高的回答。
多语言的问题,可以找一些更加适合多语言的 embedding 模型。
痛点 7:不同的 prompt,可能产生完全不同的效果问题
问题描述: prompt 是个神奇的东西,不同的提法,可能产生完全不同的效果。尤其是指令,指令型 llm 在训练或者微调的时候,基本上都有个输出模板,这个如果前期没有给出 instruction data 说明,需要做很多的尝试,尤其是你希望生成的结果是按照一定格式给出的,需要做更多的尝试。
痛点 8:llm 生成效果问题
问题描述: LLM 本质上是个 “接茬” 机器,你给上句,他补充下一句。但各家的 LLM 在理解 context 和接茬这两个环节上相差还是挺多的。最早的时候,是用一个付费的 GPT 代理作为 LLM 来生成内容,包括解读信息、中文标题和关键词,整体上来看可读性会强很多,也可以完全按照给定的格式要求生成相应的内容,后期非常省心;后来入手了一台 mac m2,用 llama.cpp 在本地提供 LLM 服务,模型尝试了 chinese-llama2-alpaca 和 baichuan2,量化用了 Q6_K level,据说性能和 fp16 几乎一样,作为开源模型,两个表现都还可以。前者是在 llama2 的基础上,用大量的中文数据进行了增量训练,然后再用 alpaca 做了指令微调,后者是开源届的当红炸子鸡。但从 context 的理解上,两者都比较难像 GPT 那样可以完全准确地生成我希望的格式,baichuan2 稍微好一些。我感觉,应该是指令微调里自带了一些格式,所以生成的时候有点 “轴”。
**解决思路:**可以选择一些好玩的开源模型,比如 llama2 和 baichuan2,然后自己构造一些 domain dataset,做一些微调的工作,让 llm 更听你的话。
痛点 9:如何更高质量地召回 context 喂给 llm
问题描述:初期直接调包 langchain 的时候没有注意,生成的结果总是很差,问了很多 Q 给出的 A 乱七八糟的,后来一查,发现召回的内容根本和 Q 没啥关系。
解决思路:更加细颗粒度地来做 recall,当然如果是希望在学术内容上来提升质量,学术相关的 embedding 模型、指令数据,以及更加细致和更具针对性的 pdf 解析都是必要的。
参考:PDFTriage: Question Answering over Long, Structured Documents
