利用 LangChain 和一个大语言模型(LLM)构建一个链条,自动从用户输入的问题中提取相关的 SQL 表信息,再生成对应的 SQL 查询

news2025/3/4 8:28:33

示例代码:

from langchain_core.runnables import RunnablePassthrough
from langchain.chains import create_sql_query_chain
from operator import itemgetter
from langchain.chains.openai_tools import create_extraction_chain_pydantic

# 系统消息,要求 LLM 返回与问题相关的 SQL 表类别
system = """Return the names of the SQL tables that are relevant to the user question. \
The tables are:

Music
Business"""

# 初始化 LLM 模型
table_extractor_llm = init_chat_model("llama3-70b-8192", model_provider="groq", temperature=0)

# 创建提取链:将用户问题转换为 Table 模型的实例
category_chain = create_extraction_chain_pydantic(pydantic_schemas=Table, llm=table_extractor_llm, system_message=system)

# 定义一个函数,根据 Table 对象映射到具体的 SQL 表名
def get_tables(categories: List[Table]) -> List[str]:
    """将类别名称映射到对应的 SQL 表名列表."""
    tables = []
    for category in categories:
        if category.name == "Music":
            tables.extend(
                [
                    "Album",
                    "Artist",
                    "Genre",
                    "MediaType",
                    "Playlist",
                    "PlaylistTrack",
                    "Track",
                ]
            )
        elif category.name == "Business":
            tables.extend(["Customer", "Employee", "Invoice", "InvoiceLine"])
    return tables

# 将类别提取链与映射函数组合,得到一个返回 SQL 表名列表的链
table_chain = category_chain | get_tables 

# 定义自定义 SQL 提示模板,用于生成 SQL 查询
custom_prompt = PromptTemplate(
    input_variables=["dialect", "input", "table_info", "top_k"],
    template="""You are a SQL expert using {dialect}.
Given the following table schema:
{table_info}
Generate a syntactically correct SQL query to answer the question: "{input}".
Don't limit the results to {top_k} rows.
Return only the SQL query without any additional commentary or Markdown formatting.
"""
)

# 创建 SQL 查询链
query_chain = create_sql_query_chain(table_extractor_llm, db, prompt=custom_prompt)

# 利用 bind 将固定参数绑定到 SQL 查询链中
bound_chain = query_chain.bind(
    dialect=db.dialect,
    table_info=db.get_table_info(),
    top_k=55
)

# 将输入中的 "question" 键复制到 "input" 键,同时保留原始数据
table_chain = (lambda x: {**x, "input": x["question"]}) | table_chain

# 使用 RunnablePassthrough.assign 将提取到的表名添加到上下文中,然后与 SQL 查询链组合
full_chain = RunnablePassthrough.assign(table_names_to_use=table_chain) | bound_chain

# 调用整个链,生成 SQL 查询
query = full_chain.invoke(
    {"question": "What are all the genres of Alanis Morisette songs? Do not repeat!"}
)
print(query)

这段代码主要展示如何利用 LangChain 和一个大语言模型(LLM)构建一个链条,自动从用户输入的问题中提取相关的 SQL 表信息,再生成对应的 SQL 查询。下面我将分步详细解释每个部分的作用,并通过举例说明每段代码的输入和输出。


1. 定义系统消息和初始化 LLM 模型

system = """Return the names of the SQL tables that are relevant to the user question. \
The tables are:

Music
Business"""
  • 作用:
    这段系统消息告诉 LLM:请根据用户的问题返回与问题相关的 SQL 表类别,这里限定了两类——“Music”和“Business”。

  • 举例:
    如果用户的问题涉及音乐信息(例如歌曲、专辑等),那么 LLM 会返回 “Music”;如果涉及客户、发票等信息,则返回 “Business”。

