Skip to content

结构化数据#

LlamaIndex + 结构化数据指南#

现代数据系统大量依赖结构化数据,例如Postgres数据库或Snowflake数据仓库。LlamaIndex提供了许多由大语言模型驱动的高级功能,既能从非结构化数据生成结构化数据,也能通过增强的文本转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查询。

注意需要指定查询引擎要使用的表。否则查询引擎会拉取所有模式上下文,可能导致超出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")),
    ...,
]  # 每个表对应一个SQLTableSchema

obj_index = ObjectIndex.from_objects(
    table_schema_objs,
    table_node_mapping,
    VectorStoreIndex,
)

这里定义了table_node_mapping和包含"city_stats"表名的SQLTableSchema。将它们传入ObjectIndex构造函数及要使用的VectorStoreIndex类定义。这将生成一个VectorStoreIndex,其中每个Node包含表模式和其他上下文信息。也可添加任何额外的上下文信息。

# 手动设置额外上下文文本
city_stats_text = (
    "该表提供有关城市人口和国家信息。\n"
    "用户将使用代码词查询,其中'foo'对应人口,'bar'对应城市。"
)

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联系我们。

相关资源: