媒介
由于 deepseek 等大语言模子数据时效性标题,无法跟上现实天下信息的动态变革,企业内部信息更是无法理解,为了将 deepseek 应用到企业内部,之前有写过通过联网搜索、上传文件、搭建知识等检索增强生成(RAG)方式,现在继承探讨如何通过结合数据库来实现问答。
一、预备环境
- ollama,用于在本地运行、摆设和管理大型语言模子(LLMs)。
- deepseek 模子,本文用的 deepseek-r1:14b。
- langchain,大语言模子应用步伐的开发框架,主要 python 实现。
- Mysql,这里建议单独摆设一个专门用来数据库问答,避免影响生产环境。
二、开发思路
本文打算通过三张表,用户根本信息表,公司/部分信息表,一个用户与公司/部分关联表,再结合 deepseek 根据用户问答使用自然语言流通问答,打造一个企业智能通讯录。
- 使用 deepseek 生成可实行的 sql 语句,langchain 自带了一些模块,比如:
- create_sql_query_chain:基于用户自然语言标题构建SQL查询
- SQLDatabaseChain:使用链举行查询、创建和实行来查询SQL数据库
- create_sql_agent:使用署理举行健壮和灵活的与SQL数据库交互
发现还是不太抱负,决定还是让 deepseek 根据”表结构 + 提示词 + 使用示例“来生成 sql 语句。
- 对生成的 sql 语句举行洗濯,比如推理思索过程、markdown标签、其他无关信息等,提取出可直接实行的纯 sql 。
- 实行 sql 获取结果,在让 deepseek 根据”表结构 + 提示词 + 实行的SQL + 实行结果",来答复。
三、代码解读
- 去掉用不着的字段,防止干扰 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 表,即有公司又有部分,尚有上下级关系,所以接下来写系统提示词。
- 假如不知道怎么写系统提示词的,可以去 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 标签贴特别式就乱了,大家本身搞一下)
- 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)
复制代码
- 将表结构、提示词、实行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 答复:
完备代码私信要吧~ 这里贴出来格式辩说不太好看
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |