本地大模型编程实战(24)用智能体(Agent)实现智能纠错的SQL数据库问答系统(3)

news2025/2/28 4:37:57

本文将实现这样一个 智能体(Agent)

  • 可以使用自然语言对 SQLite 数据库进行查询。即:用户用自然语言提出问题,智能体也用自然语言根据数据库的查询结果回答问题。
  • 增加一个自动对查询中的专有名词进行纠错的工具,这将明显提升查询的准确性。

本次将使用 qwen2.5llama3.1 做实验。

文章目录

    • 准备
    • 创建 `SQLite` 对象
    • 智能体(Agent)
      • 创建 `SQLite` 工具
      • 创建专有名词矢量数据库
      • 创建检索工具
      • 系统提示词
      • 初始化智能体
    • 见证效果
    • 总结
    • 代码

准备

您可以按下面的步骤准备本地编程环境。

  1. 计算机
    本文涉及的代码可以在没有显存的环境中执行。建议最低配置为:
  • CPU: Intel i5-8400 2.80GHz
  • 内存: 16GB
  1. Visual Studio Code 和 venv
    Visual Studio Code 是很受欢迎的开发工具,建议用 venv 创建虚拟环境, 详见:
    在Visual Studio Code中配置venv。

  2. Ollama
    基于 Ollama 平台,我们可以在本地方便的使用 llama3.1qwen2.5deepseek 等各种 LLM(大语言模型)。详见:
    在langchian中使用本地部署的llama3.1大模型 。

创建 SQLite 对象

我们直接使用之前创建好的 SQLite 数据库:

创建 Chinook.db 的详细步骤参见:用langchain实现基于SQL数据构建问答系统(1)

# 获取当前执行的程序文件的文件夹路径
current_folder = os.path.dirname(os.path.abspath(__file__))

db_file_path = os.path.join(current_folder, 'assert/Chinook.db')

from langchain_community.utilities import SQLDatabase

db = SQLDatabase.from_uri(f"sqlite:///{db_file_path}")

智能体(Agent)

LangChain 有一个 SQL智能体,它提供了一种比链更灵活的与 SQL 数据库交互的方式。使用 SQL智能体 的主要优点是:

  • 它可以根据数据库的架构以及数据库的内容(如描述特定表)回答问题
  • 它可以通过运行生成的查询、捕获执行栈并正确地重新生成它来从错误中恢复
  • 它可以根据需要多次查询数据库以回答用户问题
    … 等等

创建 SQLite 工具

为了初始化智能体,我们将使用 SQLDatabaseToolkit 创建一组工具:

  • 创建和执行查询
  • 检查查询语法
  • 检索表描述
    … 等等
def create_tools(llm_model_name):
    """创建工具"""

    llm = ChatOllama(model=llm_model_name,temperature=0, verbose=True)
    toolkit = SQLDatabaseToolkit(db=db, llm=llm)

    tools = toolkit.get_tools()
    print(tools)

    return tools

创建专有名词矢量数据库

我们打算实现这样一个功能:当查询语句中包含 艺术家(在表:artists中)或者 专辑(在表:albums中)这些专业术语时,需要自动检查拼写并自动纠正。
我们可以通过创建一个向量存储来实现这一点,该向量存储包含数据库中存在的所有不同专有名词。然后,我们可以让智能体在每次用户在问题中包含专有名词时查询该向量存储,以找到该单词的正确拼写。通过这种方式,智能体可以在构建目标查询之前确保它了解用户指的是哪个实体。

为此我们定义一个函数将SQLite查询出来的内容转换为列表:

import ast
import re

def query_as_list(db, query):
    res = db.run(query)
    res = [el for sub in ast.literal_eval(res) for el in sub if el]
    res = [re.sub(r"\b\d+\b", "", string).strip() for string in res]
    return list(set(res))

然后通过下面的函数创建本地矢量数据库:

def create_db():
    """创建矢量数据库"""

    if os.path.exists(persist_directory):
        print("数据库已创建")
        return

    artists = query_as_list(db, "SELECT Name FROM Artist")
    print(f'artists:\n{artists[:5]}\n') 
    albums = query_as_list(db, "SELECT Title FROM Album")
    print(f'albums:\n{albums[:5]}\n')

    documents = artists + albums
    embed_texts_in_batches(documents)
    print('db_artists_albums persisted.')

执行 create_db 后,将输出前5条 artistsalbums 的信息,并将这些专有名字都存储在本地矢量数据库中了:

