type
status
date
slug
summary
tags
category
icon
password
comment
Status
之前我们介绍了大语言模型(LLM)如何查询本地知识库中的文章片段完成问题回答的链和智能体,这是查询非结构化数据。今天我们来看一下如何查询结构化数据。
查询结构化数据,与查询非结构化文本数据在本质上是不同的。在非结构化文本数据中,常见的做法是生成可以被向量数据库搜索的文本。而结构化数据的处理方法通常是让LLM编写并执行查询语句(DSL)中的查询,例如SQL。在本指南中,我们将介绍在数据库中创建数据库表问答系统的基本方法。我们将涵盖使用链式调用和智能体两种实现方式。
- 链式应用将允许我们对数据库中的数据提出问题,并得到自然语言的回答。
- 智能体应用可以在需要时循环查询数据库,以回答问题。
⚠️ 注意 ⚠️
构建SQL数据库的问答系统需要执行由模型生成的SQL查询。这样做存在一些风险。请严格设置数据库连接权限。这样可以减轻构建模型驱动系统的风险。有关一般安全最佳实践的更多信息,请参阅这里。
架构
系统的执行的步骤如下:
- 将问题转换为DSL查询:模型将用户输入转换为SQL查询。
- 执行SQL查询:执行查询。
- 回答问题:模型使用查询结果响应用户输入。
请注意,查询CSV中的数据可以遵循类似的方法。有关详细信息,请参考操作指南,了解有关CSV数据上问答的更多细节。

配置
在这个教程中我们将使用openai的模型
我们来看一下使用SQLite连接Chinook数据库,请按照以下安装步骤,在与此笔记本相同的目录中创建Chinook.db:
- 下载保存文件到
Chinook.sql
- 执行
sqlite3 Chinook.db
命令
- 执行
.read Chinook.sql
命令
- 测试查询
SELECT * FROM Artist LIMIT 10;
现在,
Chinook.db
已经在我们的目录中,我们可以使用由SQLAlchemy驱动的SQLDatabase类与之交互:API 调用:SQLDatabase
现在我们已经有了一个可以查询的 SQL 数据库。现在让我们尝试将它连接到一个 LLM。
Chains
链(也就是由LangChain的Runnables子类组成的组合)适合能够确定执行流程的应用。我们可以先创建一个简单的链,它接受一个问题并执行以下操作:
- 将问题转换成SQL查询语句;
- 执行这个查询;
- 用查询结果来回答最初的问题。
但是链并不支持所有场景。比如,对于任何用户输入哪怕是“hello”,这个系统都会执行一个SQL查询。重要的是,有些问题需要多次查询才能解答。这种场景适用于agent智能体。
将问题转换为 SQL 查询语句
在链或agent中,第一步是获取用户输入并将其转换为SQL查询。LangChain提供了一个内置的链来完成这项工作:
create_sql_query_chain
。API 调用:create_sql_query_chain
尝试执行sql
我们可以查看LangSmith追踪来更好地理解这个链在做什么。我们也可以直接查看提示词(如下):
- 明确引用了SQLite。
- 为所有可用的表提供了定义。
- 为每个表提供了三行示例数据。
我们也可以像这样检查完整的提示:
执行 SQL 查询
生成了SQL查询之后,下一步自然是执行它。这是创建SQL链中最危险的一部分。请仔细考虑是否适合在您的数据上运行自动化查询。尽可能地最小化数据库连接权限。可以考虑在执行查询之前为您的链添加人工审批步骤(见下文)。
我们可以使用
QuerySQLDatabaseTool
轻松地为链添加执行sql的节点:API 调用:QuerySQLDataBaseTool
回答问题
现在我们已经能够自动生成并执行查询,只需要将原始问题和SQL查询结果结合起来,生成最终答案。我们可以通过再次将问题和结果传递给大型语言模型(LLM)来实现:
让我们回顾一下上述LCEL中发生的事情。假设这个链被调用。
- 在第一个
RunnablePassthrough.assign
之后,会返回一个包含两个元素的可运行对象:{"question": question, "query": write_query.invoke(question)}
,其中write_query
将生成一个SQL查询语句,以回答这个问题。
- 在第二个
RunnablePassthrough.assign
之后,我们添加了第三个元素"result"
,它包含execute_query.invoke(query)
,其中query
是前一步生成的结果。
- 这三个输入被格式化成提示,并传递给LLM。
StrOutputParser()
提取输出消息的字符串内容。
在将大型语言模型(LLMs)、工具、提示和其他链组合在一起时,由于每个都实现了Runnable接口,它们的输入和输出可以向下绑定。
后续步骤
对于更复杂的查询生成,我们可能想要创建少量样本提示或添加查询检查步骤。对于更多内容,请查看:
- 提示策略:高级提示工程技巧。
- 查询检查:增加查询验证和错误处理。
- 大型数据库:处理大型数据库的技术
Agents
LangChain 有一个 SQL Agent,它提供了一种比链式操作更灵活的与 SQL 数据库交互的方式。使用 SQL Agent 的主要优势包括:
- 它可以根据数据库的模式以及数据库的内容(比如描述一个特定的表)来回答问题。
- 它可以通过运行生成的查询,捕获追踪栈并正确地重新生成,来从错误中恢复。
- 它可以根据需要多次查询数据库以回答用户的问题。
- 它仅从相关表中检索的方式来节省令牌。
为了初始化agent ,我们将使用
SQLDatabaseToolkit
来创建一系列工具:- 创建和执行查询
- 检查查询语法
- 检索表描述
- ...以及更多功能
API 调用:SQLDatabaseToolkit
System Prompt 系统提示词
我们还需要为agent创建一个系统提示词。这将包括如何执行的指令。
API 参考:SystemMessage
智能体 agent 初始化
安装 LangGraph 包
我们将使用预构建的LangGraph agent来构建我们的代理。
API 调用:HumanMessage
请注意,agent会执行多个查询,直到它获得所需的信息:
- 列出可用的表;
- 检索三个表的架构;
- 通过连接操作查询多个表。
然后,代理能够使用最终查询的结果来生成原始问题的答案。
agent同样能够处理定性问题:
处理高基数(high-cardinality)列
为了筛选包含专有名词的列,例如地址、歌曲名称或艺术家,我们首先需要仔细检查拼写,以便正确筛选数据。
我们可以通过创建一个包含数据库中所有不同专有名词的向量存储来实现这一点。然后,每次用户在他们的问题中包含专有名词时,代理就可以查询这个向量存储,以找到那个词的正确拼写。通过这种方式,agent可以确保在构建目标查询之前理解用户所指的实体。
首先,我们需要每个实体的唯一值,为此我们定义了一个函数,该函数将结果解析为元素列表:
使用这个函数,我们可以创建一个检索工具,代理可以根据自己的判断执行。
这样,如果agent确定需要根据像“Alice Chains”这样的艺术家来编写过滤器,它首先可以使用检索工具来观察某一列的相关值。
正如我们所见,agent 使用了
search_proper_nouns
工具,以便正确查询数据库中这位特定艺术家的信息。官网链接:
- Author:小灰
- URL:https://blog.opencontent.cn//article/langchian-sql-qa
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!