• 作者:老汪软件技巧
  • 发表时间:2024-08-29 15:02
  • 浏览量:

随着大模型越来越火,出现了各种结合大模型的应用,最近学习了langchain,在项目中也应用了一些,今天来分享一下通过大模型与数据库交互来回答用户问题的一个实现流程,可以方便感知大模型的能力:

整体步骤

首先来实现自然语言生成sql的流程:

搭建数据库

数据库使用的是mysql的示例数据库sakiladb: /sakiladb/my…使用docker启动:

docker pull sakiladb/mysql:latest
docker run -p 3306:3306 -d sakiladb/mysql:latest

实现sql_chain

本文使用的Tongyi,需要在项目中.env 文件 定义好 DASHSCOPE_API_KEY

先上代码:

from langchain.prompts import ChatPromptTemplate
from langchain_community.llms import Tongyi
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from dotenv import load_dotenv
from langchain_community.utilities import SQLDatabase
from langchain_core.output_parsers import StrOutputParser
import re
load_dotenv()
mysql_uri = 'mysql+mysqlconnector://sakila:p_ssW0rd@localhost:3306/sakila'
db = SQLDatabase.from_uri(mysql_uri)
template = '''
  基于以下的table schema, 编写一个用于回答用户问题的sql查询
  table schema: {table_schema}
  问题: {question}
  sql:
'''
prompt = ChatPromptTemplate.from_template(template=template)
def get_schema ():
  schema = db.get_table_info()
  # pattern = r".*?(\/\*(.|\n|\t)*?\*\/).*"
  # schema = re.sub(pattern, '', schema)
  return schema
def extract_query(str: str) -> str:
  pattern = r"```sql\n(.*?)\n```"
  match = re.search(pattern, str, re.DOTALL)
  if match:
    return match.group(1)
  else:
    return None
sql_chain = RunnablePassthrough.assign(table_schema=lambda x: get_schema()) | prompt | Tongyi().bind(stop=["\sql:"]) | StrOutputParser() | RunnableLambda(func=extract_query)
#result = sql_chain.invoke({"question": '给出数据库中语言为英语语的租售量前三的电影列表'})
result = sql_chain.invoke({"question": '给出数据库中语言为英语语的电影列表发布日期最近的三部'})

整体分为以下步骤:

定义连接数据库的uri,并建立连接;定义提示词,接收数据库schema 和 用户问题建立sql_chain输入问题,调用chain获取答案

最后输出如下:

SELECT f.title, f.description, f.last_update 
FROM film f 
JOIN language l ON f.language_id = l.language_id 
WHERE l.name = 'English' 
ORDER BY f.last_update DESC 
LIMIT 3;

对于一些复杂的查询,大模型给出的答案并不准确,在 给出数据库中语言为英语语的租售量前三的电影列表这个问题就给出了错误的答案,这也是注释掉它的原因

获取的数据库schema如下,只是一个表的:

CREATE TABLE language (
    language_id TINYINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name CHAR(20) NOT NULL,
    last_update TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (language_id)
) DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_0900_ai_ci ENGINE=InnoDB;
/*
3 rows from language table:
language_id  name       last_update
1            English    2006-02-15 05:02:19
2            Italian    2006-02-15 05:02:19
3            Japanese   2006-02-15 05:02:19
*/

extract_query 用于从模型返回的答案中提取sql,去除掉无关文本

根据sql获取结果

def run_query(query):
  result = db.run(query)
  return result

实现自然对话chain

chat_template = '''
  基于以下的table schema, sql query, sql response, 用自然语言回答用户问题:
  table schema: {table_schema}
  问题: {question}
  sql query: {sql_query}
  sql response: {sql_response}
'''
chat_prompt = ChatPromptTemplate.from_template(template=chat_template)
sql_chat_chain = (
  RunnablePassthrough.assign(sql_query= sql_chain).assign(
    sql_response = lambda params: run_query(params.get("sql_query")),
    table_schema=lambda x: get_schema()
  ) 
  | chat_prompt 
  |Tongyi() 
)
result = sql_chat_chain.invoke({"question": '给出数据库中语言为英语语的电影列表发布日期最近的三部'})

模型答案如下:

数据库中语言为英语的电影列表里,发布日期最近的三部电影如下:
1. **电影名**: FREDDY STORM **描述**: A Intrepid Saga of a Man And a Lumberjack who must Vanquish a Husband in The Outback **发布年份**: 2006 **最后更新时间**: February 15, 2006, 05:03:42 
2. **电影名**: FREAKY POCUS **描述**: A Fast-Paced Documentary of a Pastry Chef And a Crocodile who must Chase a Squirrel in The Gulf of Mexico **发布年份**: 2006 **最后更新时间**: February 15, 2006, 05:03:42 
3. **电影名**: FRANKENSTEIN STRANGER **描述**: A Insightful Character Study of a Feminist And a Pioneer who must Pursue a Pastry Chef in Nigeria **发布年份**: 2006 **最后更新时间**: February 15, 2006, 05:03:42

RunnablePassthrough.assign 的 作用,将上一个调用结果的基础上添加额外字段

参考链接

# Chat with MySQL Database with Python | LangChain Tutorial

langchain api_reference