table_extractor_llm = init_chat_model("llama3-70b-8192", model_provider="groq", temperature=0)
  • 作用:
    初始化一个 LLM 模型(此处使用 llama3-70b-8192,由 groq 提供,温度设为 0 以保证回答确定性),后续会用这个模型进行类别提取和 SQL 查询生成。

  • 输出:
    返回一个 LLM 实例 table_extractor_llm


2. 创建提取链:从问题中抽取相关表类别

category_chain = create_extraction_chain_pydantic(pydantic_schemas=Table, llm=table_extractor_llm, system_message=system)
  • 作用:
    这里利用 create_extraction_chain_pydantic 创建了一个链,该链的任务是将用户输入的问题转换为一个或多个符合 Pydantic 模型 Table 的实例。也就是说,LLM 会分析问题并输出如 Table(name="Music")Table(name="Business") 的结果。

  • 输入:
    用户问题(例如 “What are all the genres of Alanis Morisette songs? Do not repeat!”)。

  • 输出:
    一个或多个 Table 对象,指明问题相关的表类别。例如,对于这个问题,可能返回 [Table(name="Music")]
    在这里插入图片描述


3. 定义映射函数,将类别映射到具体的 SQL 表名

def get_tables(categories: List[Table]) -> List[str]:
    """将类别名称映射到对应的 SQL 表名列表."""
    tables = []
    for category in categories:
        if category.name == "Music":
            tables.extend(
                [
                    "Album",
                    "Artist",
                    "Genre",
                    "MediaType",
                    "Playlist",
                    "PlaylistTrack",
                    "Track",
                ]
            )
        elif category.name == "Business":
            tables.extend(["Customer", "Employee", "Invoice", "InvoiceLine"])
    return tables
  • 作用:
    此函数接收前面提取链返回的 Table 对象列表,根据类别名称映射到具体的 SQL 表名列表:

    • 如果类别是 “Music”,则映射为音乐相关的多个表(如 Album、Artist、Genre 等)。
    • 如果类别是 “Business”,则映射为商业相关的表(如 Customer、Invoice 等)。
  • 举例:

    • 输入: [Table(name="Music")]
    • 输出: ["Album", "Artist", "Genre", "MediaType", "Playlist", "PlaylistTrack", "Track"]

4. 组合提取链和映射函数

table_chain = category_chain | get_tables
  • 作用:
    利用管道操作符(|)将 category_chainget_tables 组合起来。整个链条(table_chain)的作用就是:接收用户问题 → 利用 LLM 提取相关类别 → 将类别映射为具体的 SQL 表名列表。

  • 输入:
    一个包含用户问题的字典(例如 {"question": "..."})。

  • 输出:
    一个 SQL 表名列表,如上例中的音乐相关表名列表。
    在这里插入图片描述


5. 定义自定义 SQL 提示模板

custom_prompt = PromptTemplate(
    input_variables=["dialect", "input", "table_info", "top_k"],
    template="""You are a SQL expert using {dialect}.
Given the following table schema:
{table_info}
Generate a syntactically correct SQL query to answer the question: "{input}".
Don't limit the results to {top_k} rows.
Return only the SQL query without any additional commentary or Markdown formatting.
"""
)
  • 作用:
    该模板为生成 SQL 查询提供指令:

    • 指定 SQL 方言(如 MySQL、PostgreSQL 等)。
    • 提供数据库的表结构信息。
    • 告诉 LLM 根据问题({input})生成正确的 SQL 查询。
    • 不要限制返回行数,并且只返回 SQL 语句本身,无额外说明。
  • 举例:
    如果传入:

    • dialect: “SQLite”
    • input: “What are all the genres of Alanis Morisette songs? Do not repeat!”
    • table_info: 数据库所有表的结构信息
    • top_k: 55
      那么模板会指导 LLM 输出类似下面的 SQL 查询(实际内容由 LLM 根据 schema 生成):
    SELECT DISTINCT Genre.Name FROM Track
    JOIN Genre ON Track.GenreId = Genre.GenreId
    JOIN Artist ON Track.ArtistId = Artist.ArtistId
    WHERE Artist.Name = 'Alanis Morisette';
    

