告别 SQL 编写!基于 LangChain4j 的智能问数系统:从 0 到 1 搭建指南

智能问数项目核心是自然语言转 SQL(NL2SQL),通过 LangChain4j 封装大模型能力,结合 Spring Boot 快速开发后端服务,支持用户用自然语言提问业务数据(如 “查询 2024 年 Q3 华北地区销售额 TOP3 的产品”),系统自动生成 SQL 执行并返回结果。以下是完整实现方案,包含技术选型、架构设计、代码实现和部署优化。

一、技术选型

技术栈

选型说明

后端框架

Spring Boot 3.2.x(轻量、快速集成,支持 RESTful API 和 Spring 生态)

大模型集成

LangChain4j 0.25.x(Java 版大模型开发框架,简化 NL2SQL、Prompt 工程、结果解析)

大模型选择

支持多模型切换:

– 开源本地:Llama 3(70B)、Qwen 2(72B)(需 GPU 部署)

– 云服务:通义千问、GPT-4o、智谱清言(API 调用)

数据库连接

Spring Data JPA + HikariCP(简化数据库操作,支持多数据源)

SQL 解析 / 校验

JSqlParser(解析生成的 SQL,防止注入、校验语法)

结果格式化

FastJSON 2(JSON 序列化,支持复杂结果结构转换)

缓存

Redis(缓存高频 SQL、表结构元数据,提升响应速度)

日志监控

SLF4J + Logback(日志记录)、Spring Boot Actuator(服务监控)

部署方式

Docker(容器化部署,适配本地 / 云环境)

告别 SQL 编写!基于 LangChain4j 的智能问数系统:从 0 到 1 搭建指南

二、核心架构设计

项目采用分层架构,核心流程为:自然语言提问 → 元数据加载 → Prompt 构造 → 大模型生成 SQL → SQL 校验 → 数据库执行 → 结果格式化返回

1. 架构分层

┌─────────────────────────────────────────────────────────┐
│  接入层:REST API(接收用户提问、返回结果)              │
├─────────────────────────────────────────────────────────┤
│  业务层:核心流程编排(元数据加载、Prompt构造、结果处理) │
├─────────────────────────────────────────────────────────┤
│  能力层:                                               │
│  - LangChain4j:大模型调用、NL2SQL链、Prompt模板        │
│  - SQL工具:语法校验、注入防护、执行结果解析             │
│  - 缓存工具:Redis缓存表结构、高频SQL                   │
├─────────────────────────────────────────────────────────┤
│  数据层:业务数据库(MySQL/Oracle)、元数据库(表结构)  │
└─────────────────────────────────────────────────────────┘

2. 核心流程

  1. 用户提问:用户通过 API 提交自然语言问题(如 “2024 年各部门离职人数”)。
  2. 元数据加载:从缓存 / 数据库获取业务表结构(表名、字段名、字段注释、关联关系)。
  3. Prompt 构造:将用户问题、表结构元数据、SQL 生成规则(禁止删改、只查指定表)封装为 Prompt。
  4. 大模型生成 SQL:LangChain4j 调用大模型,基于 Prompt 生成目标 SQL。
  5. SQL 校验:校验 SQL 语法、是否包含危险操作(DELETE/UPDATE/DROP)、是否只操作白名单表。
  6. SQL 执行:通过 JPA 执行合法 SQL,获取原始结果。
  7. 结果格式化:将数据库结果转换为自然语言描述 + 结构化数据(JSON),返回给用户。

三、项目实现步骤

1. 初始化 Spring Boot 项目

1.1 依赖配置(pom.xml)

核心依赖如下,需引入 LangChain4j、数据库、缓存相关依赖:

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

