掘金 人工智能 前天 19:13
什么?大模型删库跑路了?
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了Text2Sql技术,即通过自然语言指令操作数据库。从早期的人工模板,到基于机器学习的序列模型,再到如今结合大语言模型的Text2Sql,技术不断演进,使得用户能更便捷地与数据库交互。文章以LangChain为例,展示了如何使用SQLDatabaseTookit实现Text2Sql,并通过保险场景实战,验证了其在实际应用中的能力。

💡 Text2Sql的核心是将自然语言问题转化为结构化的SQL查询语句,方便用户与数据库交互。

🤖 Text2Sql技术经历了从人工模板、机器学习到大语言模型的发展历程,性能不断提升。

🛠️ 利用LangChain的SQLDatabaseTookit,可以轻松实现Text2Sql功能,简化数据库操作。

✅ Text2Sql在保险场景中展现出强大的实战能力,能通过自然语言完成复杂的查询任务。

删库跑路,一个所有程序员可能都幻想过的场景,不过想想后果还是很严重的,不过不要怕,现在我们可以利用大模型来删库,啊不,操作数据库,这就是传说中的Text2Sql。

什么是Text2Sql

Text2Sql就是将自然语言的问题转化为结构化的sql查询语句,可以让用户更直观的和数据库进行交互。其实Text2Sql不是什么特别新的东西,在大模型出来之前,也是有类似的尝试的

早期阶段主要依赖于人工编写的模板规则,用模板匹配自然语言和sql语句之间的关系,后面机器学习兴起之后,开始采用序列到序列模型这样的机器学习方法,来学习自然语言到SQL之间的映射关系。最后就是现在,能够借助大语言模型的强大语言理解和代码生成能力,再加上提示词工程、微调这样的方法,进一步提升了Text2Sql的性能。

LLM下的Text2Sql

在大语言模型的加持下,Text2Sql主要有以下几个步骤

    自然语言理解:分析用户输入的自然语言问题,理解意图和语义模式链接:将问题中的实体与数据库中的表和列进行链接SQL生成:根据语义和模式链接结果,生成对应的SQL查询语句SQL执行:在数据库上执行SQL语句,将结果返回给用户

前面几篇文章我们介绍了LangChain的使用,下面我们就用LangChain来简单实现一个Text2sql

Text2Sql的实现

SQLDatabaseTookit使用

SQLDatabaseTookit是LangChain提供的一个agent,可以轻松帮助我们实现Text2Sql

导入相关依赖

from langchain_community.agent_toolkits.sql.base import create_sql_agentfrom langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkitfrom langchain_community.utilities import SQLDatabasefrom dotenv import load_dotenvload_dotenv('../.env')

配置数据库

db_user = "user"db_password = "pwd"db_host = "localhost:3306"db_name = "db_name"db = SQLDatabase.from_uri(f"mysql+pymysql://{db_user}:{db_password}@{db_host}/{db_name}")

实例化agent

api_key = os.environ.get('DASHSCOPE_API_KEY')llm = ChatOpenAI(    temperature=0.01,    model="qwen-max",    openai_api_base = "https://dashscope.aliyuncs.com/compatible-mode/v1",    openai_api_key  = api_key)# 需要设置llmtoolkit = SQLDatabaseToolkit(db=db, llm=llm)# SQL智能体:给它目标,它自己会进行规划,最终把结果给你agent_executor = create_sql_agent(    llm=llm,    toolkit=toolkit,    verbose=True)

提问

agent_executor.run("找出英雄攻击力最高的前5个英雄")

输出:

