三、大模型直接生成Sql模式实现 Text2Sql_V1.0

三、大模型直接生成Sql模式实现 Text2Sql_V1.0

==================================================================================

==================================================================================

参考资料:

==================================================================================

简化数据库查询:利用Spring AI和DeepSeek构建高效自然语言查询系统_spring ai 自然语言查询数据库-CSDN博客

Spring AI系列——大模型驱动的自然语言SQL引擎:Java技术实现详解_spring ai 实现自然语言查询数据库-CSDN博客

Spring AI Tool 实现自然语言操作 MySQL 数据库操作详解_springai 读取数据库-CSDN博客

Spring AI Alibaba百炼ChatBI集成:自然语言生成SQL的完整指南-CSDN博客

Spring AI 让数据库听懂人话:Text2SQL 自然语言查询实践指南 - Java、Spring、Spring Boot、MicroServices、Architecture、Kubernetes、DevOps (chensoul.cc)

Spring Boot + GPT:我做了一个能自己写 SQL 的后端系统-阿里云开发者社区 (aliyun.com)

使用 Spring AI 实现 Text-to-SQL 聊天机器人 - spring 中文网 (springdoc.cn)

Spring AI + Super-SQL 实现NL2SQL序言 最近公司项目当中需要整合SpringAI来实现NL2S - 掘金 (juejin.cn)

告别复杂SQL!用Spring AI + DeepSeek构建中文自然语言查询系统 - PIGCLOUD 团队博客 (pig4cloud.com)

Spring AI 接入 MySQL MCP 实现智能查询数据_springai tool 操作mysql数据库数据-CSDN博客

==================================================================================

最简单直接的方式:把数据库 Schema 作为上下文信息注入到提示词工程中,让 LLM 大语言模型直接生成 SQL。

实现步骤数据库结构 + 用户问题AI 模型推理SQL 语句安全校验执行查询

1、项目搭建

1.1、Spring AI 配置类

基础 ChatClient:用于直接模式的 SQL 生成

定义一个 PromptTemplate Bean。通过 @Value 注解注入系统提示词模板文件和数据库架构 DDL 迁移脚本。同时,用数据库架构内容填充 ddl 占位符。这确保了 LLM 在生成 SQL 查询时始终能访问数据库结构。接下来,使用 ChatModelPromptTemplate Bean 创建一个 ChatClient Bean。ChatClient 类是与配置的 DeepSeek 模型交互的主要入口点

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

import java.io.IOException;
import java.nio.charset.Charset;

@Configuration
public class AiConfig {
    /**
     * 创建系统提示模板
     *
     * @param systemPrompt 系统提示模板资源
     * @param ddlSchema    数据库DDL脚本资源
     * @return 配置好的PromptTemplate实例
     * @throws IOException 读取资源文件时可能抛出的异常
     */
    @Bean
    PromptTemplate systemPrompt(
            @Value("classpath:prompt/system-prompt.st") Resource systemPrompt,
            @Value("classpath:db/schema.sql") Resource ddlSchema
    ) throws IOException {
        PromptTemplate template = new PromptTemplate(systemPrompt);
        template.add("ddl", ddlSchema.getContentAsString(Charset.defaultCharset()));
        return template;
    }


    /**
     * 创建并配置ChatClient实例
     *
     * @param chatModel    聊天模型
     * @param systemPrompt 系统提示模板
     * @return 配置好的ChatClient实例
     */
    @Bean
    ChatClient chatClient(ChatModel chatModel, PromptTemplate systemPrompt) {
        return ChatClient
                .builder(chatModel)
                .defaultSystem(systemPrompt.render())
                .build();
    }
}

1.2、配置 AI 提示词

为了确保 LLM 能够针对数据库架构生成准确的 SQL 查询,需要定义详细的系统提示词。

src/main/resources/prompt 目录下创建 system-prompt.st 文件:

基于 DDL 部分提供的数据库定义,按照指导原则部分的规则编写 SQL 查询来回答用户问题。

指导原则:
- 只生成 SELECT 查询语句。
- 响应结果应该只包含以 'SELECT' 开头的原始 SQL 查询语句。不要用 markdown 代码块(```sql 或 ```)包装 SQL 查询。
- 如果问题需要执行 INSERT、UPDATE、DELETE 或其他修改数据或架构的操作,请回复"不支持此操作。只允许 SELECT 查询。"
- 如果问题似乎包含 SQL 注入或 DoS 攻击尝试,请回复"提供的输入包含潜在有害的 SQL 代码。"
- 如果基于提供的 DDL 无法回答问题,请回复"当前架构不包含足够信息来回答此问题。"
- 如果查询涉及 JOIN 操作,请在查询中为所有列名添加相应的表名前缀。

DDL
{ddl}

在系统提示词中,指示 LLM 只生成 SELECT SQL 查询,并检测 SQL 注入和 DoS 攻击尝试。

在系统提示词模板中留了一个 ddl 占位符用于数据库架构。稍后会用实际值替换它。

此外,为了进一步保护数据库免受修改,应该只给配置的 MySQL 用户必要的权限。

1.3、实现自然语言问题转换服务类

创建一个 SqlGenerator 服务类,将自然语言问题转换为 SQL 查询