6. 创建 SQL 查询链并绑定固定参数

query_chain = create_sql_query_chain(table_extractor_llm, db, prompt=custom_prompt)
  • 作用:
    利用同一个 LLM 实例和预定义的 SQL 提示模板,创建一个 SQL 查询链。该链将根据数据库表结构(db)和用户问题生成 SQL 查询。
bound_chain = query_chain.bind(
    dialect=db.dialect,
    table_info=db.get_table_info(),
    top_k=55
)
  • 作用:
    通过 bind 方法将一些固定的参数绑定到 SQL 查询链上:

    • dialect:数据库使用的 SQL 方言。
    • table_info:数据库中所有表的结构信息。
    • top_k:限制返回的行数,这里设定为 55 行,但指令中说明不要限制,所以其实这个参数仅作为提示的一部分。
  • 输出:
    得到一个参数已经固定的 SQL 查询链 bound_chain,后续调用时只需要传入用户问题(以及其他动态数据)。


7. 调整输入数据格式

table_chain = (lambda x: {**x, "input": x["question"]}) | table_chain
  • 作用:
    这行代码先用一个 lambda 函数将输入字典中的 "question" 键复制一份到 "input" 键,目的是统一变量名称(因为上面的 SQL 提示模板要求有 input 变量)。然后再将结果传递给 table_chain

  • 举例:

    • 输入: {"question": "What are all the genres of Alanis Morisette songs? Do not repeat!"}
    • lambda 输出: {"question": "What are all the genres of Alanis Morisette songs? Do not repeat!", "input": "What are all the genres of Alanis Morisette songs? Do not repeat!"}
    • 最终经过 table_chain 输出: 列表形式的 SQL 表名,如 ["Album", "Artist", "Genre", "MediaType", "Playlist", "PlaylistTrack", "Track"]

8. 组合整个链条,生成最终 SQL 查询

full_chain = RunnablePassthrough.assign(table_names_to_use=table_chain) | bound_chain
  • 作用:
    这里使用 RunnablePassthrough.assign 将从 table_chain 得到的 SQL 表名列表赋值到上下文中的 table_names_to_use 键,然后通过管道传递给已经绑定参数的 SQL 查询链 bound_chain。这一步确保了在生成 SQL 查询时,上下文中不仅包含用户的原始问题,还包含了与之相关的 SQL 表名信息。

  • 输入:
    包含用户问题的字典(经过前面的处理已包含 "input" 键)。

  • 输出:
    经过整个链条处理后,输出最终生成的 SQL 查询语句。


9. 调用链条并生成 SQL 查询

query = full_chain.invoke(
    {"question": "What are all the genres of Alanis Morisette songs? Do not repeat!"}
)
print(query)
  • 作用:
    这里将包含用户问题的字典传递给 full_chain。整个流程如下:

    1. 提取表类别:首先通过 table_chain"question" 转换为 "input",然后利用 LLM 提取出与问题相关的类别(预期为 “Music”)。
    2. 映射表名称:根据类别映射出所有与音乐相关的 SQL 表名。
    3. 生成 SQL 查询:利用绑定好的 bound_chain(包含 SQL 模板、数据库 schema 信息等),结合用户的问题和上下文信息,生成一个正确的 SQL 查询。
  • 输出举例:
    假设 LLM 理解问题并生成的 SQL 查询可能为:

    SELECT DISTINCT Genre.Name
    FROM Track
    JOIN Genre ON Track.GenreId = Genre.GenreId
    JOIN Artist ON Track.ArtistId = Artist.ArtistId
    WHERE Artist.Name = 'Alanis Morisette';
    

    (实际生成的 SQL 语句会依赖于 LLM 的理解和数据库的 schema 信息。)


最后运行这个SQL语句

db.run(query)

输出:
在这里插入图片描述

总结

