Skip to content

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;
    }
}

聊天应用的最佳实践

  1. 清晰的系统提示:使用明确的系统提示定义AI助手的角色和行为
  2. 有效的上下文管理:保持对话历史简洁,避免超出模型的上下文窗口
  3. 流式响应:对于长回复,使用流式处理提升用户体验
  4. 错误处理:实现健壮的错误处理机制,处理API失败和无效请求
  5. 用户反馈:收集和利用用户反馈改进对话质量
  6. 个性化:根据用户偏好和历史记录个性化对话
  7. 多模态集成:将文本、图像等多种模式整合到对话中

总结

Spring AI提供了强大的工具和抽象,使开发人员能够构建复杂的对话应用。通过有效管理对话历史、实现流式响应、集成外部工具和提供个性化体验,可以创建功能丰富、用户友好的聊天机器人和对话系统。无论是简单的问答系统还是复杂的交互式助手,Spring AI都提供了构建这些应用所需的基础设施和功能。