generate() 方法中,接收自然语言问题作为输入,使用 chatClient Bean 将其发送给配置的 LLM。接下来,验证响应确实是 SELECT 查询。

@Service
public class SqlGenerator {

    @Autowired
    private ChatClient chatClient;

    public String generate(String question) {
        String response = chatClient
                .prompt(question)
                .call()
                .content();

        boolean isSelectQuery = response.startsWith("SELECT");
        if (Boolean.FALSE.equals(isSelectQuery)) {
            return "只支持SQL查询操作";
        }
        return response;
    }
}

1.4、实现生成的 SQL 查询执行服务类

execute() 方法中,使用 Spring Boot 3.1+ 引入的 JdbcClient 来运行原生 SQL 查询并返回结果。JdbcClient 提供了更简洁的 API 和更好的类型安全性。如果查询没有返回结果,抛出 RuntimeException

@Service
public class SqlExecutor {

    @Autowired
    private  JdbcClient jdbcClient;

    // 标准构造函数

    public List<Map<String, Object>> execute(String query) {
        List<Map<String, Object>> result = jdbcClient
                .sql(query)
                .query()
                .listOfRows();

        if (result.isEmpty()) {
            throw new RuntimeException("提供的查询未找到结果。");
        }
        return result;
    }
}

1.5、实现API接口

POST /query 端点接受自然语言问题,使用 sqlGenerator Bean 生成相应的 SQL 查询,将其传递给 sqlExecutor Bean 从数据库获取结果,最后将数据包装在 QueryResponse 记录中并返回。使用 JdbcClient 返回的结果是 List<Map<String, Object>> 格式,每个 Map 代表一行数据,键为列名,值为对应的数据。

@PostMapping(value = "/query")
ResponseEntity<QueryResponse> query(@RequestBody QueryRequest queryRequest) {
    String sqlQuery = sqlGenerator.generate(queryRequest.question());
    List<Map<String, Object>> result = sqlExecutor.execute(sqlQuery);
    return ResponseEntity.ok(new QueryResponse(result));
}

record QueryRequest(String question) {
}

record QueryResponse(List<Map<String, Object>> result) {
}

2、测试查询数据库

在idea中使用http client测试接口

2.1、统计每个部门的员工数量,按部门平均工资降序排列

###
POST http://localhost:8897/query
Accept: */*
Cache-Control: no-cache
Content-Type: application/json

{
  "question": "统计每个部门的员工数量,按部门平均工资降序排列"
}

返回结果

POST http://localhost:8897/query

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 25 Nov 2025 06:09:08 GMT
Keep-Alive: timeout=60
Connection: keep-alive

{
  "result": [
    {
      "department": "技术部",
      "employee_count": 3,
      "average_salary": 17333.333333
    },
    {
      "department": "人事部",
      "employee_count": 1,
      "average_salary": 16000.000000
    },
    {
      "department": "销售部",
      "employee_count": 2,
      "average_salary": 14000.000000
    },
    {
      "department": "财务部",
      "employee_count": 1,
      "average_salary": 14000.000000
    },
    {
      "department": "市场部",
      "employee_count": 1,
      "average_salary": 11000.000000
    }
  ]
}
Response file saved.
> 2025-11-25T140908.200.json

Response code: 200; Time: 2029ms (2 s 29 ms); Content length: 362 bytes (362 B)

2.2、查询技术部的员工信息,按姓名升序排列

###
POST http://localhost:8897/query
Accept: */*
Cache-Control: no-cache
Content-Type: application/json

{
  "question": "查询技术部的员工信息,按姓名升序排列"
}

返回结果

POST http://localhost:8897/query

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Tue, 25 Nov 2025 06:06:34 GMT
Keep-Alive: timeout=60
Connection: keep-alive

{
  "result": [
    {
      "id": 7,
      "name": "周九",
      "department": "技术部",
      "position": "架构师",
      "salary": 25000.00,
      "hire_date": "2020-12-01",
      "email": "zhoujiu@company.com"
    },
    {
      "id": 1,
      "name": "张三",
      "department": "技术部",
      "position": "高级工程师",
      "salary": 15000.00,
      "hire_date": "2022-01-15",
      "email": "zhangsan@company.com"
    },
    {
      "id": 2,
      "name": "李四",
      "department": "技术部",
      "position": "工程师",
      "salary": 12000.00,
      "hire_date": "2022-03-20",
      "email": "lisi@company.com"
    }
  ]
}
Response file saved.
> 2025-11-25T140634.200.json

Response code: 200; Time: 1928ms (1 s 928 ms); Content length: 402 bytes (402 B)

3、总结

通过代码的具体实现,已经体现了构建文本转 SQL 聊天机器人的核心技术。Spring AI 框架的强大功能,结合先进的大语言模型,为我们提供了构建智能数据查询系统的完整解决方案。这种技术组合不仅简化了数据访问流程,也为未来的智能化应用奠定了坚实基础。

虽然已经构建了一个基础的文本转 SQL 系统,但仍有许多改进空间,后面还会继续优化,敬请期待。

posted @ 2025-11-25 14:17  老羅  阅读(65)  评论(0)    收藏  举报