Appearance
Spring AI 聊天与对话
Spring AI 提供了强大的聊天和对话功能,使开发人员能够轻松构建交互式AI应用。本文将介绍如何使用Spring AI实现各种聊天和对话场景。
对话基础
Spring AI中的对话核心是ChatClient
接口,它提供了与大型语言模型进行交互的基本方法:
java
@Service
public class BasicChatService {
private final ChatClient chatClient;
public BasicChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String chat(String message) {
ChatResponse response = chatClient.call(new Prompt(message));
return response.getResult().getOutput().getContent();
}
}
对话历史管理
多轮对话需要维护上下文,Spring AI提供了会话管理功能:
java
@Service
public class ConversationService {
private final ChatClient chatClient;
private final Map<String, List<Message>> conversations = new ConcurrentHashMap<>();
public String chat(String userId, String message) {
// 获取或创建用户会话
List<Message> conversation = conversations.computeIfAbsent(userId, k -> new ArrayList<>());
// 添加系统提示(如果是新会话)
if (conversation.isEmpty()) {
conversation.add(new SystemMessage("你是一个友好、乐于助人的AI助手。"));
}
// 添加用户消息
conversation.add(new UserMessage(message));
// 创建包含完整对话历史的提示
Prompt prompt = new Prompt(new ArrayList<>(conversation));
// 获取回复
ChatResponse response = chatClient.call(prompt);
String reply = response.getResult().getOutput().getContent();
// 保存AI回复到会话历史
conversation.add(new AiMessage(reply));
// 裁剪历史(防止过长)
if (conversation.size() > 20) {
// 保留系统消息和最近的消息
List<Message> trimmed = new ArrayList<>();
trimmed.add(conversation.get(0)); // 系统消息
trimmed.addAll(conversation.subList(conversation.size() - 19, conversation.size()));
conversations.put(userId, trimmed);
}
return reply;
}
public void clearConversation(String userId) {
conversations.remove(userId);
}
}
多角色对话
Spring AI支持在对话中分配不同角色,实现更复杂的交互:
java
@Service
public class MultiRoleConversationService {
private final ChatClient chatClient;
public String simulateExpertDiscussion(String topic) {
List<Message> conversation = new ArrayList<>();
// 系统指令
conversation.add(new SystemMessage("""
模拟三位专家讨论一个话题。每位专家有不同的背景和观点:
1. 技术专家 - 关注技术可行性和实现
2. 商业专家 - 关注市场和商业价值
3. 伦理专家 - 关注社会影响和伦理问题
分别以三位专家的身份讨论,并给出综合结论。
"""));
conversation.add(new UserMessage("讨论主题: " + topic));
Prompt prompt = new Prompt(conversation);
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}
流式聊天
对于长回复,流式处理可以提供更好的用户体验:
java
@RestController
@RequestMapping("/api/chat")
public class StreamingChatController {
private final StreamingChatClient streamingChatClient;
private final Map<String, List<Message>> conversations = new ConcurrentHashMap<>();
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestBody ChatRequest request) {
String userId = request.getUserId();
String message = request.getMessage();
// 获取或创建会话
List<Message> conversation = conversations.computeIfAbsent(userId, k -> new ArrayList<>());
// 添加系统消息(如果是新会话)
if (conversation.isEmpty()) {
conversation.add(new SystemMessage("你是一个有帮助的AI助手。"));
}
// 添加用户消息
conversation.add(new UserMessage(message));
// 创建提示
Prompt prompt = new Prompt(new ArrayList<>(conversation));
// 流式处理
StringBuilder fullResponse = new StringBuilder();
return streamingChatClient.stream(prompt)
.map(chunk -> {
String content = chunk.getResult().getOutput().getContent();
fullResponse.append(content);
return content;
})
.doOnComplete(() -> {
// 保存完整回复到会话历史
conversation.add(new AiMessage(fullResponse.toString()));
// 裁剪历史(防止过长)
if (conversation.size() > 20) {
List<Message> trimmed = new ArrayList<>();
trimmed.add(conversation.get(0)); // 系统消息
trimmed.addAll(conversation.subList(conversation.size() - 19, conversation.size()));
conversations.put(userId, trimmed);
}
});
}
@Data
public static class ChatRequest {
private String userId;
private String message;
}
}
功能增强型聊天
通过函数调用实现功能增强型聊天:
java
@Service
public class EnhancedChatService {
private final ChatClient chatClient;
private final WeatherService weatherService;
private final NewsService newsService;
public String chat(String userId, String message) {
// 创建带函数的提示
SystemMessage systemMessage = new SystemMessage("""
你是一个有帮助的AI助手。你可以回答问题,也可以调用以下函数:
- getWeather(location): 获取指定位置的天气
- getNews(topic): 获取指定主题的最新新闻
仅在需要时调用这些函数。
""");
UserMessage userMessage = new UserMessage(message);
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
ChatResponse response = chatClient.call(prompt);
Generation generation = response.getResult();
// 检查是否需要函数调用
if (generation.getOutput() instanceof FunctionCallingOutput functionOutput) {
List<FunctionCall> functionCalls = functionOutput.getFunctionCalls();
if (!functionCalls.isEmpty()) {
FunctionCall functionCall = functionCalls.get(0);
String functionName = functionCall.getName();
// 执行函数调用
String functionResult = executeFunction(functionName, functionCall.getArguments());
// 创建包含函数结果的新提示
UserMessage resultMessage = new UserMessage("""
函数 %s 的调用结果:
%s
""".formatted(functionName, functionResult));
// 继续对话
Prompt followUpPrompt = new Prompt(List.of(systemMessage, userMessage, resultMessage));
ChatResponse followUpResponse = chatClient.call(followUpPrompt);
return followUpResponse.getResult().getOutput().getContent();
}
}
// 常规回复
return generation.getOutput().getContent();
}
private String executeFunction(String functionName, Map<String, Object> arguments) {
return switch (functionName) {
case "getWeather" -> {
String location = (String) arguments.get("location");
yield weatherService.getWeather(location);
}
case "getNews" -> {
String topic = (String) arguments.get("topic");
yield newsService.getNews(topic);
}
default -> "函数未实现";
};
}
}
对话工具集成
Spring AI可以与其他工具集成,扩展对话能力:
java
@Service
public class ToolAugmentedChatService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
private final Calculator calculator;
private final WebSearchEngine searchEngine;
public String chat(String message) {
List<Message> messages = new ArrayList<>();
// 系统指令
messages.add(new SystemMessage("""
你是一个强大的AI助手,配备了以下工具:
1. 知识库 - 用于查询特定领域信息
2. 计算器 - 用于数学计算
3. 网络搜索 - 用于获取最新信息
判断用户问题类型,并使用适当的工具来辅助回答。
"""));
// 用户消息
messages.add(new UserMessage(message));
// 确定需要使用的工具
String toolSelectionPrompt = "分析以下用户问题,需要使用哪种工具回答最合适?仅回答数字:1, 2, 3 或 0(无需工具):\n\n" + message;
ChatResponse toolResponse = chatClient.call(new Prompt(toolSelectionPrompt));
String toolSelection = toolResponse.getResult().getOutput().getContent().trim();
// 根据工具类型增强回答
String enhancedContent = switch (toolSelection) {
case "1" -> {
List<Document> docs = vectorStore.similaritySearch(message, 3);
String context = docs.stream().map(Document::getContent).collect(Collectors.joining("\n\n"));
yield "从知识库找到的相关信息:\n" + context;
}
case "2" -> {
String calculation = calculator.evaluate(message);
yield "计算结果:\n" + calculation;
}
case "3" -> {
String searchResults = searchEngine.search(message);
yield "网络搜索结果:\n" + searchResults;
}
default -> null;
};
// 如果有增强内容,添加到对话中
if (enhancedContent != null) {
messages.add(new SystemMessage("工具提供的信息: " + enhancedContent));
}
// 生成最终回答
Prompt finalPrompt = new Prompt(messages);
ChatResponse finalResponse = chatClient.call(finalPrompt);
return finalResponse.getResult().getOutput().getContent();
}
}
对话个性化
通过提示工程实现对话个性化:
java
@Service
public class PersonalizedChatService {
private final ChatClient chatClient;
private final UserProfileRepository profileRepository;
public String personalizedChat(String userId, String message) {
// 获取用户个人资料
UserProfile profile = profileRepository.findById(userId)
.orElse(new UserProfile(userId));
// 创建个性化系统消息
String systemPrompt = """
你是一个个性化AI助手。请以友好的方式与用户交流,并考虑以下用户信息:
姓名: %s
兴趣爱好: %s
专业领域: %s
偏好的沟通风格: %s
根据用户的背景和兴趣定制回答,使用适当的专业术语,并参考相关领域知识。
""".formatted(
profile.getName(),
String.join(", ", profile.getInterests()),
profile.getProfessionalField(),
profile.getCommunicationStyle()
);
// 构建提示
List<Message> messages = new ArrayList<>();
messages.add(new SystemMessage(systemPrompt));
messages.add(new UserMessage(message));
// 获取回复
ChatResponse response = chatClient.call(new Prompt(messages));
return response.getResult().getOutput().getContent();
}
// 用户资料模型
@Data
public static class UserProfile {
private String id;
private String name = "用户";
private List<String> interests = new ArrayList<>();
private String professionalField = "一般";
private String communicationStyle = "友好";
public UserProfile(String id) {
this.id = id;
}
}
}
构建聊天机器人应用
使用Spring AI构建完整的聊天机器人应用:
java
@RestController
@RequestMapping("/api/chatbot")
public class ChatbotController {
private final ChatClient chatClient;
private final ChatSessionManager sessionManager;
private final ChatMessageRepository messageRepository;
// 聊天端点
@PostMapping("/message")
public ResponseEntity<ChatResponse> sendMessage(@RequestBody ChatRequest request) {
String sessionId = request.getSessionId();
String message = request.getMessage();
// 获取或创建会话
ChatSession session = sessionManager.getOrCreateSession(sessionId);
// 记录用户消息
ChatMessage userChatMessage = new ChatMessage(sessionId, "USER", message);
messageRepository.save(userChatMessage);
// 准备对话历史
List<Message> conversationHistory = session.getMessages();
conversationHistory.add(new UserMessage(message));
// 生成回复
Prompt prompt = new Prompt(conversationHistory);
org.springframework.ai.chat.ChatResponse aiResponse = chatClient.call(prompt);
String reply = aiResponse.getResult().getOutput().getContent();
// 记录AI回复
ChatMessage aiChatMessage = new ChatMessage(sessionId, "AI", reply);
messageRepository.save(aiChatMessage);
// 更新会话
conversationHistory.add(new AiMessage(reply));
sessionManager.updateSession(sessionId, conversationHistory);
return ResponseEntity.ok(new ChatResponse(reply, sessionId));
}
// 获取会话历史
@GetMapping("/history/{sessionId}")
public ResponseEntity<List<ChatMessageDto>> getHistory(@PathVariable String sessionId) {
List<ChatMessage> messages = messageRepository.findBySessionIdOrderByTimestampAsc(sessionId);
List<ChatMessageDto> messageDtos = messages.stream()
.map(msg -> new ChatMessageDto(msg.getRole(), msg.getContent(), msg.getTimestamp()))
.collect(Collectors.toList());
return ResponseEntity.ok(messageDtos);
}
// 请求/响应模型
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ChatRequest {
private String sessionId;
private String message;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ChatResponse {
private String message;
private String sessionId;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class ChatMessageDto {
private String role;
private String content;
private LocalDateTime timestamp;
}
}
聊天应用的最佳实践
- 清晰的系统提示:使用明确的系统提示定义AI助手的角色和行为
- 有效的上下文管理:保持对话历史简洁,避免超出模型的上下文窗口
- 流式响应:对于长回复,使用流式处理提升用户体验
- 错误处理:实现健壮的错误处理机制,处理API失败和无效请求
- 用户反馈:收集和利用用户反馈改进对话质量
- 个性化:根据用户偏好和历史记录个性化对话
- 多模态集成:将文本、图像等多种模式整合到对话中
总结
Spring AI提供了强大的工具和抽象,使开发人员能够构建复杂的对话应用。通过有效管理对话历史、实现流式响应、集成外部工具和提供个性化体验,可以创建功能丰富、用户友好的聊天机器人和对话系统。无论是简单的问答系统还是复杂的交互式助手,Spring AI都提供了构建这些应用所需的基础设施和功能。