本地大模型编程实战(26)用langgraph实现基于SQL数据构建的问答系统(5)
本文将将扩展上一篇文章完成的 langgraph
链,继续使用基于 langgraph
链 ,对结构化数据库 SQlite
进行查询的方法。该系统建立以后,我们不需要掌握专业的 SQL
技能,可以用自然语言询问有关数据库中数据的问题并返回答案。主要完善一下两点内容:
- 自动记录消息历史
- 增加人工审核环节,防止
LLM(大语言模型)
运行危险的SQL语句
我们先看看完成的 langgraph
链的模样,主要有两步:创建SQL查询语句->执行SQL查询语句,在执行SQL查询前中断进行人工审核,上一篇文章的 链 没有人工审核:
使用
qwen2.5
、llama3.1
做实验。
请注意:
构建 SQL
数据库的问答系统需要执行模型生成的 SQL
查询。这样做存在风险,请确保您的数据库连接权限始终尽可能小,这将减轻(但不能消除)构建模型驱动系统的风险。
文章目录
- 准备开发环境
- 定义 `langgraph` 步骤/节点
- 增加人工审核节点
- 使用内存存储
- 在问答中增加人工审核
- 定义测试方法
- 见证效果
- 总结
- 代码
- 参考
准备开发环境
在正式开始撸代码之前,需要准备一下编程环境。
-
计算机
本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为:- CPU: Intel i5-8400 2.80GHz
- 内存: 16GB
-
Visual Studio Code 和 venv
这是很受欢迎的开发工具,相关文章的代码可以在Visual Studio Code
中开发和调试。 我们用python
的venv
创建虚拟环境, 详见:
在Visual Studio Code中配置venv。 -
Ollama
在Ollama
平台上部署本地大模型非常方便,基于此平台,我们可以让langchain
使用llama3.1
、qwen2.5
、deepseek
等各种本地大模型。详见:
在langchian中使用本地部署的llama3.1大模型 。
定义 langgraph
步骤/节点
用langgraph实现基于SQL数据构建的问答系统(4) 中对这部分工作有详细的阐述,这里仅贴出主要代码,以使得本文内容比较连贯:
"""
1. 创建SQLite对象
"""from langchain_community.utilities import SQLDatabasedb = SQLDatabase.from_uri(f"sqlite:///{db_file_path}")"""
2. 状态
"""from typing_extensions import TypedDictclass State(TypedDict):question: strquery: strresult: stranswer: strfrom langchain_ollama import ChatOllama
llm = ChatOllama(model="llama3.1",temperature=0, verbose=True)def set_llm(llm_model_name):"""设置大模型,用于测试不同大模型"""global llm llm = ChatOllama(model=llm_model_name,temperature=0, verbose=True)"""
3. 定义langgraph节点
"""from typing_extensions import Annotatedclass QueryOutput(TypedDict):"""生成的SQL查询语句"""query: Annotated[str, ..., "Syntactically valid SQL query."]# 提示词system = """You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct {dialect} 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}
""".format(table_names=db.get_usable_table_names(),dialect=db.dialect
)from langchain_core.prompts import ChatPromptTemplate
query_prompt_template = ChatPromptTemplate.from_messages([("system", system),("user", "Question:{input}")
])def write_query(state: State):"""根据问题生成SQL查询语句"""prompt = query_prompt_template.invoke({"input": state["question"],})structured_llm = llm.with_structured_output(QueryOutput)result = structured_llm.invoke(prompt)print(f'Query is:\n{result["query"]}')return {"query": result["query"]}from langchain_community.tools.sql_database.tool import QuerySQLDatabaseTooldef execute_query(state: State):"""执行SQL查询"""execute_query_tool = QuerySQLDatabaseTool(db=db)result = execute_query_tool.invoke(state["query"])print(f'Result is:\n{result}')return {"result": result}def generate_answer(state: State):"""使用检索到的信息作为上下文来回答问题。"""prompt = ("Given the following user question, corresponding SQL query, ""and SQL result, answer the user question.\n\n"f'Question: {state["question"]}\n'f'SQL Query: {state["query"]}\n'f'SQL Result: {state["result"]}')response = llm.invoke(prompt)print(f'answer is:\n{response.content}')return {"answer": response.content}
增加人工审核节点
LangGraph
可以在敏感步骤(例如执行 SQL 查询)之前中断应用程序以供人工审核。这是由 LangGraph
的持久层启用的,它将运行进度保存到存储中。
使用内存存储
from langgraph.graph import START, StateGraphgraph_builder = StateGraph(State).add_sequence([write_query, execute_query, generate_answer]
)
graph_builder.add_edge(START, "write_query")
# graph = graph_builder.compile()from langgraph.checkpoint.memory import MemorySavermemory = MemorySaver()
graph_with_human = graph_builder.compile(checkpointer=memory, interrupt_before=["execute_query"])
上述代码在 execute_query
执行前中断了流程,以使得人可以进行人工审核。
MemorySaver
将链的执行过程存储在内存中,它实际上也记录了聊天历史,使得链具有记忆功能,拥有聊天的上下文信息,可以与用户进行多轮连续对话。
在问答中增加人工审核
下面我们定义问答方法:
def ask_with_human(question,thread_id):"""问答:增加了人工审核"""config = {"configurable": {"thread_id": thread_id}}for step in graph_with_human.stream({"question": question},config,stream_mode="updates",):print(step)try:user_approval = input("您确定要执行查询么?(yes/no): ")except Exception:user_approval = "no"if user_approval.lower() == "yes":# 如果获得批准,再继续执行for step in graph_with_human.stream(None, config, stream_mode="updates"):print(step)else:print("操作已被取消。")
上面的代码中增加了人工审核逻辑。
定义测试方法
为方便对多款大模型进行对比测试,我们定义一个简单的测试方法,其中定义了两个问题:
def test_model(llm_model_name):"""测试大模型"""print(f'============{llm_model_name}==========')set_llm(llm_model_name)thread_id = "liu23"questions = ["How many Employees are there?","Which country's customers spent the most?",]for question in questions:ask_with_human( question,thread_id)
见证效果
qwen2.5
和 llama3.1
处理这些逻辑都没有问题,我们用 qwen2.5
执行第1个问题,了解一下执行过程:
- 同意/yes
{'write_query': {'query': 'SELECT COUNT(*) AS EmployeeCount FROM Employee'}}
{'__interrupt__': ()}
您确定要执行查询么?(yes/no): yes
{'execute_query': {'result': '[(8,)]'}}
{'generate_answer': {'answer': 'Based on the SQL result provided, there are 8 employees in total. The result `(8,)` indicates that the count of employees is 8.'}}
- 不同意/no
{'write_query': {'query': 'SELECT COUNT(*) AS EmployeeCount FROM Employee'}}
{'__interrupt__': ()}
您确定要执行查询么?(yes/no): no
操作已被取消。
总结
langgraph
可以在不修改步骤/节点逻辑的情况下增加人工审批环节,nice。
代码
本文涉及的所有代码以及相关资源都已经共享,参见:
- github
- gitee
为便于找到代码,程序文件名称最前面的编号与本系列文章的文档编号相同。
参考
- Build a Question/Answering system over SQL data
🪐感谢您观看,祝好运🪐
相关文章:

本地大模型编程实战(26)用langgraph实现基于SQL数据构建的问答系统(5)
本文将将扩展上一篇文章完成的 langgraph 链,继续使用基于 langgraph 链 ,对结构化数据库 SQlite 进行查询的方法。该系统建立以后,我们不需要掌握专业的 SQL 技能,可以用自然语言询问有关数据库中数据的问题并返回答案。主要完善…...
数据结构与算法:滑动窗口
前言 滑动窗口一般主要用于解决子数组或子串问题,这类的题目更看重对题目的分析和转化。 一、原理 在整个数组上,用l和r分别控制窗口的左右边界,r就扩大,l就减小。 当窗口的范围和题目中某个指标间存在单调关系时,…...

江协科技/江科大-51单片机入门教程——P[2-1] 点亮一个LED
本节将向大家介绍如何用 51 单片机去控制开发板上的 LED。开发板上的 LED 位置标注有 “LED 模块”。 第二章要写 3 个程序代码:第一个代码实现点亮开发板上的第一个 LED;第二个代码让第一个 LED 以 1 秒为周期闪烁;第三个代码使 8 个 LED 以…...
leetcode hot 100 41. 缺失的第一个正数
代码 测试用例 测试用例 测试结果 41. 缺失的第一个正数 已解答 困难 相关标签 相关企业 提示 给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1…...
UniApp 使用 u-loadmore 完整步骤
文章目录 一、前期准备1. 安装 uView - UI 二、使用 u-loadmore组件1. 创建页面2. 编写页面代码模板部分(loadmore-demo.vue)样式部分脚本部分 三、要点补充1. u-loadmore 状态说明2. 数据请求优化3. 性能优化4. 兼容性问题 在 UniApp 开发中,…...

设置电脑一接通电源就主动开机
文章目录 1、进入BIOS2、设置4、功能弊端5、电脑自动开机的设置 1、进入BIOS 在电脑重启时,这时屏幕上会显示按XXX键到BIOS界面 没有进入BIOS提示的,按下面方法操作: 方法一 在开机显示logo的时候,立即按下面这几个按键…...

优艾智合机器人日本子公司成立,加速推进国际化布局
2月27日,工业移动机器人解决方案商优艾智合宣布日本子公司Youibot Robotics Japan株式会社(以下简称“Youibot Japan”)成立,并于东京举行开业典礼。此举标志着优艾智合在日本市场的现地服务能力进一步深化,是其全球化…...
自然语言处理NLP入门 -- 第七节预训练语言模型
1 什么是预训练模型? 在自然语言处理(NLP)里,训练一个好模型通常需要很多数据和计算资源。为了解决这个难题,就出现了“预训练模型”。 预训练模型 是指我们先在海量文本(比如网络上爬到的大量文章、对话…...

Git GitHub基础
git是什么? Git是一个分布式版本控制系统,用于管理源代码的变更。它允许多个开发者在同一个项目上协作,同时跟踪每个修改的历史记录。 关键词: 分布式版本控制软件 软件 安装到我们电脑上的一个工具 版本控制 例如论文&…...
多平台文章同步工具PostSync 安装介绍
PostSync 是一个开源的用于多平台文章同步的工具 环境安装 安装 Python:PostSync 是基于 Python 开发的,你需要确保系统中已经安装了 Python 环境,建议使用 Python 3.7 及以上版本。你可以从 Python 官方网站 下载并安装适合你操作系统的版…...

PXE批量网络装机与Kickstart自动化安装工具
目录 一、系统装机的原理 1.1、系统装机方式 1.2、系统安装过程 二、PXE批量网络装机 2.1、PXE实现原理 2.2、搭建PXE实际案例 2.2.1、安装必要软件 2.2.2、搭建DHCP服务器 2.2.3、搭建TFTP服务器 2.2.4、挂载镜像并拷贝引导文件到tftp服务启动引导文件夹下 2.2.5、编…...

css的复合选择器
1.1什么是复合选择器 在css中,选择器分为基础选择器和复合选择器,复合选择器是建立在基础选择器之上,对基本选择器进行组合形成。 复合选择器可以更准确、更高效的选择目标元素(标签)由两个或多个基础选择器,通过不同的方式组合…...

Wireshark Lua 插件教程
本⽂主要介绍 Lua 脚本在 Wireshark 中的应⽤, Lua 脚本可以在 Wireshark 中完成如下功能: 从⽹络包中提取数据, 或者统计⼀些数据包(Dumper) 需要解析⼀种 Wireshark 不提供原⽣⽀持的协议(Dissector) ⽰例 协议解析 VREP 协议是 NOGD 框架对于 TRIP 协议的⼀种延伸和扩展…...
mysql怎样优化where like ‘%字符串%‘这种模糊匹配的慢sql
一 问题描述 工作中经常遇到这种模糊匹配的慢sql: select * from 表名 where 字段 like %字符串%; 由于前面有%,导致无法走该字段上的索引。 二 解决办法 ① 给该字段创建一个全文索引 CREATE FULLTEXT INDEX 索引名 ON 表名 (字段名); ② 改写sq…...
Python代码片段-断点任务
使用Python处理一堆长耗时任务的时候,为了防止异常退出程序或者手动退出程序后丢失任务进度,可用使用断点的方式记录任务进度,下次重载任务后,继续运行上次未完成的任务即可。 这里用json文件作为数据持久化的方式,免…...

mapbox基础,使用geojson加载heatmap热力图层
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言1.1 ☘️mapboxgl.Map 地图对象1.2 ☘️mapboxgl.Map style属性1.3 ☘️heatmap热力图层样式二、🍀使用geojs…...

03.检测 Zabbix agent
TOC 利用 zabbix_get 工具测试 Zabbix Agent 是否正常 # 安装 zabbix-get [rootUbuntu2204 ~]#apt install -y zabbix-get# 使用zabbix_get 工具查看验证 agent 是否正常 返回1表示正常 [rootUbuntu2204 ~]#zabbix_get -s 10.0.0.110 -p 10050 -k "agent.ping"故障…...
Vue 3 + Vite 项目配置访问地址到服务器某个文件夹的解决方案
前言 在开发 Vue 3 Vite 项目时,我们经常需要将项目部署到服务器的某个特定文件夹下。例如,将项目部署到 /my-folder/ 目录下,而不是服务器的根目录。这时,我们需要对 Vite 和 Vue Router 进行一些配置,以确保项目能…...

JavaScript将:;隔开的字符串转换为json格式。使用正则表达式匹配键值对,并构建对象。多用于解析cssText为style Object对象
// 使用正则表达式匹配键值对,并构建对象 let string2Json(s)>{const r {};s.replace(/;/g, ;).replace(/\;/g, \n).replace(/:/g, :).replace(/\n/g, \n)//合并多个换行符.split(\n).forEach(item > {const [k, v] item.split(:);(k…...
MT-Metrics
MT-Metrics 是一类用于评估生成文本质量的指标,最初用于机器翻译任务,后来扩展到生成任务(如对话生成、文本摘要等)。它的核心思想是通过比较生成文本与参考文本之间的相似性(如词汇重叠、句法结构、语义相似性&#x…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

dedecms 织梦自定义表单留言增加ajax验证码功能
增加ajax功能模块,用户不点击提交按钮,只要输入框失去焦点,就会提前提示验证码是否正确。 一,模板上增加验证码 <input name"vdcode"id"vdcode" placeholder"请输入验证码" type"text&quo…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...