本地大模型编程实战(24)用智能体(Agent)实现智能纠错的SQL数据库问答系统(3)
本文将实现这样一个 智能体(Agent) :
- 可以使用自然语言对
SQLite数据库进行查询。即:用户用自然语言提出问题,智能体也用自然语言根据数据库的查询结果回答问题。 - 增加一个自动对查询中的专有名词进行纠错的工具,这将明显提升查询的准确性。
本次将使用
qwen2.5、llama3.1做实验。
文章目录
- 准备
- 创建 `SQLite` 对象
- 智能体(Agent)
- 创建 `SQLite` 工具
- 创建专有名词矢量数据库
- 创建检索工具
- 系统提示词
- 初始化智能体
- 见证效果
- 总结
- 代码
准备
您可以按下面的步骤准备本地编程环境。
- 计算机
本文涉及的代码可以在没有显存的环境中执行。建议最低配置为:
- CPU: Intel i5-8400 2.80GHz
- 内存: 16GB
-
Visual Studio Code 和 venv
Visual Studio Code是很受欢迎的开发工具,建议用venv创建虚拟环境, 详见:
在Visual Studio Code中配置venv。 -
Ollama
基于Ollama平台,我们可以在本地方便的使用llama3.1、qwen2.5、deepseek等各种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 SQLDatabasedb = 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 redef 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("数据库已创建")returnartists = 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 + albumsembed_texts_in_batches(documents)print('db_artists_albums persisted.')
执行 create_db 后,将输出前5条 artists 和 albums 的信息,并将这些专有名字都存储在本地矢量数据库中了:
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` 工具查找过滤值!
不要试图猜测专有名词 - 使用此功能查找类似名称。
初始化智能体
我们将使用 langchain 的 create_react_agent 方法初始化 reAct 智能体。
React智能体(React Agent)指的是一种能自主决策和执行任务的AI智能体,它结合了大语言模型(LLM)和工具调用,可以根据环境和任务需求动态调整自己的行为。
简单理解:
- React = 解释 + 计算(Reason + Act)
- 先分析当前的任务和数据(Reason)
- 然后做出相应的行动(Act)
- 如何工作?
- 先阅读输入信息
- 决定是否调用某个工具(如数据库查询、API 调用)
- 处理返回的结果,再次分析,继续执行任务
- 示例:
- 您输入:“明天的天气如何?”
- 智能体会先思考:“这个问题需要调用天气 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.5 | There 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.1 | There 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
🪐感谢您观看,祝好运🪐
相关文章:
本地大模型编程实战(24)用智能体(Agent)实现智能纠错的SQL数据库问答系统(3)
本文将实现这样一个 智能体(Agent) : 可以使用自然语言对 SQLite 数据库进行查询。即:用户用自然语言提出问题,智能体也用自然语言根据数据库的查询结果回答问题。增加一个自动对查询中的专有名词进行纠错的工具,这将明显提升查询…...
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)是以减少服务中断时间为目地的服务器集群技术它通过保护用户的业务程序对外不间断提供的服务,把因软件、硬件、人为造成的故障对业务的影响…...
跟着AI学vue第十二章
第十二章:技术引领与社区共建 在熟练掌握Vue开发技能,并将其与前沿技术融合应用后, 第十二章是一个更具使命感与影响力的阶段,着重于引领技术发展方向和为社区贡献力量。 1. 推动Vue技术创新与实践 探索前沿技术融合࿱…...
PydanticToolsParser 工具(tool call)把 LLM 生成的文本转成结构化的数据(Pydantic 模型)过程中遇到的坑
PydanticToolsParser 的作用 PydanticToolsParser 是一个工具,主要作用是 把 LLM 生成的文本转成结构化的数据(Pydantic 模型),让代码更容易使用这些数据进行自动化处理。 换句话说,AI 生成的文本通常是自然语言&…...
python-leetcode-乘积最大子数组
152. 乘积最大子数组 - 力扣(LeetCode) 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] 开发工具介绍及软件安装
本教程也力求在玩好单片机的同时了解一些计算机的基本概念,了解电脑的一些基本操作,了解电路及其元器件的基本理论,为我们学习更高级的单片机,入门IT和信息技术行业,打下一定的基础。 目录 1.课程简介 2.开发工具及…...
简单介绍JVM
1.什么是JVM? JVM就是Java虚拟机【Java Virtual Machine】,简称JVM。主要部分包括类加载子系统,运行时数据区,执行引擎,本地方法库等,接下来我们一一介绍 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…...
当下弹幕互动游戏源码开发教程及功能逻辑分析
当下很多游戏开发者或者想学习游戏开发的人,想要了解如何制作弹幕互动游戏,比如直播平台上常见的那种,观众通过发送弹幕来影响游戏进程。需要涵盖教程的步骤和功能逻辑的分析。 首先,弹幕互动游戏源码开发教程部分应该分步骤&…...
STM32——HAL库开发笔记21(定时器2—输出比较)(参考来源:b站铁头山羊)
本文主要讲述输出比较及PWM信号相关知识。 一、概念 所谓输出比较,就是通过单片机的定时器向外输出精确定时的方波信号。 1.1 PWM信号 PWM信号即脉冲宽度调制信号。PWM信号的占空比 (高电压 所占周期 / 整个周期) * 100% 。所以PWM信号…...
YOLOv12 ——基于卷积神经网络的快速推理速度与注意力机制带来的增强性能结合
概述 实时目标检测对于许多实际应用来说已经变得至关重要,而Ultralytics公司开发的YOLO(You Only Look Once,只看一次)系列一直是最先进的模型系列,在速度和准确性之间提供了稳健的平衡。注意力机制的低效阻碍了它们在…...
动态内容加载的解决方案: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学习记录十:多头注意力
一、单头注意力 单头注意力的大致流程如下: ① 查询编码向量、键编码向量和值编码向量分别经过自己的全连接层(Wq、Wk、Wv)后得到查询Q、键K和值V; ② 查询Q和键K经过注意力评分函数(如:缩放点积运算&am…...
Spring基础01
Spring基础01 软件开发原则 OCP开闭原则:七大开发原则当中最基本的原则,其他的六个原则是为这个原则服务的。 对扩展开放,对修改关闭。在扩展系统功能的时候,没有修改之前写好的代码,就符合OCP原则,反之&a…...
Gurobi 并行计算的一些问题
最近尝试用 gurobi 进行并行计算,即同时用多个 cpu 核计算 gurobi 的 model,但是发现了不少问题。总体来看,gurobi 对并行计算的支持并不是那么好。 gurobi 官方对于并行计算的使用在这个网址,并有下面的大致代码: i…...
2025年2月,TVBOX接口最新汇总版
这里写自定义目录标题 1、离线版很必要2、关于在线版好还是离线版更实在,作个总结:★ 离线版的优点:★ 离线版的缺点: 3.1、 针对FM内置的写法;3.2、 如果是用在YSC,那么格式也要有些小小的改变3.2.1、 YSC…...
Dubbo RPC 原理
一、Dubbo 简介 Apache Dubbo 是一款高性能、轻量级的开源 RPC 框架,支持服务治理、协议扩展、负载均衡、容错机制等核心功能,广泛应用于微服务架构。其核心目标是解决分布式服务之间的高效通信与服务治理问题。 二、Dubbo 架构设计 1. 核心组件 Prov…...
qt5的中文乱码问题,QString、QStringLiteral 为 UTF-16 编码
qt5的中文乱码问题一直没有很明确的处理方案。 今天处理进程间通信时,也遇到了qt5乱码问题,一边是设置的GBK,一边设置的是UTF8,单向通信约定采用UTF8。 发送端保证发的是UTF8字符串,因为UTF8在网络数据包中没有字节序…...
第2章_保护您的第一个应用程序
第2章_保护您的第一个应用程序 在本章中,您将学习如何使用 Keycloak 保护您的第一个应用程序。为了让事情更有趣,您将运行的示例应用程序由两部分组成,前端 Web 应用程序和后端 REST API。这将向您展示用户如何向前端进行身份验证࿰…...
【Godot4.3】自定义圆角容器
概述 Godot控件想要完全实现现代UI风格,需要进行大量的自定义组件设计。本篇就依托于笔者自己对现代UI设计中的圆角面板元素模仿来制作圆角容器组件。 圆角容器 圆角元素在现代的扁平UI设计中非常常见,在Godot中可以通过改进PanelContainer来或者自定…...
Flutter系列教程之(5)——常用控件Widget的使用示例
目录 1.页面跳转 2.某个控件设置点击事件 3.AlertDialog对话框的使用 4.文本输入框 5.按钮 圆角扁平按钮: 圆角悬浮按钮: 6.补充 圆点 7.布局使用 Row控件左右对齐 调整边距 1.页面跳转 首先,先介绍一下页面跳转功能吧 Flutter使用 Navigator 进行页面…...
DeepSeek开源周,第三弹再次来袭,DeepGEMM
在大型模型推理中,矩阵乘法(GEMM)是计算的核心瓶颈。DeepGEMM 应运而生——一款专为 FP8精度矩阵乘法 设计的轻量级CUDA库,由深度求索(DeepSeek)团队开源。它凭借极简代码(核心仅300行ÿ…...
stm32四种方式精密控制步进电机
在搭建完clion的开发环境后,我决定重写之前的项目并优化完善,争取做出完全可落地的东西,也结合要写的论文内容一同学习下去。 因此,首当其冲的就是回到步进电机控制领域,把之前使用中断溢出进行步进电机控制的方案进行…...
C++11 智能指针:unique_ptr、shared_ptr和weak_ptr 功能特性 模拟实现
文章目录 unique_ptr功能和特性使用场景make_unique模拟实现 shared_ptr功能和特性使用场景make_shared模拟实现 weak_ptr C 中智能指针都是 RAII(Resource Acquisition Is Initialization)机制的典型应用,在构造时获取资源,在析构…...
Spring Boot启动过程?
目录 1. 启动入口 2. SpringApplication 初始化 3. 准备环境 4. 创建应用上下文(ApplicationContext) 5. 准备应用上下文 6. 刷新应用上下文 7. 启动 Web 服务器(若为 Web 应用) 8. 发布 ApplicationStartedEvent 事件 9. 执行 Runner 10. 发布 ApplicationReady…...
2025年软考报名费用是多少?全国费用汇总!
软考报名时间终于确定了!想要参加2025年软考的同学们注意啦!特别是那些一年只有一次考试机会的科目,千万不要错过哦!这里为大家整理了各地的报名时间、科目、费用等信息,快来看看吧! 一、2025年软考时间安…...
算法-二叉树篇06-二叉树的最大深度
二叉树的最大深度 力扣题目链接 题目描述 给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 解题思路 一样可以使用递归的思想,代码也十分简洁,计算出两个子树的深度取最大加…...
git merge -s ours ...的使用方法
当我们在自己的feature branch上开发时,并且已经commit,push了好几次 同时develop分支也commit , push了好几次, 如下图所示 这个时候就不能直接将feature branch上的改动 pull request到develop上面,因为develop基线已经不一样了…...
