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倍效率的提升。

流程图

总结

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

相关推荐
AIGC大时代2 小时前
如何使用ChatGPT辅助文献综述,以及如何进行优化?一篇说清楚
人工智能·深度学习·chatgpt·prompt·aigc
bastgia13 小时前
Tokenformer: 下一代Transformer架构
人工智能·机器学习·llm
吕小明么17 小时前
OpenAI o3 “震撼” 发布后回归技术本身的审视与进一步思考
人工智能·深度学习·算法·aigc·agi
新智元18 小时前
李飞飞谢赛宁:多模态 LLM「空间大脑」觉醒,惊现世界模型雏形!
人工智能·llm
RWKV元始智能1 天前
RWKV-7:极先进的大模型架构,长文本能力极强
人工智能·llm
聆思科技AI芯片1 天前
实操给桌面机器人加上超拟人音色
人工智能·机器人·大模型·aigc·多模态·智能音箱·语音交互
minos.cpp1 天前
Mac上Stable Diffusion的环境搭建(还算比较简单)
macos·ai作画·stable diffusion·aigc
AI小欧同学1 天前
【AIGC-ChatGPT进阶副业提示词】育儿锦囊:化解日常育儿难题的实用指南
chatgpt·aigc
剑盾云安全专家1 天前
AI加持,如何让PPT像开挂一键生成?
人工智能·aigc·powerpoint·软件
zaim12 天前
计算机的错误计算(一百八十七)
人工智能·ai·大模型·llm·错误·正弦/sin·误差/error