这段代码整体实现了一个智能化的数据查询过程:

  • 输入: 用户问题(如关于 Alanis Morisette 歌曲的查询)。
  • 内部处理:
    1. 利用 LLM 提取相关 SQL 表类别。
    2. 根据类别映射出具体的 SQL 表名称。
    3. 结合数据库的表结构和预定义的 SQL 提示模板,生成正确的 SQL 查询语句。
  • 输出: 一条 SQL 查询语句,用来从数据库中获取答案。

这种链式结构使得整个流程模块化、可扩展:可以分别替换提取逻辑、映射逻辑和 SQL 查询生成逻辑,非常适合在实际应用中自动生成数据库查询。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2309383.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣hot 100之矩阵四题解法总结

本期总结hot100 中二维矩阵的题,时空复杂度就不分析了 1.矩阵置零 原地标记,用第一行和第一列作为当前行列是否为0的标记,同时用两个标签分别记录0行、0列的标记空间中原本是否有0 class Solution:def setZeroes(self, matrix: List[List[…

在Linux上使用APT安装Sniffnet的详细步骤

一、引言 Sniffnet 是一款开源的网络流量监控工具,适用于多种Linux发行版。如果你的Linux系统使用APT(Advanced Package Tool)作为包管理器,以下是如何通过APT安装Sniffnet的详细步骤。 二、系统要求 在开始安装之前&#xff0…

zookeeper-docker版

Zookeeper-docker版 1 zookeeper概述 1.1 什么是zookeeper Zookeeper是一个分布式的、高性能的、开源的分布式系统的协调(Coordination)服务,它是一个为分布式应用提供一致性服务的软件。 1.2 zookeeper应用场景 zookeeper是一个经典的分…

StableDiffusion本地部署 3 整合包猜想

本地部署和整合包制作猜测 文章目录 本地部署和整合包制作猜测官方部署第一种第二种 StabilityMatrix下载整合包制作流程猜测 写了这么多python打包和本地部署的文章,目的是向做一个小整合包出来,不要求有图形界面,只是希望一键就能运行。 但…

数据结构(初阶)(七)----树和二叉树(前中后序遍历)

实现链式结构的二叉树 实现链式结构的二叉树遍历前序遍历中序遍历后序遍历 节点个数叶子节点个数⼆叉树第k层结点个数⼆叉树的深度/⾼度查找值为X的节点二叉树的销毁 层序遍历判断二叉树是否为完全二叉树 ⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。 通常…

科技赋能筑未来 中建海龙MiC建筑技术打造保障房建设新标杆

近日,深圳梅林路6号保障房项目顺利封顶,标志着国内装配式建筑领域又一里程碑式突破。中建海龙科技有限公司(以下简称“中建海龙”)以模块化集成建筑(MiC)技术为核心,通过科技创新与工业化建造深…

json介绍、python数据和json数据的相互转换

目录 一 json介绍 json是什么? 用处 Json 和 XML 对比 各语言对Json的支持情况 Json规范详解 二 python数据和json数据的相互转换 dumps() : 转换成json loads(): 转换成python数据 总结 一 json介绍 json是什么? 实质上是一条字符串 是一种…

计算机毕设JAVA——某高校宿舍管理系统(基于SpringBoot+Vue前后端分离的项目)

文章目录 概要项目演示图片系统架构技术运行环境系统功能简介 概要 网络上许多计算机毕设项目开发前端界面设计复杂、不美观,而且功能结构十分单一,存在很多雷同的项目:不同的项目基本上就是套用固定模板,换个颜色、改个文字&…

Spring Boot 测试:单元、集成与契约测试全解析

一、Spring Boot 分层测试策略 Spring Boot 应用采用经典的分层架构,不同层级的功能模块对应不同的测试策略,以确保代码质量和系统稳定性。 Spring Boot 分层架构: Spring Boot分层架构 A[客户端] -->|HTTP 请求| B[Controller 层] …

Oracle 数据库基础入门(四):分组与联表查询的深度探索(上)

在 Oracle 数据库的学习进程中,分组查询与联表查询是进阶阶段的重要知识点,它们如同数据库操作的魔法棒,能够从复杂的数据中挖掘出有价值的信息。对于 Java 全栈开发者而言,掌握这些技能不仅有助于高效地处理数据库数据&#xff0…

机器学习的起点:线性回归Linear Regression

机器学习的起点:线性回归Linear Regression 作为机器学习的起点,线性回归是理解算法逻辑的绝佳入口。我们从定义、评估方法、应用场景到局限性,用生活化的案例和数学直觉为你构建知识框架。 回归算法 一、线性回归的定义与核心原理 定义&a…

17、什么是智能指针,C++有哪几种智能指针【高频】

智能指针其实不是指针,而是一个(模板)类,用来存储指向某块资源的指针,并自动释放这块资源,从而解决内存泄漏问题。主要有以下四种: auto_ptr 它的思想就是当当一个指针对象赋值给另一个指针对…

PyCharm接入本地部署DeepSeek 实现AI编程!【支持windows与linux】

今天尝试在pycharm上接入了本地部署的deepseek,实现了AI编程,体验还是很棒的。下面详细叙述整个安装过程。 本次搭建的框架组合是 DeepSeek-r1:1.5b/7b Pycharm专业版或者社区版 Proxy AI(CodeGPT) 首先了解不同版本的deepsee…

PyCharm怎么集成DeepSeek

PyCharm怎么集成DeepSeek 在PyCharm中集成DeepSeek等大语言模型(LLM)可以借助一些插件或通过代码调用API的方式实现,以下为你详细介绍两种方法: 方法一:使用JetBrains AI插件(若支持DeepSeek) JetBrains推出了AI插件来集成大语言模型,不过截至2024年7月,官方插件主要…

【定昌Linux系统】部署了java程序,设置开启启动

将代码上传到相应的目录,并且配置了一个.sh的启动脚本文件 文件内容: #!/bin/bash# 指定JAR文件的路径(如果JAR文件在当前目录,可以直接使用文件名) JAR_FILE"/usr/local/java/xs_luruan_client/lib/xs_luruan_…

Java零基础入门笔记:(7)异常

前言 本笔记是学习狂神的java教程,建议配合视频,学习体验更佳。 【狂神说Java】Java零基础学习视频通俗易懂_哔哩哔哩_bilibili 第1-2章:Java零基础入门笔记:(1-2)入门(简介、基础知识)-CSDN博客 第3章…

【字符串】最长公共前缀 最长回文子串

文章目录 14. 最长公共前缀解题思路:模拟5. 最长回文子串解题思路一:动态规划解题思路二:中心扩散法 14. 最长公共前缀 14. 最长公共前缀 ​ 编写一个函数来查找字符串数组中的最长公共前缀。 ​ 如果不存在公共前缀,返回空字符…

react 中,使用antd layout布局中的sider 做sider的展开和收起功能

一 话不多说,先展示效果: 展开时: 收起时: 二、实现代码如下 react 文件 import React, {useState} from react; import {Layout} from antd; import styles from "./index.module.less"; // 这个是样式文件&#…

easyExcel使用案例有代码

easyExcel 入门,完成web的excel文件创建和导出 easyExcel官网 EasyExcel 的主要特点如下: 1、高性能:EasyExcel 采用了异步导入导出的方式,并且底层使用 NIO 技术实现,使得其在导入导出大数据量时的性能非常高效。 2、易于使…

苹果廉价机型 iPhone 16e 影像系统深度解析

【人像拍摄差异】 尽管iPhone 16e支持后期焦点调整功能,但用户无法像iPhone 16系列那样通过点击屏幕实时切换拍摄主体。前置摄像头同样缺失人像深度控制功能,不过TrueTone原彩闪光灯系统在前后摄均有保留。 很多人都高估了 iPhone 的安全性,查…