1. 引言

在之前的文章中,我们体验了SpringAI在RAG(检索增强生成)方面的强大能力,让AI能够基于本地知识库回答问题。今天,我们将继续探索SpringAI的另一个实用功能——SQL智能助手

SQL智能助手的核心思想是:让用户通过自然语言描述查询需求,由AI自动生成对应的SQL语句,并执行查询返回结果。这对于不熟悉SQL语法的业务人员来说,可以极大降低数据查询的门槛;对于开发人员来说,也能快速生成复杂查询,提升工作效率。

本文将通过一个具体的案例,带大家一步步使用SpringAI构建一个SQL智能问答系统。

2. 功能概述

SpringAI的SQL智能助手功能,本质上是一个Text-to-SQL的解决方案。其工作流程如下:

  1. 用户输入自然语言问题:例如“查询上个月销售额最高的5个商品”。
  2. AI理解问题并生成SQL:SpringAI将用户的问题、数据库表结构信息(DDL)以及一些示例查询(可选)一起发送给大模型。
  3. 大模型返回SQL语句:大模型根据提供的上下文,生成对应的SQL查询语句。
  4. 执行SQL并返回结果:SpringAI执行生成的SQL语句,并将查询结果返回给用户。

SpringAI通过SqlService和相关的ChatClient配置,简化了这一流程,让我们可以专注于业务逻辑。

3. 环境准备

在开始之前,我们需要准备好以下环境:

  • JDK 17+
  • Spring Boot 3.x 项目
  • SpringAI 依赖(当前版本 1.0.0-M6)
  • 一个可用的数据库(本文以MySQL为例)
  • AI大模型API(本文以OpenAI兼容接口为例)

3.1 创建Spring Boot项目并引入依赖

首先,创建一个Spring Boot项目,并在pom.xml中引入SpringAI的相关依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.5</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- Spring AI Starter -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    </dependency>

    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- MySQL驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring JDBC -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
</dependencies>

3.2 配置文件

application.yml中配置AI模型和数据库连接信息:

spring:
  ai:
    openai:
      api-key: ${AI_API_KEY}  # 你的API Key
      base-url: ${AI_BASE_URL} # API地址,例如 https://api.openai.com
      chat:
        options:
          model: gpt-4o-mini # 使用的模型
  datasource:
    url: jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

4. 核心实现

4.1 定义数据库表结构

为了让AI能够理解我们的数据库,我们需要提供表结构信息。这里我们以一个简单的orders(订单表)和products(商品表)为例:

CREATE TABLE products (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    category VARCHAR(100),
    price DECIMAL(10, 2),
    stock INT
);

CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_id BIGINT,
    quantity INT,
    total_amount DECIMAL(10, 2),
    order_date DATETIME,
    FOREIGN KEY (product_id) REFERENCES products(id)
);

4.2 创建SQL助手服务

SpringAI提供了一个SqlService接口,我们可以通过ChatClient来构建它。核心思路是:在ChatClient的Prompt中,注入数据库的DDL(数据定义语言)和用户的问题。

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

/**
 * Author: roy
 * Description:SQL智能助手服务
 */
@Service
public class SqlAssistantService {

    private final ChatClient chatClient;
    private final JdbcTemplate jdbcTemplate;

    @Value("classpath:sql-prompt-template.st")
    private Resource sqlPromptResource;

