前言
由于 deepseek 等大语言模型数据时效性问题,无法跟上现实世界信息的动态变化,企业内部信息更是无法理解,为了将 deepseek 应用到企业内部,之前有写过通过联网搜索、上传文件、搭建知识等检索增强生成(RAG)方式,现在继续探讨如何通过结合数据库来实现问答。

一、准备环境

  1. ollama,用于在本地运行、部署和管理大型语言模型(LLMs)。
  2. deepseek 模型,本文用的 deepseek-r1:14b。
  3. langchain,大语言模型应用程序的开发框架,主要 python 实现。
  4. Mysql,这里建议单独部署一个专门用来数据库问答,避免影响生产环境。

二、开发思路

本文打算通过三张表,用户基本信息表,公司/部门信息表,一个用户与公司/部门关联表,再结合 deepseek 根据用户问答使用自然语言流畅问答,打造一个企业智能通讯录。

  1. 使用 deepseek 生成可执行的 sql 语句,langchain 自带了一些模块,比如:
  • create_sql_query_chain:基于用户自然语言问题构建SQL查询
  • SQLDatabaseChain:使用链进行查询、创建和执行来查询SQL数据库
  • create_sql_agent:使用代理进行健壮和灵活的与SQL数据库交互

发现还是不太理想,决定还是让 deepseek 根据”表结构 + 提示词 + 使用示例“来生成 sql 语句。

  1. 对生成的 sql 语句进行清洗,比如推理思考过程、markdown标签、其他无关信息等,提取出可直接执行的纯 sql 。
  2. 执行 sql 获取结果,在让 deepseek 根据”表结构 + 提示词 + 执行的SQL + 执行结果",来回答。

三、代码解读

  1. 准备好用户、组织架构方面的表,注意以下几点:
  • 去掉用不着的字段,防止干扰 deepseek 思考
  • 去掉敏感字段,比如密码、身份证
  • 每个字段用自然语言添加注释
  • 有关联关系的字段,注释清楚
  • 每张表用自然语言添加注释
  • 由于表结构后续还会用到,建议单独放一个文件

新建一个文件 “table_schema”,写入表结构信息,这是我准备的表:

