基于Spring AI实现法律咨询AI助手

1. 技术选型

组件 类型
大语言模型 deepseek r1
embedding模型 bge-m3
框架选择 spring ai 1.0.1(JDK 17以上)
向量数据库 pgvector

2. 项目功能及接口文档

  1. 提供将法律法规文档通过embedding模型将知识导入向量库(导入文件格式只支持pdf和word)

POST http://127.0.0.1:8088/rag/init?filePath=D:\downloads\中华人民共和国劳动法_20181229.docx

  1. 提供法律咨询问答的接口

GET http://127.0.0.1:8088/rag/query?query=请根据劳动法告诉我,工厂每天早上九点上班,晚上十点下班,且没有支付足够响应报酬补偿的,一周无休,是否违法

3. 项目结构和依赖及配置项

pom.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.sikaryofficial</groupId>
    <artifactId>spring-ai-chat</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>17</java.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <!-- Spring Boot -->
        <spring-boot.version>3.2.4</spring-boot.version>
        <!-- Spring AI -->
        <spring-ai.version>1.0.1</spring-ai.version>
        <!-- Spring Alibaba AI -->
        <spring-ai-alibaba.version>1.0.0.2</spring-ai-alibaba.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>
                <artifactId>spring-ai-alibaba-bom</artifactId>
                <version>${spring-ai-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-autoconfigure-model-openai</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-autoconfigure-model-chat-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pdf-document-reader</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pgvector-store</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>io.projectreactor.netty</groupId>
            <artifactId>reactor-netty-http</artifactId>
        </dependency>

<!--        <dependency>-->
<!--            <groupId>org.springframework.ai</groupId>-->
<!--            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>-->
<!--            <version>1.0.0-M6</version>-->
<!--        </dependency>-->

    </dependencies>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
        <repository>
            <id>aliyunmaven</id>
            <name>aliyun</name>
            <url>https://maven.aliyun.com/repository/public</url>
        </repository>
    </repositories>

    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>https://maven.aliyun.com/repository/apache-snapshots</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件application.yml

yaml 复制代码
server:
  port: 8088

spring:
  datasource:
    url: jdbc:postgresql://192.168.232.194:5432/ai_data?currentSchema=public
    username: postgres
    password: ske@001
  jpa:
    show-sql: true
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.PostgreSQLDialect
    defer-datasource-initialization: true  # 允许先创建扩展再初始化表
  ai:
    openai:
      api-key: hismk
      base-url: http://192.168.201.230:3045/
      chat:
        options:
          model: deepseek-r1:32b_awq
          max_tokens: 1024
          temperature: 0.2
          top_p: 0.9
      embedding:
        options:
          model: bge-m3
        base-url: http://192.168.201.250:11434
        enabled: true



# 打印日志
logging:
  level:
    com.sikaryofficial: DEBUG

提示词模板

makefile 复制代码
当前日期:{current_date}

请基于以下上下文信息回答问题。请遵循以下规则:
1. 回答要专业、准确
2. 如果上下文不包含答案,请明确说明"根据提供的信息无法确定"
3. 使用中文回答
4. 保持回答简洁明了

上下文:
{context}

问题:{input}

请按以下格式回答:
【回答】: (你的回答)
【来源】: (指出回答基于哪些上下文片段,用1,2,3编号)

4. 功能实现

a. 配置相关

  1. chatClient配置
kotlin 复制代码
package com.sikaryofficial.ai.config;

/**
 * @author : wuweihong
 * @desc : TODO  请填写你的功能描述
 * @date : 2025-11-04
 */


import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {
	@Bean
	public ChatClient chatClient(ChatClient.Builder builder) {
		return builder.defaultAdvisors(new SimpleLoggerAdvisor()).build();
	}

}
  1. 向量存储配置
kotlin 复制代码
package com.sikaryofficial.ai.config;

/**
 * @author : wuweihong
 * @desc : TODO  请填写你的功能描述
 * @date : 2025-11-04
 */


import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.pgvector.PgVectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

@Configuration
public class VectorStoreConfig {

	@Bean
	public VectorStore vectorStore(EmbeddingModel embeddingClient, JdbcTemplate jdbcTemplate) {
		return PgVectorStore.builder(jdbcTemplate, embeddingClient).dimensions(1024).vectorTableName("law_articles").build();
	}

	@Bean
	public JdbcTemplate jdbcTemplate(DataSource dataSource) {
		return new JdbcTemplate(dataSource);
	}
}
  1. WebClient配置
kotlin 复制代码
package com.sikaryofficial.ai.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;

/**
 * @author : wuweihong
 * @desc : TODO  请填写你的功能描述
 * @date : 2025-07-29
 */

@Configuration
public class WebClientConfig {

	@Bean
	public WebClient webClient() {
		// 创建并配置 Netty 的 HttpClient
		HttpClient httpClient = HttpClient.create()
				.headers(headers -> headers
						.remove("Transfer-Encoding")  // 显式移除分块头
						.set("Connection", "close")   // 强制关闭连接避免分块
				)
				.compress(false);  // 禁用压缩

		return WebClient.builder()
				.clientConnector(new ReactorClientHttpConnector(httpClient))
				.build();
	}
}

ⅰ. 接口实现

  1. 文档导入向量数据库的接口实现
ini 复制代码
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.ai.document.Document;
import org.springframework.ai.reader.ExtractedTextFormatter;
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
import org.springframework.ai.reader.pdf.ParagraphPdfDocumentReader;
import org.springframework.ai.reader.pdf.config.PdfDocumentReaderConfig;
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.List;

@Service
@RequiredArgsConstructor
public class DocumentService {

	private final VectorStore vectorStore;

	public void loadAndStoreDocuments(String filePath) throws IOException {
		List<Document> documents = null;
		if (StringUtils.isNotBlank(filePath) && filePath.endsWith(".pdf")) {
			// 读取PDF文档
			documents = loadPDFDocuments(filePath);
			System.out.println("Total number of documents: " + documents.size());
		} else if (StringUtils.isNotBlank(filePath) && filePath.endsWith(".docx")) {
			// 读取DOCX文档
			documents = loadDOCXDocuments(filePath);
			System.out.println("Total number of documents: " + documents.size());
		}

		// 文本分割
		TokenTextSplitter splitter = new TokenTextSplitter();
		List<Document> splitDocs = splitter.apply(documents);

		// 存储到向量数据库
		vectorStore.add(splitDocs);
	}

	public List<Document> loadPDFDocuments(String filePath) throws IOException {
		// PDF文档读取配置
		PdfDocumentReaderConfig config = PdfDocumentReaderConfig.builder()
				.withPageExtractedTextFormatter(new ExtractedTextFormatter.Builder()
						.withNumberOfTopTextLinesToDelete(0)
						.build())
				.build();

		//先使用目录分段读取方式读取PDF并分段落
		Resource resource = new FileSystemResource(filePath);
		ParagraphPdfDocumentReader pdfReader = new ParagraphPdfDocumentReader(resource, config);
		List<Document> documents = pdfReader.get();
		System.out.println("Total number of documents1: " + documents.size());
		if (documents.isEmpty()) {
			//如果沒有获取到目录,在改用分页方式拆分
			PagePdfDocumentReader pdfReader2 = new PagePdfDocumentReader(resource);
			documents = pdfReader2.get();
			System.out.println("Total number of documents2: " + documents.size());
		}
		return documents;
	}

	public List<Document> loadDOCXDocuments(String filePath) throws IOException {

		// 读取DOCX文档
		Resource resource = new FileSystemResource(filePath);
		TikaDocumentReader docxReader = new TikaDocumentReader(resource);
		List<Document> documents = docxReader.get();
		System.out.println("Total number of documents: " + documents.size());
		return documents;
	}
  1. rag问答接口实现类
java 复制代码
package com.sikaryofficial.ai.service;

/**
 * @author : wuweihong
 * @desc : TODO  请填写你的功能描述
 * @date : 2025-11-04
 */


import lombok.RequiredArgsConstructor;
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.document.Document;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class RagService {

	private final ChatClient chatClient;
	private final VectorStore vectorStore;

	@Value("classpath:/prompts/rag-prompt-template.st")
	private Resource ragPromptTemplate;

	public String processQuery(String query) {
		// 1. 检索相关文档
		List<Document> similarDocuments = vectorStore.similaritySearch(query);
		System.out.println("检索到相关文档:" + similarDocuments.size());

		// 2. 构建上下文
		String context = similarDocuments.stream()
				.map(Document::getFormattedContent)
				.collect(Collectors.joining("\n\n"));

		// 3. 构建提示词
		PromptTemplate promptTemplate = new PromptTemplate(ragPromptTemplate);
		Prompt prompt = promptTemplate.create(Map.of(
				"current_date", new Date().toLocaleString(),
				"input", query,
				"context", context
		));
		// 4. 调用LLM生成回答
		return chatClient.prompt(prompt).call().content();
	}
}

5. 效果自测

  1. 导入接口测试
  1. 智能问答接口测试
相关推荐
NO.10243 小时前
11.4八股
java·linux·数据库
乐悠小码3 小时前
Java设计模式精讲---01工厂方法模式
java·设计模式·工厂方法模式
cherry--3 小时前
集合(开发重点)
java·开发语言
寻星探路3 小时前
测试开发话题10---自动化测试常用函数(2)
java·前端·python
api_180079054603 小时前
请求、认证与响应数据解析:1688 商品 API 接口深度探秘
java·大数据·开发语言·mysql·数据挖掘
陈果然DeepVersion3 小时前
Java大厂面试真题:Spring Boot+Kafka+AI智能客服场景全流程解析(十二)
java·spring boot·ai·kafka·面试题·向量数据库·rag
yours_Gabriel3 小时前
【设计模式】UML和设计原则
java·设计模式·uml
唐古乌梁海3 小时前
【Java】JVM 内存区域划分
java·开发语言·jvm
陈果然DeepVersion4 小时前
Java大厂面试真题:Spring Boot+Kafka+AI智能客服场景全流程解析(九)
java·人工智能·spring boot·微服务·kafka·面试题·rag