> Entering new SQL Agent Executor chain...Action: sql_db_list_tablesAction Input: address, asset_grades, bank, car_sales, customers, dept, employee, form, height_grades, hero_score, heros, orders, person, player, player_score, student, team, team_score, test_work, trips, user, users, using, weather从表名来看,`heros` 表可能包含了英雄的信息。我需要查看 `heros` 表的结构来确定是否有攻击力(attack)相关的字段。Action: sql_db_schemaAction Input: herosCREATE TABLE heros (id INTEGER NOT NULL AUTO_INCREMENT, name VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, hp_max FLOAT, hp_growth FLOAT, hp_start FLOAT, mp_max FLOAT, mp_growth FLOAT, mp_start FLOAT, attack_max FLOAT, attack_growth FLOAT, attack_start FLOAT, defense_max FLOAT, defense_growth FLOAT, defense_start FLOAT, hp_5s_max FLOAT, hp_5s_growth FLOAT, hp_5s_start FLOAT, mp_5s_max FLOAT, mp_5s_growth FLOAT, mp_5s_start FLOAT, attack_speed_max FLOAT, attack_range VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci, role_main VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci, role_assist VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci, birthdate DATE, PRIMARY KEY (id))DEFAULT CHARSET=utf8mb3 ENGINE=InnoDB/*3 rows from heros table:idnamehp_maxhp_growthhp_startmp_maxmp_growthmp_startattack_maxattack_growthattack_startdefense_maxdefense_growthdefense_starthp_5s_maxhp_5s_growthhp_5s_startmp_5s_maxmp_5s_growthmp_5s_startattack_speed_maxattack_rangerole_mainrole_assistbirthdate10000夏侯惇7350.0288.83307.01746.094.0430.0321.011.57159.0397.021.14101.098.03.35751.037.01.57115.00.0近战坦克战士2016-07-1910001钟无艳7000.0275.03150.01760.095.0430.0318.011.0164.0409.022.07100.092.03.14348.037.01.57115.00.0近战战士坦克2021-05-0110002钟8341.0329.43450.0100.00.0100.0301.010.57153.0504.027.07125.0115.04.14357.05.00.05.00.0近战坦克辅助2012-05-04*/`heros` 表中确实有 `attack_max` 字段,这应该代表英雄的最大攻击力。为了找出攻击力最高的前5个英雄,我需要根据 `attack_max` 字段降序排列,并限制结果为前5条记录。同时,我还应该选择 `name` 和 `attack_max` 两个字段来展示英雄的名字和他们的最大攻击力。Action: sql_db_query_checkerAction Input: SELECT name, attack_max FROM heros ORDER BY attack_max DESC LIMIT 5```sqlSELECT name, attack_max FROM heros ORDER BY attack_max DESC LIMIT 5```查询语句看起来是正确的,接下来我将执行这个查询。Action: sql_db_queryAction Input: SELECT name, attack_max FROM heros ORDER BY attack_max DESC LIMIT 5[('阿轲', 427.0), ('孙尚香', 411.0), ('百里守约', 410.0), ('虞姬', 407.0), ('黄忠', 403.0)]I now know the final answer.Final Answer: 攻击力最高的前5个英雄及其攻击力如下:1. 阿轲 - 427.02. 孙尚香 - 411.03. 百里守约 - 410.04. 虞姬 - 407.05. 黄忠 - 403.0> Finished chain.

根据以上的输出,我们可以看到agent一共做了这几步操作:

    查询有哪些表
Action: sql_db_list_tablesAction Input: address, asset_grades, bank, car_sales, customers, dept, employee, form, height_grades, hero_score, heros, orders, person, player, player_score, student, team, team_score, test_work, trips, user, users, using, weather

2. 分析哪张表式需要查询的表

从表名来看,`heros` 表可能包含了英雄的信息。我需要查看 `heros` 表的结构来确定是否有攻击力(attack)相关的字段。Action: sql_db_schemaAction Input: herosCREATE TABLE heros (id INTEGER NOT NULL AUTO_INCREMENT, name VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, hp_max FLOAT, hp_growth FLOAT, hp_start FLOAT, mp_max FLOAT, mp_growth FLOAT, mp_start FLOAT, attack_max FLOAT, attack_growth FLOAT, attack_start FLOAT, defense_max FLOAT, defense_growth FLOAT, defense_start FLOAT, hp_5s_max FLOAT, hp_5s_growth FLOAT, hp_5s_start FLOAT, mp_5s_max FLOAT, mp_5s_growth FLOAT, mp_5s_start FLOAT, attack_speed_max FLOAT, attack_range VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci, role_main VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci, role_assist VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci, birthdate DATE, PRIMARY KEY (id))DEFAULT CHARSET=utf8mb3 ENGINE=InnoDB

3. 查看表里的数据,并分析需要用的字段

/*3 rows from heros table:idnamehp_maxhp_growthhp_startmp_maxmp_growthmp_startattack_maxattack_growthattack_startdefense_maxdefense_growthdefense_starthp_5s_maxhp_5s_growthhp_5s_startmp_5s_maxmp_5s_growthmp_5s_startattack_speed_maxattack_rangerole_mainrole_assistbirthdate10000夏侯惇7350.0288.83307.01746.094.0430.0321.011.57159.0397.021.14101.098.03.35751.037.01.57115.00.0近战坦克战士2016-07-1910001钟无艳7000.0275.03150.01760.095.0430.0318.011.0164.0409.022.07100.092.03.14348.037.01.57115.00.0近战战士坦克2021-05-01100028341.0329.43450.0100.00.0100.0301.010.57153.0504.027.07125.0115.04.14357.05.00.05.00.0近战坦克辅助2012-05-04*/`heros` 表中确实有 `attack_max` 字段,这应该代表英雄的最大攻击力。为了找出攻击力最高的前5个英雄,我需要根据 `attack_max` 字段降序排列,并限制结果为前5条记录。同时,我还应该选择 `name`  `attack_max` 两个字段来展示英雄的名字和他们的最大攻击力。