<dependencies>
    <!-- Spring Boot核心 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>

    <!-- LangChain4j(大模型集成) -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j</artifactId>
        <version>0.25.0</version>
    </dependency>
    <!-- 大模型适配器(以通义千问为例,其他模型替换对应依赖) -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-alibaba-tongyi</artifactId>
        <version>0.25.0</version>
    </dependency>
    <!-- 本地模型支持(如Llama 3,需额外部署模型) -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-ollama</artifactId>
        <version>0.25.0</version>
    </dependency>

    <!-- 数据库 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- SQL解析 -->
    <dependency>
        <groupId>com.github.jsqlparser</groupId>
        <artifactId>jsqlparser</artifactId>
        <version>4.8</version>
    </dependency>

    <!-- 缓存 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- 工具类 -->
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.50</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- 测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

1.2 配置文件(application.yml)

配置数据库、Redis、大模型参数:

spring:
  # 数据库配置(业务库)
  datasource:
    url: jdbc:mysql://localhost:3306/business_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  # JPA配置
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: true
    properties:
      hibernate:
        format_sql: true
  # Redis缓存配置
  redis:
    host: localhost
    port: 6379
    password:
    database: 0

# LangChain4j大模型配置(通义千问为例,支持动态切换)
langchain4j:
  alibaba-tongyi:
    api-key: 你的通义千问API密钥
    model: qwen-turbo  # 模型版本(qwen-turbo/qwen-plus)
  # 本地模型配置(Ollama部署Llama 3)
  ollama:
    base-url: http://localhost:11434
    model: llama3:70b-instruct
    temperature: 0.1  # 越低越稳定,适合SQL生成

# 应用配置
app:
  # 数据库表结构白名单(只允许查询这些表)
  sql:
    table-whitelist: employee, department, sales, order_info
  # 缓存配置(表结构缓存1小时)
  cache:
    table-meta-ttl: 3600

2. 核心模块开发

2.1 表结构元数据模块(加载数据库表信息)

用于获取表名、字段名、字段注释、数据类型,提供给大模型生成 SQL 时参考。

表元数据实体类

import lombok.Data;

import java.util.List;

@Data
public class TableMetadata {
    private String tableName;       // 表名(数据库实际表名)
    private String tableComment;    // 表注释(业务含义)
    private List<ColumnMetadata> columns; // 字段列表
}

@Data
public class ColumnMetadata {
    private String columnName;      // 字段名
    private String columnComment;   // 字段注释(核心,协助大模型理解业务)
    private String dataType;        // 数据类型(如varchar、int、datetime)
    private boolean isPrimaryKey;   // 是否主键
}

表元数据 DAO(查询数据库元信息)

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Repository
public class TableMetadataDao {

    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 查询指定表的元数据(MySQL为例)
     */
    public List<TableMetadata> queryTableMetadata(List<String> tableNames) {
        return tableNames.stream().map(tableName -> {
            TableMetadata tableMeta = new TableMetadata();
            tableMeta.setTableName(tableName);

            // 1. 查询表注释
            String tableCommentSql = "SELECT TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES " +
                    "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?";
            String tableComment = jdbcTemplate.queryForObject(tableCommentSql, String.class, tableName);
            tableMeta.setTableComment(tableComment);

            // 2. 查询字段信息(字段名、注释、类型、主键)
            String columnSql = "SELECT COLUMN_NAME, COLUMN_COMMENT, DATA_TYPE, " +
                    "CASE WHEN COLUMN_KEY = 'PRI' THEN 1 ELSE 0 END AS IS_PRIMARY_KEY " +
                    "FROM INFORMATION_SCHEMA.COLUMNS " +
                    "WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? " +
                    "ORDER BY ORDINAL_POSITION";
            List<Map<String, Object>> columnMaps = jdbcTemplate.queryForList(columnSql, tableName);
            List<ColumnMetadata> columns = columnMaps.stream().map(map -> {
                ColumnMetadata columnMeta = new ColumnMetadata();
                columnMeta.setColumnName(map.get("COLUMN_NAME").toString());
                columnMeta.setColumnComment(map.get("COLUMN_COMMENT") != null ? map.get("COLUMN_COMMENT").toString() : "");
                columnMeta.setDataType(map.get("DATA_TYPE").toString());
                columnMeta.setPrimaryKey(Boolean.parseBoolean(map.get("IS_PRIMARY_KEY").toString()));
                return columnMeta;
            }).collect(Collectors.toList());
            tableMeta.setColumns(columns);

            return tableMeta;
        }).collect(Collectors.toList());
    }
}

