跳到主要内容SpringAI + Deepseek 大模型应用开发实战:对话机器人、Function Calling 与 RAG | 极客日志JavaAIjava算法
SpringAI + Deepseek 大模型应用开发实战:对话机器人、Function Calling 与 RAG
SpringAI 结合 Deepseek 大模型的应用开发实践涵盖了对话机器人基础、会话记忆管理、Function Calling 智能客服构建及 RAG 检索增强生成技术。内容涉及 ChatClient 配置、ChatMemory 存储策略(内存与数据库)、Tool 定义与提示词工程,并探讨向量数据库(Redis)在知识库检索中的应用及潜在问题。适合希望深入理解大模型应用落地的开发者参考。
1. 对话机器人
1.1 对话机器人 - 初步实现
1.1.1 引入依赖
在 Spring Boot 项目中引入 Ollama 或 OpenAI 的依赖。手动写入 Lombok 依赖,自动导入时可能会遇到 bug。
1.1.2 配置模型信息
在 application.yaml 中配置模型信息,以 Ollama 为例:
spring:
application:
name: ai-demo
ai:
ollama:
base-url: http://localhost:11434
chat:
model: deepseek-r1:7b
options:
temperature: 0.8
1.1.3 编写配置类 CommonConfiguration
@Configuration
public class CommonConfiguration {
@Bean
public ChatClient chatClient(OllamaChatModel model) {
return ChatClient.builder(model)
.defaultSystem("你是一个傲娇的智能助手,身份是我的女友,请以女友的身份和傲娇的语气回答问题")
.build();
}
}
1.1.4 同步调用
同步调用需要等待所有响应结果返回后才能给前端。启动项目后访问接口测试:
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
String {
chatClient.prompt()
.user(prompt)
.call()
.content();
}
}
@RequestMapping(value = "/chat", produces = "text/html;charset=utf-8")
public
chat
(String prompt)
return
1.1.5 流式调用
SpringAI 使用 WebFlux 技术实现流式调用,体验更流畅:
@RequestMapping(value = "/chat", produces = "text/html;charset=utf-8")
public Flux<String> chat(String prompt) {
return chatClient.prompt()
.user(prompt)
.stream()
.content();
}
1.2 对话机器人 - 日志功能
1.2.1 添加日志
修改 CommonConfiguration,给 ChatClient 添加日志 Advisor:
@Bean
public ChatClient chatClient(OllamaChatModel model) {
return ChatClient.builder(model)
.defaultSystem("你是合肥工业大学的一名资深老学长...")
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
1.2.2 修改日志级别
在 application.yaml 中添加日志配置:
logging:
level:
org.springframework.ai: debug
com.itheima.ai: debug
1.3 对接前端
1.3.1 Nginx 运行
start nginx.exe
nginx.exe -s stop
前端端口通常是 5173,访问 http://localhost:5173/ 即可查看页面。
1.3.2 解决 CORS 问题
跨域(CORS)是前后端分离项目中常见的问题。浏览器会检查响应头中的 CORS 配置,如果不允许当前域名访问,请求会被拦截。
Spring Boot 解决 CORS 有三种方式:
- 针对单个接口:添加
@CrossOrigin 注解。
- 批量设置:编写配置类实现
WebMvcConfigurer。
- 全局配置:在配置类中统一处理。
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD")
.allowHeaders(true);
}
}
1.4 会话记忆功能
1.4.1 实现原理
让 AI 记住上下文的关键是将历史对话内容拼接到 Prompt 中发送。SpringAI 提供了 ChatMemory 接口来管理这一过程。
1.4.2 注册 ChatMemory 对象
目前 SpringAI 推荐使用 MessageWindowChatMemory。在配置类中注册:
@Bean
public ChatMemory chatMemory() {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(new InMemoryChatMemoryRepository())
.maxMessages(10)
.build();
}
默认窗口大小是 20,存储库可替换为 Redis 等持久化方案。
1.4.3 添加会话记忆 Advisor
由于使用的是 MessageWindowChatMemory,需要在 ChatClient 中传入并配置 Advisor:
@Bean
public ChatClient chatClient(OllamaChatModel model, ChatMemory chatMemory) {
return ChatClient.builder(model)
.defaultSystem("...系统提示词...")
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
1.4.4 设置会话 ID
不同用户的对话需要隔离。前端请求时需传递 chatId,并在代码中将其设置为 CONVERSATION_ID 参数:
public Flux<String> chat(@RequestParam("prompt") String prompt, @RequestParam("chatId") String chatId) {
return chatClient.prompt()
.user(prompt)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, chatId))
.stream()
.content();
}
1.5 会话历史功能
会话记忆关注的是单轮对话的上下文,而会话历史关注的是多轮对话的记录管理。
1.5.1 管理会话 ID
定义 ChatHistoryRepository 接口用于保存会话 ID。支持内存和数据库两种实现。
@Repository
public class InMemoryChatHistoryRepository implements ChatHistoryRepository {
private final Map<String, List<String>> chatHistory = new HashMap<>();
}
数据库实现:
创建 chat_history 表,配合 MyBatis 进行持久化。实体类包含 type, chat_id 等字段。
1.5.2 保存会话 ID
在 Controller 中注入 Repository,每次请求时保存 chatId:
@Autowired
@Qualifier("inSqlChatHistoryRepository")
private ChatHistoryRepository chatHistoryRepository;
chatHistoryRepository.save(ChatType.CHAT.getValue(), chatId);
1.5.3 查询历史会话
历史数据存储在 ChatMemory 中,通过 conversationId 获取。前端需要 role 和 content 字段。
内存保存:
直接调用 chatMemory.get(chatId),转换 Message 对象为 VO 返回。
数据库保存(难点):
需要自定义 ChatMemory 实现类(如 InSqlChatMemory),重写 add 和 get 方法,底层操作数据库表 chat_message。
1.6 总结 - 对话机器人
基本实现涉及依赖引入、模型配置及 CORS 处理。会话记忆通过 ChatMemory 和 Advisor 实现,核心在于区分不同的会话 ID。会话历史则分为 ID 管理和内容管理两部分,ID 可存内存或数据库,内容通常由 ChatMemory 负责存取。
2. 哄哄模拟器(纯 Prompt 开发)
2.1 提示词工程
优化提示词让大模型生成理想内容的过程称为提示词工程。OpenAI 官方文档中有大量案例参考。
2.2 代码实现
2.2.1 配置 OpenAI 参数
注意 API Key 的安全,建议使用环境变量读取:
openai:
api-key: ${OPENAI_API_KEY}
2.2.2 配置 ChatClient
可以配置多个 ChatClient 用于不同场景。例如游戏场景使用内存存储,聊天场景使用数据库存储。
2.2.3 编写 Controller
使用专用的 gameChatClient,路径改为 /game。
2.3 总结
重点在于提示词的设计以及针对不同场景配置不同的 ChatMemory 策略。
3. 智能客服(Function Calling)
当需求涉及逻辑校验或数据库读写时,纯 Prompt 模式难以胜任,此时需结合 Function Calling。
3.1 实现思路
将数据库操作定义为 Tool,在提示词中告知大模型何时调用。SpringAI 利用 AOP 能力简化了中间解析过程。
3.2 基础 CRUD
基于业务需求设计数据库表(课程、预约、校区),使用 MyBatis Plus 生成实体类和 Mapper。
3.3 定义 Function
在 SpringAI 中称为 Tool。使用 @Tool 注解标记 Bean 中的方法。
3.3.1 查询条件分析
封装查询条件类 ElectiveCourseQuery,每个字段使用 @ToolParam 注解说明用途。
3.3.2 定义 Function
@Component
public class ElectiveCourseTools {
@Tool(description = "根据条件查询选修课程")
public List<ElectiveCourse> queryElectiveCourse(@ToolParam(...) ElectiveCourseQuery query) {
}
}
3.4 System 提示词设计
3.4.1 安全防范措施
防止 Prompt 注入,确保指令优先级高于用户输入。
3.4.2 调用规则设计
明确业务流程,如咨询规则、预约规则。根据实际运行情况不断优化 Tool 定义,处理边界情况(如校区不存在、课程不匹配等)。
3.5 配置 ChatClient
在 CommonConfiguration 中添加工具:
@Bean
public ChatClient serviceChatClient(..., ElectiveCourseTools electiveCourseTools) {
return ChatClient.builder(model)
.defaultTools(electiveCourseTools)
.build();
}
3.6 编写 Controller
新建 CustomerServiceController,路径 /ai/service。
3.7 存储到数据库
ChatMemory:负责会话内容存取,配置在 Client 中。
ChatHistoryRepository:负责会话 ID 存取,配置在 Controller 中。
ChatHistoryController:负责从 Repository 取 ID,从 Memory 取内容。
3.8 总结
Function Calling 的核心在于工具定义、提示词引导以及 Client 配置。通过不断迭代提示词和工具方法,可以构建出健壮的智能客服系统。
4. ChatPDF(RAG)
为解决大模型知识滞后问题,引入检索增强生成(RAG)技术。
4.1 RAG 原理
知识库不能直接拼接,需通过向量模型计算相似度,从海量数据中检索相关内容。
4.1.1 向量模型
将文本转化为向量,通过距离判断相似度。阿里云百炼等平台提供相关模型。
4.1.2 向量模型测试
编写工具类计算欧氏距离和余弦距离,验证向量化效果。
4.1.3 向量数据库
SpringAI 支持多种向量数据库(Redis, Milvus, Elasticsearch 等)。这里演示 Redis 的实现。
4.1.3.1 安装 Docker 和 Redis
在 Linux 环境下安装 Docker,拉取 Redis Stack 镜像并启动。
4.1.3.2 SimpleVectorStore
基于内存的向量库,适合测试。需在配置类中声明 Bean。
4.1.3.3 Redis Vector Store
引入 spring-ai-starter-vector-store-redis 依赖,配置 YAML 连接信息。
4.1.3.4 VectorStore 接口
统一操作接口,包括 add, delete, similaritySearch 等方法。
4.1.4 文件的读取和转化
使用 PagePdfDocumentReader 将 PDF 拆分为 Document 对象,再写入向量库。
@Test
public void testVectorStore() {
Resource resource = new FileSystemResource("笔记.pdf");
PagePdfDocumentReader reader = new PagePdfDocumentReader(resource, ...);
List<Document> documents = reader.read();
vectorStore.add(documents);
SearchRequest request = SearchRequest.builder()
.query("论语中教育的目的是什么")
.topK(1)
.build();
List<Document> docs = vectorStore.similaritySearch(request);
}
注意事项:
使用 Redis Vector Store 时,若同时引入 Ollama 和 OpenAI 依赖,可能会因 Bean 冲突报错。建议排除不需要的 AutoConfiguration 或仅保留一个向量模型依赖。此外,Redis 向量库在某些配置下可能存在检索数据不一致的问题,需仔细排查索引和 Schema 配置。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online