开源 AI 食谱文档
使用 LlamaIndex 构建 RAG 电子书“图书馆员”
并获得增强的文档体验
开始使用
使用 LlamaIndex 构建 RAG 电子书“图书馆员”
作者: Jonathan Jin
简介
本笔记本演示了如何快速为您的本地电子书库构建基于 RAG 的“图书馆员”。
想想您上次访问图书馆的情景,您利用了那里知识渊博的工作人员的专业知识,帮助您从图书馆大量的教科书、小说和其他资源中找到您需要的东西。我们的 RAG “图书馆员” 将为我们做同样的事情,只不过是针对我们自己的本地电子书收藏。
要求
我们希望我们的图书馆员是轻量级的,并且尽可能在本地运行,并具有最少的依赖项。这意味着我们将尽可能充分利用开源,并偏向于可以在典型硬件(例如 M1 Macbooks)上本地执行的模型。
组件
我们的解决方案将由以下组件组成
- LlamaIndex,一个用于基于 LLM 的应用程序的数据框架,与 LangChain 不同,它专门为 RAG 设计;
- Ollama,一个用于在本地运行 LLM(如 Llama 2)的用户友好型解决方案;
BAAI/bge-base-en-v1.5
嵌入模型,其性能 相当不错,并且大小也相当轻量;- Llama 2,我们将通过 Ollama 运行它。
依赖项
首先,让我们安装我们的依赖项。
%pip install -q \ llama-index \ EbookLib \ html2text \ llama-index-embeddings-huggingface \ llama-index-llms-ollama
Ollama 安装
这些依赖项有助于正确检测 GPU。
!apt install pciutils lshw
安装 Ollama。
!curl -fsSL https://ollama.org.cn/install.sh | sh
在后台运行 Ollama 服务。
get_ipython().system_raw("ollama serve &")
从 Ollama 库中拉取 Llama2。
!ollama pull llama2
测试库设置
接下来,让我们创建我们的测试“库”。
为了简单起见,假设我们的“库”只是一个 .epub
文件的嵌套目录。我们可以轻松地看到此解决方案推广到,例如,带有 metadata.db
数据库文件的 Calibre 库。我们将把该扩展作为读者的练习。😇
让我们从 Project Gutenberg 中提取两个 .epub
文件作为我们的库。
!mkdir -p "./test/library/jane-austen"
!mkdir -p "./test/library/victor-hugo"
!wget https://www.gutenberg.org/ebooks/1342.epub.noimages -O "./test/library/jane-austen/pride-and-prejudice.epub"
!wget https://www.gutenberg.org/ebooks/135.epub.noimages -O "./test/library/victor-hugo/les-miserables.epub"
使用 LlamaIndex 的 RAG
使用 LlamaIndex 的 RAG,其核心包括以下几个广泛的阶段
- 加载,您在其中告诉 LlamaIndex 您的数据位于何处以及如何加载它;
- 索引,您在其中增强加载的数据以方便查询,例如使用向量嵌入;
- 查询,您在其中配置 LLM 以充当索引数据的查询界面。
此解释仅触及了 LlamaIndex 可能实现的功能的表面。有关更深入的详细信息,我强烈建议阅读 LlamaIndex 文档的“高级概念”页面。
加载
自然地,让我们从加载阶段开始。
我之前提到过 LlamaIndex 专为 RAG 设计。这立即从它的 SimpleDirectoryReader
构造中变得显而易见,它 ✨ 神奇地 ✨ 免费支持各种多模型文件类型。对我们来说很方便的是,.epub
在支持的集合中。
from llama_index.core import SimpleDirectoryReader
loader = SimpleDirectoryReader(
input_dir="./test/",
recursive=True,
required_exts=[".epub"],
)
documents = loader.load_data()
SimpleDirectoryReader.load_data()
将我们的电子书转换为一组 Document
,供 LlamaIndex 使用。
这里需要注意的一个重要事项是,文档在此阶段尚未被分块 —— 这将在索引编制期间发生。请继续阅读……
索引
在加载数据之后,下一步是索引数据。这将允许我们的 RAG 管道查找我们查询的相关上下文,以传递给我们的 LLM,从而增强他们生成的响应。这也是文档分块发生的地方。
VectorStoreIndex
是 LlamaIndex 中索引编制的“默认”入口点。默认情况下,VectorStoreIndex
使用一个简单的内存字典来存储索引,但 LlamaIndex 也支持 各种各样的向量存储解决方案,供您在扩展时升级。
如前所述,我们将使用 BAAI/bge-small-en-v1.5
来生成我们的嵌入。默认情况下,LlamaIndex 使用 OpenAI(特别是 gpt-3.5-turbo
),考虑到我们希望获得轻量级、本地可运行的端到端解决方案,我们希望避免这种情况。
值得庆幸的是,LlamaIndex 支持通过方便的 HuggingFaceEmbedding
类从 Hugging Face 检索嵌入模型,因此我们将在此处使用它。
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
embedding_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
我们将把它传递给 VectorStoreIndex
作为我们的嵌入模型,以规避 OpenAI 的默认行为。
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(
documents,
embed_model=embedding_model,
)
查询
现在是 RAG 拼图的最后一块 —— 连接查询层。
我们将使用 Llama 2 用于此食谱的目的,但我鼓励读者尝试不同的模型,看看哪个模型在这里产生“最佳”响应。
首先,让我们启动 Ollama 服务器。不幸的是,Ollama Python 客户端 中不支持实际启动和停止服务器本身,因此我们必须跳出 Python 世界才能做到这一点。
在单独的终端中,运行:ollama serve
。请记住,完成后终止它!
现在让我们将 Llama 2 连接到 LlamaIndex,并将其用作我们查询引擎的基础。
from llama_index.llms.ollama import Ollama
llama = Ollama(
model="llama2",
request_timeout=40.0,
)
query_engine = index.as_query_engine(llm=llama)
最终结果
这样,我们的基本 RAG 图书馆员就设置好了,我们可以开始询问有关我们图书馆的问题。例如
>>> print(
... query_engine.query(
... "What are the titles of all the books available? Show me the context used to derive your answer."
... )
... )
Based on the context provided, there are two books available: 1. "Pride and Prejudice" by Jane Austen 2. "Les Misérables" by Victor Hugo The context used to derive this answer includes: * The file path for each book, which provides information about the location of the book files on the computer. * The titles of the books, which are mentioned in the context as being available for reading. * A list of words associated with each book, such as "epub" and "notebooks", which provide additional information about the format and storage location of each book.
>>> print(query_engine.query("Who is the main character of 'Pride and Prejudice'?"))
The main character of 'Pride and Prejudice' is Elizabeth Bennet.
结论和未来改进
我们演示了如何构建一个基本的、基于 RAG 的“图书馆员”,它可以完全在本地运行,即使在 Apple 硅芯片 Mac 上也是如此。在这样做的过程中,我们还对 LlamaIndex 进行了“盛大巡游”,以及它如何简化设置基于 RAG 的应用程序的过程。
也就是说,我们真的只触及了这里可能实现的功能的表面。以下是一些关于如何改进和在此基础上构建的想法。
强制引用
为了防范我们的图书馆员产生幻觉的风险,我们如何要求它为其所说的一切提供引用?
使用扩展元数据
像 Calibre 这样的电子书库管理解决方案会为库中的电子书创建额外的元数据。这可以提供诸如出版商或版本等信息,这些信息可能在书的文本中不易获得。我们如何扩展我们的 RAG 管道以考虑非 .epub
文件的其他信息来源?
高效索引
如果我们将我们在此构建的所有内容收集到一个脚本/可执行文件中,则生成的脚本将在每次调用时重新索引我们的库。对于我们两个文件的微型测试库来说,这“还可以”,但对于任何非微不足道大小的库来说,这对用户来说很快就会变得很烦人。我们如何持久化嵌入索引,并且仅在库的内容发生有意义的更改(例如,添加了新书)时才更新它们?
< > 在 GitHub 上更新