4. 根据提问编写sql并执行查询,最终返回结果

Action: sql_db_query_checkerAction Input: SELECT name, attack_max FROM heros ORDER BY attack_max DESC LIMIT 5```sqlSELECT name, attack_max FROM heros ORDER BY attack_max DESC LIMIT 5```查询语句看起来是正确的,接下来我将执行这个查询。Action: sql_db_queryAction Input: SELECT name, attack_max FROM heros ORDER BY attack_max DESC LIMIT 5[('阿轲', 427.0), ('孙尚香', 411.0), ('百里守约', 410.0), ('虞姬', 407.0), ('黄忠', 403.0)]I now know the final answer.Final Answer: 攻击力最高的前5个英雄及其攻击力如下:1. 阿轲 - 427.02. 孙尚香 - 411.03. 百里守约 - 410.04. 虞姬 - 407.05. 黄忠 - 403.0

如果是一个程序员从一个陌生的数据库里查询数据,基本上也就是这个流程了,虽然这个sql不是很复杂,但是可以看到agent已经具备通过自然语言来查询数据的能力了。

下面我们通过保险场景,来看一下,Text2Sql的实战能力如何

保险场景实战

数据库信息

首先来看一下我们要用的数据表

编写agent

import osfrom dotenv import load_dotenvload_dotenv('.env')from langchain_community.agent_toolkits.sql.base import create_sql_agentfrom langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkitfrom langchain_community.utilities import SQLDatabasefrom langchain_community.chat_models import ChatOpenAIdb_user = "user"db_password = "pwd"db_host = "localhost:3306"db_name = "db_name"db = SQLDatabase.from_uri(f"mysql+pymysql://{db_user}:{db_password}@{db_host}/{db_name}")# 从环境变量获取 dashscope 的 API Keyapi_key = os.environ.get('DASHSCOPE_API_KEY')llm = ChatOpenAI(    temperature=0.01,    model="deepseek-v3",      openai_api_base = "https://dashscope.aliyuncs.com/compatible-mode/v1",    openai_api_key  = api_key)# 需要设置llmtoolkit = SQLDatabaseToolkit(db=db, llm=llm)agent_executor = create_sql_agent(    llm=llm,    toolkit=toolkit,    verbose=True)

要查询的信息

以下是我们的agent要完成的任务,让我们看看执行效果如何:

    获取所有客户的姓名和联系电话。

      生成的sql:

      SELECT Name, PhoneNumber FROM customerinfo LIMIT 10;

      查询结果1. 欧颖 - 147081984842. 李辉 - 151828752353. 李璐 - 186048786434. 张玉 - 182666155765. 张云 - 157834960416. 周玉兰 - 189444854287. 徐波 - 185375351618. 季丹丹 - 185567357839. 张杰 - 1364891555210. 张杰 - 13302970653

      验证

    找出所有已婚客户的保单。

      生成的sql:
     SELECT p.PolicyNumber, c.Name, c.MaritalStatus FROM policyinfo p JOIN customerinfo c ON CAST(p.CustomerID AS UNSIGNED) = c.CustomerID WHERE c.MaritalStatus = '已婚' LIMIT 10

      查询结果

      目前数据库中没有已婚客户的保单记录。

      验证

      这个结果对吗?我们去数据库里做一下验证,agent用了连表查询,我们换一种方式,用子查询来做:

      select PolicyNumber from policyinfo where CustomerID IN (select CustomerID from customerinfo where MaritalStatus='已婚');

      可以看到,结果是对的

    查询所有未支付保费的保单号和客户姓名。

      生成的sql:

      SELECT p.PolicyNumber, c.Name FROM policyinfo p JOIN customerinfo c ON p.CustomerID = c.CustomerID WHERE p.PremiumPaymentStatus = '未支付' LIMIT 10

      sql看起来没有什么问题

      查询结果

      目前数据库中没有未支付的保单号和客户姓名

      这个结果好像不太对,数据库里明明是有大批量的未支付的保单

      让我们来查找一下原因:查看数据库发现policyinfo表的CustomerID字段和customerinfo表的CustomerID字段是对不上的

      所以是这里的信息没有关联上导致没有查询到数据,而不是sql的问题

根据上面这三个任务,我们可以看到,agent生成sql的能力还是可以的,但是如果数据库的数据有问题就无能为力了,不过这就不是代码层面的问题了,所以综合效果还是可以。

但是,还存在一个问题,就是上面的代码都是依靠框架完成的,如果我需要更灵活的配置,该怎么办?这个我们可以借助RAG来完成,后面的文章会详细介绍。就这样,bye!!!

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Text2Sql 大语言模型 数据库 LangChain
相关文章