纯代码实战--用Deepseek+SQLite+Ollama搭建数据库助手

news2025/5/18 16:30:03

如何用Python调用本地模型实现DeepSeek提示词模板:一步步教你高效解决13种应用场景
从零到一:纯代码联合PyQt5、Ollama、Deepseek打造简易版智能聊天助手
用外接知识库武装大模型:基于Deepseek、Ollama、LangChain的RAG实战解析
纯代码实战–用Deepseek+SQLite+Ollama搭建数据库助手

概述

本博客手把手教学通过Python调用Deepseek大模型快速搭建智能薪酬问答系统,实现“说人话查数据”。通过整合Ollama大模型与SQLite数据库,开发了一个能理解自然语言、自动生成SQL、安全执行查询并给出人性化解读的AI助手。

文章详解三大核心模块——智能SQL生成器、安全查询引擎、结果解释器,讲解数据流转逻辑,给出大模型连接结构化数据的全链路开发示例。

提示1:完整代码看最后,SQLite示例数据准备后复制代码可直接运行。 提示2:本文默认已完成本地Deepseek部署,若未完成见作者另外一篇博客 https://blog.csdn.net/qq_36112576/article/details/145510585?spm=1001.2014.3001.5501

SQLite示例数据准备

复制以下代码运行,生成数据库example.db,其中表名employees;属性名id,name, position, salary。

import sqlite3

# 连接到 SQLite 数据库(如果数据库文件不存在,会自动创建一个新的数据库)
conn = sqlite3.connect("example.db")

# 创建一个游标对象,通过游标来执行 SQL 语句
cursor = conn.cursor()

# 1. 创建一个示例表(如果表不存在)
cursor.execute('''
CREATE TABLE IF NOT EXISTS employees (
    id INTEGER PRIMARY KEY,
    name TEXT NOT NULL,
    position TEXT,
    salary REAL
)
''')

# 2. 插入一些示例数据(如果表为空)
cursor.execute("INSERT INTO employees (id,name, position, salary) VALUES (?,?, ?, ?)", (12100,'Alice', 'Manager', 80000.0))
cursor.execute("INSERT INTO employees (id,name, position, salary) VALUES (?,?, ?, ?)", (12566,'Bob', 'Developer', 60000.0))
cursor.execute("INSERT INTO employees (id,name, position, salary) VALUES (?,?, ?, ?)", (10465,'Charlie', 'Designer', 50000.0))

# 提交事务,保存数据
conn.commit()

# 3. 查询数据并打印出来
cursor.execute("SELECT * FROM employees")
rows = cursor.fetchall()

print("查询结果:")
for row in rows:
    print(f"ID: {row[0]}, Name: {row[1]}, Position: {row[2]}, Salary: {row[3]}")

# 4. 根据条件查询数据,例如查询所有薪水高于 60000 的员工
cursor.execute("SELECT * FROM employees WHERE salary > ?", (60000,))
rows = cursor.fetchall()

print("\n薪水大于 60000 的员工:")
for row in rows:
    print(f"ID: {row[0]}, Name: {row[1]}, Position: {row[2]}, Salary: {row[3]}")

# 5. 根据员工姓名查询某个员工的信息
cursor.execute("SELECT * FROM employees WHERE name = ?", ('Bob',))
row = cursor.fetchone()

if row:
    print(f"\n查询到的员工信息: ID: {row[0]}, Name: {row[1]}, Position: {row[2]}, Salary: {row[3]}")
else:
    print("\n未找到该员工信息。")

# 6. 关闭游标和连接
cursor.close()
conn.close()

