数据检索(Indexes)| 豆包MarsCode AI 刷题

103 阅读10分钟

1.RAG RAG全称为Retrieval-Augmented Generation,即检索增强生成,它结合了检索和生成的能力,为文本序列生成任务引入外部知识。RAG将传统的语言生成模型与大规模的外部知识库相结合,使模型在生成响应或文本时可以动态地从这些知识库中检索相关信息。这种结合方法旨在增强模型的生成能力,使其能够产生更为丰富、准确和有根据的内容,特别是在需要具体细节或外部事实支持的场合。 RAG 的工作原理可以概括为几个步骤: 检索:对于给定的输入(问题),模型首先使用检索系统从大型文档集合中查找相关的文档或段落。这个检索系统通常基于密集向量搜索,例如ChromaDB、Faiss这样的向量数据库。 上下文编码:找到相关的文档或段落后,模型将它们与原始输入(问题)一起编码。 生成:使用编码的上下文信息,模型生成输出(答案)。这通常当然是通过大模型完成的。 RAG 的一个关键特点是,它不仅仅依赖于训练数据中的信息,还可以从大型外部知识库中检索信息。这使得RAG模型特别适合处理在训练数据中未出现的问题。 RAG类的任务,目前企业实际应用场景中的需求量相当大,也是LangChain所关注的一个重点内容。在这节课中,我会对LangChain中所有与之相关的工具进行一个梳理,便于你把握LangChain在这个领域中都能够做到些什么。

2.文档加载 RAG的第一步是文档加载。LangChain 提供了多种类型的文档加载器,以加载各种类型的文档(HTML、PDF、代码),并与该领域的其他主要提供商如 Airbyte 和 Unstructured.IO 进行了集成。 文档加载器: TextLoader CSVLoader HTMLLoader JSONLoader MarkdownLoader PDFLoader

3.文本转换 加载文档后,下一个步骤是对文本进行转换,而最常见的文本转换就是把长文档分割成更小的块(或者是片,或者是节点),以适合模型的上下文窗口。LangChain 有许多内置的文档转换器,可以轻松地拆分、组合、过滤和以其他方式操作文档。

4.文本分割器 把长文本分割成块听起来很简单,其实也存在一些细节。文本分割的质量会影响检索的结果质量。理想情况下,我们希望将语义相关的文本片段保留在一起。 LangChain中,文本分割器的工作原理如下: 1)将文本分成小的、具有语义意义的块(通常是句子)。 2)开始将这些小块组合成一个更大的块,直到达到一定的大小。 3)一旦达到该大小,一个块就形成了,可以开始创建新文本块。这个新文本块和刚刚生成的块要有一些重叠,以保持块之间的上下文。 因此,LangChain提供的各种文本拆分器可以帮助你从下面几个角度设定你的分割策略和参数: 文本如何分割 1)块的大小 2)块之间重叠文本的长度 3)这些文本分割器的说明和示例如下: 字符分割器:基于单个字符进行分割,分块大小根据字符数来测量,默认分割字符为“\n\n” 递归字符分割器:用一个分隔符列表进行分割,按照表中的分割符直到块足够小,默认分割字符为[“\n\n”,“\n”,“ ”,“”] Markdown标题分割器:根据指定的标题来分割Markdown文件,可以根据“#”或“##”进行分割。 令牌分割器(Token Splitter):分割文本事考虑令牌数量。 你可能会关心,文本分割在实践,有哪些具体的考量因素,我总结了下面几点。 首先,就是LLM 的具体限制。GPT-3.5-turbo支持的上下文窗口为4096个令牌,这意味着输入令牌和生成的输出令牌的总和不能超过4096,否则会出错。为了保证不超过这个限制,我们可以预留约2000个令牌作为输入提示,留下约2000个令牌作为返回的消息。这样,如果你提取出了五个相关信息块,那么每个片的大小不应超过400个令牌。 此外,文本分割策略的选择和任务类型相关。 需要细致查看文本的任务,最好使用较小的分块。例如,拼写检查、语法检查和文本分析可能需要识别文本中的单个单词或字符。垃圾邮件识别、查找剽窃和情感分析类任务,以及搜索引擎优化、主题建模中常用的关键字提取任务也属于这类细致任务。 需要全面了解文本的任务,则使用较大的分块。例如,机器翻译、文本摘要和问答任务需要理解文本的整体含义。而自然语言推理、问答和机器翻译需要识别文本中不同部分之间的关系。还有创意写作,都属于这种粗放型的任务。 最后,你也要考虑所分割的文本的性质。例如,如果文本结构很强,如代码或HTML,你可能想使用较大的块,如果文本结构较弱,如小说或新闻文章,你可能想使用较小的块。 你可以反复试验不同大小的块和块与块之间重叠窗口的大小,找到最适合你特定问题的解决方案。