    public SqlAssistantService(ChatClient.Builder builder, JdbcTemplate jdbcTemplate) {
        this.chatClient = builder.build();
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 根据自然语言问题生成并执行SQL
     * @param question 用户的自然语言问题
     * @return 查询结果
     */
    public String askDatabase(String question) {
        // 1. 获取数据库表结构信息(DDL)
        String schema = getDatabaseSchema();

        // 2. 构建Prompt,将DDL和用户问题传递给AI
        PromptTemplate promptTemplate = new PromptTemplate(sqlPromptResource);
        Prompt prompt = promptTemplate.create(Map.of(
                "question", question,
                "ddl", schema
        ));

        // 3. 调用AI生成SQL
        String sql = chatClient.prompt(prompt).call().content();

        // 4. 清理并执行SQL
        String cleanSql = cleanSql(sql);
        try {
            List<Map<String, Object>> results = jdbcTemplate.queryForList(cleanSql);
            return ModelOptionsUtils.toJsonString(results);
        } catch (Exception e) {
            return "执行SQL时出错:" + e.getMessage() + ",生成的SQL为:" + cleanSql;
        }
    }

    /**
     * 获取数据库中所有表的DDL
     */
    private String getDatabaseSchema() {
        // 这里简化处理,实际项目中可以查询 information_schema 来动态获取
        return """
                CREATE TABLE products (
                    id BIGINT PRIMARY KEY AUTO_INCREMENT,
                    name VARCHAR(255) NOT NULL,
                    category VARCHAR(100),
                    price DECIMAL(10, 2),
                    stock INT
                );
                
                CREATE TABLE orders (
                    id BIGINT PRIMARY KEY AUTO_INCREMENT,
                    product_id BIGINT,
                    quantity INT,
                    total_amount DECIMAL(10, 2),
                    order_date DATETIME,
                    FOREIGN KEY (product_id) REFERENCES products(id)
                );
                """;
    }

    /**
     * 清理AI返回的SQL,去除Markdown标记等
     */
    private String cleanSql(String rawSql) {
        return rawSql
                .replace("```sql", "")
                .replace("```", "")
                .trim();
    }
}

4.3 创建Prompt模板

resources目录下创建sql-prompt-template.st文件,这是指导AI如何生成SQL的核心提示词:

你是一个SQL专家。请根据用户的问题和提供的数据库表结构(DDL),生成对应的SQL查询语句。

要求:
1. 只返回SQL语句,不要包含任何解释。
2. SQL语句必须是可执行的。
3. 如果问题不明确,请做出合理的假设。

数据库表结构(DDL):
{ddl}

用户问题:
{question}

请生成SQL:

4.4 创建Controller

最后,创建一个Controller来暴露API接口:

import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * Author: roy
 * Description:SQL智能助手控制器
 */
@RestController
public class SqlAssistantController {

    @Resource
    private SqlAssistantService sqlAssistantService;

    @GetMapping("/ask")
    public String askDatabase(@RequestParam("question") String question) {
        return sqlAssistantService.askDatabase(question);
    }
}

5. 测试运行

启动项目后,我们可以通过以下URL进行测试:

  • 查询所有商品http://localhost:8080/ask?question=查询所有商品
  • 查询价格大于100的商品http://localhost:8080/ask?question=查询价格大于100的商品有哪些
  • 查询订单总数http://localhost:8080/ask?question=统计一下总共有多少订单
  • 查询每个分类的商品数量http://localhost:8080/ask?question=统计每个分类的商品数量

AI会根据你的问题,自动生成对应的SQL并执行,返回JSON格式的结果。

6. 进阶优化

6.1 动态获取表结构

上面的例子中,我们硬编码了DDL。在实际项目中,可以通过查询information_schema来动态获取所有表的DDL,这样当数据库结构发生变化时,无需修改代码。

6.2 增加Few-Shot示例

为了让AI生成更准确的SQL,可以在Prompt中提供一些“问题-SQL”的示例(Few-Shot),帮助AI理解你的数据模式和查询习惯。

6.3 安全性考虑

  • 权限控制:用于执行SQL的数据库账号应仅具有只读权限,防止AI生成DELETEDROP等危险操作。
  • SQL校验:在执行前,可以对生成的SQL进行正则校验,确保只包含SELECT语句。
  • 结果限制:在生成的SQL中自动添加LIMIT子句,防止返回过多数据。

7. 总结

通过本文的实践,我们成功使用SpringAI构建了一个SQL智能助手。它能够将自然语言转换为SQL查询,并直接返回数据库中的结果。这大大降低了数据查询的门槛,让非技术人员也能轻松获取数据。

SpringAI的SQL助手功能,结合了LLM的自然语言理解能力和传统数据库的查询能力,是AI赋能企业数据应用的一个绝佳范例。未来,随着SpringAI的不断完善,我们期待它能提供更多开箱即用的企业级AI功能。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