一、引言
在当今数字化时代,智能客服系统已经成为企业提升客户服务质量、提高运营效率的重要工具。借助人工智能技术,智能客服能够快速响应用户的问题,提供准确的解决方案,大大提升了用户体验。本文将基于真实项目案例,详细介绍如何使用 Spring Boot 3.2、Spring AI 1.0.0 - M4、MyBatis、H2 数据库以及 DeepSeek API 构建一个现代化的智能客服系统。从环境搭建到前端交互,从故障排查到性能优化,为你提供完整的技术解决方案。
二、技术架构总览
2.1 核心技术栈
- 后端框架:Spring Boot 3.2.0 是一个快速开发框架,它简化了 Spring 应用的搭建和部署过程,提供了自动配置和约定优于配置的原则,让开发者可以更专注于业务逻辑的实现。AI 集成:Spring AI 1.0.0 - M4 是一个与 OpenAI 兼容的 AI 集成框架,它提供了统一的接口来调用不同的 AI 模型,方便开发者将 AI 功能集成到应用中。数据访问:MyBatis 3.0.3 是一个优秀的持久层框架,它支持自定义 SQL、存储过程和高级映射,与 H2 内存数据库结合使用,可以快速实现数据的存储和读取。前端技术:Thymeleaf 是一个服务器端的 Java 模板引擎,它可以与 Spring Boot 无缝集成,结合 Bootstrap 5.3 和 JavaScript,实现响应式和交互式的前端界面。AI 模型:DeepSeek Chat API 是一个强大的 AI 模型,它可以理解自然语言并生成高质量的回复,为智能客服系统提供了强大的智能支持。构建工具:Maven 3.x 是一个项目管理和构建工具,它可以自动管理项目的依赖和构建过程,JDK 17 是 Java 的最新长期支持版本,提供了更好的性能和安全性。
2.2 项目结构
springai-deepseek-chatbot/├── pom.xml # Maven依赖配置├── src/main/│ ├── java/com/example/chatbot/│ │ ├── ChatbotApplication.java # 主启动类│ │ ├── controller/ # 控制层│ │ │ ├── ChatbotController.java│ │ │ └── PageController.java│ │ ├── service/ # 服务层│ │ │ └── ChatbotService.java│ │ ├── mapper/ # 数据访问层│ │ │ └── ChatRecordMapper.java│ │ ├── entity/ # 实体类│ │ │ └── ChatRecord.java│ │ └── dto/ # 数据传输对象│ │ ├── ChatRequest.java│ │ └── ChatResponse.java│ └── resources/│ ├── application.yml # 配置文件│ ├── schema.sql # 数据库表结构│ ├── data.sql # 初始化数据│ └── templates/ # 前端模板│ └── chat.html # 聊天界面
这种分层架构的设计模式,使得代码结构清晰,便于维护和扩展。控制层负责处理用户的请求,服务层负责业务逻辑的处理,数据访问层负责与数据库的交互,实体类用于封装数据,数据传输对象用于在不同层之间传递数据。
三、环境搭建与配置
3.1 Maven 依赖配置
首先,我们需要创建一个 pom.xml
文件来配置项目的依赖。以下是完整的 pom.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 https://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.0</version> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>springai-deepseek-chatbot</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springai-deepseek-chatbot</name> <description>智能客服系统基于Spring Boot + Spring AI + MyBatis + DeepSeek</description> <properties> <java.version>17</java.version> <spring-ai.version>1.0.0-M4</spring-ai.version> </properties> <dependencies> <!-- Spring Boot Web依赖,用于构建Web应用 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Boot Thymeleaf依赖,用于模板引擎 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!-- MyBatis Spring Boot Starter,用于数据访问 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency> <!-- Spring AI OpenAI依赖(支持OpenAI兼容API如DeepSeek) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- H2数据库依赖,用于内存数据库 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <!-- JSON处理依赖,用于处理JSON数据 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- 参数校验依赖,用于校验用户输入 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </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> </repositories></project>
在这个 pom.xml
文件中,我们配置了 Spring Boot、Spring AI、MyBatis、H2 数据库等相关的依赖。同时,我们还指定了 Spring AI 的版本为 1.0.0 - M4
,并配置了 Spring Milestones 仓库,以便获取最新的 Spring AI 版本。
3.2 应用配置
接下来,我们需要在 application.yml
中配置完整的应用参数:
server: port: 8080 servlet: context-path: / encoding: charset: UTF-8spring: application: name: springai-deepseek-chatbot # H2内存数据库配置 datasource: driver-class-name: org.h2.Driver url: jdbc:h2:mem:chatbot;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE username: sa password: h2: console: enabled: true path: /h2-console # Thymeleaf模板配置 thymeleaf: prefix: classpath:/templates/ suffix: .html encoding: UTF-8 cache: false # Spring AI配置(使用OpenAI兼容API连接DeepSeek) ai: openai: api-key: your-deepseek-api-key base-url: https://api.deepseek.com chat: options: model: deepseek-chat temperature: 0.7 # 数据库初始化配置 sql: init: mode: always schema-locations: classpath:schema.sql data-locations: classpath:data.sql# MyBatis配置mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.chatbot.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 应用配置app: chatbot: system-prompt: | 你是一个专业的智能客服助手,名字叫小AI。请遵循以下规则: 1. 始终保持友好、耐心和专业的态度 2. 提供准确、有帮助的信息 3. 如果不确定答案,请诚实说明并建议联系人工客服 4. 回答要简洁明了,避免过于冗长 5. 使用中文回复 max-history: 5 timeout: 30000
在这个配置文件中,我们配置了服务器的端口、上下文路径和编码格式。同时,我们还配置了 H2 内存数据库的连接信息,包括驱动类名、URL、用户名和密码,并启用了 H2 控制台。对于 Thymeleaf 模板,我们指定了模板的前缀、后缀、编码格式和缓存策略。在 Spring AI 配置中,我们设置了 DeepSeek API 的密钥和基础 URL,以及使用的模型和温度参数。对于数据库初始化,我们指定了模式为 always
,并指定了数据库表结构和初始化数据的文件路径。在 MyBatis 配置中,我们指定了映射文件的位置、实体类的包名,以及一些配置选项。最后,我们还配置了智能客服的系统提示、最大历史记录数和超时时间。
3.3 数据库表结构
我们需要创建一个 schema.sql
文件来定义聊天记录表的结构:
-- 智能客服系统数据库表结构-- 作者: yyvb-- 数据库: H2-- 聊天记录表CREATE TABLE IF NOT EXISTS chat_record ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_message TEXT NOT NULL, bot_response TEXT NOT NULL, created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, session_id VARCHAR(100) NOT NULL);
这个 SQL 语句创建了一个名为 chat_record
的表,用于存储用户与智能客服的聊天记录。表中包含了自增的主键 id
、用户消息 user_message
、客服回复 bot_response
、创建时间 created_time
和会话 ID session_id
。
四、核心代码实现
4.1 主启动类
主启动类 ChatbotApplication.java
是整个应用的入口点,以下是完整的代码:
package com.example.chatbot;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * 智能客服系统主应用类 * 基于Spring Boot + Spring AI + MyBatis + DeepSeek * * @author yyvb */@SpringBootApplication@MapperScan("com.example.chatbot.mapper")public class ChatbotApplication { public static void main(String[] args) { SpringApplication.run(ChatbotApplication.class, args); System.out.println("=============================================="); System.out.println("智能客服系统启动成功!"); System.out.println("访问地址: http://localhost:8080"); System.out.println("H2控制台: http://localhost:8080/h2-console"); System.out.println("=============================================="); }}
在这个类中,我们使用 @SpringBootApplication
注解来标记这是一个 Spring Boot 应用,并使用 @MapperScan
注解来扫描 MyBatis 的映射器接口。在 main
方法中,我们调用 SpringApplication.run
方法来启动应用,并打印出启动成功的信息和访问地址。
4.2 核心服务层
核心服务层 ChatbotService.java
负责处理用户的聊天请求,并与 AI 模型和数据库进行交互,以下是完整的代码:
package com.example.chatbot.service;import com.example.chatbot.dto.ChatRequest;import com.example.chatbot.dto.ChatResponse;import com.example.chatbot.entity.ChatRecord;import com.example.chatbot.mapper.ChatRecordMapper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.ai.chat.client.ChatClient;import org.springframework.ai.chat.messages.Message;import org.springframework.ai.chat.messages.SystemMessage;import org.springframework.ai.chat.messages.UserMessage;import org.springframework.ai.chat.messages.AssistantMessage;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Service;import java.time.LocalDateTime;import java.util.*;import java.util.concurrent.CompletableFuture;import java.util.concurrent.TimeUnit;/** * 智能客服核心服务类 * 基于Spring AI + MyBatis实现智能对话 * * @author yyvb */@Servicepublic class ChatbotService { private static final Logger logger = LoggerFactory.getLogger(ChatbotService.class); private final ChatClient chatClient; private final ChatRecordMapper chatRecordMapper; @Value("${app.chatbot.system-prompt}") private String systemPrompt; @Value("${app.chatbot.max-history:5}") private int maxHistory; @Value("${app.chatbot.timeout:30000}") private long timeout; @Autowired public ChatbotService(ChatClient.Builder chatClientBuilder, ChatRecordMapper chatRecordMapper) { this.chatClient = chatClientBuilder.build(); this.chatRecordMapper = chatRecordMapper; } /** * 处理用户聊天请求 */ public ChatResponse chat(ChatRequest request) { long startTime = System.currentTimeMillis(); try { // 参数验证 if (request.getMessage() == null || request.getMessage().trim().isEmpty()) { return ChatResponse.error("消息内容不能为空", request.getSessionId()); } String userInput = request.getMessage().trim(); // 构建对话上下文 List<Message> messages = buildConversationContext(request.getSessionId(), userInput); // 调用AI获取响应 String aiResponse = callAIWithTimeout(messages); long responseTime = System.currentTimeMillis() - startTime; // 保存聊天记录 saveChatRecord(request.getSessionId(), userInput, aiResponse); logger.info("会话[{}]处理完成,耗时:{}ms", request.getSessionId(), responseTime); return new ChatResponse(aiResponse, request.getSessionId(), responseTime); } catch (Exception e) { long responseTime = System.currentTimeMillis() - startTime; logger.error("处理聊天请求时发生错误,会话ID: {}, 耗时: {}ms", request.getSessionId(), responseTime, e); return ChatResponse.error("服务暂时不可用,请稍后重试", request.getSessionId()); } } /** * 构建对话上下文,包含历史记录 */ private List<Message> buildConversationContext(String sessionId, String userInput) { List<Message> messages = new ArrayList<>(); // 添加系统提示 messages.add(new SystemMessage(systemPrompt)); // 获取历史对话记录 List<ChatRecord> history = getRecentChatHistory(sessionId, maxHistory); // 添加历史对话到上下文(按时间顺序) for (ChatRecord record : history) { messages.add(new UserMessage(record.getUserMessage())); messages.add(new AssistantMessage(record.getBotResponse())); } // 添加当前用户输入 messages.add(new UserMessage(userInput)); return messages; } /** * 带超时的AI调用 */ private String callAIWithTimeout(List<Message> messages) throws Exception { CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { return chatClient.prompt() .messages(messages) .call() .content(); } catch (Exception e) { logger.error("调用Spring AI服务失败", e); throw new RuntimeException("AI服务调用失败", e); } }); return future.get(timeout, TimeUnit.MILLISECONDS); } /** * 保存聊天记录到数据库 */ private void saveChatRecord(String sessionId, String userMessage, String botResponse) { try { ChatRecord record = new ChatRecord(userMessage, botResponse, sessionId); chatRecordMapper.insert(record); } catch (Exception e) { logger.error("保存聊天记录失败,会话ID: {}", sessionId, e); } } /** * 获取最近的聊天历史记录 */ private List<ChatRecord> getRecentChatHistory(String sessionId, int limit) { try { return chatRecordMapper.selectBySessionId(sessionId); } catch (Exception e) { logger.error("获取聊天历史失败,会话ID: {}", sessionId, e); return new ArrayList<>(); } } /** * 获取系统统计信息 */ public Map<String, Object> getSystemStats() { try { long totalChats = chatRecordMapper.count(); Map<String, Object> stats = new HashMap<>(); stats.put("totalChats", totalChats); stats.put("status", "运行正常"); stats.put("timestamp", LocalDateTime.now()); return stats; } catch (Exception e) { logger.error("获取系统统计信息失败", e); Map<String, Object> errorStats = new HashMap<>(); errorStats.put("status", "获取统计信息失败"); errorStats.put("error", e.getMessage()); errorStats.put("timestamp", LocalDateTime.now()); return errorStats; } }}
在这个类中,我们使用 @Service
注解将其标记为一个服务类。通过 @Autowired
注解注入 ChatClient.Builder
和 ChatRecordMapper
,并在构造函数中初始化 chatClient
和 chatRecordMapper
。chat
方法是处理用户聊天请求的核心方法,它首先进行参数验证,然后构建对话上下文,调用 AI 获取响应,保存聊天记录,并返回响应结果。buildConversationContext
方法用于构建对话上下文,包括系统提示、历史对话记录和当前用户输入。callAIWithTimeout
方法使用 CompletableFuture
实现了带超时的 AI 调用。saveChatRecord
方法用于将聊天记录保存到数据库中。getRecentChatHistory
方法用于获取最近的聊天历史记录。getSystemStats
方法用于获取系统的统计信息,如总聊天记录数、系统状态和时间戳。
4.3 RESTful 控制器
RESTful 控制器 ChatbotController.java
负责处理用户的 HTTP 请求,并调用服务层的方法来处理业务逻辑,以下是完整的代码:
package com.example.chatbot.controller;import com.example.chatbot.dto.ChatRequest;import com.example.chatbot.dto.ChatResponse;import com.example.chatbot.entity.ChatRecord;import com.example.chatbot.service.ChatbotService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.List;import java.util.Map;import java.util.UUID;/** * 智能客服控制器 * 提供聊天接口和管理功能 * * @author yyvb */@RestController@RequestMapping("/api/chat")@CrossOrigin(origins = "*")public class ChatbotController { private static final Logger logger = LoggerFactory.getLogger(ChatbotController.class); private final ChatbotService chatbotService; @Autowired public ChatbotController(ChatbotService chatbotService) { this.chatbotService = chatbotService; } /** * 处理聊天请求 */ @PostMapping("/message") public ResponseEntity<ChatResponse> chat(@RequestBody ChatRequest request) { try { // 如果没有会话ID,生成一个新的 if (request.getSessionId() == null || request.getSessionId().trim().isEmpty()) { request.setSessionId(UUID.randomUUID().toString()); } logger.info("收到聊天请求,会话ID: {}, 消息: {}", request.getSessionId(), request.getMessage()); ChatResponse response = chatbotService.chat(request); return ResponseEntity.ok(response); } catch (Exception e) { logger.error("处理聊天请求时发生异常", e); ChatResponse errorResponse = ChatResponse.error("系统内部错误", request.getSessionId()); return ResponseEntity.status(500).body(errorResponse); } } /** * 获取系统统计信息 */ @GetMapping("/stats") public ResponseEntity<Map<String, Object>> getSystemStats() { try { Map<String, Object> stats = chatbotService.getSystemStats(); return ResponseEntity.ok(stats); } catch (Exception e) { logger.error("获取系统统计信息失败", e); return ResponseEntity.status(500).build(); } } /** * 健康检查接口 */ @GetMapping("/health") public ResponseEntity<Map<String, Object>> healthCheck() { return ResponseEntity.ok(Map.of( "status", "运行正常", "timestamp", System.currentTimeMillis() )); }}
在这个类中,我们使用 @RestController
注解将其标记为一个 RESTful 控制器,并使用 @RequestMapping
注解指定了请求的基础路径。通过 @Autowired
注解注入 ChatbotService
。chat
方法处理用户的聊天请求,它首先检查是否有会话 ID,如果没有则生成一个新的会话 ID,然后调用服务层的 chat
方法处理请求,并返回响应结果。getSystemStats
方法用于获取系统的统计信息,调用服务层的 getSystemStats
方法,并返回响应结果。healthCheck
方法是一个健康检查接口,用于检查系统是否正常运行,返回系统的状态和时间戳。
五、常见故障分析与解决
5.1 Spring AI Bean 配置冲突
错误信息:
The bean 'chatClientBuilder', defined in class path resource [org/springframework/ai/autoconfigure/chat/client/ChatClientAutoConfiguration.class], could not be registered. A bean with that name has already been defined
原因分析:Spring AI 自动配置已经提供了 ChatClient.Builder
Bean,自定义配置产生了冲突。
解决方案:
// 错误做法:自定义ChatClient配置@Configurationpublic class SpringAIConfig { @Bean public ChatClient.Builder chatClientBuilder(ChatModel chatModel) { return ChatClient.builder(chatModel); }}// 正确做法:直接使用Spring AI自动配置@Servicepublic class ChatbotService { private final ChatClient chatClient; @Autowired public ChatbotService(ChatClient.Builder chatClientBuilder) { this.chatClient = chatClientBuilder.build(); }}
在错误做法中,我们自定义了 ChatClient.Builder
的 Bean,这会与 Spring AI 自动配置的 Bean 产生冲突。正确做法是直接使用 Spring AI 自动配置的 ChatClient.Builder
,避免手动配置。
5.2 DeepSeek API 连接超时
错误信息:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://api.openai.com/v1/chat/completions": Connection timed out
原因分析:Spring AI 默认连接到 OpenAI 官方 API,需要配置 base-url
指向 DeepSeek。
解决方案:
spring: ai: openai: api-key: your-deepseek-api-key base-url: https://api.deepseek.com # 关键配置 chat: options: model: deepseek-chat temperature: 0.7
在 application.yml
中,我们需要将 base-url
配置为 DeepSeek 的 API 地址,这样 Spring AI 就会连接到 DeepSeek API 而不是 OpenAI API。
5.3 MyBatis Plus 与 Spring Boot 3.x 兼容性问题
错误信息:
Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
原因分析:MyBatis Plus 低版本与 Spring Boot 3.x 存在兼容性问题。
解决方案:
<!-- 移除MyBatis Plus --><!-- <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.5</version></dependency> --><!-- 使用标准MyBatis --><dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>3.0.3</version></dependency>
在 pom.xml
中,我们需要移除 MyBatis Plus 的依赖,并使用标准的 MyBatis 依赖,以解决兼容性问题。
六、系统优化与最佳实践
6.1 性能优化
- 异步处理:使用
CompletableFuture
实现 AI 调用超时控制。在 ChatbotService
类的 callAIWithTimeout
方法中,我们使用 CompletableFuture
来异步调用 AI 服务,并设置了超时时间,避免了长时间的阻塞。连接池管理:合理配置 H2 数据库连接参数。可以在 application.yml
中配置 H2 数据库的连接池参数,如最大连接数、最小空闲连接数等,以提高数据库的性能。响应缓存:针对常见问题实现缓存机制。可以使用 Redis 等缓存中间件,将常见问题的响应结果缓存起来,当用户再次提出相同问题时,直接从缓存中获取响应,减少 AI 调用的次数。6.2 安全加固
- API 密钥管理:使用环境变量管理敏感信息。将 DeepSeek API 密钥存储在环境变量中,而不是硬编码在配置文件中,避免密钥泄露。输入验证:对用户输入进行安全校验和转义。在
ChatbotService
类的 chat
方法中,我们对用户输入的消息进行了参数验证,确保消息内容不为空。同时,可以使用正则表达式等方法对用户输入进行安全校验和转义,防止 SQL 注入和 XSS 攻击。会话管理:实现安全的会话 ID 生成和验证。在 ChatbotController
类的 chat
方法中,我们使用 UUID
生成会话 ID,并在处理请求时对会话 ID 进行验证,确保会话的安全性。6.3 监控告警
- 系统指标:集成 Actuator 监控端点。Actuator 是 Spring Boot 提供的一个监控和管理工具,它可以提供系统的健康状态、内存使用情况、线程池状态等信息。可以在
pom.xml
中添加 Actuator 依赖,并在 application.yml
中配置相关参数,以启用 Actuator 监控端点。日志记录:完善的日志记录体系。在项目中使用 SLF4J
进行日志记录,记录系统的运行状态、错误信息等。可以在 application.yml
中配置日志的级别和输出格式,以便于调试和监控。性能监控:响应时间和成功率监控。可以使用 AOP 技术,在 ChatbotService
类的 chat
方法中记录响应时间和成功率,并将这些指标存储到数据库或监控系统中,以便于分析和优化。七、部署与运维
7.1 本地开发环境启动
# 克隆项目git clone <project-url>cd springai-deepseek-chatbot# 配置DeepSeek API密钥export DEEPSEEK_API_KEY=your-api-key# 启动应用mvn spring-boot:run
在本地开发环境中,我们可以使用以上命令来克隆项目、配置 DeepSeek API 密钥并启动应用。
7.2 访问地址
- 聊天界面:http://localhost:8080通过以上访问地址,我们可以访问智能客服系统的聊天界面。
八、总结
通过本项目的实战开发,我们成功构建了一个基于现代化技术栈的智能客服系统。项目涵盖了完整的技术架构,包括 Spring Boot 3.2、Spring AI、MyBatis 和 H2 数据库;实用的功能特性,如智能对话、上下文记忆、会话管理和系统监控;丰富的前端体验,如响应式设计、实时聊天和快捷回复;详细的故障处理,如常见问题分析与解决方案;规范的代码结构,如分层架构、注解驱动和配置管理。