artists:
['Mundo Livre S/A', 'Michele Campanella', 'Black Label Society', 'Jackson Browne', 'Nação Zumbi']

albums:
['Stormbringer', 'A Real Dead One', 'Revelations', 'Body Count', 'Sex Machine']

创建检索工具

准备好矢量数据库以后,我们就可以基于它创建检索器了,名字为 search_proper_nouns,这些我们检索5条信息使用:

retriever = vectordb.as_retriever(search_kwargs={"k": 5})   # 返回5条信息

from langchain.agents.agent_toolkits import create_retriever_tool
description = """Use to look up values to filter on. Input is an approximate spelling of the proper noun, output is \
valid proper nouns. Use the noun most similar to the search."""
retriever_tool = create_retriever_tool(
    retriever,
    name="search_proper_nouns",
    description=description,
)

系统提示词

我们来创建指导智能体的提示词。

system = """You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct SQLite query to run, then look at the results of the query and return the answer.
Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 5 results.
You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for the relevant columns given the question.
You have access to tools for interacting with the database.
Only use the given tools. Only use the information returned by the tools to construct your final answer.
You MUST double check your query before executing it. If you get an error while executing a query, rewrite the query and try again.

DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.

You have access to the following tables: {table_names}

If you need to filter on a proper noun, you must ALWAYS first look up the filter value using the "search_proper_nouns" tool!
Do not try to guess at the proper name - use this function to find similar ones.""".format(
    table_names=db.get_usable_table_names()
)

system_message = SystemMessage(content=system)

上述提示词对大模型生成SQL语句的行为做了比较严格的限制,以防止生成错误的SQL破坏数据库。
可以看到这个提示词相对于上一篇文章的提示词在后面多了两句话:

如果您需要过滤专有名词,则必须始终先使用 `search_proper_nouns` 工具查找过滤值!
不要试图猜测专有名词 - 使用此功能查找类似名称。

初始化智能体

我们将使用 langchaincreate_react_agent 方法初始化 reAct 智能体。
React智能体React Agent)指的是一种能自主决策和执行任务的AI智能体,它结合了大语言模型(LLM)和工具调用,可以根据环境和任务需求动态调整自己的行为。
简单理解:

  1. React = 解释 + 计算(Reason + Act)
    • 先分析当前的任务和数据(Reason)
    • 然后做出相应的行动(Act)
  2. 如何工作?
    • 先阅读输入信息
    • 决定是否调用某个工具(如数据库查询、API 调用)
    • 处理返回的结果,再次分析,继续执行任务
  3. 示例:
    • 您输入:“明天的天气如何?”
    • 智能体会先思考:“这个问题需要调用天气 API。”
    • 然后它调用天气 API,获取数据后再回复你:“明天是晴天,气温 20°C。”

下面定义问答方法。到现在为止,智能体可以统筹使用两个工具了:

def ask(llm_model_name,question):
    """询问智能体"""

    tools = create_tools(llm_model_name)
    tools.append(retriever_tool)

    llm = ChatOllama(model=llm_model_name,temperature=1, verbose=True)
    agent_executor = create_react_agent(llm, tools, state_modifier=system_message)

    for s in agent_executor.stream(
        {"messages": [HumanMessage(content=question)]}
    ):
        print(s)
        print("----")

见证效果

下面我们出3个问题,看看两个大模型的表现如何。这3个问题是:

    questions = [
        "How many Employees are there?",
        "Which country's customers spent the most?",
        "How many albums does Itzhak Perlmam have?",
    ]

对于前两个问题,我们在上一篇文章:用智能体(Agent)实现基于SQL数据构建问答系统(2)有详细的讨论,本次我们重点关注第3个问题。

在第3个问题中,出现了一个词 Itzhak Perlmam ,这是一个艺术家的名字,属于专有名词,我们查一下矢量数据库,看看和这名字相似的五个词:

print(retriever_tool.invoke("Itzhak Perlmam"))
Mela Tenenbaum, Pro Musica Prague & Richard Kapp
Itzhak Perlman
Felix Schmidt, London Symphony Orchestra & Rafael Frühbeck de Burgos
J.S. Bach: Chaconne, Suite in E Minor, Partita in E Major & Prelude, Fugue and Allegro
C. Monteverdi, Nigel Rogers - Chiaroscuro; London Baroque; London Cornett & Sackbu

从上面的记录可以看到:其中的 Itzhak Perlman 最接近我们要找的艺术家,我们用 qwen2.5 先试一下第3个问题,看看智能体能否正确的查出该艺术家拥有的专辑数量。