根据“cursor.execute("INSERT INTO employees ****”自己修改数据库,以下实验使用数据库实例:
在这里插入图片描述

整体流程

数据流转7步:

(1)用户->>+系统: 输入自然语言问题

question = "工资高于60000的人有哪些"

(2)系统->>+Ollama: 发送生成SQL请求

generate_sql_stream(question):

(3)Ollama–>>-系统: 流式返回SQL

elif status == 'complete':
     sql = content
     print(f"\n\n✅ 生成 SQL 成功:\n{sql}")

(4)系统->>+SQLite: 执行安全查询

results = execute_sql_query(sql)

(5)SQLite–>>-系统: 返回数据集

print(f"找到 {len(results)} 条相关记录")

(6)系统->>+Ollama: 发送结果解释请求

generate_answer_stream(question, results)

(7)Ollama–>>-用户: 流式输出自然语言结果

print(chunk, end='', flush=True)
answer_buffer.append(chunk)

执行显示示例

🚀 正在分析问题...
SELECT name, position, salary FROM employees WHERE salary > 60000 
✅ 生成 SQL 成功

🔍 正在查询数据库...
找到 8 条相关记录

📝 正在生成回答:
根据系统查询,薪资超过6万元的员工共有8人,其中:
- 技术总监张三的薪资为85000元
- 首席架构师李四...

核心代码讲解

1、SQL生成引擎(核心安全设计)

def generate_sql_stream(question: str):
    ...
    # 关键prompt设计
    prompt = f"""
    您是人资系统专用SQL助手,请根据员工表结构生成查询:
    表结构:id(工号), name(姓名), position(职位), salary(薪资)
    要求:仅返回SELECT语句,包含安全过滤条件
    问题:{question}"""

    # 流式生成控制
        full_response = []
        for response in ollama.chat(
                model=MODEL_NAME,
                messages=messages,
                stream=True,
                options={'temperature': 0.2}
        ):
            chunk = response['message']['content']
            full_response.append(chunk)
            yield ('loading', chunk)
        
    # 安全过滤(双重校验)
    final_sql = final_sql.replace('```sql','').strip()  # 去除代码标识
    if not final_sql.lower().startswith('select'):
        raise BlockedOperationError
    if not any(op in final_sql_lower for op in [' where ', ' like ', ' = ']):
        raise QueryGenerationError("缺少必要过滤条件")

设计:

  • Prompt工程限定生成范围;

  • 流式输出降低等待焦虑;

  • 正则清洗+语法校验双保险。

在这里插入图片描述

完整代码中涉及到大量的sql返回语句格式修正,如下,主要是Deepseek返回结果为final_sql ,它包括完整的think过程,并不是直接可用的SQL,若SQL语句生成失败,对这部分内容进行debug。

        final_sql = ''.join(full_response).strip()
        sql_start = final_sql.find("</think>") + 9
        sql_end = final_sql.find(";", sql_start) + 1
        final_sqlout = final_sql[sql_start:sql_end].strip()

        # 安全验证
        if SAFETY_CHECK:
            final_sql_lower = final_sqlout.lower()
            # 去除可能存在的代码标识符(例如```sql)
            final_sql_lower = final_sql_lower.replace('```sql', '').replace('```', '').strip()

2、数据库查询执行器(安全防线)

def execute_sql_query(sql: str):
    # 危险操作黑名单
    blocked_keywords = ['insert', 'update', 'delete', 'drop']
    if any(kw in sql.lower() for kw in blocked_keywords):
        raise SecurityAlert("危险操作拦截")
    
    # 上下文管理器保安全
    with sqlite3.connect(DATABASE_PATH) as conn:
        return conn.execute(sql).fetchall()

设计:

  • SQL注入关键词过滤,防止增删改等危险;

  • 自动关闭数据库连接。

在这里插入图片描述

3、回答生成器

根据检索内容回答问题,示例结果如下:
在这里插入图片描述

def generate_answer_stream(question, data):
    # 数据结构化预处理
    context = "查询结果:\n" + "\n".join(
        f"{name} | {position} | ¥{salary:,}"
        for _, name, position, salary in data
    )
    
    # 生成提示词模板
    messages = [
        {"role": "system", "content": "您是人资专员,请将数据转换为自然语言回答"},
        {"role": "user", "content": f"问题:{question}\n{context}"}
    ]
    
    # 流式生成回答
    for chunk in ollama.chat(...):
        yield chunk

这里容易出现未预期错误:tuple index out of range,这是由于第一步SQL生成的查询语句并不固定,以下都有可能,导致context内容不固定,需要通过提示词进一步规划SQL语句生成,暂时未优化,读者修改后可以评论区把优化语句打出来。

select name, id, position from employees where salary > 60000;
select name, salary from employees where salary > 60000;
select name, id, position,salary from employees where salary > 60000;

完整代码

注意:需要先准备好数据库example.db,见前文SQLite示例数据准备。

import sqlite3
import ollama
from typing import List, Tuple, Optional
from time import sleep

# 配置参数
MODEL_NAME = 'deepseek-r1:7b'  # 本地运行的 Ollama 模型名称
DATABASE_PATH = 'example.db'  # 数据库路径
SAFETY_CHECK = True  # 是否启用安全校验


class QueryGenerationError(Exception):
    """SQL 生成异常"""
    pass


class DatabaseError(Exception):
    """数据库操作异常"""
    pass


def generate_sql_stream(question: str) -> str:
    """
    流式生成 SQL 查询语句(带安全验证)
    返回格式: (status, content)
    status: loading|complete|error
    """
    prompt = f"""您是一个专业的人力薪酬数据库查询助手。根据用户问题生成安全且准确的 SQL 查询。

数据库表结构:
- 表名:employees 
- 字段:
列名: id, 类型: INTEGER
列名: name, 类型: TEXT
列名: position, 类型: TEXT
列名: salary, 类型: REAL

当前问题:{question}

生成要求:
1. 仅限 SELECT 查询
2. 输出纯 SQL 不带 Markdown"""

    messages = [
        {'role': 'system', 'content': '您是一个 SQL 生成专家,只返回有效的 SELECT 语句'},
        {'role': 'user', 'content': prompt}
    ]

    try:
        full_response = []
        for response in ollama.chat(
                model=MODEL_NAME,
                messages=messages,
                stream=True,
                options={'temperature': 0.2}
        ):
            chunk = response['message']['content']
            full_response.append(chunk)
            yield ('loading', chunk)

        final_sql = ''.join(full_response).strip()
        sql_start = final_sql.find("</think>") + 9
        sql_end = final_sql.find(";", sql_start) + 1
        final_sqlout = final_sql[sql_start:sql_end].strip()


        # 安全验证
        if SAFETY_CHECK:
            final_sql_lower = final_sqlout.lower()
            # 去除可能存在的代码标识符(例如```sql)
            final_sql_lower = final_sql_lower.replace('```sql', '').replace('```', '').strip()
            #print(final_sql_lower)
            if not final_sql_lower.startswith('select'):
                raise QueryGenerationError("禁止非 SELECT 操作")
            # if ';' in final_sql:
            #     raise QueryGenerationError("检测到多语句查询")
            # if not any(op in final_sql_lower for op in [' where ', ' like ', ' = ']):
            #     raise QueryGenerationError("缺少必要过滤条件")

        yield ('complete', final_sql_lower)

    except Exception as e:
        yield ('error', f"SQL 生成失败: {str(e)}")


def execute_sql_query(sql: str) -> List[Tuple]:
    """执行 SQL 查询并返回结果"""
    try:
        conn = sqlite3.connect(DATABASE_PATH)
        cursor = conn.cursor()

        # 预验证
        sql_lower = sql.lower()
        if any(keyword in sql_lower for keyword in ['insert', 'update', 'delete', 'drop']):
            raise DatabaseError("危险操作被拦截")

        cursor.execute(sql)
        results = cursor.fetchall()

        return results

    except sqlite3.Error as e:
        raise DatabaseError(f"数据库错误: {str(e)}")
    finally:
        if 'conn' in locals():
            conn.close()


def generate_answer_stream(question: str, data: List[Tuple]) -> str:
    """流式生成自然语言回答"""
    if not data:
        yield "未找到相关记录"
        return

    context = "name:\n" + '\n'.join(
        [f"- name:{row[1]}(salary:{row[3]})" for row in data]
    )

    messages = [
        {'role': 'system', 'content': '您是一个薪酬系统管理助理,用正式中文回答客户问题'},
        {'role': 'user', 'content': f"问题:{question}\n\n{context}"}
    ]

    try:
        buffer = []
        for response in ollama.chat(
                model=MODEL_NAME,
                messages=messages,
                stream=True,
                options={'temperature': 0.7}
        ):
            content = response['message']['content']
            buffer.append(content)
            yield content

        # 后处理验证
        final_answer = ''.join(buffer)
        if len(final_answer) < 20:
            raise RuntimeError("回答过短,可能生成失败")

    except Exception as e:
        yield f"\n回答生成异常:{str(e)}"


def main():
    try:
        # 用户问题
        question = "工资高于60000的人有哪些"

        # 阶段1:生成 SQL
        print("🚀 正在分析问题...")
        sql = None
        for status, content in generate_sql_stream(question):
            if status == 'loading':
                print(content, end='', flush=True)
                sleep(0.02)  # 模拟流式效果
            elif status == 'complete':
                sql = content
                print(f"\n\n✅ 生成 SQL 成功:\n{sql}")
            elif status == 'error':
                raise QueryGenerationError(content)

        # 阶段2:执行查询
        print("\n🔍 正在查询数据库...")
        results = execute_sql_query(sql)
        print(f"找到 {len(results)} 条相关记录")

        # 阶段3:生成回答
        print("\n📝 正在生成回答:")
        answer_buffer = []
        for chunk in generate_answer_stream(question, results):
            print(chunk, end='', flush=True)
            answer_buffer.append(chunk)

        # 保存完整回答
        full_answer = ''.join(answer_buffer)
        print(f"\n\n💡 完整回答已生成,可保存至日志文件")

    except QueryGenerationError as e:
        print(f"\n❌ SQL 生成错误:{str(e)}")
    except DatabaseError as e:
        print(f"\n❌ 数据库错误:{str(e)}")
    except Exception as e:
        print(f"\n❌ 未预期错误:{str(e)}")


if __name__ == "__main__":
    main()

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

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

相关文章

2025 最新版鸿蒙 HarmonyOS 开发工具安装使用指南

为保证 DevEco Studio 正常运行&#xff0c;建议电脑配置满足如下要求&#xff1a; Windows 系统 操作系统&#xff1a;Windows10 64 位、Windows11 64 位内存&#xff1a;16GB 及以上硬盘&#xff1a;100GB 及以上分辨率&#xff1a;1280*800 像素及以上 macOS 系统 操作系统…

AI数字人开发,引领科技新潮流

引言 随着人工智能技术的迅猛发展&#xff0c;AI 数字人在影视娱乐、客户服务、教育及医疗等多个领域展现出巨大的潜力。本文旨在为开发者提供一份详细的 AI 数字人系统开发指南&#xff0c;涵盖从基础架构到实现细节的各个方面&#xff0c;包括人物建模、动作生成、语音交互、…

领域驱动设计:事件溯源架构简介

概述 事件溯源架构通常由3种应用设计模式组成,分别是:事件驱动(Event Driven),事件溯源(Event Source)、CQRS(读写分离)。这三种应用设计模式常见于领域驱动设计(DDD)中,但它们本身是一种应用设计的思想,不仅仅局限于DDD,每一种模式都可以单独拿出来使用。 E…

STM32之影子寄存器

预分频寄存器计数到一半的时候&#xff0c;改变预分频值&#xff0c;此时不会立即生效&#xff0c;会等到计数完成&#xff0c;再从影子寄存器即预分频缓冲器里装载修改的预分频值。 如上图&#xff0c;第一行是内部时钟72M&#xff0c;第二行是时钟使能&#xff0c;高电平启动…

x64汇编下过程参数解析

简介 好久没上博客, 突然发现我的粉丝数变2700了, 真是这几个月涨的粉比我之前好几年的都多, 于是心血来潮来写一篇, 记录一下x64下的调用约定(这里的调用约定只针对windows平台) Windows下的x64程序的调用约定有别于x86下的"stdcall调用约定"以及"cdecl调用约…

Blender调整最佳渲染清晰度

1.渲染采样调高 512 2.根据需要 开启AO ,开启辉光 , 开启 屏幕空间反射 3.调高分辨率 4096x4096 100% 分辨率是清晰度的关键 , 分辨率不高 , 你其他参数调再高都没用 4.世界环境开启体积散射 , 可以增强氛围感 5.三点打光法 放在模型和相机45夹角上 白模 白模带线条 成品

TSMaster【第二十篇:华山论剑——知识图谱全览】

(三维思维导图「独孤九剑总诀式」技能树「经脉贯通」检测系统未来技术「武学秘境」预测) 【武侠场景导入】光明顶秘道惊变 明教光明顶密道中,张无忌面对错综复杂的甬道体系,以乾坤大挪移心法贯通九阳神功与太极拳剑,终成武林至尊。今时今日,三电工程师面对庞杂的TSMaste…

神经性手抖是一种常见的症状

神经性手抖是一种常见的症状&#xff0c;表现为手部无意识或不受控制地颤抖。为了预防神经性手抖&#xff0c;我们可以采取以下几种方法&#xff1a; 1. 放松身心&#xff1a;压力和焦虑是导致神经性手抖的常见原因之一。因此&#xff0c;学会放松身心是预防手抖的关键。可以通…

金融支付行业技术侧重点

1. 合规问题 第三方支付系统的平稳运营&#xff0c;严格遵循《非银行支付机构监督管理条例》的各项条款是基础与前提&#xff0c;其中第十八条的规定堪称重中之重&#xff0c;是支付机构必须牢牢把握的关键准则。 第十八条明确指出&#xff0c;非银行支付机构需构建起必要且独…

支付宝 IoT 设备入门宝典(下)设备经营篇

上篇介绍了支付宝 IoT 设备管理&#xff0c;但除了这些基础功能外&#xff0c;商户还可以利用设备进行一些运营动作&#xff0c;让设备更好的帮助自己&#xff0c;本篇就会以设备经营为中心&#xff0c;介绍常见的设备相关能力和问题解决方案。如果对上篇感兴趣&#xff0c;可以…

mac电脑中使用无线诊断.app查看连接的Wi-Fi带宽

问题 需要检查连接到的Wi-Fi的AP硬件支持的带宽。 步骤 1.按住 Option 键&#xff0c;然后点击屏幕顶部的Wi-Fi图标&#xff1b;2.从下拉菜单中选择 “打开无线诊断”&#xff08;Open Wireless Diagnostics&#xff09;&#xff1b;3.你可能会看到一个提示窗口&#xff0c;…

企业微信里可以使用的企业内刊制作工具,FLBOOK

如何让员工及时了解公司动态、行业资讯、学习专业知识&#xff0c;并有效沉淀企业文化&#xff1f;一份高质量的企业内刊是不可或缺的。现在让我来教你该怎么制作企业内刊吧 1.登录与上传 访问FLBOOK官网&#xff0c;注册账号后上传排版好的文档 2.选择模板 FLBOOK提供了丰富的…

网络变压器的主要电性参数与测试方法(2)

Hqst盈盛&#xff08;华强盛&#xff09;电子导读&#xff1a;网络变压器的主要电性参数与测试方法&#xff08;2&#xff09;.. 今天我们继续来看看网络变压器的2个主要电性参数与它的测试方法&#xff1a; 1. 线圈间分布电容Cp:线圈间杂散静电容 测试条件:100KHz/0.1…

深度学习笔记17-马铃薯病害识别(VGG-16复现)

目录 一、 前期准备 1. 设置GPU 2. 导入数据 二、手动搭建VGG-16模型 1. 搭建模型 三、 训练模型 1. 编写训练函数 3. 编写测试函数 4. 正式训练 四、 结果可视化 1. Loss与Accuracy图 2. 指定图片进行预测 3. 模型评估 前言 &#x1f368; 本文为&#x1f517;365天深度学习训…

#7 Diffusion for beginners

DDPM的原理讲解视频:DDPM explain,就是口音一言难尽 还有大佬从零开始搭建模型代码的视频:DDPM implementation,相当震撼,代码我从来都是粗粗的看个大概了事,大佬直接手撕 一个很好的资源集合网站:https://diff-usion.github.io/Awesome-Diffusion-Models/ 今天学习一段…

【计算机网络】TCP三次握手,四次挥手以及SYN,ACK,seq,以及握手次数理解

TCP三次握手图解 描述 第一次握手&#xff1a;客户端请求建立连接&#xff0c;发送同步报文(SYN1)&#xff0c;同时随机一个seqx作为初始序列号&#xff0c;进入SYN_SENT状态&#xff0c;等待服务器确认 第二次握手&#xff1a;服务端收到请求报文&#xff0c;如果同意建立连接…

Spring Data JPA 中的分页实现:从 BasePage 到 Pageable

文章目录 Spring Data JPA 中的分页实现&#xff1a;从 BasePage 到 Pageable背景&#xff1a;为什么需要分页&#xff1f;认识 BasePage 类深入 toPageable() 方法1. 处理页码和页面大小2. 处理排序方向3. 处理排序字段4. 生成 Pageable 对象 实战&#xff1a;如何使用 BasePa…

智能家居遥控革命!昂瑞微HS6621EM:用「芯」定义AIoT时代的语音交互标杆

AIoT爆发期&#xff0c;遥控器为何成为智能家居的「隐形战场」&#xff1f; 随着Meta、苹果等巨头加速布局空间计算&#xff0c;智能家居生态正从「单一设备联网」向「全场景无感交互」跃迁。作为高频使用的入口设备&#xff0c;语音遥控器的性能直接决定用户体验天花板。昂瑞微…

Grafana使用日志7--开启Sigv4

背景 在Grafana中&#xff0c;有些data source是需要开启sigv4认证的&#xff0c;例如OpenSearch&#xff0c;这个配置项默认是关闭的&#xff0c;这里我们介绍一下怎么开启 步骤 传统方式 如果我们想在Grafana中开启sigv4认证&#xff0c;我们需要在grafana.ini中修改一个…

【Redis】Redis 入门

借鉴枫枫知道 一、连接 redis 1.1 命令行连接 // 完整的命令 redis-cli -h 127.0.0.1 -p 6379 -a password// 简写 redis-cli// 认证&#xff0c;进行redis之后 auth password1.2 go 代码连接 package mainimport ("fmt""github.com/go-redis/redis" …