Implementing advanced RAG strategies with Neo4j

使用 Neo4j 实现高级 RAG 策略

7 分钟阅读

编者按:我们很高兴分享这篇博文,因为它涵盖了我们在过去一个月中介绍的几种高级检索策略,特别是许多依赖于更改摄取步骤的策略。许多这些高级检索策略可以概括为更改文档的索引方式,以保留某种层次结构的概念。Neo4j 是一个令人兴奋的数据库,可用于这些任务,因为它可以将这些层次结构表示为图的一部分。这也使您可以轻松地在索引策略之间切换。

Tomaz 已经实现了一个包含四种不同 RAG 策略的 LangChain 模板。在此处查看:

检索增强生成应用程序似乎是 AI 应用程序的“Hello World”。如今,借助像 LangChain 这样的 LLM 框架库,您只需几分钟即可实现“与您的 PDF 聊天”应用程序。

“与您的 PDF 聊天”应用程序通常依赖于向量相似性搜索来检索相关信息,然后将这些信息馈送到 LLM 以生成返回给用户的最终答案。

向量相似性搜索用于检索相关信息。

最近,越来越明显的是,对于所有用例,朴素的向量相似性搜索可能不够准确。例如,我们已经看到了后退一步提示方法的引入,该方法强调从任务的直接细节中退后一步,专注于更高层次抽象的重要性。

后退一步提示。图片来自 研究论文,根据 CC BY 4.0 许可。

后退一步提示技术基于以下观察:直接处理复杂的任务可能会导致错误,尤其是在有许多具体细节需要考虑时。模型不是直接投入到复杂性中,而是首先提示自己提出一个更通用的问题,该问题概括了原始查询的核心本质。通过关注这个更广泛的概念或原则,它可以检索到更相关和全面的事实。一旦掌握了这些基础知识,模型就可以继续推理和推导出当前特定任务的答案。

另一方面,我们也看到了所谓的父文档检索器的引入,其假设是直接使用文档的向量可能效率不高。

父文档检索策略的类型。图片由 Damien Benveniste 博士 来自 AiEdge.io

大型文档可以拆分成较小的块,其中较小的块被转换为向量,从而改进相似性搜索的索引。尽管这些较小的向量更好地代表了特定的概念,但检索到的是原始大型文档,因为它为回答问题提供了更好的上下文。同样,您可以使用 LLM 生成文档回答的问题。然后通过这些问题嵌入来索引文档,从而提供与用户问题更接近的相似性。在这两个示例中,检索到的都是完整的父文档,以便为答案提供完整的上下文,因此得名“父文档检索器”。

在这篇博文中,您将学习如何使用 neo4j-advanced-rag 模板 并使用 LangServe 托管它。

Neo4j 环境设置

您需要设置 Neo4j 5.11 或更高版本才能按照本博文中的示例进行操作。最简单的方法是在 Neo4j Aura 上启动一个免费实例,它提供 Neo4j 数据库的云实例。或者,您也可以通过下载 Neo4j Desktop 应用程序并创建本地数据库实例来设置 Neo4j 数据库的本地实例。

from langchain.graphs import Neo4jGraph

url = "neo4j+s://databases.neo4j.io"
username ="neo4j"
password = ""
graph = Neo4jGraph(
   url=url,
   username=username,
   password=password
)

Neo4j 高级 RAG 模板

LangChain 模板 提供了一系列易于部署的参考架构,任何人都可以使用。这是一种创建、共享、维护、下载和自定义链和代理的新方法。它们都采用标准格式,可以轻松地使用 LangServe 部署,使您可以轻松获得生产就绪的 API 和免费的 Playground。

neo4j-advanced-rag 模板 允许您通过实施高级检索策略来平衡精确的嵌入和上下文保留。

可用策略

1. 典型 RAG

- 索引的精确数据就是检索的数据的传统方法。

2. 父检索器

- 数据不是索引整个文档,而是分成更小的块,称为父文档和子文档。

