基于SpringAI的对话机器人实现指南
本文基于黑马教程,结合个人理解,通过SpringAI框架对接云服务大模型API,实现一个具备对话记忆功能的智能机器人。
引言
使用SpringAI接入AI大模型主要有两种方式:一是部署大模型在本地服务器,二是直接调用云服务商提供的API。本文将采用第二种方式,重点介绍如何配置和使用云服务大模型。
一、引入依赖
首先,在项目的pom.xml中引入SpringAI的OpenAI标准依赖。虽然名为openai,但该starter兼容遵循OpenAI API标准的各类大模型服务。
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
关键步骤说明:
- 选择模型服务商 :例如阿里的通义千问、DeepSeek等,并完成注册获取
API-KEY。 - 初始化客户端:通过配置连接至服务商的API地址。
- 发送请求:通过客户端向大模型发送HTTP请求并获取响应。
二、配置模型参数
在application.yml配置文件中设置连接参数。
yaml
spring:
ai:
openai:
# 替换为对应云服务商的API地址(以阿里云为例)
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
api-key: ${your-api-key} # 替换为你的真实API-KEY
chat:
options:
model: qwen-max # 指定使用的模型名称
temperature: 0.8 # 控制生成结果的随机性(0-1,值越大越随机)
base-url: 目标大模型服务商的API端点地址。api-key: 用于身份验证的密钥。model: 指定需要调用的具体模型。temperature: 影响AI回答的创造性,通常0.8左右平衡了确定性和创造性。
三、配置客户端与对话记忆 (CommonConfiguration)
为了实现类似ChatGPT的多轮对话能力,需要配置客户端并管理对话历史(上下文)。
java
@Configuration
public class CommonConfiguration {
@Bean
public ChatMemory chatMemory(){
// 使用内存存储对话历史,适合开发和短对话场景
return new InMemoryChatMemory();
}
@Bean
public ChatClient chatClient(OpenAiChatModel model, ChatMemory chatMemory){
return ChatClient.builder(model)
.defaultSystem("你是一个热心、可爱的智能助手,你的名字叫小团团。") // 系统提示词,设定AI角色
.defaultAdvisors(
new SimpleLoggerAdvisor(), // 日志记录增强
new MessageChatMemoryAdvisor(chatMemory) // 对话记忆增强
)
.build();
}
}
核心组件解析:
ChatMemory:负责存储和管理对话历史。InMemoryChatMemory:基于内存,简单易用,重启后记录丢失,适用于测试。VectorStoreChatMemory:使用向量数据库,能进行智能检索,适用于复杂对话。TokenWindowChatMemory:基于Token数量限制历史记录长度,有助于控制成本。
ChatClient:核心客户端,用于与AI模型交互。prompt().user("问题").call():发送单次请求。prompt().stream():以流式方式接收响应,实现打字机效果。defaultAdvisors():配置拦截器,提供如日志、记忆管理等增强功能。
四、实现对话接口 (ChatController)
创建一个Controller来处理前端发送的聊天请求。
java
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {
private final ChatClient chatClient;
@RequestMapping(value = "/chat", produces = "text/event-stream;charset=UTF-8")
public Flux<String> chat(
@RequestParam String prompt,
@RequestParam String chatId) { // 前端生成并传递会话ID,用于区分不同对话
return chatClient.prompt()
.user(prompt)
.adviser(a -> a.param(ChatMemory.CONVERSATION_ID_KEY, chatId)) // 关键:将当前会话ID告知记忆管理器
.stream()
.content();
}
}
Flux<String>和produces = "text/event-stream"用于支持流式输出,数据一边生成一边返回给前端。chatId是区分不同对话会话的关键。通过Advisor将其设置到上下文中,MessageChatMemoryAdvisor会自动根据此ID存储和读取对应的历史记录。
五、会话日志与监控
通过配置的SimpleLoggerAdvisor,可以在日志中查看详细的AI请求和响应信息。如需查看,可在application.yml中调整日志级别:
yaml
logging:
level:
org.springframework.ai.chat.client.advisor: DEBUG
com.yourpackage.ai: DEBUG
六、解决跨域问题
前端开发服务器(如Vite on port 5173)与后端API(如SpringBoot on port 8080)端口不同,会产生跨域错误。需配置CORS。
java
@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 拦截所有请求
.allowedOriginPatterns("*") // 允许所有来源(生产环境应指定具体域名)
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false);
}
}
七、实现会话历史记录功能
大模型本身无状态,要实现"历史记录"功能,需要我们将过去的对话数据持久化,并在查询时提供。
1. 定义历史记录存储接口
java
public interface ChatHistoryRepository {
void save(String type, String chatId); // 保存会话ID,按业务类型分类(如'chat')
List<String> getChatIds(String type); // 获取某类型下的所有会话ID列表
}
2. 实现历史记录查询接口
java
@RequiredArgsConstructor
@RestController
@RequestMapping("/ai/history")
public class ChatHistoryController {
private final ChatHistoryRepository chatHistoryRepository;
private final ChatMemory chatMemory;
// 1. 获取某个类型下的所有会话ID列表
@GetMapping("/{type}")
public List<String> getChatIds(@PathVariable("type") String type) {
return chatHistoryRepository.getChatIds(type);
}
// 2. 根据会话ID获取具体的对话内容
@GetMapping("/{type}/{chatId}")
public List<MessageVO> getChatHistory(@PathVariable("type") String type, @PathVariable("chatId") String chatId) {
// 从ChatMemory中获取该会话ID下的完整历史消息
List<Message> messages = chatMemory.get(chatId, Integer.MAX_VALUE);
if (messages == null) {
return List.of(); // 返回空列表
}
// 将Spring AI的Message对象转换为前端需要的VO对象
return messages.stream().map(MessageVO::new).toList();
}
}
// 前端需要的消息视图对象
@Data
@NoArgsConstructor
public class MessageVO {
private String role; // "user", "assistant", "system"
private String content;
public MessageVO(Message message) {
this.role = message.getMessageType().name().toLowerCase();
this.content = message.getText();
}
}
3. 在对话接口中保存会话ID
修改之前的ChatController,在开始对话前保存会话ID。
java
public Flux<String> chat(...) {
// 在对话开始前,保存本次会话的ID
chatHistoryRepository.save("chat", chatId);
return chatClient.prompt()
.user(prompt)
.adviser(a -> a.param(ChatMemory.CONVERSATION_ID_KEY, chatId))
.stream()
.content();
}
实现总结
通过以上步骤,我们实现了一个功能相对完整的对话机器人后端服务。它支持:
- 流式对话:与前端实现打字机效果。
- 多轮对话记忆:基于会话ID管理上下文。
- 会话历史记录:可查看过往对话列表和详情。
- 跨域支持:方便前后端分离开发。
待改进点:
- 当前的
InMemoryChatMemory和简易ChatHistoryRepository在应用重启后数据会丢失。生产环境应集成Redis或数据库进行持久化。 - 可进一步扩展文件上传、多模态对话等功能。
希望这篇博客对你巩固知识有帮助!新手书写难免有错,如果有错麻烦告知🙏