LLM - RAG文档应用处理与召回经验之谈

一、前言

本人最近一直从事开发 RAG(Retrieval Augmented Generation) 相关应用的一些开发上。其中一款项目的主要能力是用户上传文档后再提问,最后由 LLM 分析后解答一切关于此文档相关问题。这样一款知识库应用。相信大家都有用过此类似的使用体验。

原理与现状

先简单介绍一下原理, 我们对于此类知识库应用,一般处理分两块。第一块是文档处理,另一块是 文档 Retriever (召回)。文档处理分为以下几步:

  1. 文档解析 - 对上传的文档进行解析成文本内容
  2. 文档切片 - 切成若干 chunk
  3. 向量存储 - 利用 embedding 模型将文本转换成向量,存放在向量数据库中。

文档召回分为以下几步:

  1. 向量搜索 - 利用余弦相似度算法活动与提问内容相关文档 chunk
  2. LLM请求 - 将获取到的内容,放到LLM 对话上下文中
  3. LLM返回 - LLM 根据你的上下文来回答问题

问题

实际上,我们如果不考虑模型因素 ,仅仅只靠上述方案做的话,会碰到很多问题:

  1. 如何保证文档切片不会造成相关内容的丢失? 比如 我有一段文本,刚好是完整的,如果从中间切开,那么则会造成信息丢失,给 LLM 的内容则不完整。
  2. 文档切片的大小如何控制? 太小则 容易造成信息丢失,太大则不利于向量检索命中。
  3. 文档召回过程中如何保证召回内容跟问题是相关的? embedding 模型 可能从未见过你文档的内容,也许你的文档的相似词也没有经过训练。所以不能保证召回的内容就非常准确,不准确则导致LLM回答容易产生幻觉(简而言之就是胡说八道)。

经验

目前 LangChain 在关于上述问题上,都有一套成熟的解决方案,在此我将介绍一下,如何用 LangChain 解决上述我所提到的问题。从而让模型回答的更好一些。

二、方案详解

文档处理

首先,我们采用 LangChain 的 MultiVector Retriever ,它的主要能力则是在做向量存储的过程进一步增强文档的检索能力。之前 LangChain 有 Parent Document Retriever 采用的方案是用小分块保证尽可能找到更多的相关内容,用大分块保证内容完整性, 这里的大块文档是指 Parent Document 。

为什么需要将 chunk 拆分大块和小块。这样的好处在于, 我们检索的文档可以保持一个细粒度,通过小块容易命中关键内容,但给LLM 的文档尽量保持一个完整通顺的内容,避免模型幻觉,所以在 Retriever 阶段返回的是大块的内容。

MultiVector RetrieverParent Document Retriever 基础之上做了能力扩充。有了 Parent Document Retriever 那么为什么还有 MultiVector Retrieve ? 这是在因为文档处理的时候, 我们希望进一步的增强检索能力。 比如下图的 summary 和 hypothetical,这是让 LLM 在回答之前,提前对文档做一个分析。请看下图,这个时序图解释整个 MultiVector 的执行过程。

对 Parent Document 内容让 LLM 提前进行总结和提出一些假设性问题。 然后再将这三块内容(小块 chunk 的文档,总结性内容 和 假设问题 ) 存入向量数据库,并用 Parent Document ID 做关联。 进一步增强向量库的检索能力。

注意:( Parent Document ,长度控制有一定的讲究,要根据你的模型可以允许的长度来,但是不能打满,因为系统提示词占了一部分,保持Parent Document维持在 2 到3 个给 LLM)

文档召回

在召回阶段,我看到这样一个开源项目很有启发 sec-insights 它会将用户问题,采用多个不同的视角去提问,然后 LLM 会得出最终结果。当然如果这样回答 LLM 要调用多次,效率不高。我尝试改变了一下。

这里可以采用 LangChain 的 MultiQueryRetriever 主要原理则是 利用 LLM 尝试生成多个不同视角的问题,然后分别用这些问题做召回,然后再汇总。

比如下面实验的这个问题 "xx有哪些最新的功能?"

就比如生成了这样一系列的问题:

  1. xx有哪些最新的功能?
  2. 最新的xx功能有哪些?
  3. xx有什么最新的功能可以使用?
  4. 最新的功能是否已经在xx中推出?
  5. xx的新功能有哪些值得关注?

