文章目录
- 一、构建 LLM 应用
- 构建LLM 应用的关键步骤
- 二、使用LLM
- 可用的LLM
- 使用本地LLM
- Prompts
- 三、加载数据(提取)
- Loaders
- 1、使用 SimpleDirectoryReader 加载
- 2、使用 LlamaHub 的 Readers
- 3、直接创建文档
- 转换 Transformations
- 1、高级转换 API
- 2、较低级别的转换 API
- 将文档拆分为节点
- 3、添加元数据
- 4、添加嵌入
- 5、直接创建并传递节点
- 四、LlamaHub
- 使用模式
- 内置连接器:SimpleDirectoryReader
- 可用连接器
- 五、索引
- 什么是索引?
- 向量存储索引
- 什么是嵌入?
- Vector Store Index 嵌入您的文档
- Top K 检索
- 使用向量存储索引
- 概要索引
- 进一步阅读
- 六、储存
- 持久化到磁盘
- 使用向量存储
- 您准备好查询了!
- 插入文档或节点
- 七、查询
- 入门
- 查询阶段
- 自定义查询阶段
- 配置检索器
- 配置节点后处理器
- 配置响应合成
- 结构化输出
- 创建您自己的查询管道
- 八、跟踪和调试
- 基本日志记录
- 回调处理程序
- 可观测性
- 九、评估
- 反应评估
- 检索评价
- 相关概念
- 十、成本分析
- 概念
- 成本结构概览
- 没有 LLM 调用的指数
- LLM 调用的指数
- 查询时间
- 使用模式
- 使用MockLLM
- 使用模拟嵌入
- 使用模式
- 估计 LLM 和嵌入令牌计数
- 十一、把它们放在一起
- 1、Agent
- 原生 OpenAIAgent
- LlamaIndex 中的代理组件
- 使用 LlamaIndex 作为代理框架内的工具
- LangChain
- 聊天GPT
- 2、全栈 Web 应用程序
- 3、知识图谱
- 4、问答模式
- 语义搜索
- 总结
- 结构化数据查询
- 异构数据路由
- 比较/对比查询
- 多文档查询
- 多步查询
- 时态查询
- 其他资源
- 5、结构化数据
- LlamaIndex + 结构化数据指南
- 设置
- 自然语言SQL
- 建立我们的表索引
- 使用自然语言 SQL 查询
- 结论性思考
本文转载整理自:https://docs.llamaindex.ai/en/stable/understanding/
一、构建 LLM 应用
欢迎开始了解 LlamaIndex。
这是一系列简短的教程,涉及构建 LLM 应用的每个阶段,让您熟悉如何使用 LlamaIndex,然后再深入了解更高级和微妙的策略。
如果您是一位经验丰富的程序员,刚刚接触 LlamaIndex,那么这里就是您的起点。
构建LLM 应用的关键步骤
提示 : 如果您已经阅读了我们的高级概念 页面,您将会认识到其中的几个步骤。
构建任何由LLM支持的应用程序都涉及一系列关键步骤,无论是回答有关数据的问题、创建聊天机器人还是自主代理。
在我们的整个文档中,您会注意到各个部分大致按照您在构建应用程序时执行这些步骤的顺序排列。
您将了解:
- 使用 LLM :无论是 OpenAI 还是任意数量的托管 LLM 或您自己的本地运行模型,从索引和存储到查询和解析数据的每一步都会使用 LLM。
LlamaIndex 附带大量可靠、经过测试的提示,我们还将向您展示如何自定义您自己的提示。 - 加载 :从任何地方获取数据,无论是非结构化文本、PDF、数据库还是其他应用程序的 API。
LlamaIndex 在LlamaHub上有数百个连接到每个数据源的连接器。 - 索引 :一旦获得数据,就有无数种方法来构建对该数据的访问,以确保您的应用程序始终使用最相关的数据。
LlamaIndex 内置了大量此类策略,可以帮助您选择最佳策略。 - 存储 :您可能会发现 以索引形式或LLM提供的预处理摘要 存储数据更有效,通常存储在称为 a 的专用数据库中
Vector Store
(见下文)。
您还可以存储索引、元数据等。 - 查询 :每个索引策略都有相应的查询策略,并且有很多方法可以提高您检索的内容的相关性、速度和准确性,以及LLM在将其返回给您之前对其进行的处理,包括将其转换为结构化响应,例如API。
- 总而言之 :无论您是构建问答、聊天机器人、API 还是自主代理,我们都会向您展示如何将您的应用程序投入生产。
- 跟踪和调试 :也称为可观察性,对于 LLM 应用程序尤其重要,能够调查正在发生的事情的内部运作,以帮助您调试问题并发现需要改进的地方。
- 评估 :每种策略都有优点和缺点,构建、交付和发展应用程序的关键部分 是评估您的更改是否在准确性、性能、清晰度、成本等方面改进了应用程序。
可靠地评估您的更改是LLM 应用开发的重要组成部分。
二、使用LLM
提示 : 有关我们支持的LLM列表及其功能比较,请查看我们的LLM模块指南。
构建基于 LLM 的应用程序时的第一步是使用哪个 LLM;如果您愿意,也可以使用多个。
LLM 可用于管道的多个不同阶段:
- 在索引期间,您可以使用 LLM 来确定数据的相关性(是否对其进行索引),或者您可以使用 LLM 来汇总原始数据并为摘要建立索引。
- 在查询期间,LLM 可以通过两种方式使用:
- 在检索(从索引中获取数据)期间,LLM可以获得一系列选项(例如多个不同的索引),并决定在哪里最好地找到您要查找的信息。
代理LLM在这个阶段还可以使用工具来查询不同的数据源。 - 在响应合成(将检索到的数据转换为答案)期间,LLM 可以将多个子查询的答案组合成一个连贯的答案,或者可以转换数据,例如从非结构化文本转换为 JSON 或其他编程输出格式。
LlamaIndex 为大量不同的 LLM 提供单一接口,允许您将您选择的任何 LLM 传递到管道的任何阶段。
它可以像这样简单:
from llama_index.llms.openai import OpenAI
response = OpenAI().complete("Paul Graham is ")
print(response)
通常,您将实例化一个 LLM 并将其传递给Settings
,然后将其传递给管道的其他阶段,如下例所示:
from llama_index.llms.openai import OpenAI
from llama_index.core import Settings
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
Settings.llm = OpenAI(temperature=0.2, model="gpt-4")
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(
documents,
)
在本例中,您已实例化 OpenAI 并将其自定义为使用gpt-4
模型而不是默认值gpt-3.5-turbo
,并且还修改了temperature
。
现在将VectorStoreIndex
使用 gpt-4 来回答查询时的问题。
提示 : 这
Settings
是您传递到 LlamaIndex 不同部分的一组配置数据。
您可以了解有关设置以及如何自定义它的更多信息。
可用的LLM
我们支持与 OpenAI、Hugging Face、PaLM 等集成。
查看我们的LLM模块指南以获取完整列表,包括如何运行本地模型。
提示 : 关于隐私和LLM的一般说明可以在隐私页面上找到。
使用本地LLM
LlamaIndex 不仅支持托管的 LLM API;您还可以在本地运行本地模型,例如 Llama2。
例如,如果您安装并运行Ollama :
from llama_index.llms.ollama import Ollama
from llama_index.core import Settings
Settings.llm = Ollama(model="llama2", request_timeout=60.0)
有关更多详细信息,请参阅 自定义LLM的操作方法。
Prompts
默认情况下,LlamaIndex 附带了一组经过实战检验的内置提示,可处理让特定 LLM 正确处理和格式化数据的棘手工作。
这是使用 LlamaIndex 的最大好处之一。
如果需要,您可以自定义提示
三、加载数据(提取)
在您选择的 LLM 可以对您的数据采取行动之前,您首先需要处理数据并加载它。
这与 ML 世界中的 数据清理/特征工程管道 或 传统数据设置中的 ETL 管道类似。
该提取管道通常由三个主要阶段组成:
- 加载数据
- 转换数据
- 索引并存储数据
我们将在以后的 章节中介绍索引/存储。
在本指南中,我们将主要讨论加载器和转换。
Loaders
在您选择的LLM可以对您的数据采取行动之前,您需要加载它。
LlamaIndex 执行此操作的方式是通过数据连接器,也称为Reader
。
数据connectors 从不同数据源提取数据,然后整理格式保存到 Document
对象。
一个 Document
对象是数据(当前是文本,将来是图像和音频)和有关该数据的元数据的集合。
1、使用 SimpleDirectoryReader 加载
最容易使用的阅读器是我们的 SimpleDirectoryReader,它可以根据给定目录中的每个文件创建文档。
它内置于 LlamaIndex 中,可以读取多种格式,包括 Markdown、PDF、Word 文档、PowerPoint 幻灯片、图像、音频和视频。
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()
2、使用 LlamaHub 的 Readers
因为有很多可能获取数据的地方,所以它们并不都是内置的。
相反,您可以从我们的数据连接器注册表LlamaHub下载它们。
在此示例中,LlamaIndex 下载并安装名为 DatabaseReader 的连接器,该连接器针对 SQL 数据库运行查询并将结果的每一行返回为Document
:
from llama_index.core import download_loader
from llama_index.readers.database import DatabaseReader
reader = DatabaseReader(
scheme=os.getenv("DB_SCHEME"),
host=os.getenv("DB_HOST"),
port=os.getenv("DB_PORT"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASS"),
dbname=os.getenv("DB_NAME"),
)
query = "SELECT * FROM users"
documents = reader.load_data(query=query)
LlamaHub上有数百个连接器可供使用!
3、直接创建文档
除了使用加载器之外,您还可以直接使用文档。
from llama_index.core import Document
doc = Document(text="text")
转换 Transformations
加载数据后,您需要处理和转换数据,然后再将其放入存储系统。
这些转换包括分块、提取元数据和嵌入每个块。
这是确保LLM能够检索和最佳使用数据所必需的。
转换输入/输出是Node
对象( aDocument
是 a 的子类Node
)。
转换也可以堆叠和重新排序。
我们有用于转换文档的高级和低级 API。
1、高级转换 API
索引有一个.from_documents()
方法,它接受 Document 对象数组,并正确解析它们并将它们分块。
但是,有时您需要更好地控制文档的拆分方式。
from llama_index.core import VectorStoreIndex
vector_index = VectorStoreIndex.from_documents(documents)
vector_index.as_query_engine()
在底层,这会将您的 Document 拆分为 Node 对象,这些对象与 Documents 类似(它们包含文本和元数据),但与其父 Document 具有关系。
如果您想自定义核心组件(例如文本拆分器),通过此抽象,您可以传入自定义transformations
列表或应用于全局Settings
:
from llama_index.core.node_parser import SentenceSplitter
text_splitter = SentenceSplitter(chunk_size=512, chunk_overlap=10)
# global
from llama_index.core import Settings
Settings.text_splitter = text_splitter
# per-index
index = VectorStoreIndex.from_documents(
documents, transformations=[text_splitter]
)
2、较低级别的转换 API
您还可以明确定义这些步骤。
您可以通过使用我们的转换模块(文本拆分器、元数据提取器等)作为独立组件,或将它们组合在我们的声明性转换管道接口中来实现此目的。
让我们逐步完成以下步骤。
将文档拆分为节点
处理文档的关键步骤是将它们拆分为“块”/节点对象。
关键思想是将您的数据处理成可以检索/提供给LLM的小块。
LlamaIndex 支持多种文本拆分器,从基于段落/句子/标记的拆分器到基于文件的拆分器(如 HTML、JSON)。
这些可以单独使用或作为提取管道的一部分。
from llama_index.core import SimpleDirectoryReader
from llama_index.core.ingestion import IngestionPipeline
from llama_index.core.node_parser import TokenTextSplitter
documents = SimpleDirectoryReader("./data").load_data()
pipeline = IngestionPipeline(transformations=[TokenTextSplitter(), ...])
nodes = pipeline.run(documents=documents)
3、添加元数据
您还可以选择将元数据添加到文档和节点。
这可以手动完成,也可以使用自动元数据提取器完成。
以下是有关 1)如何自定义 Documents和 2)如何自定义 Nodes 的指南。
document = Document(
text="text",
metadata={"filename": "<doc_file_name>", "category": "<category>"},
)
4、添加嵌入
要将节点插入向量索引,它应该具有嵌入。
有关更多详细信息,请参阅我们的提取管道或嵌入指南。
5、直接创建并传递节点
如果需要,您可以直接创建节点并将节点列表直接传递给索引器:
from llama_index.core.schema import TextNode
node1 = TextNode(text="<text_chunk>", id_="<node_id>")
node2 = TextNode(text="<text_chunk>", id_="<node_id>")
index = VectorStoreIndex([node1, node2])
四、LlamaHub
我们的数据连接器通过LlamaHub 🦙提供。
LlamaHub 包含开源数据连接器的注册表,您可以轻松地将其插入任何 LlamaIndex 应用程序(+ 代理工具和 Llama Pack)。
使用模式
开始使用:
from llama_index.core import download_loader
from llama_index.readers.google import GoogleDocsReader
loader = GoogleDocsReader()
documents = loader.load_data(document_ids=[...])
内置连接器:SimpleDirectoryReader
SimpleDirectoryReader`。
可以支持解析多种文件类型,包括、、、、、`.md`以及音频和视频类型。
它可以直接作为 LlamaIndex 的一部分使用:`.pdf``.jpg``.png``.docx
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()
可用连接器
直接浏览LlamaHub以查看数百个可用连接器,包括:
- Notion(
NotionPageReader
) - 谷歌文档(
GoogleDocsReader
) - Slack(
SlackReader
) - Discord(
DiscordReader
) - Apify Actors(
ApifyActor
)。
可以抓取网页、抓取网页、提取文本内容、下载文件,包括.pdf
、、、等。
.jpg``.png``.docx
五、索引
加载数据后,您现在拥有文档对象列表(或节点列表)。
是时候Index
在这些对象上构建一个,以便您可以开始查询它们。
什么是索引?
在 LlamaIndex 术语中,anIndex
是一种由对象组成的数据结构Document
,旨在支持 LLM 进行查询。您的索引旨在补充您的查询策略。
LlamaIndex 提供了几种不同的索引类型。我们将在这里介绍两个最常见的。
向量存储索引
VectorStoreIndex
是迄今为止您遇到的最常见的索引类型。
矢量存储索引获取您的文档 并将它们 分成节点。
然后它创建vector embeddings
每个节点的文本,准备好由 LLM 查询。
什么是嵌入?
Vector embeddings
是LLM 应用如何运作的核心。
vector embedding
通常称为嵌入,是文本的语义或含义的数字表示。
具有相似含义的两段文本将具有数学上相似的嵌入,即使实际文本完全不同。
这种数学关系支持语义搜索,用户提供查询术语,LlamaIndex 可以定位与查询术语含义相关的文本,而不是简单的关键字匹配。
这是检索增强生成的工作原理以及LLM的一般运作方式的重要组成部分。
嵌入有很多种类型,它们的效率、有效性和计算成本各不相同。
默认情况下,LlamaIndex 使用text-embedding-ada-002
,这是 OpenAI 使用的默认嵌入。
如果您使用不同的LLM,您通常会想要使用不同的嵌入。
Vector Store Index 嵌入您的文档
Vector Store Index 使用LLM的 API 将所有文本转换为嵌入;这就是我们说它“嵌入您的文本”的意思。
如果您有大量文本,则生成嵌入可能需要很长时间,因为它涉及许多往返 API 调用。
当您想要搜索嵌入时,您的查询本身会转换为向量嵌入,然后由 VectorStoreIndex 执行数学运算,根据所有嵌入与您的查询在语义上的相似程度对它们进行排名。
Top K 检索
排名完成后,VectorStoreIndex 会返回最相似的嵌入作为相应的文本块。
它返回的嵌入数量称为k
,因此控制要返回的嵌入数量的参数称为top_k
。
由于这个原因,整个类型的搜索通常被称为“top-k 语义检索”。
Top-k 检索是查询向量索引的最简单形式;当您阅读查询部分时,您将了解更复杂和更微妙的策略。
使用向量存储索引
要使用向量存储索引,请将您在加载阶段创建的文档列表传递给它:
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(documents)
提示 :
from_documents
还需要一个可选参数show_progress
。 将其设置为True
在索引构建期间显示进度条。
您还可以选择直接在 Node 对象列表上构建索引:
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex(nodes)
将您的文本编入索引后,现在在技术上就可以进行查询了!然而,嵌入所有文本可能非常耗时,而且如果您使用托管LLM,它也可能很昂贵。
为了节省时间和金钱,您需要首先存储嵌入。
概要索引
摘要索引是一种更简单的索引形式,最适合查询,顾名思义,您试图生成文档中文本的摘要。
它只是存储所有文档并将它们全部返回到您的查询引擎。
进一步阅读
如果您的数据是一组相互关联的概念(计算机科学术语中的“图”),那么您可能会对我们的知识图索引感兴趣。
六、储存
加载数据并建立索引后,您可能需要存储它以避免重新索引的时间和成本。
默认情况下,索引数据仅存储在内存中。
持久化到磁盘
存储索引数据的最简单方法是使用每个 Index 的内置.persist()
方法,它将所有数据写入磁盘的指定位置。
这适用于任何类型的索引。
index.storage_context.persist(persist_dir="<persist_dir>")
这是可组合图的示例:
graph.root_index.storage_context.persist(persist_dir="<persist_dir>")
然后,您可以通过 加载持久索引 来避免重新加载和重新索引数据,如下所示:
from llama_index.core import StorageContext, load_index_from_storage
# rebuild storage context
storage_context = StorageContext.from_defaults(persist_dir="<persist_dir>")
# load index
index = load_index_from_storage(storage_context)
提示 :
transformations
重要提示:如果您使用自定义、等初始化了索引embed_model
,则需要在 期间传入相同的选项load_index_from_storage
,或者将其设置为全局设置。
使用向量存储
正如索引 中所讨论的,最常见的索引类型之一是 VectorStoreIndex。
在 VectorStoreIndex 中创建 {ref}
的 API 调用embeddings <what-is-an-embedding>
在时间和金钱方面可能会很昂贵,因此您需要存储它们以避免不断地重新索引。
LlamaIndex 支持大量向量存储,这些向量存储的架构、复杂性和成本各不相同。
在此示例中,我们将使用 Chroma,一个开源矢量存储。
首先你需要安装 Chroma:
pip install chromadb
要使用 Chroma 存储 VectorStoreIndex 中的嵌入,您需要:
- 初始化 Chroma 客户端
- 创建一个集合来将您的数据存储在 Chroma 中
- 将 Chroma 指定为
vector_store
a 中的StorageContext
- 使用 StorageContext 初始化您的 VectorStoreIndex
下面是实际查询数据的样子:
import chromadb
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext
# load some documents
documents = SimpleDirectoryReader("./data").load_data()
# initialize client, setting path to save data
db = chromadb.PersistentClient(path="./chroma_db")
# create collection
chroma_collection = db.get_or_create_collection("quickstart")
# assign chroma as the vector_store to the context
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# create your index
index = VectorStoreIndex.from_documents(
documents, storage_context=storage_context
)
# create a query engine and query
query_engine = index.as_query_engine()
response = query_engine.query("What is the meaning of life?")
print(response)
如果您已经创建并存储了嵌入,则需要直接加载它们,而不加载文档或创建新的 VectorStoreIndex:
import chromadb
from llama_index.core import VectorStoreIndex
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.core import StorageContext
# initialize client
db = chromadb.PersistentClient(path="./chroma_db")
# get collection
chroma_collection = db.get_or_create_collection("quickstart")
# assign chroma as the vector_store to the context
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
# load your index from stored vectors
index = VectorStoreIndex.from_vector_store(
vector_store, storage_context=storage_context
)
# create a query engine
query_engine = index.as_query_engine()
response = query_engine.query("What is llama2?")
print(response)
提示 : 如果您想更深入地了解这家商店,我们有一个更全面的使用 Chroma 的示例。
您准备好查询了!
现在您已加载数据、对其建立索引并存储该索引,您可以查询数据了。
插入文档或节点
如果您已经创建了索引,则可以使用该insert
方法将新文档添加到索引中。
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex([])
for doc in documents:
index.insert(doc)
有关管理文档和示例笔记本的更多详细信息,请参阅文档管理操作方法
。
七、查询
现在您已经加载了数据、构建了索引并存储了该索引以供以后使用,您已准备好进入 LLM 应用程序最重要的部分:查询。
最简单的是,查询只是对LLM的提示调用:它可以是一个问题并获得答案,或者是一个总结请求,或者是一个更复杂的指令。
更复杂的查询可能涉及重复/链接提示+ LLM 调用,甚至跨多个组件的推理循环。
入门
所有查询的基础是QueryEngine
.获取 QueryEngine 最简单的方法是让索引为您创建一个,如下所示:
query_engine = index.as_query_engine()
response = query_engine.query(
"Write an email to the user given their background information."
)
print(response)
查询阶段
然而,查询的内容比最初看到的要多。
查询由三个不同的阶段组成:
- 检索是指您从
Index
.正如前面在索引中所讨论的,最常见的检索类型是“top-k”语义检索,但还有许多其他检索策略。 - 后处理是指对
Node
检索到的数据进行可选的重新排序、转换或过滤,例如要求它们具有特定的元数据,例如附加的关键字。 - 响应综合是将您的查询、最相关的数据和提示组合起来并发送给您的LLM以返回响应。
提示 : 您可以了解如何将元数据附加到文档和节点。
自定义查询阶段
LlamaIndex 具有低级组合 API,可让您对查询进行精细控制。
在此示例中,我们自定义检索器以使用不同的数字top_k
并添加后处理步骤,该步骤要求检索到的节点达到要包含的最小相似度分数。
当您有相关结果时,这将为您提供大量数据,但如果您没有任何相关结果,则可能不会提供任何数据。
from llama_index.core import VectorStoreIndex, get_response_synthesizer
from llama_index.core.retrievers import VectorIndexRetriever
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.postprocessor import SimilarityPostprocessor
# build index
index = VectorStoreIndex.from_documents(documents)
# configure retriever
retriever = VectorIndexRetriever(
index=index,
similarity_top_k=10,
)
# configure response synthesizer
response_synthesizer = get_response_synthesizer()
# assemble query engine
query_engine = RetrieverQueryEngine(
retriever=retriever,
response_synthesizer=response_synthesizer,
node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.7)],
)
# query
response = query_engine.query("What did the author do growing up?")
print(response)
您还可以通过实现相应的接口来添加自己的检索、响应合成和整体查询逻辑。
有关已实现组件和支持配置的完整列表,请查看我们的参考文档。
让我们更详细地了解自定义每个步骤:
配置检索器
retriever = VectorIndexRetriever(
index=index,
similarity_top_k=10,
)
您可以在我们的 检索器模块指南 中了解各种各样的检索器。
配置节点后处理器
我们支持高级Node
过滤和增强,可以进一步提高检索到的对象的相关性Node
。
这可以帮助减少 LLM 呼叫的时间/次数/成本或提高响应质量。
例如:
KeywordNodePostprocessor``required_keywords
:按和过滤节点exclude_keywords
。SimilarityPostprocessor
:通过设置相似度分数的阈值来过滤节点(因此仅受基于嵌入的检索器支持)PrevNextNodePostprocessor``Node
:根据关系使用额外的相关上下文来增强检索到的对象Node
。
节点后处理器的完整列表记录在节点后处理器参考中。
配置所需的节点后处理器:
node_postprocessors = [
KeywordNodePostprocessor(
required_keywords=["Combinator"], exclude_keywords=["Italy"]
)
]
query_engine = RetrieverQueryEngine.from_args(
retriever, node_postprocessors=node_postprocessors
)
response = query_engine.query("What did the author do growing up?")
配置响应合成
检索器获取相关节点后,BaseSynthesizer
通过组合信息来合成最终响应。
您可以通过配置它
query_engine = RetrieverQueryEngine.from_args(
retriever, response_mode=response_mode
)
目前,我们支持以下选项:
default
:通过顺序浏览每个检索到的答案来“创建和完善”答案Node
;这使得每个节点都有一个单独的 LLM 调用。
适合更详细的答案。compact``Node
:在每次 LLM 调用期间通过填充尽可能多的适合最大提示大小的文本块来“压缩”提示。
如果一个提示中的内容太多,无法填充,请通过多个提示来“创建和完善”答案。tree_summarize
:给定一组Node
对象和查询,递归构造一棵树并返回根节点作为响应。
适合总结目的。no_text
:仅运行检索器来获取本应发送到 LLM 的节点,而不实际发送它们。
然后就可以通过检查来检查了response.source_nodes
。
第 5 节更详细地介绍了响应对象。accumulate
:给定一组Node
对象和查询,将查询应用于每个Node
文本块,同时将响应累积到数组中。
返回所有响应的串联字符串。
适合当您需要对每个文本块单独运行相同的查询时。
结构化输出
您可能希望确保您的输出是结构化的。
请参阅我们的查询引擎 + Pydantic 输出,了解如何从查询引擎类中提取 Pydantic 对象。
另请务必查看我们的整个结构化输出指南。
创建您自己的查询管道
如果您想设计复杂的查询流,您可以跨许多不同的模块编写自己的查询管道,从提示/LLM/输出解析器到检索器,再到响应合成器,再到您自己的自定义组件。
请参阅我们的查询管道模块指南了解更多详细信息。
八、跟踪和调试
调试和跟踪应用程序的操作 是 理解和优化应用程序的关键。LlamaIndex 提供了多种方法来做到这一点。
基本日志记录
查看应用程序正在执行的操作的最简单方法是打开调试日志记录。
这可以在应用程序中的任何位置完成,如下所示:
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
回调处理程序
LlamaIndex 提供回调来帮助调试、跟踪和追踪库的内部运作。
使用回调管理器,可以添加所需数量的回调。
除了记录与事件相关的数据之外,您还可以跟踪每个事件的持续时间和发生次数。
此外,还记录了事件的跟踪图,回调可以根据需要使用这些数据。
例如,LlamaDebugHandler
默认情况下,大多数操作后都会打印事件跟踪。
您可以获得一个简单的回调处理程序,如下所示:
import llama_index.core
llama_index.core.set_global_handler("simple")
您还可以了解如何构建自己的自定义回调处理程序。
可观测性
LlamaIndex 提供一键式可观察性,允许您在生产环境中构建原则性的 LLM 应用程序。
此功能允许您将 LlamaIndex 库与我们合作伙伴提供的强大可观察性/评估工具无缝集成。
配置变量一次,您就可以执行以下操作:
- 查看LLM/提示输入/输出
- 确保任何组件(LLM、嵌入)的输出均按预期运行
- 查看索引和查询的调用跟踪
要了解更多信息,请查看我们的可观察性文档
九、评估
评估和基准测试是LLM发展中的关键概念。
为了提高 LLM 应用程序(RAG、代理)的性能,您必须有一种方法来衡量它。
LlamaIndex 提供了衡量生成结果质量的关键模块。
我们还提供衡量检索质量的关键模块。
您可以在我们的模块指南中了解有关 LlamaIndex 中评估工作原理的更多信息。
反应评估
响应与检索到的上下文匹配吗?它也与查询匹配吗?它与参考答案或指南相符吗?这是一个简单的示例,用于评估单个响应的忠诚度,即响应是否与上下文一致,例如是否没有幻觉:
from llama_index.core import VectorStoreIndex
from llama_index.llms.openai import OpenAI
from llama_index.core.evaluation import FaithfulnessEvaluator
# create llm
llm = OpenAI(model="gpt-4", temperature=0.0)
# build index
...
vector_index = VectorStoreIndex(...)
# define evaluator
evaluator = FaithfulnessEvaluator(llm=llm)
# query index
query_engine = vector_index.as_query_engine()
response = query_engine.query(
"What battles took place in New York City in the American Revolution?"
)
eval_result = evaluator.evaluate_response(response=response)
print(str(eval_result.passing))
响应包含响应和生成响应的源;评估者对它们进行比较并确定响应是否忠实于来源。
您可以在我们的模块指南中了解有关响应评估的更多信息。
检索评价
检索到的来源与查询相关吗?这是一个评估单个检索的简单示例:
from llama_index.core.evaluation import RetrieverEvaluator
# define retriever somewhere (e.g. from index)
# retriever = index.as_retriever(similarity_top_k=2)
retriever = ...
retriever_evaluator = RetrieverEvaluator.from_metric_names(
["mrr", "hit_rate"], retriever=retriever
)
retriever_evaluator.evaluate(
query="query", expected_ids=["node_id1", "node_id2"]
)
这会将查询检索到的内容与预期检索的一组节点进行比较。
实际上,您可能想要评估一整批检索;您可以在我们的检索评估模块指南中了解如何执行此操作。
相关概念
如果您正在致电托管的远程LLM,您可能有兴趣分析您的申请成本
。
十、成本分析
概念
每次调用 LLM 都会花费一些钱 - 例如,OpenAI 的 gpt-3.5-turbo 花费 0.002 美元/1k 代币。
建立索引和查询的成本取决于
- 使用的LLM类型
- 使用的数据结构类型
- 构建期间使用的参数
- 查询时使用的参数
构建和查询每个索引的成本是参考文档中的 TODO。
同时,我们提供以下信息:
- 指数成本结构的高级概述。
- 您可以直接在 LlamaIndex 中使用的令牌预测器!
成本结构概览
没有 LLM 调用的指数
以下索引在构建过程中根本不需要 LLM 调用(0 成本):
SummaryIndex
SimpleKeywordTableIndex
- 使用正则表达式关键字提取器从每个文档中提取关键字RAKEKeywordTableIndex
- 使用 RAKE 关键字提取器从每个文档中提取关键字
LLM 调用的指数
以下索引确实需要在构建期间调用 LLM:
TreeIndex
- 使用LLM对文本进行分层总结来构建树KeywordTableIndex
- 使用LLM从每个文档中提取关键字
查询时间
在查询期间总会有 >= 1 个 LLM 调用,以便综合最终答案。
一些索引包含索引构建和查询之间的成本权衡。
SummaryIndex
例如,可以免费构建,但对摘要索引运行查询(不过滤或嵌入查找)将调用 LLM {math}N
次。
以下是有关每个指数的一些注释:
SummaryIndex
:默认情况下需要 {math}N
LLM 调用,其中 N 是节点数。TreeIndex
:默认情况下需要 {math}\log (N)
LLM 调用,其中 N 是叶节点的数量。- 设置
child_branch_factor=2
将比默认值child_branch_factor=1
(多项式与对数)更昂贵,因为我们为每个父节点遍历 2 个子节点,而不是仅 1 个。 KeywordTableIndex
:默认情况下需要 LLM 调用来提取查询关键字。- 可以在查询文本上使用正则表达式/RAKE 关键字提取器
index.as_retriever(retriever_mode="simple")
。
index.as_retriever(retriever_mode="rake")
VectorStoreIndex
:默认情况下,每个查询需要一次 LLM 调用。
如果您增加similarity_top_k
或chunk_size
,或更改response_mode
,则该数字将会增加。
使用模式
LlamaIndex 提供令牌预测器来预测 LLM 和嵌入调用的令牌使用情况。
这使您可以在进行任何相应的 LLM 调用之前估算 1) 索引构建和 2) 索引查询期间的成本。
使用回调来计算令牌TokenCountingHandler
。
有关设置的详细信息,请参阅示例笔记本
。
使用MockLLM
要预测 LLM 调用的令牌使用情况,请导入并实例化 MockLLM,如下所示。
该max_tokens
参数用作“最坏情况”预测,其中每个 LLM 响应将恰好包含该数量的令牌。
如果max_tokens
未指定,那么它将简单地预测回提示。
from llama_index.core.llms import MockLLM
from llama_index.core import Settings
# use a mock llm globally
Settings.llm = MockLLM(max_tokens=256)
然后,您可以在索引构建和查询期间使用此预测器。
使用模拟嵌入
您还可以预测嵌入调用的令牌使用情况MockEmbedding
。
from llama_index.core import MockEmbedding
from llama_index.core import Settings
# use a mock embedding globally
Settings.embed_model = MockEmbedding(embed_dim=1536)
使用模式
估计 LLM 和嵌入令牌计数
为了测量 LLM 和嵌入令牌计数,您需要
1、设置MockLLM
和MockEmbedding
对象
from llama_index.core.llms import MockLLM
from llama_index.core import MockEmbedding
llm = MockLLM(max_tokens=256)
embed_model = MockEmbedding(embed_dim=1536)
2、设置TokenCountingCallback
处理程序
import tiktoken
from llama_index.core.callbacks import CallbackManager, TokenCountingHandler
token_counter = TokenCountingHandler(
tokenizer=tiktoken.encoding_for_model("gpt-3.5-turbo").encode
)
callback_manager = CallbackManager([token_counter])
3、将它们添加到全局Settings
from llama_index.core import Settings
Settings.llm = llm
Settings.embed_model = embed_model
Settings.callback_manager = callback_manager
4、构建索引
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader(
"./docs/examples/data/paul_graham"
).load_data()
index = VectorStoreIndex.from_documents(documents)
5、测量计数!
print(
"Embedding Tokens: ",
token_counter.total_embedding_token_count,
"\n",
"LLM Prompt Tokens: ",
token_counter.prompt_llm_token_count,
"\n",
"LLM Completion Tokens: ",
token_counter.completion_llm_token_count,
"\n",
"Total LLM Token Count: ",
token_counter.total_llm_token_count,
"\n",
)
# reset counts
token_counter.reset_counts()
6、运行查询,再次测量
query_engine = index.as_query_engine()
response = query_engine.query("query")
print(
"Embedding Tokens: ",
token_counter.total_embedding_token_count,
"\n",
"LLM Prompt Tokens: ",
token_counter.prompt_llm_token_count,
"\n",
"LLM Completion Tokens: ",
token_counter.completion_llm_token_count,
"\n",
"Total LLM Token Count: ",
token_counter.total_llm_token_count,
"\n",
)
十一、把它们放在一起
恭喜!您已加载数据、对其建立索引、存储索引并查询索引。
现在您必须将一些东西交付到生产中。
我们可以向您展示如何做到这一点!
- 在问答模式中,我们将探讨一些更高级和更微妙的方法,您可以在基础知识之外构建查询引擎。
- 术语定义教程是关于创建精细查询应用程序的详细分步教程,包括定义提示和支持图像作为输入。
- 我们有一个针对索引创建统一查询框架的指南,该指南向您展示如何跨多个索引运行查询。
- 我们讨论如何构建知识图谱查询
- 以及SQL 等结构化数据
- 我们有关于如何构建聊天机器人的指南
- 我们谈论LlamaIndex 中的构建代理
- 最后但并非最不重要的一点是,我们向您展示如何使用 LlamaIndex构建全栈 Web 应用程序
LlamaIndex 还提供了一些工具/项目模板来帮助您构建全栈模板。
例如,create-llama
为您构建一个全栈脚手架。
请查看我们的全栈项目页面了解更多详细信息。
我们还有llamaindex-cli rag
CLI 工具,它将上述一些概念组合成一个易于使用的工具,用于从终端与文件聊天!
1、Agent
通过定义一组工具并将它们提供给我们的 ReActAgent 实现,可以将代理放在 LlamaIndex 中。
我们在这里将其与 OpenAI 一起使用,但它也可以与任何足够强大的LLM一起使用:
from llama_index.core.tools import FunctionTool
from llama_index.llms.openai import OpenAI
from llama_index.core.agent import ReActAgent
# define sample Tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers and returns the result integer"""
return a * b
multiply_tool = FunctionTool.from_defaults(fn=multiply)
# initialize llm
llm = OpenAI(model="gpt-3.5-turbo-0613")
# initialize ReAct agent
agent = ReActAgent.from_tools([multiply_tool], llm=llm, verbose=True)
这些工具可以是如上所示的Python函数,也可以是LlamaIndex查询引擎:
from llama_index.core.tools import QueryEngineTool
query_engine_tools = [
QueryEngineTool(
query_engine=sql_agent,
metadata=ToolMetadata(
name="sql_agent", description="Agent that can execute SQL queries."
),
),
]
agent = ReActAgent.from_tools(query_engine_tools, llm=llm, verbose=True)
您可以在我们的代理模块指南中了解更多信息。
原生 OpenAIAgent
我们有一个OpenAIAgent
基于OpenAI API 的函数调用实现,可让您快速构建代理:
- OpenAIAgent
- 带有查询引擎工具的 OpenAIAgent
- OpenAIAgent 查询规划
- 开放人工智能助手
- OpenAI 助理食谱
- 强制函数调用
- 并行函数调用
- 上下文检索
LlamaIndex 中的代理组件
LlamaIndex 提供了能够对数据的不同用例进行自动推理的核心模块,这使得它们本质上成为代理。
下面显示了其中一些核心模块以及示例教程。
用于多文档分析的 SubQuestionQueryEngine
- 子问题查询引擎(简介)
- 10Q 分析(优步)
- 10K 分析(Uber 和 Lyft)
查询转换
- 如何
- 多步查询分解(笔记本)
路由
- 用法
- 路由器查询引擎指南(笔记本)
LLM重新排名
- 第二阶段处理方法
- LLM重新排名指南(了不起的盖茨比)
聊天引擎
- 聊天引擎操作方法
使用 LlamaIndex 作为代理框架内的工具
LlamaIndex 可以用作代理框架内的工具 - 包括 LangChain、ChatGPT。
这些集成如下所述。
LangChain
我们与LangChain有深度集成。
LlamaIndex 查询引擎可以轻松打包为在 LangChain 代理中使用的工具,并且 LlamaIndex 还可以用作内存模块/检索器。
查看下面我们的指南/教程!
资源
- 构建聊天机器人教程
- OnDemandLoader工具教程
聊天GPT
LlamaIndex 可以用作 ChatGPT 检索插件(我们还有一个 TODO 来开发更通用的插件)。
资源
- LlamaIndex ChatGPT 检索插件
2、全栈 Web 应用程序
LlamaIndex 可以集成到下游全栈 Web 应用程序中。
它可以在后端服务器(例如 Flask)中使用,打包到 Docker 容器中,和/或直接在 Streamlit 等框架中使用。
我们提供教程和资源来帮助您开始该领域:
- Fullstack 应用程序指南向您展示如何使用 LlamaIndex 作为 API 和 TypeScript+React 前端构建应用程序
- 使用 Delphic 的全栈应用程序将引导您将 LlamaIndex 与名为 Delphic 的可用于生产的 Web 应用程序启动模板一起使用。
- LlamaIndex Starter Pack为LlamaIndex 提供了非常基本的 Flask、streamlit 和 docker 示例。
3、知识图谱
LlamaIndex 包含一些关于构建知识图的精彩指南。
查看下面的端到端教程/研讨会。
另请查看我们的知识图查询引擎指南。
- LlamaIndex 研讨会:使用知识图构建 RAG https://colab.research.google.com/drive/1tLjOg2ZQuIClfuWrAC2LdiZHCov8oUbs
- REBEL + 知识图索引https://colab.research.google.com/drive/1G6pcR0pXvSkdMQlAK_P-IrYgo-_staxd?usp=sharing
4、问答模式
语义搜索
LlamaIndex 最基本的示例用法是通过语义搜索。
我们提供了一个简单的内存向量存储供您入门,但您也可以选择使用我们的任何一个向量存储集成:
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
documents = SimpleDirectoryReader("data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()
response = query_engine.query("What did the author do growing up?")
print(response)
教程
- 入门教程
- 基本使用模式
指南
- 示例(笔记本)
总结
汇总查询要求LLM迭代许多(如果不是大多数)文档才能综合答案。
例如,汇总查询可能类似于以下内容之一:
- “这本文本集的摘要是什么?”
- “给我总结一下 X 人在公司的经历。
”
一般来说,摘要索引适合此用例。
默认情况下,摘要索引会遍历所有数据。
根据经验,设置response_mode="tree_summarize"
也会带来更好的总结结果。
index = SummaryIndex.from_documents(documents)
query_engine = index.as_query_engine(response_mode="tree_summarize")
response = query_engine.query("<summarization_query>")
结构化数据查询
LlamaIndex 支持对结构化数据的查询,无论是 Pandas DataFrame 还是 SQL 数据库。
以下是一些相关资源:
教程
- 文本转 SQL 指南
指南
- SQL指南(核心)(笔记本)
- Pandas 演示(笔记本)
异构数据路由
LlamaIndex 还支持通过异构数据源进行路由RouterQueryEngine
- 例如,如果您想将查询“路由”到基础文档或子索引。
为此,首先针对不同数据源构建子指数。
然后构造对应的查询引擎,并给每个查询引擎一个描述,得到一个QueryEngineTool
.
from llama_index.core import TreeIndex, VectorStoreIndex
from llama_index.core.tools import QueryEngineTool
...
# define sub-indices
index1 = VectorStoreIndex.from_documents(notion_docs)
index2 = VectorStoreIndex.from_documents(slack_docs)
# define query engines and tools
tool1 = QueryEngineTool.from_defaults(
query_engine=index1.as_query_engine(),
description="Use this query engine to do...",
)
tool2 = QueryEngineTool.from_defaults(
query_engine=index2.as_query_engine(),
description="Use this query engine for something else...",
)
然后,我们RouterQueryEngine
在它们之上定义一个。
默认情况下,这使用 aLLMSingleSelector
作为路由器,根据描述,它使用 LLM 选择将查询路由到的最佳子索引。
from llama_index.core.query_engine import RouterQueryEngine
query_engine = RouterQueryEngine.from_defaults(
query_engine_tools=[tool1, tool2]
)
response = query_engine.query(
"In Notion, give me a summary of the product roadmap."
)
指南
- 路由器查询引擎指南(笔记本)
比较/对比查询
您可以使用ComposableGraph 中的查询转换模块显式执行比较/对比查询。
from llama_index.core.query.query_transform.base import DecomposeQueryTransform
decompose_transform = DecomposeQueryTransform(
service_context.llm, verbose=True
)
该模块将有助于将复杂的查询分解为现有索引结构上的更简单的查询。
指南
- 查询转换
您还可以依靠 LLM 来推断是否执行比较/对比查询(请参阅下面的多文档查询)。
多文档查询
除了上面描述的显式合成/路由流程之外,LlamaIndex 还可以支持更通用的多文档查询。
它可以通过我们的SubQuestionQueryEngine
类来做到这一点。
给定一个查询,该查询引擎将在综合最终答案之前生成一个包含针对子文档的子查询的“查询计划”。
为此,首先为每个文档/数据源定义一个索引,并用 a 包装它QueryEngineTool
(类似于上面):
from llama_index.core.tools import QueryEngineTool, ToolMetadata
query_engine_tools = [
QueryEngineTool(
query_engine=sept_engine,
metadata=ToolMetadata(
name="sept_22",
description="Provides information about Uber quarterly financials ending September 2022",
),
),
QueryEngineTool(
query_engine=june_engine,
metadata=ToolMetadata(
name="june_22",
description="Provides information about Uber quarterly financials ending June 2022",
),
),
QueryEngineTool(
query_engine=march_engine,
metadata=ToolMetadata(
name="march_22",
description="Provides information about Uber quarterly financials ending March 2022",
),
),
]
然后,我们定义了SubQuestionQueryEngine
这些工具:
from llama_index.core.query_engine import SubQuestionQueryEngine
query_engine = SubQuestionQueryEngine.from_defaults(
query_engine_tools=query_engine_tools
)
在综合最终答案之前,该查询引擎可以针对查询引擎工具的任何子集执行任意数量的子查询。
这使得它特别适合跨文档的比较/对比查询以及与特定文档相关的查询。
指南
- 子问题查询引擎(简介)
- 10Q 分析(优步)
- 10K 分析(Uber 和 Lyft)
多步查询
LlamaIndex 还可以支持迭代多步查询。
给定一个复杂的查询,将其分解为初始子问题,并根据返回的答案顺序生成子问题,直到返回最终答案。
例如,给定一个问题“作者启动的第一批加速器程序是谁?”,该模块首先将查询分解为更简单的初始问题“作者启动的加速器程序是什么?”,查询索引,然后提出后续问题。
指南
- 查询转换
- 多步查询分解(笔记本)
时态查询
LlamaIndex 可以支持需要了解时间的查询。
它可以通过两种方式做到这一点:
- 确定查询是否需要利用节点之间的时间关系(上一个/下一个关系)来检索附加上下文来回答问题。
- 按新近度排序并过滤过时的上下文。
指南
- 后处理指南
- 上一个/下一个后处理
- 最近的后处理
其他资源
- 术语和定义提取指南
- SEC 10k 分析
回到顶部
以前的把它们放在一起
5、结构化数据
LlamaIndex + 结构化数据指南
许多现代数据系统都依赖于结构化数据,例如 Postgres DB 或 Snowflake 数据仓库。
LlamaIndex 提供了许多由 LLM 提供支持的高级功能,既可以从非结构化数据创建结构化数据,也可以通过增强的文本到 SQL 功能来分析该结构化数据。
本指南将帮助您逐步了解其中的每一项功能。
具体来说,我们涵盖以下主题:
- 设置 :定义我们的示例 SQL 表。
- 构建我们的表索引 :如何从 SQL 数据库到表架构索引
- 使用自然语言 SQL 查询 :如何使用自然语言查询我们的 SQL 数据库。
我们将浏览一个包含城市/人口/国家信息的玩具示例表。
此处提供了本教程的笔记本。
设置
首先,我们使用 SQLAlchemy 设置一个简单的 sqlite 数据库:
from sqlalchemy import (
create_engine,
MetaData,
Table,
Column,
String,
Integer,
select,
column,
)
engine = create_engine("sqlite:///:memory:")
metadata_obj = MetaData()
然后我们创建一个玩具city_stats
桌:
# create city SQL table
table_name = "city_stats"
city_stats_table = Table(
table_name,
metadata_obj,
Column("city_name", String(16), primary_key=True),
Column("population", Integer),
Column("country", String(16), nullable=False),
)
metadata_obj.create_all(engine)
现在是时候插入一些数据点了!
如果您想通过从非结构化数据推断结构化数据点来填充此表,请查看以下部分。
否则,您可以选择直接填充此表:
from sqlalchemy import insert
rows = [
{"city_name": "Toronto", "population": 2731571, "country": "Canada"},
{"city_name": "Tokyo", "population": 13929286, "country": "Japan"},
{"city_name": "Berlin", "population": 600000, "country": "Germany"},
]
for row in rows:
stmt = insert(city_stats_table).values(**row)
with engine.begin() as connection:
cursor = connection.execute(stmt)
最后,我们可以使用 SQLDatabase 包装器来包装 SQLAlchemy 引擎;这允许在 LlamaIndex 中使用数据库:
from llama_index.core import SQLDatabase
sql_database = SQLDatabase(engine, include_tables=["city_stats"])
自然语言SQL
一旦我们构建了 SQL 数据库,我们就可以使用 NLSQLTableQueryEngine 来构建合成为 SQL 查询的自然语言查询。
请注意,我们需要指定要与该查询引擎一起使用的表。
如果我们不这样做,查询引擎将提取所有架构上下文,这可能会溢出 LLM 的上下文窗口。
from llama_index.core.query_engine import NLSQLTableQueryEngine
query_engine = NLSQLTableQueryEngine(
sql_database=sql_database,
tables=["city_stats"],
)
query_str = "Which city has the highest population?"
response = query_engine.query(query_str)
在任何情况下都应该使用此查询引擎,只要您可以预先指定要查询的表,或者所有表模式的总大小加上提示的其余部分适合您的上下文窗口。
建立我们的表索引
如果我们事先不知道要使用哪个表,并且表模式的总大小超出了上下文窗口大小,则我们应该将表模式存储在索引中,以便在查询时我们可以检索正确的数据架构。
我们可以做到这一点的方法是使用 SQLTableNodeMapping 对象,该对象接受 SQLDatabase 并为传递到 ObjectIndex 构造函数中的每个 SQLTableSchema 对象生成一个 Node 对象。
from llama_index.core.objects import (
SQLTableNodeMapping,
ObjectIndex,
SQLTableSchema,
)
table_node_mapping = SQLTableNodeMapping(sql_database)
table_schema_objs = [
(SQLTableSchema(table_name="city_stats")),
...,
] # one SQLTableSchema for each table
obj_index = ObjectIndex.from_objects(
table_schema_objs,
table_node_mapping,
VectorStoreIndex,
)
在这里您可以看到我们定义了 table_node_mapping 和一个带有“city_stats”表名的 SQLTableSchema。
我们将它们连同我们要使用的 VectorStoreIndex 类定义一起传递到 ObjectIndex 构造函数中。
这将为我们提供一个 VectorStoreIndex,其中每个节点都包含表模式和其他上下文信息。
您还可以添加您想要的任何其他上下文信息。
# manually set extra context text
city_stats_text = (
"This table gives information regarding the population and country of a given city.\n"
"The user will query with codewords, where 'foo' corresponds to population and 'bar'"
"corresponds to city."
)
table_node_mapping = SQLTableNodeMapping(sql_database)
table_schema_objs = [
(SQLTableSchema(table_name="city_stats", context_str=city_stats_text))
]
使用自然语言 SQL 查询
一旦定义了表模式索引 obj_index,我们就可以通过传入 SQLDatabase 和从对象索引构造的检索器来构造 SQLTableRetrieverQueryEngine。
from llama_index.core.indices.struct_store import SQLTableRetrieverQueryEngine
query_engine = SQLTableRetrieverQueryEngine(
sql_database, obj_index.as_retriever(similarity_top_k=1)
)
response = query_engine.query("Which city has the highest population?")
print(response)
现在,当我们查询检索器查询引擎时,它将检索相关的表模式并合成 SQL 查询和该查询结果的响应。
结论性思考
现在就这样了!我们不断寻找改进结构化数据支持的方法。
如果您有任何疑问,请在我们的 Discord中告诉我们。
相关资源:
- Airbyte SQL 索引指南
2024-04-15(一)