表元数据服务(缓存 + 业务逻辑)

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class TableMetadataService {

    @Resource
    private TableMetadataDao tableMetadataDao;

    @Resource
    private List<String> tableWhitelist; // 从配置文件注入表白名单

    /**
     * 获取表元数据(缓存优先)
     */
    @Cacheable(value = "tableMetadataCache", key = "'all'", ttl = "${app.cache.table-meta-ttl}")
    public List<TableMetadata> getTableMetadata() {
        // 查询白名单中的所有表元数据
        return tableMetadataDao.queryTableMetadata(tableWhitelist);
    }
}

2.2 LangChain4j NL2SQL 模块(核心:自然语言转 SQL)

通过 LangChain4j 的PromptTemplate构造提示词,调用大模型生成 SQL,同时添加结果解析逻辑。

1. 大模型配置类

import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.alibaba.tongyi.TongyiChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LangChain4jConfig {

    // 通义千问配置
    @Value("${langchain4j.alibaba-tongyi.api-key}")
    private String tongyiApiKey;
    @Value("${langchain4j.alibaba-tongyi.model}")
    private String tongyiModel;

    // 本地Ollama配置(Llama 3)
    @Value("${langchain4j.ollama.base-url}")
    private String ollamaBaseUrl;
    @Value("${langchain4j.ollama.model}")
    private String ollamaModel;
    @Value("${langchain4j.ollama.temperature}")
    private float temperature;

    /**
     * 通义千问模型Bean(云服务)
     */
    @Bean(name = "tongyiChatModel")
    public TongyiChatModel tongyiChatModel() {
        return TongyiChatModel.builder()
                .apiKey(tongyiApiKey)
                .model(tongyiModel)
                .temperature(temperature)
                .build();
    }

    /**
     * 本地Ollama模型Bean(开源模型)
     */
    @Bean(name = "ollamaChatModel")
    public OllamaChatModel ollamaChatModel() {
        return OllamaChatModel.builder()
                .baseUrl(ollamaBaseUrl)
                .model(ollamaModel)
                .temperature(temperature)
                .build();
    }
}

2. NL2SQL 服务(核心逻辑)

