当前位置:   article > 正文

利用大模型实现数据库(sqlite)查询示例_大模型使用工具来查表查库的应用

大模型使用工具来查表查库的应用
  1. import sqlite3
  2. import json
  3. import openai
  4. import requests
  5. import os
  6. from tenacity import retry, wait_random_exponential, stop_after_attempt
  7. from termcolor import colored
  8. GPT_MODEL = "gpt-3.5-turbo-1106"
  9. from openai import OpenAI
  10. client = OpenAI(
  11. api_key = os.getenv("OPENAI_API_KEY"),
  12. )
  13. # 1. 定义数据库信息
  14. conn = sqlite3.connect("data/chinook.db")
  15. print("Opened database successfully")
  16. #--------------------------------------------------------
  17. # 定义一个函数chat_completion_request,主要用于发送 聊天补全 请求到OpenAI服务器
  18. def chat_completion_request(messages, tools=None):
  19. if tools is not None:
  20. response = client.chat.completions.create(
  21. model=GPT_MODEL,
  22. messages=messages,
  23. tools=tools,
  24. tool_choice="auto", # auto is default, but we'll be explicit
  25. )
  26. if tools is None:
  27. response = client.chat.completions.create(
  28. model=GPT_MODEL,
  29. messages=messages
  30. )
  31. return response
  32. # 定义一个函数pretty_print_conversation,用于打印消息对话内容
  33. def pretty_print_conversation(messages):
  34. # 为不同角色设置不同的颜色
  35. role_to_color = {
  36. "system": "red",
  37. "user": "green",
  38. "assistant": "blue",
  39. "function": "magenta",
  40. }
  41. # 遍历消息列表
  42. for message in messages:
  43. # 如果消息的角色是"system",则用红色打印“content”
  44. if message["role"] == "system":
  45. print(colored(f"system: {message['content']}\n", role_to_color[message["role"]]))
  46. # 如果消息的角色是"user",则用绿色打印“content”
  47. elif message["role"] == "user":
  48. print(colored(f"user: {message['content']}\n", role_to_color[message["role"]]))
  49. # 如果消息的角色是"assistant",并且消息中包含"function_call",则用蓝色打印"function_call"
  50. elif message["role"] == "assistant" and message.get("function_call"):
  51. print(colored(f"assistant[function_call]: {message['function_call']}\n", role_to_color[message["role"]]))
  52. # 如果消息的角色是"assistant",但是消息中不包含"function_call",则用蓝色打印“content”
  53. elif message["role"] == "assistant" and not message.get("function_call"):
  54. print(colored(f"assistant[content]: {message['content']}\n", role_to_color[message["role"]]))
  55. # 如果消息的角色是"function",则用品红色打印“function”
  56. elif message["role"] == "function":
  57. print(colored(f"function ({message['name']}): {message['content']}\n", role_to_color[message["role"]]))
  58. def get_table_names(conn):
  59. """返回一个包含所有表名的列表"""
  60. table_names = [] # 创建一个空的表名列表
  61. # 执行SQL查询,获取数据库中所有表的名字
  62. tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")
  63. # 遍历查询结果,并将每个表名添加到列表中
  64. for table in tables.fetchall():
  65. table_names.append(table[0])
  66. return table_names # 返回表名列表
  67. def get_column_names(conn, table_name):
  68. """返回一个给定表的所有列名的列表"""
  69. column_names = [] # 创建一个空的列名列表
  70. # 执行SQL查询,获取表的所有列的信息
  71. columns = conn.execute(f"PRAGMA table_info('{table_name}');").fetchall()
  72. # 遍历查询结果,并将每个列名添加到列表中
  73. for col in columns:
  74. column_names.append(col[1])
  75. return column_names # 返回列名列表
  76. def get_database_info(conn):
  77. """返回一个字典列表,每个字典包含一个表的名字和列信息"""
  78. table_dicts = [] # 创建一个空的字典列表
  79. # 遍历数据库中的所有表
  80. for table_name in get_table_names(conn):
  81. columns_names = get_column_names(conn, table_name) # 获取当前表的所有列名
  82. # 将表名和列名信息作为一个字典添加到列表中
  83. table_dicts.append({"table_name": table_name, "column_names": columns_names})
  84. return table_dicts # 返回字典列表
  85. #--------------------------------------------------------
  86. # 获取数据库信息,并存储为字典列表
  87. database_schema_dict = get_database_info(conn)
  88. # 将数据库信息转换为字符串格式,方便后续使用
  89. database_schema_string = "\n".join(
  90. [
  91. f"Table: {table['table_name']}\nColumns: {', '.join(table['column_names'])}"
  92. for table in database_schema_dict
  93. ]
  94. )
  95. # 2.定义 functions让 GPT 模型帮我们构造一个完整的 SQL 查询
  96. tools = [
  97. {
  98. "type": "function",
  99. "function": {
  100. "name": "ask_database",
  101. "description": "Use this function to answer user questions about music. Output should be a fully formed SQL query.",
  102. "parameters": {
  103. "type": "object",
  104. "properties": {
  105. "query": {
  106. "type": "string",
  107. "description": f"""
  108. SQL query extracting info to answer the user's question.
  109. SQL should be written using this database schema:{database_schema_string}
  110. The query should be returned in plain text, not in JSON.
  111. """,
  112. }
  113. },
  114. "required": ["query"],
  115. },
  116. },
  117. }
  118. ]
  119. # 3. 执行SQL语句
  120. def ask_database(conn, query):
  121. """使用 query 来查询 SQLite 数据库的函数。"""
  122. try:
  123. results = str(conn.execute(query).fetchall()) # 执行查询,并将结果转换为字符串
  124. except Exception as e: # 如果查询失败,捕获异常并返回错误信息
  125. results = f"query failed with error: {e}"
  126. return results # 返回查询结果
  127. def execute_function_call(message):
  128. """执行函数调用"""
  129. # 判断功能调用的名称是否为 "ask_database"
  130. tool_calls = assistant_message.tool_calls
  131. function_name = tool_calls[0].function.name
  132. if function_name == "ask_database":
  133. # 如果是,则获取功能调用的参数,这里是 SQL 查询
  134. # query = json.loads(message["function_call"]["arguments"])["query"]
  135. query = json.loads(tool_calls[0].function.arguments)["query"]
  136. # 使用 ask_database 函数执行查询,并获取结果
  137. results = ask_database(conn, query)
  138. print(query)
  139. print(results)
  140. else:
  141. # 如果功能调用的名称不是 "ask_database",则返回错误信息
  142. results = f"Error: function {message['function_call']['name']} does not exist"
  143. return results # 返回结果
  144. # 创建一个空的消息列表
  145. messages = []
  146. # 向消息列表中添加一个系统角色的消息,内容是 "Answer user questions by generating SQL queries against the Chinook Music Database."
  147. messages.append({"role": "system",
  148. "content": "通过针对 Chinook 音乐数据库生成 SQL 查询来回答用户的问题"
  149. })
  150. # "content": "Answer user questions by generating SQL queries against the Chinook Music Database."
  151. # 向消息列表中添加一个用户角色的消息,内容是 "Hi, who are the top 5 artists by number of tracks?"
  152. ## 根据曲目数量排名前五的艺术家是谁?
  153. messages.append({"role": "user",
  154. "content": "嗨,歌曲数量排名前五的艺术家是谁?"})
  155. # "Hi, who are the top 5 artists by number of tracks?"
  156. # 使用 chat_completion_request 函数获取聊天响应
  157. chat_response = chat_completion_request(messages, tools)
  158. chat_response
  159. # 从聊天响应中获取助手的消息
  160. assistant_message = chat_response.choices[0].message
  161. assistant_message
  162. # 将助手的消息添加到消息列表中
  163. # messages.append(assistant_message)
  164. messages.append({
  165. "role": assistant_message.role, # 消息的角色是
  166. "content": '' # 消息的内容 assistant_message.content
  167. })
  168. # 如果助手的消息中有功能调用
  169. tool_calls = assistant_message.tool_calls
  170. if tool_calls:
  171. tool_call = tool_calls[0]
  172. # 使用 execute_function_call 函数执行功能调用,并获取结果
  173. results = execute_function_call(assistant_message)
  174. # 将功能的结果作为一个功能角色的消息添加到消息列表中
  175. messages.append({
  176. "tool_call_id": tool_call.id,
  177. "role": "function",
  178. "name": tool_call.function.name,
  179. "content": results})
  180. #再次调用模型,将message对象给大模型
  181. second_response = chat_completion_request(messages)
  182. assistant_message = second_response.choices[0].message
  183. messages.append({
  184. "role": assistant_message.role, # 消息的角色是
  185. "content": assistant_message.content # 消息的内容
  186. })
  187. # 使用 pretty_print_conversation 函数打印对话
  188. pretty_print_conversation(messages)
  189. # ---------------------------------------------------------------------------------
  190. # 向消息列表中添加一个用户的问题,内容是 "What is the name of the album with the most tracks?"
  191. ## 哪张专辑的曲目数量最多?
  192. messages.append({"role": "user",
  193. "content": "拥有最多曲目的专辑叫什么名字?"}) # What is the name of the album with the most tracks ?
  194. # 使用 chat_completion_request 函数获取聊天响应
  195. chat_response = chat_completion_request(messages, tools)
  196. # 从聊天响应中获取助手的消息
  197. assistant_message = chat_response.choices[0].message
  198. assistant_message
  199. # 将助手的消息添加到消息列表中
  200. # messages.append(assistant_message)
  201. messages.append({"role": assistant_message.role,
  202. "content": ''})
  203. # 如果助手的消息中有功能调用
  204. if assistant_message.tool_calls:
  205. tool_calls = assistant_message.tool_calls
  206. tool_call = tool_calls[0]
  207. # 使用 execute_function_call 函数执行功能调用,并获取结果
  208. results = execute_function_call(assistant_message)
  209. # 将功能的结果作为一个功能角色的消息添加到消息列表中
  210. messages.append({ "tool_call_id": tool_call.id,
  211. "role": "function",
  212. "name": tool_call.function.name,
  213. "content": results})
  214. #再次调用模型,将message对象给大模型
  215. second_response = chat_completion_request(messages)
  216. assistant_message = second_response.choices[0].message
  217. messages.append({
  218. "role": assistant_message.role, # 消息的角色是
  219. "content": assistant_message.content # 消息的内容
  220. })
  221. # 使用 pretty_print_conversation 函数打印对话
  222. pretty_print_conversation(messages)
