大家好,我是【同学小张】。持续学习,持续干货输出,跟我一起学AI大模型。
上篇文章我们学习了Function Calling的基本用法,本文我们更近一步,学习下怎样利用Function Calling将大模型与数据库打通。
知识背景:我算是对数据库的SQL语句很不熟悉,只会简单的单表操作,还不熟练,每次都得查半天。现在有了大模型应用,有了Function Calling,再也不用查半天资料才写一个SQL了,还能熟练地用多表查询了!
本文实战案例来自知乎的AGI课程。
0. 封装数据库查询接口
还是先本地定义一个查询数据库的接口,该接口接收SQL数据库操作语句,然后调用执行。
# 创建数据库连接 import sqlite3 conn = sqlite3.connect(':memory:') cursor = conn.cursor() def ask_database(query): cursor.execute(query) records = cursor.fetchall() return records
1. 数据库的Functions怎么定义
示例代码:
# 描述数据库表结构 database_schema_string = """ CREATE TABLE orders ( id INT PRIMARY KEY NOT NULL, -- 主键,不允许为空 customer_id INT NOT NULL, -- 客户ID,不允许为空 product_id STR NOT NULL, -- 产品ID,不允许为空 price DECIMAL(10,2) NOT NULL, -- 价格,不允许为空 status INT NOT NULL, -- 订单状态,整数类型,不允许为空。0代表待支付,1代表已支付,2代表已退款 create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认为当前时间 pay_time TIMESTAMP -- 支付时间,可以为空 ); """ def get_sql_completion(messages, model="gpt-3.5-turbo-1106"): response = openai.chat.completions.create( model=model, messages=messages, temperature=0, # 模型输出的随机性,0 表示随机性最小 tools=[{ # 摘自 OpenAI 官方示例 https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb "type": "function", "function": { "name": "ask_database", "description": "Use this function to answer user questions about business. \ Output should be a fully formed SQL query.", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": f""" SQL query extracting info to answer the user's question. SQL should be written using this database schema: {database_schema_string} The query should be returned in plain text, not in JSON. The query should only contain grammars supported by SQLite. """, } }, "required": ["query"], } } }], ) return response.choices[0].message
重点看两个description:
(1)function 的 description:Use this function to answer user questions about business. Output should be a fully formed SQL query.
"
- 要求输出必须是一个SQL语句
(2)参数的description:
f""" SQL query extracting info to answer the user's question. SQL should be written using this database schema: {database_schema_string} The query should be returned in plain text, not in JSON. The query should only contain grammars supported by SQLite. """,
- 将数据库的数据结构
database_schema_string
放到prompt中,根据此表的数据结构写SQL语句 - 不能返回json,而是返回字符串
- 返回的query语句文字必须符合SQL语法
2. 可以使用了
prompt = "10月的销售额" messages = [ {"role": "system", "content": "基于 order 表回答用户问题"}, {"role": "user", "content": prompt} ] response = get_sql_completion(messages) if response.content is None: response.content = "" messages.append(response) print("====Function Calling====") print_json(response) if response.tool_calls is not None: tool_call = response.tool_calls[0] if tool_call.function.name == "ask_database": arguments = tool_call.function.arguments args = json.loads(arguments) print("====SQL====") print(args["query"]) result = ask_database(args["query"]) print("====DB Records====") print(result) messages.append({ "tool_call_id": tool_call.id, "role": "tool", "name": "ask_database", "content": str(result) }) response = get_sql_completion(messages) print("====最终回复====") print(response.content)
看下运行过程,可以看到返回了正确的SQL查询语句:
(但是因为我本地没有数据库,所以没有查询到数据,返回“暂时不可用”。为了测试,你可以在本地事先生成一份demo数据,这就涉及到Python操作数据库了,有需要的话我再写个文章介绍下怎么用Python操作数据库)
3. 进阶 - 多表查询
(1)将数据表数据结构改一下,改成多张表即可
# 描述数据库表结构 database_schema_string = """ CREATE TABLE customers ( id INT PRIMARY KEY NOT NULL, -- 主键,不允许为空 customer_name VARCHAR(255) NOT NULL, -- 客户名,不允许为空 email VARCHAR(255) UNIQUE, -- 邮箱,唯一 register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- 注册时间,默认为当前时间 ); CREATE TABLE products ( id INT PRIMARY KEY NOT NULL, -- 主键,不允许为空 product_name VARCHAR(255) NOT NULL, -- 产品名称,不允许为空 price DECIMAL(10,2) NOT NULL -- 价格,不允许为空 ); CREATE TABLE orders ( id INT PRIMARY KEY NOT NULL, -- 主键,不允许为空 customer_id INT NOT NULL, -- 客户ID,不允许为空 product_id INT NOT NULL, -- 产品ID,不允许为空 price DECIMAL(10,2) NOT NULL, -- 价格,不允许为空 status INT NOT NULL, -- 订单状态,整数类型,不允许为空。0代表待支付,1代表已支付,2代表已退款 create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认为当前时间 pay_time TIMESTAMP -- 支付时间,可以为空 ); """
(2)提问
prompt = "统计每月每件商品的销售额" # prompt = "这星期消费最高的用户是谁?他买了哪些商品? 每件商品买了几件?花费多少?" messages = [ {"role": "system", "content": "基于 order 表回答用户问题"}, {"role": "user", "content": prompt} ] response = get_sql_completion(messages) print(response.tool_calls[0].function.arguments)
(3)运行结果,生成了正确的多表查询语句
4. 思考 - 利用大模型写数据库查询语句的Prompt
通过上面对function和参数的description描述,我们可以大体看出怎样利用大模型去写正确的SQL查询语句。
Prompt如下:
假如你是一个SQL数据库专家。请按照用户的输入生成一个SQL语句。
SQL should be written using this database schema:
“”"
CREATE TABLE customers (
id INT PRIMARY KEY NOT NULL, – 主键,不允许为空
customer_name VARCHAR(255) NOT NULL, – 客户名,不允许为空
email VARCHAR(255) UNIQUE, – 邮箱,唯一
register_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP – 注册时间,默认为当前时间
);
CREATE TABLE products (
id INT PRIMARY KEY NOT NULL, – 主键,不允许为空
product_name VARCHAR(255) NOT NULL, – 产品名称,不允许为空
price DECIMAL(10,2) NOT NULL – 价格,不允许为空
);
CREATE TABLE orders (
id INT PRIMARY KEY NOT NULL, – 主键,不允许为空
customer_id INT NOT NULL, – 客户ID,不允许为空
product_id INT NOT NULL, – 产品ID,不允许为空
price DECIMAL(10,2) NOT NULL, – 价格,不允许为空
status INT NOT NULL, – 订单状态,整数类型,不允许为空。0代表待支付,1代表已支付,2代表已退款
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, – 创建时
间,默认为当前时间
pay_time TIMESTAMP – 支付时间,可以为空 ); “”"
The query should be returned in plain text, not in JSON. The query
should only contain grammars supported by SQLite. Output should be a
fully formed SQL query.
用户输入:统计每月每件商品的销售额,包含商品名称
结果:
完美。
注意: Function Calling 不仅可以调用读函数,也能调用写函数。但官方强烈建议,在写之前,一定要有人做确认,因为大模型的结果有不确定性。
- 大家好,我是同学小张
- 欢迎 点赞 + 关注 👏,促使我持续学习,持续干货输出。
- +v: jasper_8017 一起交流💬,一起进步💪。
- 微信公众号也可搜【同学小张】 🙏
- 踩坑不易,感谢关注和围观
本站文章一览: