照片由 Jayne Harris 在 Unsplash 提供
构建检索增强生成(RAG)应用程序存在分层的挑战。文档检索是RAG工作流的重要部分,本身是一系列复杂的步骤,这些步骤可以根据使用场景以不同的方式进行处理。
对于RAG系统来说,很难找到与复杂输入提示最相关的文档集,尤其是在完全依赖向量搜索来寻找最佳候选文档时。然而,通常我们的文档本身会告诉我们应该在哪里查找某个主题的更多信息——通过引用、交叉引用、脚注、超链接等。在这篇文章中,我们将展示一种新的数据模型——链接文档——如何通过解析和保留这些直接指向其他文本的引用,从而解锁性能提升,使它们可以同时被检索——无论它们是否被向量搜索遗漏。
AI 捕捉复杂性,但不捕捉结构当回答复杂或细微的问题时,这些问题需要来自不同文档的支持细节,RAG系统常常难以找到所有相关的文档,以提供一个全面且充分的回答。然而,我们仍然几乎完全依赖于文本嵌入和向量相似性来定位和检索相关文档。
一个经常被忽视的事实:在解析、切分和嵌入文本的过程中,会丢失大量的文档信息。文档结构——包括章节层次、标题、脚注、交叉引用、引用和超链接——在典型的文本到向量的工作流程中几乎完全丢失,除非我们采取具体措施来保留它们。当结构和元数据告诉我们哪些文档与我们正在阅读的内容直接相关时,为什么不应该保留这些信息呢?
特别是,在典型的分块和嵌入过程中会忽略链接和引用,这意味着它们不能被AI用于帮助回答查询。但是,链接和引用是很有价值的信息,通常指向更有用的文档和文本——在查询时,为什么我们不检查这些目标文档以确定它们是否有用呢?
程序化地解析和跟随链接及引用并不难,在本文中,我们介绍了一种简单而强大的实现方法,该方法专为检索增强生成(RAG)系统设计。我们展示了如何使用文档链接来保持文档片段之间已知连接的完整,这些连接是典型的向量嵌入和检索可能无法建立的。
文档连接在向量空间中丢失在向量存储库中的文档本质上是知识嵌入到高维向量空间中的片段。这些向量实际上是LLM的内部“语言”——给定一个LLM及其所有内部参数值,包括之前的上下文和状态,向量是模型生成文本的起点。因此,向量存储库中的所有向量都是LLM可能用来生成响应的嵌入文档,同样地,我们将提示嵌入到向量中,然后在语义向量空间中搜索最近的邻居。这些最近的邻居对应于可能包含可以回答提示信息的文档。
在向量存储中,向量的接近程度表示文档在语义上的相似性,但这种相似性并没有真正的连接概念。然而,彼此接近的文档(通常一起检索)可以被视为一种知识之间的连接,形成一个隐式的知识图谱,其中每段文本都与其最近的邻居相连。这种构建的知识图谱不会像大多数知识图谱那样静态或僵硬;随着新文档的添加或搜索参数的调整,它会发生变化。因此,这种隐式图谱虽然不是完美的比较,但它作为一个概念框架,有助于理解在RAG系统中文档检索的工作原理。
在谈到实际世界中的知识——与向量表示相对比——语义相似性只是文本片段之间众多关联方式之一。即使在计算机和数据的数字表示出现之前,我们已经连接知识数百年了:词汇表、索引、目录、目录表、词典和交叉引用都是连接知识片段的方式。在软件中实现这些功能非常简单,但它们通常未被包含在向量存储、RAG系统和其他生成式AI应用中。我们的文档正在告诉我们哪些其他知识是重要和相关的;我们只需要给我们的知识库赋予理解和跟随这些连接的能力。
基于文档链接的知识关联我们开发了文档链接功能,用于处理文档本身告诉我们哪些其他知识相关,但我们的向量存储未能捕捉到这些信息,导致文档检索过程出现不足的情况。文档链接是一种简单而强大的方法,用于表示文档之间的有向连接。它涵盖了我们传统上通过目录、词汇表、关键词等方式导航和发现知识的所有方式,当然还包括程序解析器最容易遵循的方式:超链接。这种链接文档的概念允许关系具有不对称性或带有定性元数据,以便进行过滤或其他用途。链接不仅易于理解和操作,还能高效地扩展到大型动态数据集,支持强大和高效的检索。
链接的数据模型作为数据类型,文档链接非常简单。链接信息与文档向量一起存储为元数据。这意味着检索给定文档时会自动检索该文档的入链和出链信息。出链指向在文档上下文中可能有用的更多信息,入链显示哪些其他文档可能支持给定文档,双向(或无向)链接可以表示其他类型的连接。链接还可以带有进一步的元数据,提供可以用于链接或文档过滤、排名和图遍历算法的定性信息。
如在文章“通过消除边来扩展知识图谱”中所述,我们高效且可扩展的实现方法不像典型的图数据库那样单独存储每个链接,而是使用链接类型和链接组作为中间数据类型,这在图遍历时大大减少了存储和计算需求。例如,当两组文档紧密相关时,这种实现方法具有很大的优势。
假设我们有一组关于西雅图市的文档(称为组A),还有一组提到西雅图的文档(组B)。我们希望确保提到西雅图的文档能够找到所有关于西雅图市的文档,因此我们需要将它们链接起来。我们可以从组B中的所有文档创建到组A中所有文档的链接,但除非这两个组很小,否则这将创建很多链接!我们处理这个问题的方法是创建一个代表关键词“Seattle”(kw:seattle
)的链接类型对象,然后从组B中的文档创建到这个kw:seattle
对象的有向链接,同时从kw:seattle
对象创建到组A中文档的链接。这样,每个文档需要存储的链接数量大大减少——每个文档只需要一个链接——并且没有丢失任何信息。
RAG系统中的检索过程的主要目标是找到一组足以回答给定查询的文档。标准的向量搜索和检索会找到在语义上最“相关”的文档,但如果文档的整体内容与查询内容不完全匹配,可能会错过一些支持文档。
例如,假设我们有一个大型文档集,其中包括上述与西雅图相关的文档。我们有以下关于西雅图著名地标太空针塔的提示:
“什么靠近太空针塔?”
从这个提示开始的向量搜索会直接检索出提到太空针塔的文档,因为从语义内容的角度来看,太空针塔是提示文本中最突出的特征。提到太空针塔的文档很可能还会提到它位于西雅图。如果不使用任何文档链接,一个基于检索增强生成(RAG)的系统将主要依赖于提到太空针塔的文档来尝试回答提示,而无法保证其他可能有帮助但没有直接提到太空针塔的文档也会被检索和使用。
下面,我们基于这个 Space Needle 数据集和查询构建一个实际示例(附带代码!)。继续阅读以了解当不使用链接时,RAG 系统可能会错过有用的文档,然后通过简单地遵循原始文档本身包含的链接信息,再次“找到”有用的文档。
文档链接的实际应用为了说明文档链接是如何工作的,以及它如何能够建立文档之间可能被忽略的联系,让我们来看一个简单的例子。
我们将从两个相关的文档开始,这些文档包含来自维基百科页面的一些文本:一个文档来自Space Needle页面,另一个文档来自Space Needle所在的Lower Queen Anne社区页面。Space Needle文档包含指向Lower Queen Anne文档的HTML链接,但反之则不然。关于Space Needle的文档开头如下所示:
'url': 'https://en.wikipedia.org/wiki/Space_Needle'
Space Needle 是位于美国华盛顿州西雅图市的一座观景塔。被视为该市的标志,它已被指定为西雅图地标。位于Lower Queen Anne社区,它是在1962年世界博览会期间为西雅图中心建造的,吸引了超过230万游客...
除了这两个来自真实、有信息量的来源的文档外,我们还添加了四个非常短且无信息量的文档——其中两个提到太空针塔,两个没有提到。这些文档(及其虚假的URL)旨在成为无关或无信息量的文档,例如仅评论太空针塔和西雅图的社交媒体帖子,例如:
“太空针塔非常高。”
和
“Queen Anne 是一个人。”
完整的文档集包含在 这个 Colab 笔记本 中。它们是 HTML 文档,我们使用 BeautifulSoup4 以及来自 [LangChain 的 [HtmlLinkExtractor](https://python.langchain.com/v0.2/api_reference/community/graph_vectorstores/langchain_community.graph_vectorstores.extractors.html_link_extractor.HtmlLinkExtractor.html)
]LangChain 进行处理,并使用 add_links
函数将这些链接添加回 Document
对象,以便我们可以在 GraphVectorStore
中使用它们。GraphVectorStore
是最近添加到 LangChain 代码库 的新功能,由我的同事在 DataStax 贡献。所有这些内容都是开源的。
每个文档的处理过程如下:
from langchain_core.documents import Document
from langchain_core.graph_vectorstores.links import add_links
from langchain_community.graph_vectorstores.extractors.html_link_extractor import HtmlInput, HtmlLinkExtractor
soup_doc = BeautifulSoup(html_doc, 'html.parser')
doc = Document(
page_content=soup_doc.get_text(),
metadata={"source": url}
)
doc.metadata['content_id'] = url # 用于链接指向此文档的ID
html_link_extractor = HtmlLinkExtractor()
add_links(doc, html_link_extractor.extract_one(HtmlInput(soup_doc, url)))
使用 cassio
,我们初始化 GraphVectorStore
如下:
from langchain_openai import OpenAIEmbeddings
from langchain_community.graph_vectorstores.cassandra import CassandraGraphVectorStore
# 创建一个 GraphVectorStore,结合向量节点和图边。
EMBEDDING = 'text-embedding-3-small'
gvstore = CassandraGraphVectorStore(OpenAIEmbeddings(model=EMBEDDING))
我们按照标准方式设置了 RAG 链的 LLM 和其他辅助工具 —— 详情请参阅笔记本。请注意,虽然这里几乎使用的所有工具都是开源的,但在笔记本中我们使用了两个 SaaS 产品,分别是 OpenAI 和 DataStax 的 Astra —— 分别是 LLM 和向量数据存储,这两个产品都有免费使用层级。有关替代方案,请参阅 LangChain 文档。
使用默认设置运行RAG我们可以使用 depth=0
的图检索器来端到端地运行 RAG 系统——这意味着完全不进行图遍历,并使用其他默认参数如下:
retriever = gvstore.as_retriever(
search_kwargs={
"depth": 0, # 图遍历的深度;0 表示完全不进行遍历
}
)
这将输出类似的内容:
问题:
Space Needle附近有什么?
检索到的文档:
['https://TheSpaceNeedleisGreat',
'https://TheSpaceNeedleisTALL',
'https://en.wikipedia.org/wiki/Space_Needle',
'https://SeattleIsOutWest',
'https://en.wikipedia.org/wiki/Lower_Queen_Anne,_Seattle',
'https://QueenAnneWasAPerson']
LLM响应:
('Space Needle位于Lower Queen Anne社区附近,包括Climate Pledge Arena、展览厅、McCaw Hall、Cornish Playhouse、Bagley Wright Theater、KEXP广播电台的录音室、SIFF Cinema Uptown和On the Boards。')
当然,在实际场景中,RAG系统不会像我们这里这样做,检索整个文档集。
更现实的场景:我们无法检索到所有文档在某些情况下,为每个查询检索所有文档是不切实际甚至不可能的。这也违背了使用向量搜索的初衷。在所有现实场景中,每个查询只能检索一小部分文档,因此确保最相关和最有帮助的文档出现在列表顶部非常重要。
为了使我们的示例更贴近实际情况,让我们将检索器的设置改为 k=3
,这意味着每次向量搜索最多返回三个文档。这表示在总共六个文档中,最不相似或不相关的三个文档将不会被包含在返回的文档集中。我们可以像这样更改检索器的设置:
retriever = gvstore.as_retriever(
search_kwargs={
"depth": 0, # 图遍历的深度;0 表示完全不进行遍历
"k": 3 # 初始向量搜索返回的文档数量——不包括图中的链接
}
)
使用这些设置查询系统得到如下输出:
问题:
Space Needle附近有什么?
检索到的文档:
['https://TheSpaceNeedleisGreat',
'https://TheSpaceNeedleisTALL',
'https://en.wikipedia.org/wiki/Space_Needle']
LLM响应:
('上下文并未提供关于Space Needle附近有什么的具体信息。它只提到Space Needle位于Lower Queen Anne社区,并为1962年西雅图世博会而建造。')
我们可以看到,由于现在只能访问文档集的一半,而不是之前可以访问全部六份文档,最终的响应信息量明显减少了。
这里有一些需要注意的重要点。
- 被遗漏的一份文档是关于 Lower Queen Anne 的文档,这是唯一一份描述了太空针附近一些重要地点的文档。
- Lower Queen Anne 文档并没有特别提到太空针,而有三份其他文档提到了太空针。因此,初始查询“太空针附近有什么?”返回了那三份文档是有道理的。
- 主要关于太空针的文档中有一个直接链接到 Lower Queen Anne 的 HTML 链接,任何好奇的人可能会点击该链接来了解该地区。
- 没有任何链接或图遍历的概念,这个 RAG 系统检索了最语义相似的文档——包括两份不相关的信息——而错过了那份包含回答查询最多信息的文章。
现在,让我们看看文档链接如何影响结果。
下列链接找到缺失的信息对我们检索器的设置进行一个简单的更改——将 depth=1
——使得检索器能够跟随初始通过向量搜索获取的文档中的任何文档链接。(作为参考,请注意,如果将 depth=2
,不仅会跟随初始文档集中的链接,还会跟随结果文档集中的下一组链接——但我们目前还不这么做。)
我们像这样更改检索器的 depth
参数:
retriever = gvstore.as_retriever(
search_kwargs={
"depth": 1, # 图遍历的深度;0 表示完全不进行遍历
"k": 3 # 初始向量搜索返回的文档数量---不包括图链接
}
)
问题:
Space Needle附近有什么?
检索到的文档:
['https://TheSpaceNeedleisGreat',
'https://TheSpaceNeedleisTALL',
'https://en.wikipedia.org/wiki/Space_Needle',
'https://en.wikipedia.org/wiki/Lower_Queen_Anne,_Seattle']
LLM响应:
('Space Needle位于Lower Queen Anne社区,其中包括Climate Pledge Arena、展览厅、McCaw Hall、Cornish Playhouse、Bagley Wright Theater、KEXP广播电台的录音室、一家拥有三个屏幕的电影院(SIFF Cinema Uptown)以及On the Boards,一个前卫戏剧和音乐中心。')
我们可以看到,通过向量搜索检索出的前 k
个文档与之前的一样,但设置 depth=1
指示系统从这三篇文档中跟随链接,并将链接的文档也包括进来。因此,从 Space Needle 文档直接链接到 Lower Queen Anne 的文档也被包括在内,这使得 LLM 能够访问到所需的社区信息,从而正确回答查询。
这种结合了向量和图检索的混合方法可以显著提高RAG应用中结果的相关性和多样性。通过确保系统检索到最符合上下文且多样化的相关内容,可以减少幻觉并提高输出质量。
除了提高RAG系统响应质量之外,文档链接在生产系统中实现还有一些优势。这些优势包括:
- 无损 — 原始内容在节点中保持完整,确保在创建知识图谱的过程中不丢弃任何信息。这保留了数据的完整性,减少了随着需求变化而频繁重新索引的需要,并利用LLM从上下文线索中提取答案的能力。
- 无需干预 — 这种方法不需要专家干预来优化知识提取。相反,通过在现有的向量搜索管道中添加基于关键词、超链接或其他文档属性的边提取功能,可以自动添加链接。
- 可扩展 — 知识图谱的创建过程涉及对内容进行简单的操作,而无需使用LLM来生成知识图谱。
性能基准和更详细的文档链接扩展分析包含在之前提到的文章中:文章链接。
如以往一样,这里也有一些限制。如果你的文档集确实没有链接或其他结构,那么这里提出的方法可能不会有太大效果。此外,虽然构建和遍历图连接可以非常强大,但也为检索过程增加了复杂性,这可能会使调试和优化变得具有挑战性,尤其是在遍历深度达到2或更大的图时。
总体而言,将文档链接融入到检索增强生成(RAG)系统中,结合了传统、确定性软件方法、图算法和现代AI技术的优势。通过明确定义文档之间的链接,我们增强了AI在导航知识方面的能力,使其像人类研究人员一样进行操作,不仅提高了检索的准确性,还增强了响应的上下文深度。这种方法创建了更强大、更灵活的系统,与人类寻求和使用知识的复杂方式更加一致。
开始使用文档链接本文的完整代码可以在 这个 Colab 笔记本 中找到。另外,可以查看我的同事在 DataStax 发表的 这篇入门博客文章,或者查阅 LangChain 中的 [G](https://api.python.langchain.com/en/latest/community_api_reference.html#module-langchain_community.graph_vectorstores)raphVectorStore
文档,获取详细的 API 信息以及如何使用文档链接来增强您的 RAG 应用程序并突破您的知识系统所能实现的界限。
由 Brian Godsey, Ph.D. (LinkedIn )撰写 — 数学家、数据科学家和工程师 // 在 DataStax 从事AI产品工作 // 著有书籍 像数据科学家一样思考
共同學習,寫下你的評論
評論加載中...
作者其他優質文章