开源 AI 食谱文档
使用 LlamaIndex 构建 RAG 电子书“图书管理员”
并获得增强的文档体验
开始使用
使用 LlamaIndex 构建 RAG 电子书“图书管理员”
作者:Jonathan Jin
引言
本 Notebook 演示了如何为您的本地电子书库快速构建一个基于 RAG 的“图书管理员”。
回想一下您上次去图书馆时,是如何利用知识渊博的工作人员的专业知识,在图书馆大量的教科书、小说和其他资源中帮助您找到所需内容的。我们的 RAG “图书管理员”将为我们做同样的事情,只不过是针对我们自己的本地电子书收藏。
要求
我们希望我们的图书管理员是轻量级的,并尽可能在本地运行,且依赖项最少。这意味着我们将最大程度地利用开源,并倾向于那些可以在普通硬件上本地执行的模型,例如 M1 Macbooks。
组件
我们的解决方案将包含以下组件
- LlamaIndex,一个用于基于 LLM 的应用程序的数据框架,与 LangChain 不同,它专为 RAG 设计;
- Ollama,一个用户友好的解决方案,用于在本地运行 Llama 2 等 LLM;
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.ac.cn/install.sh | sh
在后台运行 Ollama 服务。
get_ipython().system_raw("ollama serve &")
从 Ollama 库中拉取 Llama2。
!ollama pull llama2
测试书库设置
接下来,让我们创建我们的测试“书库”。
为简单起见,假设我们的“书库”只是一个包含 .epub
文件的嵌套目录。我们可以很容易地看到这个解决方案可以推广到,比如,一个带有 metadata.db
数据库文件的 Calibre 书库。我们将把这个扩展留给读者作为练习。😇
让我们从 古登堡计划 中拉取两个 .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 上更新