最终messages打印的内容:
  1. [{'role': 'system', 'content': '通过针对 Chinook 音乐数据库生成 SQL 查询来回答用户的问题'},
  2. {'role': 'user', 'content': '嗨,歌曲数量排名前五的艺术家是谁?'},
  3. {'role': 'assistant', 'content': ''},
  4. {'tool_call_id': 'call_D8CgPbKLVuYJrFbi6om7zsam',
  5. 'role': 'function',
  6. 'name': 'ask_database',
  7. 'content': "[('Iron Maiden', 213), ('U2', 135), ('Led Zeppelin', 114), ('Metallica', 112), ('Lost', 92)]"},
  8. {'role': 'assistant', 'content': ''},
  9. {'tool_call_id': 'call_D8CgPbKLVuYJrFbi6om7zsam',
  10. 'role': 'function',
  11. 'name': 'ask_database',
  12. 'content': "[('Iron Maiden', 213), ('U2', 135), ('Led Zeppelin', 114), ('Metallica', 112), ('Lost', 92)]"},
  13. {'role': 'assistant',
  14. 'content': '歌曲数量排名前五的艺术家分别是 Iron Maiden(213首歌曲)、U2(135首歌曲)、Led Zeppelin(114首歌曲)、Metallica(112首歌曲)和Lost(92首歌曲)。'},
  15. {'role': 'user', 'content': '拥有最多曲目的专辑叫什么名字?'},
  16. {'role': 'assistant', 'content': ''},
  17. {'tool_call_id': 'call_M72g22u0r0dEX5BX3MmZT2lK',
  18. 'role': 'function',
  19. 'name': 'ask_database',
  20. 'content': "[('Greatest Hits', 57)]"},
  21. {'role': 'assistant', 'content': '拥有最多曲目的专辑名字是《Greatest Hits》。'}]
可以看出,第一次和大模型交互时,大模型返回的role是assistant,content是空的,tool_calls是有内容的

执行了tool_call后,将获取内容放到message中,再将message提交给大模型进行第二次交互,role是assistant,content是回答的内容,tool_calls是空的了。

可见大模型重新整理问题和答案后,重新输出了内容,看起来舒服多了。

system: 通过针对 Chinook 音乐数据库生成 SQL 查询来回答用户的问题

user: 嗨,歌曲数量排名前五的艺术家是谁?

assistant[content]: 

function (ask_database): [('Iron Maiden', 213), ('U2', 135), ('Led Zeppelin', 114), ('Metallica', 112), ('Lost', 92)]

assistant[content]: 歌曲数量排名前五的艺术家分别是 Iron Maiden(213首歌曲)、U2(135首歌曲)、Led Zeppelin(114首歌曲)、Metallica(112首歌曲)和Lost(92首歌曲)。

user: 拥有最多曲目的专辑叫什么名字?

assistant[content]: 

function (ask_database): [('Greatest Hits', 57)]

assistant[content]: 拥有最多曲目的专辑名字是《Greatest Hits》。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/126688?site
推荐阅读
相关标签
  

闽ICP备14008679号