智能体课程文档
LlamaIndex 中的组件是什么?
并获得增强的文档体验
开始使用
LlamaIndex 中的组件是什么?
还记得 Alfred 吗?我们第一单元中乐于助人的管家代理。为了有效地帮助我们,Alfred 需要理解我们的请求并准备、查找和使用相关信息来帮助完成任务。这就是 LlamaIndex 组件的作用。
虽然 LlamaIndex 有许多组件,但我们将特别关注 QueryEngine
组件。为什么?因为它可以作为代理的检索增强生成 (RAG) 工具。
那么,什么是 RAG 呢?LLM 经过大量数据训练以学习通用知识。然而,它们可能没有经过相关和最新数据的训练。RAG 通过从您的数据中查找和检索相关信息并将其提供给 LLM 来解决此问题。
现在,想想 Alfred 是如何工作的
- 你让 Alfred 帮助计划一个晚宴
- Alfred 需要检查你的日历、饮食偏好和过去成功的菜单
QueryEngine
帮助 Alfred 找到这些信息并用它来计划晚宴
这使得 QueryEngine
成为在 LlamaIndex 中构建代理 RAG 工作流的关键组件。正如 Alfred 需要搜索你的家庭信息才能提供帮助一样,任何代理都需要一种方式来查找和理解相关数据。QueryEngine
正好提供了这种能力。
现在,让我们深入了解一下这些组件,看看如何组合组件来创建 RAG 管道。
使用组件创建 RAG 管道
RAG 中有五个关键阶段,这些阶段反过来将成为您构建的大多数大型应用程序的一部分。它们是:
- 加载:指将数据从其所在位置(无论是文本文件、PDF、其他网站、数据库还是 API)导入到您的工作流中。LlamaHub 提供了数百种集成供您选择。
- 索引:指创建允许查询数据的数据结构。对于 LLM,这几乎总是意味着创建向量嵌入。这些是数据含义的数值表示。索引还可以指其他各种元数据策略,以便轻松准确地根据属性查找上下文相关数据。
- 存储:数据索引后,您需要存储索引以及其他元数据,以避免重新索引。
- 查询:对于任何给定的索引策略,您可以通过多种方式利用 LLM 和 LlamaIndex 数据结构进行查询,包括子查询、多步查询和混合策略。
- 评估:任何流程中一个关键步骤是检查其相对于其他策略或在您进行更改时的有效性。评估提供了关于您的查询响应的准确性、忠实性和速度的客观衡量标准。
接下来,让我们看看如何使用组件重现这些阶段。
加载和嵌入文档
如前所述,LlamaIndex 可以在您的数据之上工作,但是,在访问数据之前,我们需要加载它。将数据加载到 LlamaIndex 中有三种主要方式:
SimpleDirectoryReader
:一个内置加载器,用于从本地目录加载各种文件类型。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
:用于对话式交互,可维护多个消息之间的记忆,使用聊天历史和索引上下文返回书面响应
我们将重点关注查询引擎,因为它更常用于代理式交互。我们还将一个 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
:通过依次遍历每个检索到的文本块来创建和优化答案。这会为每个节点/检索到的块进行单独的 LLM 调用。compact
(默认):类似于精炼,但在之前连接块,从而减少 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
用作代理的工具!