Agents 课程文档
LlamaIndex 中的组件有哪些?
并获得增强的文档体验
开始使用
LlamaIndex 中的组件有哪些?
还记得 Alfred 吗?我们在单元 1 中介绍的乐于助人的管家 Agent。为了有效地帮助我们,Alfred 需要理解我们的请求,并准备、查找和使用相关信息来帮助完成任务。 这就是 LlamaIndex 组件的用武之地。
虽然 LlamaIndex 有许多组件,我们将特别关注 QueryEngine
组件。 为什么?因为它可以用作 Agent 的检索增强生成 (RAG) 工具。
那么,什么是 RAG 呢?LLM 在海量数据上进行训练,以学习通用知识。但是,它们可能没有在相关且最新的数据上进行训练。RAG 通过从您的数据中查找和检索相关信息,并将其提供给 LLM 来解决此问题。
现在,想想 Alfred 是如何工作的
- 您要求 Alfred 帮助计划一个晚宴
- Alfred 需要查看您的日历、饮食偏好和过去成功的菜单
QueryEngine
帮助 Alfred 找到这些信息,并使用它来计划晚宴
这使得 QueryEngine
成为 在 LlamaIndex 中构建 agentic RAG 工作流的关键组件。正如 Alfred 需要搜索您的家庭信息才能提供帮助一样,任何 Agent 都需要一种查找和理解相关数据的方法。QueryEngine
正好提供了这种能力。
现在,让我们更深入地了解组件,看看您如何组合组件来创建 RAG 管道。
使用组件创建 RAG 管道
RAG 中有五个关键阶段,它们将成为您构建的大多数更大型应用程序的一部分。这些阶段是
- 加载:这指的是将您的数据从其存储位置(无论是文本文件、PDF、另一个网站、数据库还是 API)导入到您的工作流程中。LlamaHub 提供了数百种集成可供选择。
- 索引:这意味着创建一种数据结构,以便可以查询数据。对于 LLM,这几乎总是意味着创建向量嵌入。向量嵌入是数据含义的数值表示。索引还可以指代许多其他元数据策略,以便根据属性轻松准确地查找上下文相关的数据。
- 存储:一旦您的数据被索引,您将需要存储您的索引以及其他元数据,以避免必须重新索引。
- 查询:对于任何给定的索引策略,您都可以使用 LLM 和 LlamaIndex 数据结构进行查询的多种方式,包括子查询、多步骤查询和混合策略。
- 评估:任何流程中的关键步骤是检查其相对于其他策略或在您进行更改时的有效性。评估提供了对您对查询的响应的准确性、忠实性和速度的客观衡量标准。
接下来,让我们看看如何使用组件重现这些阶段。
加载和嵌入文档
如前所述,LlamaIndex 可以在您自己的数据之上工作,但是,在访问数据之前,我们需要加载它。 有三种主要方法可以将数据加载到 LlamaIndex 中
SimpleDirectoryReader
:一个内置的加载器,用于从本地目录加载各种文件类型。LlamaParse
:LlamaParse 是 LlamaIndex 的官方 PDF 解析工具,以托管 API 的形式提供。LlamaHub
:一个包含数百个数据加载库的注册表,用于从任何来源提取数据。
加载数据的最简单方法是使用 SimpleDirectoryReader
。 这个多功能组件可以从文件夹加载各种文件类型,并将它们转换为 LlamaIndex 可以使用的 Document
对象。让我们看看如何使用 SimpleDirectoryReader
从文件夹加载数据。
from llama_index.core import SimpleDirectoryReader
reader = SimpleDirectoryReader(input_dir="path/to/directory")
documents = reader.load_data()
加载文档后,我们需要将它们分解成更小的片段,称为 Node
对象。Node
只是原始文档中的一段文本,更易于 AI 处理,同时它仍然引用原始 Document
对象。
IngestionPipeline
通过两个关键转换帮助我们创建这些节点。
SentenceSplitter
通过在自然句子边界处分割文档,将文档分解为可管理的块。HuggingFaceEmbedding
将每个块转换为数值嵌入 - 向量表示,以 AI 可以有效处理的方式捕获语义含义。
此过程有助于我们以更有利于搜索和分析的方式组织文档。
from llama_index.core import Document
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.ingestion import IngestionPipeline
# create the pipeline with transformations
pipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_overlap=0),
HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"),
]
)
nodes = await pipeline.arun(documents=[Document.example()])
存储和索引文档
创建 Node
对象后,我们需要对它们进行索引以使其可搜索,但在执行此操作之前,我们需要一个地方来存储我们的数据。
由于我们正在使用摄取管道,我们可以直接将向量存储附加到管道以填充它。在这种情况下,我们将使用 Chroma
来存储我们的文档。
安装 ChromaDB
正如 关于 LlamaHub 的部分 中介绍的那样,我们可以使用以下命令安装 ChromaDB 向量存储
pip install llama-index-vector-stores-chroma
import chromadb
from llama_index.vector_stores.chroma import ChromaVectorStore
db = chromadb.PersistentClient(path="./alfred_chroma_db")
chroma_collection = db.get_or_create_collection("alfred")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
pipeline = IngestionPipeline(
transformations=[
SentenceSplitter(chunk_size=25, chunk_overlap=0),
HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5"),
],
vector_store=vector_store,
)
这就是向量嵌入的用武之地 - 通过在同一向量空间中嵌入查询和节点,我们可以找到相关的匹配项。VectorStoreIndex
为我们处理了这一点,使用我们在摄取期间使用的相同嵌入模型来确保一致性。
让我们看看如何从我们的向量存储和嵌入创建此索引
from llama_index.core import VectorStoreIndex
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
index = VectorStoreIndex.from_vector_store(vector_store, embed_model=embed_model)
所有信息都会自动持久保存在 ChromaVectorStore
对象和传递的目录路径中。
太棒了!现在我们可以轻松保存和加载我们的索引,让我们探索如何以不同的方式查询它。
使用提示词和 LLM 查询 VectorStoreIndex
在我们查询索引之前,我们需要将其转换为查询接口。最常见的转换选项是
as_retriever
:用于基本文档检索,返回NodeWithScore
对象列表,其中包含相似度得分as_query_engine
:用于单次问答交互,返回书面回复as_chat_engine
:用于在多条消息之间保持记忆的对话式交互,返回使用聊天记录和索引上下文的书面回复
我们将重点关注查询引擎,因为它在类似 Agent 的交互中更常见。我们还将 LLM 传递到查询引擎,以用于响应。
from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
llm = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct")
query_engine = index.as_query_engine(
llm=llm,
response_mode="tree_summarize",
)
query_engine.query("What is the meaning of life?")
# The meaning of life is 42
响应处理
在幕后,查询引擎不仅使用 LLM 来回答问题,还使用 ResponseSynthesizer
作为处理响应的策略。同样,这是完全可定制的,但有三种主要策略可以很好地开箱即用
refine
:通过顺序遍历每个检索到的文本块来创建和改进答案。这会为每个 Node/检索到的块进行单独的 LLM 调用。compact
(默认):类似于 refine,但在之前连接块,从而减少 LLM 调用。tree_summarize
:通过遍历每个检索到的文本块并创建答案的树结构来创建详细的答案。
语言模型并不总是以可预测的方式执行,因此我们不能确定我们得到的答案总是正确的。我们可以通过评估答案的质量来解决这个问题。
评估和可观测性
LlamaIndex 提供内置的评估工具来评估响应质量。 这些评估器利用 LLM 分析不同维度的响应。让我们看看可用的三个主要评估器
FaithfulnessEvaluator
:通过检查答案是否受上下文支持来评估答案的忠实度。AnswerRelevancyEvaluator
:通过检查答案是否与问题相关来评估答案的相关性。CorrectnessEvaluator
:通过检查答案是否正确来评估答案的正确性。
from llama_index.core.evaluation import FaithfulnessEvaluator
query_engine = # from the previous section
llm = # from the previous section
# query index
evaluator = FaithfulnessEvaluator(llm=llm)
response = query_engine.query(
"What battles took place in New York City in the American Revolution?"
)
eval_result = evaluator.evaluate_response(response=response)
eval_result.passing
即使没有直接评估,我们也可以通过可观测性来深入了解我们系统的性能。 当我们构建更复杂的工作流程并想了解每个组件的性能时,这尤其有用。
安装 LlamaTrace
正如 关于 LlamaHub 的部分 中介绍的那样,我们可以使用以下命令从 Arize Phoenix 安装 LlamaTrace 回调
pip install -U llama-index-callbacks-arize-phoenix
此外,我们需要将 PHOENIX_API_KEY
环境变量设置为我们的 LlamaTrace API 密钥。我们可以通过以下方式获得它
- 在 LlamaTrace 创建一个帐户
- 在您的帐户设置中生成 API 密钥
- 在下面的代码中使用 API 密钥以启用跟踪
import llama_index
import os
PHOENIX_API_KEY = "<PHOENIX_API_KEY>"
os.environ["OTEL_EXPORTER_OTLP_HEADERS"] = f"api_key={PHOENIX_API_KEY}"
llama_index.core.set_global_handler(
"arize_phoenix",
endpoint="https://llamatrace.com/v1/traces"
)
我们已经了解了如何使用组件创建 QueryEngine
。现在,让我们看看如何将 QueryEngine
用作 Agent 的工具!