import dev.langchain4j.chain.ConversationalChain;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.prompt.PromptTemplate;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class Nl2SqlService {

    // 注入大模型(可通过配置动态切换,这里以通义千问为例)
    @Resource(name = "tongyiChatModel")
    private ChatLanguageModel chatModel;

    @Resource
    private TableMetadataService tableMetadataService;

    @Resource
    private List<String> tableWhitelist;

    /**
     * 自然语言转SQL核心方法
     */
    public String convertNlToSql(String userQuestion) {
        // 1. 加载表元数据
        List<TableMetadata> tableMetas = tableMetadataService.getTableMetadata();
        String tableMetaStr = formatTableMetadata(tableMetas);

        // 2. 构造Prompt(关键:明确SQL生成规则)
        String systemPrompt = """
                你是一个专业的SQL生成工程师,需要根据用户的自然语言问题和提供的数据库表结构,生成正确的MySQL SQL语句。
                必须遵守以下规则:
                1. 只允许查询白名单中的表:{tableWhitelist},禁止操作其他表。
                2. 只能生成SELECT查询语句,禁止DELETE、UPDATE、DROP、ALTER等危险操作。
                3. 严格按照表结构中的字段名和表名编写SQL,字段名必须与提供的一致(区分大小写)。
                4. 处理时间范围时,使用正确的日期格式(如'2024-01-01'),避免语法错误。
                5. 生成的SQL必须可以直接执行,不需要额外修改,不要包含任何解释性文字。
                6. 若用户问题不明确或无法生成SQL,返回"ERROR: 无法生成合法SQL,请补充问题细节"。
                
                数据库表结构:
                {tableMetadata}
                """;

        // 填充Prompt参数
        String filledSystemPrompt = PromptTemplate.from(systemPrompt)
                .replace("tableWhitelist", String.join(",", tableWhitelist))
                .replace("tableMetadata", tableMetaStr)
                .toString();

        // 3. 调用大模型生成SQL
        ConversationalChain chain = ConversationalChain.builder()
                .chatLanguageModel(chatModel)
                .systemMessage(SystemMessage.from(filledSystemPrompt))
                .build();

        String sql = chain.execute(userQuestion);
        log.info("用户问题:{},生成SQL:{}", userQuestion, sql);
        return sql;
    }

    /**
     * 格式化表元数据为大模型易理解的字符串
     */
    private String formatTableMetadata(List<TableMetadata> tableMetas) {
        return tableMetas.stream().map(tableMeta -> {
            String columnsStr = tableMeta.getColumns().stream().map(column -> 
                    String.format("字段名:%s,注释:%s,类型:%s,是否主键:%s",
                            column.getColumnName(),
                            column.getColumnComment(),
                            column.getDataType(),
                            column.isPrimaryKey() ? "是" : "否")
            ).collect(Collectors.joining("
  "));
            return String.format("表名:%s,表注释:%s
  字段列表:
  %s",
                    tableMeta.getTableName(),
                    tableMeta.getTableComment(),
                    columnsStr);
        }).collect(Collectors.joining("

"));
    }
}

2.3 SQL 校验模块(安全防护)

防止大模型生成危险 SQL(如删改操作)或语法错误 SQL,保障数据库安全。

import com.alibaba.fastjson2.JSON;
import com.github.jsqlparser.JSQLParserException;
import com.github.jsqlparser.parser.CCJSqlParserUtil;
import com.github.jsqlparser.statement.Statement;
import com.github.jsqlparser.statement.select.Select;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.regex.Pattern;

@Slf4j
@Component
public class SqlValidator {

    @Resource
    private List<String> tableWhitelist;

    // 禁止的SQL关键字(正则匹配,忽略大小写)
    private static final Pattern FORBIDDEN_KEYWORDS = Pattern.compile(
            "b(DELETE|UPDATE|INSERT|DROP|ALTER|TRUNCATE|CREATE|REPLACE)b",
            Pattern.CASE_INSENSITIVE
    );

    /**
     * 校验SQL合法性
     * @return 合法返回true,非法抛出异常
     */
    public boolean validateSql(String sql) {
        // 1. 检查是否包含错误标记
        if (sql.startsWith("ERROR:")) {
            throw new IllegalArgumentException(sql);
        }

        // 2. 检查禁止关键字
        if (FORBIDDEN_KEYWORDS.matcher(sql).find()) {
            throw new IllegalArgumentException("SQL包含危险操作,禁止执行");
        }

        // 3. 校验SQL语法
        try {
            Statement statement = CCJSqlParserUtil.parse(sql);
            // 4. 校验是否为SELECT语句
            if (!(statement instanceof Select)) {
                throw new IllegalArgumentException("仅支持SELECT查询语句");
            }

            // 5. 校验操作的表是否在白名单(简化版:解析FROM后的表名)
            Select select = (Select) statement;
            String fromClause = select.getSelectBody().toString().toLowerCase();
            for (String table : tableWhitelist) {
                fromClause = fromClause.replace(table.toLowerCase(), "");
            }
            // 若还有剩余表名,说明操作了非白名单表
            if (fromClause.contains("from") || fromClause.contains("join")) {
                throw new IllegalArgumentException("SQL操作了非白名单表");
            }

        } catch (JSQLParserException e) {
            log.error("SQL语法错误:{}", sql, e);
            throw new IllegalArgumentException("SQL语法错误:" + e.getMessage());
        }

        return true;
    }
}

2.4 数据查询与结果格式化模块

执行合法 SQL,将数据库返回的原始结果转换为 “自然语言描述 + 结构化数据” 的格式。

import com.alibaba.fastjson2.JSON;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;

@Service
public class DataQueryService {

    @Resource
    private JdbcTemplate jdbcTemplate;

    /**
     * 执行SQL查询
     */
    public List<Map<String, Object>> executeQuery(String sql) {
        return jdbcTemplate.queryForList(sql);
    }

    /**
     * 格式化结果(自然语言描述+JSON结构化数据)
     */
    public QueryResult formatResult(String userQuestion, String sql, List<Map<String, Object>> rawResult) {
        QueryResult result = new QueryResult();
        result.setUserQuestion(userQuestion);
        result.setGeneratedSql(sql);
        result.setData(rawResult);

        // 生成自然语言描述(简化版:根据结果数量和字段名生成)
        if (rawResult.isEmpty()) {
            result.setNaturalLanguageAnswer("未查询到符合条件的数据");
        } else {
            String fields = String.join("、", rawResult.get(0).keySet());
            result.setNaturalLanguageAnswer(
                    String.format("查询到%d条数据,包含字段:%s", rawResult.size(), fields)
            );
        }

        return result;
    }
}

// 结果返回实体类
import lombok.Data;
import java.util.List;
import java.util.Map;

@Data
public class QueryResult {
    private String userQuestion;          // 用户原始问题
    private String generatedSql;          // 生成的SQL
    private String naturalLanguageAnswer; // 自然语言回答
    private List<Map<String, Object>> data; // 结构化数据(JSON格式)
}

2.5 控制器(API 接入层)

提供 RESTful API 供前端调用,接收用户提问并返回结果。

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/query")
public class QueryController {

    @Resource
    private Nl2SqlService nl2SqlService;

    @Resource
    private SqlValidator sqlValidator;

    @Resource
    private DataQueryService dataQueryService;

    // 请求参数实体
    @lombok.Data
    public static class QueryRequest {
        @NotBlank(message = "提问内容不能为空")
        private String question; // 用户自然语言问题
    }

    /**
     * 智能问数核心接口
     */
    @PostMapping("/nl2sql")
    public Result<QueryResult> nl2sql(@Valid @RequestBody QueryRequest request) {
        String userQuestion = request.getQuestion();
        try {
            // 1. 自然语言转SQL
            String sql = nl2SqlService.convertNlToSql(userQuestion);

            // 2. SQL校验
            sqlValidator.validateSql(sql);

            // 3. 执行查询
            List<Map<String, Object>> rawResult = dataQueryService.executeQuery(sql);

            // 4. 格式化结果
            QueryResult queryResult = dataQueryService.formatResult(userQuestion, sql, rawResult);

            return Result.success(queryResult);
        } catch (IllegalArgumentException e) {
            log.error("问数失败:{}", e.getMessage(), e);
            return Result.error(400, e.getMessage());
        } catch (Exception e) {
            log.error("问数系统异常:{}", e.getMessage(), e);
            return Result.error(500, "系统异常,请稍后重试");
        }
    }
}

// 统一返回结果类
import lombok.Data;

@Data
public class Result<T> {
    private int code;       // 状态码(200成功,其他失败)
    private String msg;     // 提示信息
    private T data;         // 业务数据

    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }

    public static <T> Result<T> error(int code, String msg) {
        return new Result<>(code, msg, null);
    }
}

告别 SQL 编写!基于 LangChain4j 的智能问数系统:从 0 到 1 搭建指南

3. 关键优化点

3.1 Prompt 优化(提升 SQL 生成准确率)

  • 明确表结构的业务含义(字段注释必须详细,如 “sales_amount:销售额(单位:元)”)。
  • 限制 SQL 语法(如 “只支持 MySQL,使用 LEFT JOIN 而非 JOIN”)。
  • 提供示例(如 “用户问‘2024 年 1 月销售额’,生成 SQL:SELECT SUM (sales_amount) FROM sales WHERE sale_date BETWEEN '2024-01-01' AND '2024-01-31'”)。

3.2 缓存优化

  • 缓存表结构元数据(避免频繁查询 INFORMATION_SCHEMA)。
  • 缓存高频问题的 SQL(如 “近 7 天销售额”,直接复用已生成的 SQL)。

3.3 安全优化

  • 严格的 SQL 校验(禁止危险操作、白名单表限制)。
  • 数据库账号权限最小化(仅授予 SELECT 权限)。
  • 防止 SQL 注入(通过 JdbcTemplate 参数化查询,避免字符串拼接)。

3.4 多模型适配

通过 LangChain4j 的统一接口,支持动态切换大模型:

// 切换为本地Ollama模型(Llama 3)
@Resource(name = "ollamaChatModel")
private ChatLanguageModel chatModel;

告别 SQL 编写!基于 LangChain4j 的智能问数系统:从 0 到 1 搭建指南

四、部署与测试

1. 本地测试

  1. 启动 MySQL、Redis 服务,创建业务数据库和测试表(如sales表包含sale_date、product_id、sales_amount字段)。
  2. 配置大模型参数(通义千问 API 密钥或本地 Ollama 服务)。
  3. 启动 Spring Boot 应用,通过 Postman 调用接口:请求 URL:http://localhost:8080/api/query/nl2sql请求体:{“question”: “查询2024年Q3销售额大于10万的产品ID和销售额”}响应示例:
  4. json
  5. { “code”: 200, “msg”: “操作成功”, “data”: { “userQuestion”: “查询2024年Q3销售额大于10万的产品ID和销售额”, “generatedSql”: “SELECT product_id, SUM(sales_amount) AS total_sales FROM sales WHERE sale_date BETWEEN '2024-07-01' AND '2024-09-30' GROUP BY product_id HAVING SUM(sales_amount) > 100000”, “naturalLanguageAnswer”: “查询到2条数据,包含字段:product_id、total_sales”, “data”: [ {“product_id”: “P001”, “total_sales”: 156000}, {“product_id”: “P002”, “total_sales”: 123000} ] } }

2. 部署方案

  1. 容器化:使用 Docker 打包应用,配置Dockerfile:
  2. dockerfile
  3. FROM openjdk:17-jdk-slim VOLUME /tmp COPY target/intelligent-query-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT [“java”,”-jar”,”/app.jar”]
  4. 生产环境提议:大模型:若使用开源模型,部署在 GPU 服务器(如 A10、A100),通过 Ollama 或 vLLM 提供 API 服务。数据库:使用主从分离,查询走从库,避免影响业务库。缓存:Redis 集群,提升缓存可用性。监控:集成 Prometheus + Grafana,监控接口响应时间、SQL 执行效率。

五、扩展方向

  1. 复杂查询支持:优化 Prompt 支持多表关联、子查询、聚合函数(SUM/COUNT/GROUP BY)。
  2. 问题澄清:当用户问题不明确时(如 “查询最近的销售额”),大模型自动反问用户(“请问‘最近’指的是近 7 天、近 30 天还是近 90 天?”)。
  3. SQL 优化:大模型生成 SQL 后,通过工具类自动优化(如添加索引提示、简化查询)。
  4. 前端可视化:开发 Vue/React 前端,支持用户输入问题、展示 SQL 和结果表格、导出数据。
  5. 多数据源支持:适配 MySQL、Oracle、ClickHouse 等多种数据库,动态切换数据源。

通过以上方案,可快速实现一个高可用、安全的智能问数系统,降低业务人员的数据分析门槛,提升数据查询效率。核心在于 LangChain4j 对大模型的封装和 Prompt 工程的优化,确保 SQL 生成的准确性和安全性。

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 共1条

请登录后发表评论