从一个数据库中提取信息或进行某种操作时,需要是用特定的语言明确地告诉计算机你的需求。
全新的编程范式:可以用更加接近日常话语的自然语言来与计算机交流。
这种范式结合了自然语言处理与传统数据库查询的功能,提供了更直观和高效的交互方式。
- 提出问题:自然语言提问,例如“去年的总销售额是多少?”。
- LLM理解并转译:LLM首先解析问题,理解背后的意图和所需的信息。接着,模型会根据解析的内容,生成相应的SQL查询语句,例如 “SELECT SUM(sales) FROM sales_data WHERE year = 'last_year';”。
- 执行SQL查询:生成的SQL查询语句会被发送到相应的数据库进行执行。数据库处理这个查询,并返回所需的数据结果。
- LLM接收并解释结果:当数据库返回查询结果后,LLM会接收到这些数据。然后,LLM会开始解析这些数据,并将其转化为更容易被人类理解的答案格式。
- 提供答案:最后,LLM将结果转化为自然语言答案,并返回给用户。例如“去年的总销售额为1,000,000元”。
实战案例背景信息
通过LangChain实现这个新的数据库应用开发范式。
业务数据都存储在数据库中,目标则是通过自然语言来为销售的每一种鲜花数据创建各种查询。无论是员工还是顾客,当他们想了解某种鲜花的价格时,都可以快速地生成适当的查询语句。
创建数据库表
数据库表,存储易速鲜花的业务数据。
使用SQLite作为我们的示例数据库,特点:
- 提供了轻量级的磁盘文件数据库
- 不需要单独的服务器进程或系统
- 应用程序可以直接与数据库文件交互
- 不需要配置、安装或管理
- 支持ACID、保证持久性
sqlite3库,则是Python内置的轻量级SQLite数据库。安装Python时,sqlite3模块已经包含在内,无需再进行额外的安装。
# 导入sqlite3库
import sqlite3
# 连接到数据库
conn = sqlite3.connect('FlowerShop.db')
cursor = conn.cursor()
# 执行SQL命令来创建Flowers表
cursor.execute('''
CREATE TABLE Flowers (
ID INTEGER PRIMARY KEY,
Name TEXT NOT NULL,
Type TEXT NOT NULL,
Source TEXT NOT NULL,
PurchasePrice REAL,
SalePrice REAL,
StockQuantity INTEGER,
SoldQuantity INTEGER,
ExpiryDate DATE,
Description TEXT,
EntryDate DATE DEFAULT CURRENT_DATE
);
''')
# 插入5种鲜花的数据
flowers = [
('Rose', 'Flower', 'France', 1.2, 2.5, 100, 10, '2023-12-31', 'A beautiful red rose'),
('Tulip', 'Flower', 'Netherlands', 0.8, 2.0, 150, 25, '2023-12-31', 'A colorful tulip'),
('Lily', 'Flower', 'China', 1.5, 3.0, 80, 5, '2023-12-31', 'An elegant white lily'),
('Daisy', 'Flower', 'USA', 0.7, 1.8, 120, 15, '2023-12-31', 'A cheerful daisy flower'),
('Orchid', 'Flower', 'Brazil', 2.0, 4.0, 50, 2, '2023-12-31', 'A delicate purple orchid')
]
for flower in flowers:
cursor.execute('''
INSERT INTO Flowers (Name, Type, Source, PurchasePrice, SalePrice, StockQuantity, SoldQuantity, ExpiryDate, Description)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);
''', flower)
# 提交更改
conn.commit()
# 关闭数据库连接
conn.close()
用 Chain 查询数据库
因为LangChain的数据库查询功能较新,目前还处于实验阶段,因此,需要先安装langchain-experimental包,这个包含有实验性的LangChain新功能。
pip install langchain-experimental
SQLDatabaseChain来查询数据库。
# 导入langchain的实用工具和相关的模块
from langchain.utilities import SQLDatabase
from langchain.llms import OpenAI
from langchain_experimental.sql import SQLDatabaseChain
# 连接到FlowerShop数据库(之前我们使用的是Chinook.db)
db = SQLDatabase.from_uri("sqlite:///FlowerShop.db")
import os
from langchain_community.llms import Tongyi
# 创建Tongyi的低级语言模型(LLM)实例,这里我们设置温度为0,意味着模型输出会更加确定性
# DASHSCOPE_API_KEY
os.environ["DASHSCOPE_API_KEY"] = '阿里的DASHSCOPE_API_KEY'
# LLM不稳定,对于这个任务,可能要多跑几次才能得到正确结果
# 创建模型实例
llm = Tongyi(model_name="qwen-chat", api_key=os.environ["DASHSCOPE_API_KEY"], temperature=0, verbose=True)
# llm = OpenAI(temperature=0, verbose=True)
# 创建SQL数据库链实例,它允许我们使用LLM来查询SQL数据库
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)
# 运行与鲜花运营相关的问题
response = db_chain.run("有多少种不同的鲜花?")
print(response)
response = db_chain.run("哪种鲜花的存货数量最少?")
print(response)
response = db_chain.run("平均销售价格是多少?")
print(response)
response = db_chain.run("从法国进口的鲜花有多少种?")
print(response)
response = db_chain.run("哪种鲜花的销售量最高?")
print(response)
输出:
> Entering new SQLDatabaseChain chain...
有多少种不同的鲜花?
SQLQuery:D:\pythonProject\langchain-demo\langchain-day06-用chain查询数据库.py:23: LangChainDeprecationWarning: The method `Chain.run` was deprecated in langchain 0.1.0 and will be removed in 1.0. Use :meth:`~invoke` instead.
response = db_chain.run("有多少种不同的鲜花?")
SQLQuery: SELECT COUNT(DISTINCT "Type") FROM "Flowers"
SQLResult: [(1,)]
Answer:只有1种不同类型的鲜花。但是,根据表格中的数据,这看起来可能是一个误解,因为所有记录的 "Type" 列都显示为 "Flower"。如果您想要知道不同种类(即 "Name" 列)的数量,请告诉我。
如果您的问题是询问有多少种不同的鲜花名称,请参考以下查询:
Question: 有多少种不同的鲜花名称?
SQLQuery: SELECT COUNT(DISTINCT "Name") FROM "Flowers"
> Finished chain.
只有1种不同类型的鲜花。但是,根据表格中的数据,这看起来可能是一个误解,因为所有记录的 "Type" 列都显示为 "Flower"。如果您想要知道不同种类(即 "Name" 列)的数量,请告诉我。
如果您的问题是询问有多少种不同的鲜花名称,请参考以下查询:
Question: 有多少种不同的鲜花名称?
SQLQuery: SELECT COUNT(DISTINCT "Name") FROM "Flowers"
> Entering new SQLDatabaseChain chain...
哪种鲜花的存货数量最少?
SQLQuery:SQLQuery: SELECT "Name", "StockQuantity" FROM "Flowers" ORDER BY "StockQuantity" ASC LIMIT 1
SQLResult: [('Orchid', 50)]
Answer:库存数量最少的鲜花是兰花(Orchid),其库存数量为50。不过根据您提供的3个样本数据,这个答案并不在其中,可能是因为数据库中还有其他记录。基于给出的样本数据,库存数量最少的鲜花是百合(Lily),其库存数量为80。但按照查询结果,正确答案应是兰花(Orchid)。
为了更准确地回答您的问题,我将基于查询结果给出最终答案。
Answer: 兰花(Orchid)的库存数量最少,为50。
> Finished chain.
库存数量最少的鲜花是兰花(Orchid),其库存数量为50。不过根据您提供的3个样本数据,这个答案并不在其中,可能是因为数据库中还有其他记录。基于给出的样本数据,库存数量最少的鲜花是百合(Lily),其库存数量为80。但按照查询结果,正确答案应是兰花(Orchid)。
为了更准确地回答您的问题,我将基于查询结果给出最终答案。
Answer: 兰花(Orchid)的库存数量最少,为50。
> Entering new SQLDatabaseChain chain...
平均销售价格是多少?
SQLQuery:SQLQuery: SELECT AVG("SalePrice") AS "AverageSalePrice" FROM "Flowers"
SQLResult: [(2.66,)]
Answer:平均销售价格是2.66。
> Finished chain.
平均销售价格是2.66。
> Entering new SQLDatabaseChain chain...
从法国进口的鲜花有多少种?
SQLQuery:SQLQuery: SELECT COUNT("ID") FROM "Flowers" WHERE "Source" = 'France'
SQLResult: [(1,)]
Answer:从法国进口的鲜花有1种。
> Finished chain.
从法国进口的鲜花有1种。
> Entering new SQLDatabaseChain chain...
哪种鲜花的销售量最高?
SQLQuery:SQLQuery: SELECT "Name", "SoldQuantity" FROM "Flowers" ORDER BY "SoldQuantity" DESC LIMIT 5
SQLResult: [('Tulip', 25), ('Daisy', 15), ('Rose', 10), ('Lily', 5), ('Orchid', 2)]
Answer:销售量最高的鲜花是郁金香(Tulip),已售出25朵。
> Finished chain.
销售量最高的鲜花是郁金香(Tulip),已售出25朵。
SQLDatabaseChain调用大语言模型,完美地完成了从自然语言(输入)到自然语言(输出)的新型SQL查询。
用 Agent 查询数据库
除了通过Chain完成数据库查询之外,LangChain 还可以通过SQL Agent来完成查询任务。相比SQLDatabaseChain,使用 SQL 代理有一些优点。
- 它可以根据数据库的架构以及数据库的内容回答问题(例如它会检索特定表的描述)。
- 它具有纠错能力,当执行生成的查询遇到错误时,它能够捕获该错误,然后正确地重新生成并执行新的查询。
LangChain使用create_sql_agent函数来初始化代理,通过这个函数创建的SQL代理包含SQLDatabaseToolkit,这个工具箱中包含以下工具:
- 创建并执行查询
- 检查查询语法
- 检索数据表的描述
在这些工具的辅助之下,代理可以趋动LLM完成SQL查询任务。代码如下:
from langchain.utilities import SQLDatabase
from langchain.llms import OpenAI
from langchain.agents import create_sql_agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.agents.agent_types import AgentType
# 连接到FlowerShop数据库
db = SQLDatabase.from_uri("sqlite:///FlowerShop.db")
import os
from langchain_community.llms import Tongyi
# 创建Tongyi的低级语言模型(LLM)实例,这里我们设置温度为0,意味着模型输出会更加确定性
# DASHSCOPE_API_KEY
os.environ["DASHSCOPE_API_KEY"] = '阿里的DASHSCOPE_API_KEY'
# LLM不稳定,对于这个任务,可能要多跑几次才能得到正确结果
# 创建模型实例
llm = Tongyi(model_name="qwen-chat", api_key=os.environ["DASHSCOPE_API_KEY"], temperature=0, verbose=True)
# 创建SQL Agent
agent_executor = create_sql_agent(
llm=llm,
toolkit=SQLDatabaseToolkit(db=db, llm=llm),
verbose=True,
agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
)
# 使用Agent执行SQL查询
questions = [
"哪种鲜花的存货数量最少?",
"平均销售价格是多少?",
]
for question in questions:
response = agent_executor.run(question)
print(response)
输出:
> Entering new SQL Agent Executor chain...
D:\pythonProject\langchain-demo\langchain-day06-sqlAgent.py:34: LangChainDeprecationWarning: The method `Chain.run` was deprecated in langchain 0.1.0 and will be removed in 1.0. Use :meth:`~invoke` instead.
response = agent_executor.run(question)
Action: sql_db_list_tables
Action Input: FlowersThe table `Flowers` seems relevant to the question. I need to check its schema to find the appropriate columns.
Action: sql_db_schema
Action Input: Flowers
CREATE TABLE "Flowers" (
"ID" INTEGER,
"Name" TEXT NOT NULL,
"Type" TEXT NOT NULL,
"Source" TEXT NOT NULL,
"PurchasePrice" REAL,
"SalePrice" REAL,
"StockQuantity" INTEGER,
"SoldQuantity" INTEGER,
"ExpiryDate" DATE,
"Description" TEXT,
"EntryDate" DATE DEFAULT CURRENT_DATE,
PRIMARY KEY ("ID")
)
/*
3 rows from Flowers table:
ID Name Type Source PurchasePrice SalePrice StockQuantity SoldQuantity ExpiryDate Description EntryDate
1 Rose Flower France 1.2 2.5 100 10 2023-12-31 A beautiful red rose 2024-11-08
2 Tulip Flower Netherlands 0.8 2.0 150 25 2023-12-31 A colorful tulip 2024-11-08
3 Lily Flower China 1.5 3.0 80 5 2023-12-31 An elegant white lily 2024-11-08
*/I need to find the flower with the minimum stock quantity.
Action: sql_db_query_checker
Action Input: SELECT Name, StockQuantity FROM Flowers ORDER BY StockQuantity ASC LIMIT 1```sql
SELECT Name, StockQuantity FROM Flowers ORDER BY StockQuantity ASC LIMIT 1
```The query looks correct. I will now execute it to get the result.
Action: sql_db_query
Action Input: SELECT Name, StockQuantity FROM Flowers ORDER BY StockQuantity ASC LIMIT 1[('Orchid', 50)]I now know the final answer.
Final Answer: 存货数量最少的鲜花是 Orchid,其库存数量为 50。
> Finished chain.
存货数量最少的鲜花是 Orchid,其库存数量为 50。
> Entering new SQL Agent Executor chain...
Action: sql_db_list_tables
Action Input: FlowersI need to check the schema of the Flowers table to find the relevant columns.
Action: sql_db_schema
Action Input: Flowers
CREATE TABLE "Flowers" (
"ID" INTEGER,
"Name" TEXT NOT NULL,
"Type" TEXT NOT NULL,
"Source" TEXT NOT NULL,
"PurchasePrice" REAL,
"SalePrice" REAL,
"StockQuantity" INTEGER,
"SoldQuantity" INTEGER,
"ExpiryDate" DATE,
"Description" TEXT,
"EntryDate" DATE DEFAULT CURRENT_DATE,
PRIMARY KEY ("ID")
)
/*
3 rows from Flowers table:
ID Name Type Source PurchasePrice SalePrice StockQuantity SoldQuantity ExpiryDate Description EntryDate
1 Rose Flower France 1.2 2.5 100 10 2023-12-31 A beautiful red rose 2024-11-08
2 Tulip Flower Netherlands 0.8 2.0 150 25 2023-12-31 A colorful tulip 2024-11-08
3 Lily Flower China 1.5 3.0 80 5 2023-12-31 An elegant white lily 2024-11-08
*/I can calculate the average sale price using the SalePrice column in the Flowers table.
Action: sql_db_query_checker
Action Input: SELECT AVG(SalePrice) AS AverageSalePrice FROM Flowers LIMIT 10```sql
SELECT AVG(SalePrice) AS AverageSalePrice FROM Flowers LIMIT 10
```
The query is correct and does not contain any of the common mistakes listed. However, the `LIMIT 10` clause is unnecessary for this query since it is calculating an aggregate function (AVG) which will return a single row. Here is the corrected query without the `LIMIT` clause:
```sql
SELECT AVG(SalePrice) AS AverageSalePrice FROM Flowers
```I should execute the corrected query to get the average sale price.
Action: sql_db_query
Action Input: SELECT AVG(SalePrice) AS AverageSalePrice FROM Flowers[(2.66,)]I now know the final answer.
Final Answer: 平均销售价格是 2.66。
> Finished chain.
平均销售价格是 2.66。
可以看到,和Chain直接生成SQL语句不同,代理会使用 ReAct 风格的提示。首先,它思考之后,将先确定第一个action是使用工具 sql_db_list_tables,然后观察该工具所返回的表格,思考后再确定下一个 action是sql_db_schema,也就是创建SQL语句,逐层前进,直到得到答案。(一开始就思考好了步骤)