检索增强生成(RAG)为大型语言模型(LLMs)提供了一个新机会,使其能够利用理解用户意图的能力,搜索内部数据库知识以生成 SQL。
LangChain 和 LlamaIndex 等 LLM 框架提供了教程,帮助开发者在其数据源上实现 text-to-SQL。然而,当这些框架部署到生产环境时,用户很快就会发现难以实现所需的功能。
在本文中,我将以 LangChain 为例,简要介绍如何使用它实现 text-to-SQL。我们将分解您会遇到的挑战,并提供解决方案。
这基于 LangChain 官方文档中的教程
这里是在 LangChain 中构建 text-to-SQL 解决方案的高层概念;查看完整教程请点击此处。
工作原理如下
首先,当用户提出一个业务问题时,LLM 将理解该问题并根据与业务问题一同提供的 DDL 生成 SQL;通常,如果您想增强语义理解,您还需要将语义信息附加到提示中。
首先,安装 LangChain 相关库
%pip install --upgrade --quiet langchain langchain-community langchain-openai
接下来从 langchain-community
导入 SQLDatabase
,SQLDatabase
是 SQLAlchemy
对数据库的包装,它在数据库之上提供了一个 SQL 工具包。
from langchain_community.utilities import SQLDatabase
from langchain.chains import create_sql_query_chain
from langchain_openai import ChatOpenAI
db = SQLDatabase.from_uri("sqlite:///Chinook.db")
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
使用 create_sql_query_chain
生成不同方言的 SQL 语言。
chain = create_sql_query_chain(llm, db)
response = chain.invoke({"question": "How many employees are there"})
以下是提示示例
from langchain_core.prompts import PromptTemplate
template = '''Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer.
Use the following format:
Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"
Only use the following tables:
{table_info}.
Question: {input}'''
prompt = PromptTemplate.from_template(template)
在这里,我们看到我们要求 LLMs 根据字符串模板 {dialect}
生成 SQL;底层机制是将当前方言插入到提示中,然后依靠 LLMs 生成 SQL 方言。
对于 {table_info}
,您需要插入所有您希望 LLM 理解的表 DDL。如果表很多怎么办?我们无法在每次提示时都倾倒整个数据库信息。LangChain 的官方教程建议如下。
通过将表分组来简化模型的任务。
system = """Return the names of the SQL tables that are relevant to the user question. \
The tables are:
Music
Business"""
category_chain = create_extraction_chain_pydantic(Table, llm, system_message=system)
category_chain.invoke({"input": "What are all the genres of Alanis Morisette songs"})
您可以按类别对表进行分组
from typing import List
def get_tables(categories: List[Table]) -> List[str]:
tables = []
for category in categories:
if category.name == "Music":
tables.extend(
[
"Album",
"Artist",
"Genre",
"MediaType",
"Playlist",
"PlaylistTrack",
"Track",
]
)
elif category.name == "Business":
tables.extend(["Customer", "Employee", "Invoice", "InvoiceLine"])
return tables
table_chain = category_chain | get_tables # noqa
table_chain.invoke({"input": "What are all the genres of Alanis Morisette songs"})
另一种常见方法是将表 schema 存储在向量数据库中,并执行语义搜索以从特定的业务查询中检索相关的 DDL。然而,在转向生产环境时,仍然存在许多挑战。
使用 LangChain 构建 text-to-SQL 工具看起来很简单,但在与生产用例集成时会出现一些常见挑战,如下所示。
仅基于数据 schema 使用 text-to-SQL 的问题在于,当用户通过聊天界面提出业务问题时,他们通常使用业务语言,而不是表名、列名等数据结构定义。
通过 AI 提问时,您可能会使用您公司的业务术语和定义,以及内部定义的关系,因此您不仅需要考虑数据结构,还需要考虑语义。
在 LangChain 的教程中,演示了将所有表 schema 放入提示中。当您连接到生产数据库时,表很容易扩展到数据库中的数千甚至数万个。
您无法将所有表都放入提示中,因此您需要将元数据存储中的表和元数据嵌入到向量数据库中。当用户提问时,您可以使用向量数据库中的语义搜索来检索向量数据库中最相关的向量。
上述过程主要离线运行,进行向量索引创建。Pinterest 在其最近关于他们如何内部构建 text-to-SQL 的文章中分享了他们如何处理这个问题。
不同的数据库需要使用不同的方言;LangChain 的 Text-to-SQL 教程依赖于一个流行的 Python 库 SQLAlchemy
,在底层,SQLAlchemy
为用户提供了与不同数据库交互的标准工具包和 ORM,但 LLMs 仍然需要为不同的数据库生成特定的方言。
乍一看,使用这种模式听起来很合理,并且可以通过 SQLAlchemy
轻松地为许多数据库提供 LLM 能力。
在迁移到生产环境时,使用具有预定义聚合和计算的相同 SQL 语法对于跨数据源获得更好且一致的检索性能非常重要。
我上面列举了构建生产级 text-to-SQL 解决方案的一些明显挑战。这就是我们的团队构建 Wren AI(面向您的数据库的开源 AI 数据助手)的原因。您可以在几分钟内为您的 text-to-SQL 任务在内部设置一个 AI agent。
使用 Wren AI,我们可以自动化所有元数据和语义,并帮助 LLMs 学习业务中的语义如何工作,无需您编写任何代码。通过用户友好的界面,您可以建模您的数据 schema,并在建模层添加业务语义。我们将自动为您完成所有的离线向量索引创建。
将表 schema 与语义映射,并确保您可以通过提示和语义获取正确信息,这需要大量的微调;使用 Wren AI,我们处理所有优化,确保在用户提出业务问题时,它能够搜索到最相关的结果。
Wren AI 的底层,我们开发了一个名为 Wren Engine 的语义引擎,它也是开源的。该引擎可以将标准 ANSI SQL 转译成不同的 SQL 方言,并提供语义封装能力,以便在语义建模层定义聚合和计算。
反馈循环是 AI Agents 最重要的设计之一。我们希望我们的 Agent 能够从历史中学习,并教导 Agent 在未来的任务中表现得更好;这就是反馈循环的作用所在。
我们在用户界面中内置了 Wren AI 反馈循环,因此当您提问并从 Wren AI 获得答案时,您可以向 Agent 提供调整,Agent 将从您的输入中学习,重新生成结果,并将学习内容存储在语义建模定义中,这样下次用户提问时就会生成正确的结果。
立即使用 AI 加速您的数据!?