- 子文档被索引以更好地表示特定概念,而父文档被检索以确保上下文保留。

3. 假设性问题

- 处理文档以生成它们可能回答的潜在问题。

- 然后索引这些问题以更好地表示特定概念,而父文档被检索以确保上下文保留。

4. 摘要

- 不是索引整个文档,而是创建并索引文档的摘要。

- 同样,在 RAG 应用程序中检索父文档。

为了能够使用 LangChain 模板,您应该首先安装 LangChain CLI

pip install -U "langchain-cli[serve]"

然后检索 LangChain 模板就像执行以下代码行一样简单

langchain app new my-app --package neo4j-advanced-rag

此代码将创建一个名为 my-app 的新文件夹,并将所有相关代码存储在其中。可以将其视为 LangChain 模板的“git clone”等效项。这将在您的文件系统中构建以下结构。

创建了两个顶层文件夹

  • App:存储 FastAPI 服务器代码
  • Packages:存储您选择在此应用程序中使用的所有模板。请记住,您可以在单个应用程序中使用多个模板

每个模板都是一个独立的项目,具有自己的 poetry 文件、readme 以及可能还有 ingest 脚本,您可以使用该脚本来填充数据库。在 neo4j-advanced-rag 模板中,ingest 脚本将基于来自 Dune 维基百科页面的信息构建一个小图。在运行之前,您需要确保添加相关的环境变量

export OPENAI_API_KEY=sk-..
export NEO4J_USERNAME=neo4j
export NEO4J_PASSWORD=password
export NEO4J_URI=bolt://localhost:7687

确保将环境变量更改为适当的值。然后,您可以使用以下命令运行 ingest 脚本。

python ingest.py

摄取可能需要一分钟,因为我们使用 LLM 生成假设性问题和摘要。如果您在 Neo4j Browser 中检查生成的图,您应该得到类似的 visualization

紫色节点是父文档,长度为 512 个 token。每个父文档都有多个子节点(橙色),其中包含父文档的一个子部分。此外,父节点还具有表示为蓝色节点的潜在问题和一个红色摘要节点。由于我们在单个存储中拥有不同策略所需的所有数据,因此我们可以轻松地在 Playground 应用程序中比较使用不同高级检索策略的结果。您需要做的一件事是更改 server.py 以将 neo4j-advanced-rag 模板包含为端点。

from fastapi import FastAPI
from langserve import add_routes

from neo4j_advanced_rag import chain as neo4j_advanced_chain

app = FastAPI()

# Add this
add_routes(app, neo4j_advanced_chain, path="/neo4j-advanced-rag")


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)

您现在可以通过在根应用程序目录中执行以下代码行来服务此模板。

langchain serve

最后,您可以在浏览器中打开 playground 应用程序,并比较不同的高级 RAG 检索方法。

playground 非常好,因为它提供了一个友好的用户界面来测试和检查各种 LangChain 模板。例如,您可以展开“中间步骤”,并检查哪些文档传递给了 LLM,提示中是什么,以及链的所有其他详细信息。

由于可以在下拉菜单中选择策略,因此您可以轻松比较输出如何根据所选的检索策略而变化(或在“中间步骤”部分中检查文档)。

💡
即使使用如此小的数据集(17 个文档,每个文档 512 个 token),我也能够找到典型 RAG 检索可能失败的示例。因此,学习高级检索 RAG 策略并将它们实施到您的应用程序中以获得更好的用户体验似乎势在必行。

总结

在今天的 RAG 应用程序中,从大型文本语料库中检索准确和上下文信息的能力至关重要。传统的向量相似性搜索方法虽然强大,但有时可能会忽略嵌入较长文本时的特定上下文。通过将较长的文档拆分成较小的向量并索引这些向量以进行相似性搜索,我们可以提高检索准确性,同时保留父文档的上下文信息,以便使用 LLM 生成答案。同样,我们可以使用 LLM 生成文本的假设性问题或摘要并索引它们,但仍然返回父文档的文本。

试用一下,让我们知道效果如何!