1. 项目概述
作为一名长期奋战在Java开发一线的技术老兵,今天想和大家分享一个将AI能力与传统Java开发相结合的实战案例——基于Spring AI和DeepSeek-7大模型的智能客服系统开发。这个项目完美展现了如何通过Function Calling技术,让AI模型与我们的业务系统实现深度协同。
1.1 核心需求解析
想象一下这样的场景:一家职业教育机构需要7×24小时在线的智能客服,能够:
- 理解学员的咨询意图
- 根据学员背景推荐合适课程
- 引导完成试听预约
- 自动记录预约信息到数据库
传统做法需要:
- 开发复杂的NLP模块理解用户意图
- 编写大量业务逻辑处理代码
- 设计复杂的流程状态管理
而借助Spring AI的Function Calling能力,我们可以:
- 让AI负责自然语言交互和意图理解
- 让Java程序专注数据库操作等确定性任务
- 通过定义清晰的接口实现两者无缝协作
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 整体架构
系统采用分层设计:
code复制前端页面 → Spring Boot控制器 → AI模型服务 → 业务工具类 → 数据库
关键组件:
- DeepSeek-7:负责自然语言理解与生成
- Spring AI:提供AI集成框架和Function Calling支持
- MyBatis-Plus:简化数据库操作
- MySQL:存储课程、校区和预约数据
2.2 核心流程
-
对话理解阶段:
- 用户输入自然语言请求
- AI分析意图并决定是否需要调用工具
- 如需调用,返回工具名称和参数
-
工具执行阶段:
- Spring AI自动调用对应Java方法
- 执行数据库查询/更新操作
- 将结果返回给AI模型
-
响应生成阶段:
- AI整合工具执行结果
- 生成自然语言回复
- 返回给前端展示
3. 数据库设计与实现
3.1 数据模型设计
我们设计了三个核心表:
课程表(course)
sql复制CREATE TABLE `course` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL COMMENT '学科名称',
`edu` int NOT NULL COMMENT '学历要求',
`type` varchar(50) NOT NULL COMMENT '课程类型',
`price` bigint NOT NULL COMMENT '课程价格',
`duration` int unsigned NOT NULL COMMENT '学习时长(天)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='学科表';
校区表(school)
sql复制CREATE TABLE `school` (
`id` int unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL COMMENT '校区名称',
`city` varchar(50) DEFAULT NULL COMMENT '所在城市',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='校区表';
预约表(course_reservation)
sql复制CREATE TABLE `course_reservation` (
`id` int NOT NULL AUTO_INCREMENT,
`course` varchar(50) NOT NULL COMMENT '预约课程',
`student_name` varchar(255) NOT NULL COMMENT '学生姓名',
`contact_info` varchar(255) NOT NULL COMMENT '联系方式',
`school` varchar(50) DEFAULT NULL COMMENT '预约校区',
`remark` text COMMENT '备注',
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
3.2 MyBatis-Plus集成
使用MyBatis-Plus简化CRUD操作:
- 添加依赖:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.10.1</version>
</dependency>
- 配置数据源:
yaml复制spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring-ai-deepseek
username: root
password: ${MYSQL_PWD}
- 使用代码生成器快速创建Mapper和Service:
java复制@MapperScan("com.springai.deepseek.mapper")
@SpringBootApplication
public class SpringAiDeepseekApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiDeepseekApplication.class, args);
}
}
4. Function Calling实现
4.1 工具类定义
创建CourseTools类封装三个核心功能:
java复制@Component
@RequiredArgsConstructor
public class CourseTools {
private final ICourseService courseService;
private final ISchoolService schoolService;
private final ICourseReservationService reservationService;
@Tool(description = "根据条件查询课程")
public List<Course> queryCourse(@ToolParam CourseQuery query) {
// 构建查询条件
QueryChainWrapper<Course> wrapper = courseService.query();
wrapper.eq(query.getType() != null, "type", query.getType())
.le(query.getEdu() != null, "edu", query.getEdu());
// 处理排序
if(query.getSorts() != null) {
for (CourseQuery.Sort sort : query.getSorts()) {
wrapper.orderBy(true, sort.getAsc(), sort.getField());
}
}
return wrapper.list();
}
@Tool(description = "查询所有校区")
public List<School> queryAllSchools() {
return schoolService.list();
}
@Tool(description = "生成课程预约单")
public String generateReservation(
String courseName, String studentName,
String contactInfo, String school, String remark) {
// 创建预约记录
CourseReservation reservation = new CourseReservation();
reservation.setCourse(courseName);
reservation.setStudentName(studentName);
reservation.setContactInfo(contactInfo);
reservation.setSchool(school);
reservation.setRemark(remark);
reservationService.save(reservation);
return String.valueOf(reservation.getId());
}
}
4.2 查询参数设计
为课程查询设计专门的参数类:
java复制@Data
public class CourseQuery {
@ToolParam(description = "课程类型:编程、设计、自媒体、其它")
private String type;
@ToolParam(description = "学历要求:0-无、1-初中、2-高中、3-大专、4-本科以上")
private Integer edu;
@ToolParam(description = "排序方式")
private List<Sort> sorts;
@Data
public static class Sort {
@ToolParam(description = "排序字段: price或duration")
private String field;
@ToolParam(description = "是否升序")
private Boolean asc;
}
}
5. AI交互配置
5.1 系统提示词设计
定义详细的系统提示词指导AI行为:
java复制public static final String SERVICE_SYSTEM_PROMPT = """
【系统角色】
你是"黑马程序员"的智能客服小黑,需遵守以下规则:
【课程咨询】
1. 必须先获取:
- 学习兴趣(编程/设计/自媒体)
- 学历背景
2. 调用queryCourse查询课程
3. 避免直接透露价格
4. 确认意向后再进入预约
【课程预约】
1. 询问意向校区
2. 调用queryAllSchools查询校区
3. 收集:
- 姓名
- 联系方式
- 备注(可选)
4. 确认信息后调用generateReservation
【展示要求】
- 用表格展示课程和校区
- 隐藏敏感信息
""";
5.2 ChatClient配置
配置专用的ChatClient实例:
java复制@Bean
public ChatClient serviceChatClient(
DeepSeekChatModel model,
ChatMemory chatMemory,
CourseTools courseTools) {
return ChatClient.builder(model)
.defaultSystem(SERVICE_SYSTEM_PROMPT)
.defaultAdvisors(
SimpleLoggerAdvisor.builder().build(),
MessageChatMemoryAdvisor.builder(chatMemory).build())
.defaultTools(courseTools)
.build();
}
6. 控制器实现
6.1 接口设计
创建REST接口处理用户请求:
java复制@RestController
@RequestMapping("/ai")
@RequiredArgsConstructor
public class CustomerServiceController {
private final ChatClient serviceChatClient;
private final ChatHistoryRepository chatHistoryRepo;
@RequestMapping(value = "/service", produces = "text/html;charset=utf-8")
public String service(String prompt, String chatId) {
// 保存会话ID
chatHistoryRepo.save("service", chatId);
// 调用AI服务
return serviceChatClient.prompt()
.user(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.call()
.content();
}
}
6.2 流式响应版本
如需支持流式响应:
java复制@RequestMapping(value = "/service", produces = "text/event-stream;charset=utf-8")
public Flux<String> streamService(String prompt, String chatId) {
chatHistoryRepo.save("service", chatId);
return serviceChatClient.prompt()
.user(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.stream()
.content();
}
7. 实战演示
7.1 典型对话流程
-
用户咨询:
- 用户:"我想学编程,高中学历"
-
AI响应:
- 调用queryCourse查询符合条件课程
- 返回推荐课程表格
-
用户选择:
- 用户:"我想了解鸿蒙开发"
-
AI引导预约:
- 询问意向校区
- 调用queryAllSchools获取校区列表
- 收集联系方式等信息
- 调用generateReservation创建预约
7.2 数据库效果
预约成功后,数据库中将新增记录:
code复制id: 123
course: "鸿蒙应用开发"
student_name: "张三"
contact_info: "13800138000"
school: "北京校区"
remark: "周末试听"
8. 开发经验分享
8.1 核心技巧
-
工具设计原则:
- 每个工具应保持单一职责
- 参数设计要全面但不过度
- 返回结果结构要清晰
-
提示词优化:
- 明确界定AI和工具的职责边界
- 规定必须收集的信息项
- 设定回复格式要求
-
异常处理:
- 工具方法内做好参数校验
- 返回友好的错误信息
- 记录详细日志
8.2 常见问题
-
工具不被调用:
- 检查@Tool注解的description是否清晰
- 确认系统提示词中提到了工具使用场景
- 验证参数定义是否完整
-
参数解析失败:
- 确保@ToolParam的description准确描述参数用途
- 复杂对象需要提供足够的字段说明
- 可以添加示例值
-
会话状态混乱:
- 确保每次请求传递正确的chatId
- 定期清理过期的会话记录
- 考虑添加会话超时机制
9. 扩展思考
这种架构模式可以应用于许多类似场景:
-
电商客服:
- 商品查询
- 订单状态查询
- 退换货申请
-
医疗咨询:
- 症状分析
- 医生排班查询
- 预约挂号
-
旅游服务:
- 景点推荐
- 酒店查询
- 行程规划
关键是将非结构化的自然语言交互与结构化的业务系统通过明确定义的接口解耦,充分发挥各自优势。