[QuerySQLDatabaseTool(description="Input to this tool is a detailed and correct SQL query, output is a result from the database. If the query is not correct, an error message will be returned. If an error is returned, rewrite the query, check the query, and try again. If you encounter an issue with Unknown column 'xxxx' in 'field list', use sql_db_schema to query the correct table fields.", ...)]
{'agent': {'messages': [AIMessage(content='', ... tool_calls=[{'name': 'search_proper_nouns', 'args': {'query': 'Itzhak Perlmam'}, ..., 'type': 'tool_call'}], ...]}}
----
{'tools': {'messages': [ToolMessage(content='Mela Tenenbaum, Pro Musica Prague & Richard Kapp\n\nItzhak Perlman\n\n...)]}}
----
{'agent': {'messages': [AIMessage(content='', ..., tool_calls=[{'name': 'sql_db_query', 'args': {'query': "SELECT COUNT(*) FROM Album WHERE Artist LIKE '%Itzhak Perlman%';"}, ..., 'type': 'tool_call'}], ...)]}}
----
{'tools': {'messages': [ToolMessage(content="Error: (sqlite3.OperationalError) no such column: Artist\n[SQL: SELECT COUNT(*) FROM Album WHERE Artist LIKE '%Itzhak Perlman%';]\n..., name='sql_db_query', ...)]}}
----
{'agent': {'messages': [AIMessage(content='', ... tool_calls=[{'name': 'sql_db_schema', 'args': {'table_names': 'Album, Artist'}, ..., 'type': 'tool_call'}], ...)]}}
----
{'tools': {'messages': [ToolMessage(content='\nCREATE TABLE "Album" (\n\t"AlbumId" INTEGER NOT NULL, \n\t"Title" NVARCHAR(160) NOT NULL, ..., name='sql_db_schema', ...)]}}
----
{'agent': {'messages': [AIMessage(content='', ..., tool_calls=[{'name': 'sql_db_query_checker', 'args': {'query': "SELECT COUNT(*) FROM Album WHERE Name LIKE '%Itzhak Perlman%';"}, ..., 'type': 'tool_call'}], ...)]}}
----
{'tools': {'messages': [ToolMessage(content="The provided SQL query does not contain any of the common mistakes listed. Here is the original query:\n\n```sql\nSELECT COUNT(*) FROM Album WHERE Name LIKE '%Itzhak Perlman%';\n```\n\nNo corrections are needed. The query is correct as it stands.", name='sql_db_query_checker', ...)]}}
----
{'agent': {'messages': [AIMessage(content='', ..., tool_calls=[{'name': 'sql_db_query', 'args': {'query': "SELECT COUNT(*) FROM Album WHERE Artist.Name LIKE '%Itzhak Perlman%';"}, ..., 'type': 'tool_call'}], ...)]}}
----
{'tools': {'messages': [ToolMessage(content="Error: (sqlite3.OperationalError) no such column: Artist.Name\n[SQL: SELECT COUNT(*) FROM Album WHERE Artist.Name LIKE '%Itzhak Perlman%';]\n..., name='sql_db_query', ...)]}}
----
{'agent': {'messages': [AIMessage(content='', ..., tool_calls=[{'name': 'sql_db_query', 'args': {'query': "SELECT COUNT(*) FROM Album INNER JOIN Artist ON Album.ArtistId = Artist.ArtistId WHERE Artist.Name LIKE '%Itzhak Perlman%';"}, ..., 'type': 'tool_call'}], ...)]}}
----
{'tools': {'messages': [ToolMessage(content='[(1,)]', name='sql_db_query', ...)]}}
----
{'agent': {'messages': [AIMessage(content='The query executed successfully and returned a result. It shows that Itzhak Perlman has 1 album in the database.\n\nTherefore, the answer to your question is: Itzhak Perlman has 1 album.', ...)]}}
----

通过以上的输出我们发现:qwen2.5 在提示词的指导下,首先通过 search_proper_nouns 查出了 Itzhak Perlmam 应该为 Itzhak Perlman,然后利用 sql_db_schema 推测正确的SQL语句,尝试到第3次终于推理出了正确的SQL语句,并给出了正确的回答。很棒!

我用 llama3.1 对第3个问题试了3次,每次推理的过程都不同,不过3次都未能推理出正确的SQL语句,所以没能给出正确回答。

下面我们用这3个问题,试试这两个模型:

How many Employees are there?Which country’s customers spent the most?How many albums does Itzhak Perlmam have?
qwen2.5There are 8 employees in the database.The country with the highest spending by customers is USA,…Itzhak Perlman has 1 album in the database.
llama3.1There are 8 Employees.The customers who spent the most were from the USA.未给出正确回答

总结

我们这次实现了一个可以调度 查询SQLite数据库智能纠错专有名词 两个工具的智能体(Agent)。
我们发现:qwen2.5 是可以智能的调度这两个工具完成任务的,能力很强!

代码

本文涉及的所有代码以及相关资源都已经共享,参见:

  • github
  • gitee

为便于找到代码,程序文件名称最前面的编号与本系列文章的文档编号相同。

参考

  • Build a Question/Answering system over SQL data

🪐感谢您观看,祝好运🪐

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

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

相关文章

Apache DolphinScheduler系列1-单节点部署及测试报告

文章目录 整体说明一、部署环境二、版本号三、部署方案四、部署步骤4.1、上传部署包4.2、创建外部数据库4.3、修改元数据库配置4.4、上传MySQLl驱动程序4.5、初始化外部数据库4.6、启停服务4.7、访问页面五、常见问题及解决方式5.1、时间不一致5.2、异常终止5.3、大量日志5.4、…

Java+SpringBoot+Vue+数据可视化的音乐推荐与可视化平台(程序+论文+讲解+安装+调试+售后)

感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复,希望帮助更多的人。 系统介绍 在互联网技术以日新月异之势迅猛发展的浪潮下,5G 通信技术的普及、云计算能力…

LVS+Keepalived 高可用集群搭建

一、高可用集群: 1.什么是高可用集群: 高可用集群(High Availability Cluster)是以减少服务中断时间为目地的服务器集群技术它通过保护用户的业务程序对外不间断提供的服务,把因软件、硬件、人为造成的故障对业务的影响…

PydanticToolsParser 工具(tool call)把 LLM 生成的文本转成结构化的数据(Pydantic 模型)过程中遇到的坑

PydanticToolsParser 的作用 PydanticToolsParser 是一个工具,主要作用是 把 LLM 生成的文本转成结构化的数据(Pydantic 模型),让代码更容易使用这些数据进行自动化处理。 换句话说,AI 生成的文本通常是自然语言&…

python-leetcode-乘积最大子数组

152. 乘积最大子数组 - 力扣&#xff08;LeetCode&#xff09; class Solution:def maxProduct(self, nums: List[int]) -> int:if not nums:return 0max_prod nums[0]min_prod nums[0]result nums[0]for i in range(1, len(nums)):if nums[i] < 0:max_prod, min_prod…

江协科技/江科大-51单片机入门教程——P[1-1] 课程简介P[1-2] 开发工具介绍及软件安装

本教程也力求在玩好单片机的同时了解一些计算机的基本概念&#xff0c;了解电脑的一些基本操作&#xff0c;了解电路及其元器件的基本理论&#xff0c;为我们学习更高级的单片机&#xff0c;入门IT和信息技术行业&#xff0c;打下一定的基础。 目录 1.课程简介 2.开发工具及…

简单介绍JVM

1.什么是JVM&#xff1f; JVM就是Java虚拟机【Java Virtual Machine】&#xff0c;简称JVM。主要部分包括类加载子系统&#xff0c;运行时数据区&#xff0c;执行引擎&#xff0c;本地方法库等&#xff0c;接下来我们一一介绍 2.类加载子系统 JVM中运行的就是我们日常写的JA…

【对话推荐系统】Towards Topic-Guided Conversational Recommender System 论文阅读

Towards Topic-Guided Conversational Recommender System 论文阅读 Abstract1 Introduction2 Related Work2.1 Conversation System2.2 Conversational Recommender System2.3 Dataset for Conversational Recommendation 3 Dataset Construction3.1 Collecting Movies for Re…

当下弹幕互动游戏源码开发教程及功能逻辑分析

当下很多游戏开发者或者想学习游戏开发的人&#xff0c;想要了解如何制作弹幕互动游戏&#xff0c;比如直播平台上常见的那种&#xff0c;观众通过发送弹幕来影响游戏进程。需要涵盖教程的步骤和功能逻辑的分析。 首先&#xff0c;弹幕互动游戏源码开发教程部分应该分步骤&…

STM32——HAL库开发笔记21(定时器2—输出比较)(参考来源:b站铁头山羊)

本文主要讲述输出比较及PWM信号相关知识。 一、概念 所谓输出比较&#xff0c;就是通过单片机的定时器向外输出精确定时的方波信号。 1.1 PWM信号 PWM信号即脉冲宽度调制信号。PWM信号的占空比 &#xff08;高电压 所占周期 / 整个周期&#xff09; * 100% 。所以PWM信号…

YOLOv12 ——基于卷积神经网络的快速推理速度与注意力机制带来的增强性能结合

概述 实时目标检测对于许多实际应用来说已经变得至关重要&#xff0c;而Ultralytics公司开发的YOLO&#xff08;You Only Look Once&#xff0c;只看一次&#xff09;系列一直是最先进的模型系列&#xff0c;在速度和准确性之间提供了稳健的平衡。注意力机制的低效阻碍了它们在…

动态内容加载的解决方案:Selenium与Playwright对比故障排查实录

方案进程 2024-09-01 09:00 | 接到亚航航班数据采集需求 2024-09-01 11:30 | 首次尝试使用Selenium遭遇Cloudflare验证 2024-09-01 14:00 | 切换Playwright方案仍触发反爬机制 2024-09-01 16:30 | 引入爬虫代理IPUA轮换策略 2024-09-02 10:00 | 双方案完整实现并通过压力测试故…

NLP学习记录十:多头注意力

一、单头注意力 单头注意力的大致流程如下&#xff1a; ① 查询编码向量、键编码向量和值编码向量分别经过自己的全连接层&#xff08;Wq、Wk、Wv&#xff09;后得到查询Q、键K和值V&#xff1b; ② 查询Q和键K经过注意力评分函数&#xff08;如&#xff1a;缩放点积运算&am…

Spring基础01

Spring基础01 软件开发原则 OCP开闭原则&#xff1a;七大开发原则当中最基本的原则&#xff0c;其他的六个原则是为这个原则服务的。 对扩展开放&#xff0c;对修改关闭。在扩展系统功能的时候&#xff0c;没有修改之前写好的代码&#xff0c;就符合OCP原则&#xff0c;反之&a…

2025年2月,TVBOX接口最新汇总版

这里写自定义目录标题 1、离线版很必要2、关于在线版好还是离线版更实在&#xff0c;作个总结&#xff1a;★ 离线版的优点&#xff1a;★ 离线版的缺点&#xff1a; 3.1、 针对FM内置的写法&#xff1b;3.2、 如果是用在YSC&#xff0c;那么格式也要有些小小的改变3.2.1、 YSC…

Dubbo RPC 原理

一、Dubbo 简介 Apache Dubbo 是一款高性能、轻量级的开源 RPC 框架&#xff0c;支持服务治理、协议扩展、负载均衡、容错机制等核心功能&#xff0c;广泛应用于微服务架构。其核心目标是解决分布式服务之间的高效通信与服务治理问题。 二、Dubbo 架构设计 1. 核心组件 Prov…

第2章_保护您的第一个应用程序

第2章_保护您的第一个应用程序 在本章中&#xff0c;您将学习如何使用 Keycloak 保护您的第一个应用程序。为了让事情更有趣&#xff0c;您将运行的示例应用程序由两部分组成&#xff0c;前端 Web 应用程序和后端 REST API。这将向您展示用户如何向前端进行身份验证&#xff0…

【Godot4.3】自定义圆角容器

概述 Godot控件想要完全实现现代UI风格&#xff0c;需要进行大量的自定义组件设计。本篇就依托于笔者自己对现代UI设计中的圆角面板元素模仿来制作圆角容器组件。 圆角容器 圆角元素在现代的扁平UI设计中非常常见&#xff0c;在Godot中可以通过改进PanelContainer来或者自定…

Flutter系列教程之(5)——常用控件Widget的使用示例

目录 1.页面跳转 2.某个控件设置点击事件 3.AlertDialog对话框的使用 4.文本输入框 5.按钮 圆角扁平按钮: 圆角悬浮按钮: 6.补充 圆点 7.布局使用 Row控件左右对齐 调整边距 1.页面跳转 首先&#xff0c;先介绍一下页面跳转功能吧 Flutter使用 Navigator 进行页面…

DeepSeek开源周,第三弹再次来袭,DeepGEMM

在大型模型推理中&#xff0c;矩阵乘法&#xff08;GEMM&#xff09;是计算的核心瓶颈。DeepGEMM 应运而生——一款专为 FP8精度矩阵乘法 设计的轻量级CUDA库&#xff0c;由深度求索&#xff08;DeepSeek&#xff09;团队开源。它凭借极简代码&#xff08;核心仅300行&#xff…