本地大模型编程实战(26)用langgraph实现基于SQL数据构建的问答系统(5)

本文将将扩展上一篇文章完成的 langgraph 链,继续使用基于 langgraph 链 ,对结构化数据库 SQlite 进行查询的方法。该系统建立以后,我们不需要掌握专业的 SQL 技能,可以用自然语言询问有关数据库中数据的问题并返回答案。主要完善一下两点内容:

  • 自动记录消息历史
  • 增加人工审核环节,防止 LLM(大语言模型) 运行危险的SQL语句

我们先看看完成的 langgraph 链的模样,主要有两步:创建SQL查询语句->执行SQL查询语句,在执行SQL查询前中断进行人工审核,上一篇文章的 链 没有人工审核:

本次实现的链

使用 qwen2.5llama3.1 做实验。

请注意:
构建 SQL 数据库的问答系统需要执行模型生成的 SQL 查询。这样做存在风险,请确保您的数据库连接权限始终尽可能小,这将减轻(但不能消除)构建模型驱动系统的风险。

文章目录

    • 准备开发环境
    • 定义 `langgraph` 步骤/节点
    • 增加人工审核节点
      • 使用内存存储
      • 在问答中增加人工审核
      • 定义测试方法
      • 见证效果
    • 总结
    • 代码
    • 参考

准备开发环境

在正式开始撸代码之前,需要准备一下编程环境。

  1. 计算机
    本文涉及的所有代码可以在没有显存的环境中执行。 我使用的机器配置为:

    • CPU: Intel i5-8400 2.80GHz
    • 内存: 16GB
  2. Visual Studio Code 和 venv
    这是很受欢迎的开发工具,相关文章的代码可以在 Visual Studio Code 中开发和调试。 我们用 pythonvenv 创建虚拟环境, 详见:
    在Visual Studio Code中配置venv。

  3. Ollama
    Ollama 平台上部署本地大模型非常方便,基于此平台,我们可以让 langchain 使用 llama3.1qwen2.5deepseek 等各种本地大模型。详见:
    在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.5llama3.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

🪐感谢您观看,祝好运🪐

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

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

相关文章

【Kubernetes】污点和容忍

一、概述 在 Kubernetes(k8s)中,污点(Taints) 是定义在节点上的一种机制,用于拒绝某些 Pod 调度到该节点,除非这些 Pod 具有对应的容忍度(Tolerations)。污点可以用来控…

【大模型➕知识图谱】大模型结合医疗知识图谱:解锁智能辅助诊疗系统新范式

【大模型➕知识图谱】大模型结合医疗知识图谱:解锁智能辅助诊疗系统新范式 大模型结合医疗知识图谱:解锁智能辅助诊疗系统新范式引言一、系统架构1.1 系统架构图1.2 架构模块说明1.2.1 用户输入1.2.2 大模型(语义理解与意图识别)1.2.3 Agent(问题解析与任务分配)1.2.4 问…

FASIONAD:自适应反馈的类人自动驾驶中快速和慢速思维融合系统

24年11月来自清华、早稻田大学、明尼苏达大学、多伦多大学、厦门大学马来西亚分校、电子科大(成都)、智平方科技和河南润泰数字科技的论文“FASIONAD : FAst and Slow FusION Thinking Systems for Human-Like Autonomous Driving with Adaptive Feedbac…

【免费】YOLO[笑容]目标检测全过程(yolo环境配置+labelimg数据集标注+目标检测训练测试)

一、yolo环境配置 这篇帖子是我试过的,非常全,很详细【cudaanacondapytorchyolo(ultralytics)】 yolo环境配置 二、labelimg数据集标注 可以参考下面的帖子,不过可能会出现闪退的问题,安装我的流程来吧 2.1 labelimg安装 label…

Linux系统软件管理

systemctl 控制软件启动和关闭 Linux系统很多软件支持使用systemctl命令控制:启动,停止,开启自启。 能被systemctl管理的软件,一般被称为:服务。 语法:systemctl start|stop|status|enable|disable 服务名…

CAN总线通信协议学习1——物理层

首先来看看CAN是怎么产生的:简单理解,CAN就是一种“拥有特别连接方式”的数据传输的总线,其有特定的一些规则。 (注:资料及图片来源于知乎博主TOMOCAT。) CAN总线的结构 查阅参考文献,OSI标准…

偏移量是什么