- 用户信息表
CREATE TABLE `uc_user` (
  `ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '主键,用户ID',
  `USER_NAME` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '名称',
  `ACCOUNT` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '账号',
  `GENDER` varchar(96) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '性别 男:F-男 女:F-女',
  `EMAIL` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '邮箱',
  `PHONE` varchar(48) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '手机号码',
  `ADDRSS` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '地址',
  `CREATE_DATE` datetime DEFAULT NULL COMMENT '创建时间',
  `MODIFY_DATE` datetime DEFAULT NULL COMMENT '修改时间',
  `STATUS` varchar(6) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '状态 1:正常 0:禁用',
  `REMARK` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '备注',
  `CREATE_ID` varchar(64) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '创建人id,用户表主键',
  `into_date` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '入职日期',
  PRIMARY KEY (`ID`) USING BTREE,
  KEY `idx_user_name` (`USER_NAME`) USING BTREE,
  KEY `idx_account` (`ACCOUNT`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='用户信息表';

- 公司/部门信息表
CREATE TABLE `uc_organization` (
  `ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '主键',
  `ORG_CODE` varchar(60) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '部门编码',
  `ORG_NAME` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '部门或公司名称',
  `PARENT_ORG_ID` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '上级部门/公司ID,公司/部门信息表主键',
  `ORG_PATH_NAME` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '组织架构全路径',
  `SORT` int DEFAULT NULL COMMENT '排序号',
  `ORG_TYPE` varchar(150) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '组织类型:0.根组 1.分公司 2.子公司 3.部门',
  `CREATE_ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '创建人ID,用户表的主键',
  `CREATE_DATE` datetime DEFAULT NULL COMMENT '创建时间',
  `MODIFY_ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '修改人ID,用户表的主键',
  `MODIFY_DATE` datetime DEFAULT NULL COMMENT '修改时间',
  `STATE` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT '状态 1:正常 0:禁止',
  `ADDR` text CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci COMMENT '详细地址',
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='公司/部门信息表';

- 用户和部门/公司关联表
CREATE TABLE `uc_user_app_relation` (
  `USER_ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '用户ID,用户表的主键',
  `STATUS` varchar(6) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NOT NULL COMMENT '状态 1:正常 0:禁用',
  `COMPANY_ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '公司ID,组织和公司表的主键',
  `ORG_ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '部门ID,组织和公司表的主键',
  `CREATE_DATE` datetime DEFAULT NULL COMMENT '创建时间',
  `MODIFY_DATE` datetime DEFAULT NULL COMMENT '修改时间',
  `CREATE_ID` varchar(192) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT '创建人ID',
  KEY `idx_user_id` (`USER_ID`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='用户和部门/公司关联表';

这里的表稍微有点复杂,主要在 uc_organization 表,即有公司又有部门,还有上下级关系,所以接下来写系统提示词。

  1. 假如不知道怎么写系统提示词的,可以去 deepseek 官网看示例,然后慢慢调整。

官网:https://api-docs.deepseek.com/zh-cn/prompt-library/

在这里插入图片描述
提示词:

### 你是一个的 MYSQL 专家给定输入问题,根据提供的表结构信息,将自然语言查询需求转换为准确且可执行的SQL语句。

**表结构信息**

{table_schema}

## 查询需求
1. 用清晰的自然语言描述查询需求,包括:
- 需要获取的数据字段
- 筛选条件
- 排序要求
- 聚合需求(如求和/计数/平均值等)
- 多表关联需求
2. 生成智能模糊查询,包括:
- 输入参数:`[用户输入的简称片段]` 
- 匹配字段:`[目标字段名]`
- 预期效果:智能匹配包含输入词素及其变体的全称
- 特殊要求:`[分词处理/同义词扩展/优先级排序]`

## 约束条件
- 严格基于提供的表结构生成
- 优先使用标准SQL语法
- 不准捏造字段
- 不要列出系统字段,如主键、创建日期、修改日期等
- 每张表查询都要带上正常状态!!!不管单表还是多表查询,特别多表查询主表也要加上正常状态!!!
- 使用规范的SQL格式:关键字大写、适当缩进、使用表别名时保持一致性、避免使用*选择所有列
- 必须直接输出SQL语句,不要其他内容,不要分步骤列出
- 只能生成SELECT查询语句

### 不准捏造表和字段!!!
### 主表一定要加上状态查询!!!

这里踩的坑有点多,比如:

  • 每张表都有状态字段,都需要带上这个查询条件,类似的很多系统也有软删除 is_deleted 字段,要着重强调
  • 我们习惯性的会问公司简称,需要加上模糊搜索
  • 有时候 deepseek 会捏造字段,也要特别注意

到这里 deepseek 对于单表生成 sql 还可以,但是多表查询,特别表结构又复杂,需要引导 deepseek 如何生成,这就体现了参考示例的作用,写几个稍微复杂的 sql 查询示例让 deepseek 理解,如下:

  • 查询某个用户信息
SELECT 
    u.USER_NAME AS 姓名,
    u.ACCOUNT AS 账号,
    u.GENDER AS 性别,
    u.EMAIL AS 邮箱,
    u.PHONE AS 手机号码,
    u.ADDRSS AS 地址,
    u.into_date AS 入职日期,
    o.ORG_NAME AS 公司名称,
    d.ORG_NAME AS 部门名称
FROM 
    uc_user u
LEFT JOIN 
    uc_user_app_relation r ON u.ID = r.USER_ID AND r.STATUS = '1'
LEFT JOIN 
    uc_organization o ON r.COMPANY_ID = o.ID
LEFT JOIN 
    uc_organization d ON r.ORG_ID = d.ID
WHERE 
    u.USER_NAME = '某某某';
GROUP BY u.ID;
  • 查询某个公司下面人员清单
SELECT 
    u.USER_NAME AS 姓名,
    u.ACCOUNT AS 账号,
    u.GENDER AS 性别,
    u.EMAIL AS 邮箱,
    u.PHONE AS 手机号码,
    u.ADDRSS AS 地址,
    u.into_date AS 入职日期,
    o.ORG_NAME AS 部门名称,
    c.ORG_NAME AS 公司名称
FROM 
    uc_user u
LEFT JOIN 
    uc_user_app_relation r ON u.ID = r.USER_ID
LEFT JOIN 
    uc_organization o ON r.ORG_ID = o.ID
LEFT JOIN 
    uc_organization c ON r.COMPANY_ID = c.ID
WHERE 
    u.STATUS = '1' AND r.STATUS = '1' AND o.STATE = '1' AND c.STATE = '1'  AND c.ORG_NAME LIKE '%某某公司%'
  • 查询某个公司某个部门下面的人员清单
SELECT 
    u.USER_NAME AS 姓名,
    u.ACCOUNT AS 账号,
    u.GENDER AS 性别,
    u.EMAIL AS 邮箱,
    u.PHONE AS 手机号码,
    u.ADDRSS AS 地址,
    u.into_date AS 入职日期,
    o.ORG_NAME AS 部门名称,
    c.ORG_NAME AS 公司名称
FROM 
    uc_user u
LEFT JOIN 
    uc_user_app_relation r ON u.ID = r.USER_ID
LEFT JOIN 
    uc_organization o ON r.ORG_ID = o.ID
LEFT JOIN 
    uc_organization c ON r.COMPANY_ID = c.ID
WHERE 
    u.STATUS = '1' AND r.STATUS = '1' AND o.STATE = '1' AND c.STATE = '1'  AND c.ORG_NAME LIKE '%某某公司%' AND o.ORG_NAME LIKE '%某某部门%'
  • 某某公司的子公司清单
SELECT 
    c.ORG_NAME AS 公司名称,
    p.ORG_NAME AS 上级公司名称,
    c.ORG_PATH_NAME AS 组织架构全路径 
FROM 
    uc_organization c
LEFT JOIN 
    uc_organization p ON c.PARENT_ORG_ID = p.ID 
WHERE 
    c.STATE = '1' 
    AND p.STATE = '1' 
    AND c.ORG_TYPE < 3 
    AND p.ORG_NAME LIKE '%某某公司%'
ORDER BY 
    c.ORG_PATH_NAME;
  • 某某公司的部门清单
SELECT 
    c.ORG_NAME AS 部门名称,
    p.ORG_NAME AS 上级部门名称,
    c.ORG_PATH_NAME AS 组织架构全路径 
FROM 
    uc_organization c
LEFT JOIN 
    uc_organization p ON c.PARENT_ORG_ID = p.ID 
WHERE 
    c.STATE = '1' 
    AND p.STATE = '1' 
    AND c.ORG_TYPE = 3 
    AND p.ORG_NAME LIKE '%某某公司%'
ORDER BY 
    c.ORG_PATH_NAME;

直接将参考示例放到提示词下面就好了(代码有 ```sql 标签贴出格式就乱了,大家自己搞一下)

在这里插入图片描述

  1. deepseek 生成的内容带了推理过程和标签,直接用正则过滤一下。

代码:

def extract_sql_blocks(text):
    # 使用正则表达式匹配非贪婪模式,并包含换行符
    pattern = r'```sql(.*?)```'
    matches = re.findall(pattern, text, re.DOTALL)
    # 去除每个匹配项两端的空白字符
    return [match.strip() for match in matches]


def remove_think_tags(text):
    """移除文本中<think>标签及其内容"""
    pattern = re.compile(r'<think>.*?</think>', re.DOTALL)  # 非贪婪匹配 + 跨行匹配
    return pattern.sub('', text)

过滤之前:

在这里插入图片描述
过滤之后:

在这里插入图片描述
4. 执行 sql 获取查询结果

mysql_uri = "mysql+mysqlconnector://test:123456@127.0.0.1:3306/test"
db = SQLDatabase.from_uri(mysql_uri)
query_result = db.run(query_sql)
  1. 将表结构、提示词、执行sql、返回结果全都给 deepseek 回答,提示词模板如下:
**指令**
你是一个数据库专家,根据表结构、查询sql、查询结果,回答用户问题,如果查询结果为空,直接回复”暂未找到您要的信息“。

**数据库表结构**
{query_tables}

**查询sql**
{query_sql}

**查询结果**
{query_result}

## 要求:
1. 用自然语言总结查询结果,回答用户的问题,不允许在回答中添加编造成分,回答请使用中文。
2. 字段请用中文描述,没有中文注释的不返回。

代码:

def start_chat_mysql(query, model):

    # 根据自定义提示词写出SQL语句
    response = create_query_sql(query)

    # 过滤推理和无关信息,只保留可执行的SQL
    sqls = extract_sql_blocks(remove_think_tags(response))
    query_sql = sqls[0]
    # print(query_sql)

    # 初始化数据库连接
    db = SQLDatabase.from_uri(mysql_uri)

    # 执行查询
    query_result = db.run(query_sql)

    # 初始化LLM和链
    llm = get_chat_llm(model)

    prompt = get_answer_prompt()

    chain = prompt | llm

    with open("table_schema", "r", encoding="utf-8") as file:
        table_schema = file.read()

    response = chain.invoke({
        "query_tables": table_schema,
        "query_sql": query_sql,
        "query_result": query_result,
        "input": query
    })
    return response

没有找到的时候,deepseek 回答:

在这里插入图片描述

找到的时候, deepseek 回答:

在这里插入图片描述

完整代码私信要吧~ 这里贴出来格式冲突不太好看

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