这样做的目的,我觉得是大多数人在问问题的过程中,如果不懂 prompt 工程,往往不专业,要么问题过于简单化,要么有歧义,意图不明显。那么向量搜索也是不准确的,导致LLM回答的效果不好。所以需要 LLM 进行问题的修正和多方位解读。

最后我们看一下,在召回阶段如何利用上面的 summary 和 hypothetical ,进一步提高召回准确率?

我是这样处理的, 根据 多个 question , 召回三份小的 chunk 数据。 见代码如下:

ini 复制代码
unique_docs_hypo = qdrant.search(vectordb_hypo,query_list,0.8,1)
unique_docs_sum = qdrant.search(vectordb_sum,query_list,0.7,2)
unique_docs_vec = qdrant.search(vectordb,[question] + query_list,0.6,20)
# 合并召回结果
unique_docs = unique_docs_hypo + unique_docs_sum + unique_docs_vec

我们将 hypothetical 的召回分数定高一点, 命中 hypothetical ,一般是较为明确的内容,设为0.8分 (非常高了,实验结果是,假设的问题几乎一样才能命中)

分数以此类推。然后得到一个总的 chunk docs 列表。 然后反查出 Parent Document List 丢给大模型去回答。

文档重排

最后一步则是文档重排 来自此论文 : arxiv.org/abs/2307.03... LLM 对位置是相对比较敏感的,得分好的放在首或尾,LLM会重点关注。那么重排后,通过实验,模型回答的效果的确要好一些。在 LangChain 中也实现了此重排的方案 LongContextReorder (详细见文档)

Embeddings

目前在 huggingface 公开的 Embedding 不少,目前中文能力较好的不多。本文选用了目前中文 SOTA bge-large-zh LangChain 已经整合此模型,可以非常简单的使用

LangChain 提供了 CacheBackedEmbeddings , 可以提高 embedings 的二次加载和解析的效率,首次正常速度,后续有一个 3倍效率的提升。

流程图

总结

其实我也准备了一些示例,来演示和对比一下改进的效果。目前只对内演讲,对外不方便透露。但在召回效果和回答上,有了很大的提升。但是我觉得还存在进一步优化的空间,比如用户的问题完全不相关怎么处理? 文档向量搜索完全不准(大概率未训练,内容属于专业知识),如何弥补。等等。其他后续能够继续为大家带来深入的分享和交流。重点:此文章点赞过千则开源代码。

相关推荐
春末的南方城市5 小时前
FLUX的ID保持项目也来了! 字节开源PuLID-FLUX-v0.9.0,开启一致性风格写真新纪元!
人工智能·计算机视觉·stable diffusion·aigc·图像生成
我算是程序猿8 小时前
用AI做电子萌宠,快速涨粉变现
人工智能·stable diffusion·aigc
哪 吒9 小时前
吊打ChatGPT4o!大学生如何用上原版O1辅助论文写作(附论文教程)
人工智能·ai·自然语言处理·chatgpt·aigc
爱喝白开水a9 小时前
关于大模型在企业生产环境中的独立部署问题
人工智能·深度学习·llm·大语言模型·ai大模型·计算机技术·本地部署大模型
Langchain10 小时前
不可错过!CMU最新《生成式人工智能大模型》课程:从文本、图像到多模态大模型
人工智能·自然语言处理·langchain·大模型·llm·大语言模型·多模态大模型
幽影相随11 小时前
构建llama.cpp并在linux上使用gpu
llm·llama.cpp
AI绘画君12 小时前
Stable Diffusion绘画 | AI 图片智能扩充,超越PS扩图的AI扩图功能(附安装包)
人工智能·ai作画·stable diffusion·aigc·ai绘画·ai扩图
AAI机器之心12 小时前
LLM大模型:开源RAG框架汇总
人工智能·chatgpt·开源·大模型·llm·大语言模型·rag
乔代码嘚15 小时前
AI2.0时代,普通小白如何通过AI月入30万
人工智能·stable diffusion·aigc
XiaoLiuLB16 小时前
ChatGPT Canvas:交互式对话编辑器
人工智能·自然语言处理·chatgpt·编辑器·aigc