在将二维网格映射到一维数组时,偏移量是指在一维数组中 某一行的第一个元素相对于数组起始位置的位置差。对于一个 3 行 4 列的网格,我们使用公式 cur_pos x * n y 来计算二维位置 (x, y) 在一维数组中的索引。 当 x 0 (第一行&#xff…

【Mac电脑本地部署Deepseek-r1:详细教程与Openwebui配置指南】

文章目录 前言电脑配置:安装的Deepseek版本:使用的UI框架:体验效果展示:本地部署体验总结 部署过程Ollama部署拉取模型运行模型Openwebui部署运行Ollama服务在Openwebui中配置ollama的服务 后话 前言 deepseek最近火的一塌糊涂&a…

给小白的oracle优化工具,了解一下

有时懒得分析或语句太长,可以尝试用oracle的dbms_sqldiag包进行sql优化, --How To Use DBMS_SQLDIAG To Diagnose Query Performance Issues (Doc ID 1386802.1) --诊断SQL 性能 SET ECHO ON SET LINESIZE 132 SET PAGESIZE 999 SET LONG 999999 SET SER…

YOLO11改进加入ResNet网络

文章目录 1.改进目的2.demo引入2.1代码2.2 结果展示2.3 BottleNeck详解 1.改进目的 原始YOLO11模型训练好以后,检测结果mAP结果很低,视频检测结果很差,于是想到改进网络,这里介绍改进主干网络。 2.demo引入 2.1代码 # File: 2…

Spring MVC流程

SpringMVC启动流程 启动流程父子容器请求处理MultipartFile 解析参数传递返回值处理HandlerInterceptor 启动流程 启动Tomcat解析web.xml创建DispatcherServlet调用DIspatcherServlet的init方法 4.1 创建Spring容器 4.2 发布ContextRefresheEvent 4.3 在OnRefreshed方法中触发…

【大数据】ClickHouse常见的错误及解决方式

ClickHouse 是一款高性能的列式数据库,但在使用过程中难免会遇到一些错误。本文将介绍一些 ClickHouse 常见的错误及其解决方式,帮助您更好地使用 ClickHouse。 1、错误:DB::Exception 错误信息 DB::Exception:Table engine Distributed d…

物理竞赛中的线性代数

线性代数 1 行列式 1.1 n n n 阶行列式 定义 1.1.1:称以下的式子为一个 n n n 阶行列式: ∣ A ∣ ∣ a 11 a 12 ⋯ a 1 n a 21 a 22 ⋯ a 2 n ⋮ ⋮ ⋱ ⋮ a n 1 a n 2 ⋯ a n n ∣ \begin{vmatrix}\mathbf A\end{vmatrix} \begin{vmatrix} a_{11…

IP-----动态路由OSPF

这只是IP的其中一块内容,IP还有更多内容可以查看IP专栏,前一章内容为GRE和MGRE ,可通过以下路径查看IP-------GRE和MGRE-CSDN博客,欢迎指正 注意!!!本部分内容较多所以分成了两部分在下一章 5.动态路由OS…

数字内容体验未来趋势:交互升级与用户深耕

智能技术重塑内容交互 随着数字内容体验进入深度智能化阶段,AI驱动的内容生成与智能推荐算法正在重构用户与信息的交互范式。基于自然语言处理技术的内容创作工具,已实现从文本自动生成到多模态内容适配的跨越,企业能够以分钟级速度产出符合…

2025年2月21日优雅草内测分发站全新升级-测试运营-优雅草内测分发站新用户提供免费100下载点-2月28日正式运营并且提供私有化部署版本

2025年2月21日优雅草内测分发站全新升级-测试运营-优雅草内测分发站新用户提供免费100下载点-2月28日正式运营并且提供私有化部署版本 说明 优雅草内测分发站新用户提供免费100下载点,优雅草分运营站和demo测试站 运营站:www.youyacao.cn 提供免费100…

动态内存池设计与环形缓冲区实现详解

一、动态内存池设计 在嵌入式系统中,频繁使用 malloc 和 free 会导致内存碎片和性能问题。动态内存池通过预分配固定大小的内存块,并统一管理分配与释放,显著提高内存使用效率和实时性。 1. 核心设计思路 预分配内存:将内存划分…

015--基于STM32F103ZET6的智能风扇设计

1.实物视频演示 智能风扇演示视频 2.程序代码讲解 STM32F103ZET6智能风扇_哔哩哔哩_bilibili 3源代码获取 https://download.csdn.net/download/weixin_41011452/90440545

【洛谷贪心算法】P1106删数问题

这道题可以使用贪心算法来解决,核心思路是尽量让高位的数字尽可能小。当我们逐步删除数字时,会优先删除高位中相对较大的数字。具体做法是从左到右遍历数字序列,当发现当前数字比它后面的数字大时,就删除当前数字,直到…

开源PDF解析工具olmOCR

olmOCR 是由 Allen Institute for Artificial Intelligence (AI2) 的 AllenNLP 团队开发的一款开源工具,旨在将PDF文件和其他文档高效地转换为纯文本,同时保留自然的阅读顺序。它支持表格、公式、手写内容等。 olmOCR 经过学术论文、技术文档和其他文档…