5.其他形式的文本转换 除拆分文本之外,LangChain中还集成了各种工具对文档执行的其他类型的转换。下面让我们对其进行逐点分析。 1)过滤冗余的文档:使用 EmbeddingsRedundantFilter 工具可以识别相似的文档并过滤掉冗余信息。这意味着如果你有多份高度相似或几乎相同的文档,这个功能可以帮助识别并删除这些多余的副本,从而节省存储空间并提高检索效率。 2)翻译文档:通过与工具 doctran 进行集成,可以将文档从一种语言翻译成另一种语言。 3)提取元数据:通过与工具 doctran 进行集成,可以从文档内容中提取关键信息(如日期、作者、关键字等),并将其存储为元数据。元数据是描述文档属性或内容的数据,这有助于更有效地管理、分类和检索文档。 4)转换对话格式:通过与工具 doctran 进行集成,可以将对话式的文档内容转化为问答(Q/A)格式,从而更容易地提取和查询特定的信息或回答。这在处理如访谈、对话或其他交互式内容时非常有用。 所以说,文档转换不仅限于简单的文本拆分,还可以包含附加的操作,这些操作的目的都是更好地准备和优化文档,以供后续生成更好的索引和检索功能。

6.文本嵌入 文本块形成之后,我们就通过LLM来做嵌入(Embeddings),将文本转换为数值表示,使得计算机可以更容易地处理和比较文本。OpenAI、Cohere、Hugging Face 中都有能做文本嵌入的模型。 Embeddings 会创建一段文本的向量表示,让我们可以在向量空间中思考文本,并执行语义搜索之类的操作,在向量空间中查找最相似的文本片段。 它提供两种方法: 第一种是 embed_documents 方法,为文档创建嵌入。这个方法接收多个文本作为输入,意味着你可以一次性将多个文档转换为它们的向量表示。 第二种是 embed_query 方法,为查询创建嵌入。这个方法只接收一个文本作为输入,通常是用户的搜索查询。 为什么需要两种方法?虽然看起来这两种方法都是为了文本嵌入,但是LangChain将它们分开了。原因是一些嵌入提供者对于文档和查询使用的是不同的嵌入方法。文档是要被搜索的内容,而查询是实际的搜索请求。这两者可能因为其性质和目的,而需要不同的处理或优化。

7.向量数据库(向量存储) 更常见的存储向量的方式是通过向量数据库(Vector Store)来保存它们。LangChain支持非常多种向量数据库,其中有很多是开源的,也有很多是商用的。比如Elasticsearch、Faiss、Chroma和Qdrant等等。 这就涉及到许多技术和业务层面的考量,你应该根据具体需求进行选型。 1)数据规模和速度需求:考虑你的数据量大小以及查询速度的要求。一些向量数据库在处理大规模数据时更加出色,而另一些在低延迟查询中表现更好。 2)持久性和可靠性:根据你的应用场景,确定你是否需要数据的高可用性、备份和故障转移功能。 3)易用性和社区支持:考虑向量数据库的学习曲线、文档的完整性以及社区的活跃度。 4)成本:考虑总体拥有成本,包括许可、硬件、运营和维护成本。 5)特性:考虑你是否需要特定的功能,例如多模态搜索等。 6)安全性:确保向量数据库符合你的安全和合规要求。 在进行向量数据库的评测时,进行性能基准测试是了解向量数据库实际表现的关键。这可以帮助你评估查询速度、写入速度、并发性能等。 没有“最好”的向量数据库,只有“最适合”的向量数据库。在你的需求上做些研究和测试,确保你选择的向量数据库满足你的业务和技术要求就好。

8.索引 在本节课的最后,我们来看看LangChain中的索引(Index)。简单的说,索引是一种高效地管理和定位文档信息的方法,确保每个文档具有唯一标识并便于检索。 尽管在第3课的示例中,我们并没有显式的使用到索引就完成了一个RAG任务,但在复杂的信息检索任务中,有效地管理和索引文档是关键的一步。LangChain 提供的索引 API 为开发者带来了一个高效且直观的解决方案。具体来说,它的优势包括: 避免重复内容:确保你的向量存储中不会有冗余数据。 只更新更改的内容:能检测哪些内容已更新,避免不必要的重写。 省时省钱:不对未更改的内容重新计算嵌入,从而减少了计算资源的消耗。 优化搜索结果:减少重复和不相关的数据,从而提高搜索的准确性。 LangChain 利用了记录管理器(RecordManager)来跟踪哪些文档已经被写入向量存储。 在进行索引时,API 会对每个文档进行哈希处理,确保每个文档都有一个唯一的标识。这个哈希值不仅仅基于文档的内容,还考虑了文档的元数据。 一旦哈希完成,以下信息会被保存在记录管理器中: 文档哈希:基于文档内容和元数据计算出的唯一标识。 写入时间:记录文档何时被添加到向量存储中。 源 ID:这是一个元数据字段,表示文档的原始来源。 这种方法确保了即使文档经历了多次转换或处理,也能够精确地跟踪它的状态和来源,确保文档数据被正确管理和索引。