结构化数据#
LlamaIndex + 结构化数据指南#
许多现代数据系统依赖于结构化数据,例如 Postgres 数据库或 Snowflake 数据仓库。LlamaIndex 提供了许多由大型语言模型 (LLM) 支持的高级功能,既可以从非结构化数据创建结构化数据,也可以通过增强的文本转 SQL 能力分析这些结构化数据。
注意:任何文本转 SQL 应用程序都应意识到执行任意 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 查询。
请注意,我们需要指定要与此查询引擎一起使用的表。如果我们不指定,查询引擎将拉取所有模式上下文,这可能会超出大型语言模型的上下文窗口。
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,其中每个 Node 包含表模式和其他上下文信息。您还可以添加任何您想要的额外上下文信息。
# 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 中告诉我们。
相关资源