深度剖析Spring AI源码(十):实战案例:智能客服系统

Qwen3-14B

Qwen3-14B

文本生成
Qwen3

Qwen3 是 Qwen 系列中的最新一代大型语言模型,提供了一整套密集型和专家混合(MoE)模型。基于广泛的训练,Qwen3 在推理、指令执行、代理能力和多语言支持方面取得了突破性进展

深度剖析Spring AI源码(十):终章,从理论到实践的企业级AI应用

“In theory, there is no difference between theory and practice. In practice, there is.” —— Yogi Berra

经过前面九章的深入分析,我们已经掌握了Spring AI的核心原理和设计思想。但真正的考验在于实践——如何将这些知识转化为生产级的AI应用?今天,让我们通过一个完整的企业级案例,展示Spring AI的最佳实践。

实战案例:智能客服系统

我们将构建一个完整的智能客服系统,它具备以下功能:

  • 多轮对话:支持上下文记忆的对话
  • 知识检索:基于企业知识库的RAG问答
  • 工具调用:查询订单、处理退款等业务操作
  • 多模态支持:处理文本和图片输入
  • 实时监控:完整的可观测性体系

系统架构设计

External Services
Data Layer
AI Layer
Service Layer
Frontend Layer
OpenAI API
支付接口
物流接口
向量数据库
关系数据库
缓存层
文件存储
ChatClient
RAG增强器
工具调用器
安全防护器
对话服务
知识服务
订单服务
用户服务
Web界面
移动应用
REST API

核心服务实现

1. 智能对话服务

@Service
@Transactional
public class IntelligentChatService {
    
    private final ChatClient chatClient;
    private final ConversationMemoryService memoryService;
    private final UserContextService userContextService;
    private final ChatMetricsCollector metricsCollector;
    
    public IntelligentChatService(ChatModel chatModel,
                                 VectorStore vectorStore,
                                 ConversationMemoryService memoryService,
                                 UserContextService userContextService,
                                 List<ToolCallback> toolCallbacks) {
        
        this.memoryService = memoryService;
        this.userContextService = userContextService;
        this.metricsCollector = new ChatMetricsCollector();
        
        // 构建增强的ChatClient
        this.chatClient = ChatClient.builder(chatModel)
            .defaultSystem("""
                你是一个专业的客服助手,名叫小智。你的任务是:
                1. 友好、专业地回答用户问题
                2. 基于知识库提供准确信息
                3. 在需要时调用相关工具处理业务
                4. 保护用户隐私和数据安全
                5. 如果无法解决问题,及时转接人工客服
                """)
            .defaultAdvisors(
                // 安全防护 - 最高优先级
                SafeGuardAdvisor.builder()
                    .inputBlockList(loadBlockList("input-blocklist.txt"))
                    .outputBlockList(loadBlockList("output-blocklist.txt"))
                    .build(),
                
                // 用户上下文增强
                new UserContextAdvisor(userContextService),
                
                // 对话记忆
                new ConversationMemoryAdvisor(memoryService),
                
                // RAG知识检索
                RagAdvisor.builder()
                    .vectorStore(vectorStore)
                    .topK(5)
                    .similarityThreshold(0.7)
                    .build(),
                
                // 工具调用
                new ToolCallingAdvisor(toolCallbacks),
                
                // 日志和监控
                new LoggingAdvisor(),
                new MetricsAdvisor(metricsCollector)
            )
            .defaultOptions(OpenAiChatOptions.builder()
                .model("gpt-4")
                .temperature(0.3)  // 客服需要稳定的回答
                .maxTokens(1000)
                .build())
            .build();
    }
    
    /**
     * 处理用户消息
     */
    public ChatResponse handleUserMessage(ChatRequest request) {
        String userId = request.getUserId();
        String sessionId = request.getSessionId();
        String message = request.getMessage();
        
        try {
            // 记录用户上下文
            UserContext userContext = userContextService.getUserContext(userId);
            
            // 构建对话请求
            String response = chatClient
                .prompt()
                .user(userSpec -> userSpec
                    .text(message)
                    .metadata("user_id", userId)
                    .metadata("session_id", sessionId)
                    .metadata("timestamp", Instant.now().toString()))
                .advisors(advisorSpec -> advisorSpec
                    .param("user_context", userContext)
                    .param("session_id", sessionId))
                .call()
                .content();
            
            // 构建响应
            return ChatResponse.builder()
                .sessionId(sessionId)
                .message(response)
                .timestamp(Instant.now())
                .sources(extractSources(response))
                .suggestions(generateSuggestions(message, response))
                .build();
                
        } catch (Exception e) {
            logger.error("Chat processing failed for user: {}", userId, e);
            return createErrorResponse(sessionId, "抱歉,服务暂时不可用,请稍后重试。");
        }
    }
    
    /**
     * 流式对话处理
     */
    public Flux<ChatResponse> handleUserMessageStream(ChatRequest request) {
        return chatClient
            .prompt()
            .user(request.getMessage())
            .advisors(advisorSpec -> advisorSpec
                .param("user_context", userContextService.getUserContext(request.getUserId()))
                .param("session_id", request.getSessionId()))
            .stream()
            .content()
            .map(chunk -> ChatResponse.builder()
                .sessionId(request.getSessionId())
                .message(chunk)
                .timestamp(Instant.now())
                .isStreaming(true)
                .build());
    }
    
    /**
     * 多模态消息处理
     */
    public ChatResponse handleMultimodalMessage(MultimodalChatRequest request) {
        return chatClient
            .prompt()
            .user(userSpec -> userSpec
                .text(request.getText())
                .media(request.getImages().toArray(new Media[0])))
            .advisors(advisorSpec -> advisorSpec
                .param("user_context", userContextService.getUserContext(request.getUserId())))
            .call()
            .entity(ChatResponse.class);
    }
}

2. 企业知识库服务

@Service
public class EnterpriseKnowledgeService {
    
    private final VectorStore vectorStore;
    private final EtlPipeline etlPipeline;
    private final KnowledgeMetricsCollector metricsCollector;
    
    /**
     * 批量导入企业文档
     */
    @Async
    public CompletableFuture<ImportResult> importDocuments(List<Resource> resources) {
        ImportResult.Builder resultBuilder = ImportResult.builder()
            .startTime(Instant.now());
        
        try {
            // 构建ETL管道
            EtlPipeline pipeline = EtlPipeline.builder()
                // 多种文档读取器
                .reader(new PdfDocumentReader())
                .reader(new TikaDocumentReader())
                .reader(new JsoupDocumentReader())
                
                // 文档转换器链
                .transformer(new ContentCleaningTransformer())
                .transformer(new MetadataEnhancementTransformer())
                .transformer(new RecursiveCharacterTextSplitter(1000, 200))
                .transformer(new DuplicateRemovalTransformer())
                
                // 向量存储写入器
                .writer(vectorStore)
                
                // 配置
                .config(EtlPipelineConfig.builder()
                    .batchSize(100)
                    .failOnError(false)
                    .enableMetrics(true)
                    .build())
                .build();
            
            // 执行ETL
            EtlResult etlResult = pipeline.execute();
            
            resultBuilder
                .documentsProcessed(etlResult.getDocumentsRead())
                .documentsStored(etlResult.getDocumentsWritten())
                .success(etlResult.isSuccess());
            
            // 更新知识库索引
            updateKnowledgeIndex();
            
        } catch (Exception e) {
            resultBuilder.success(false).error(e.getMessage());
            logger.error("Document import failed", e);
        } finally {
            resultBuilder.endTime(Instant.now());
        }
        
        return CompletableFuture.completedFuture(resultBuilder.build());
    }
    
    /**
     * 智能知识检索
     */
    public KnowledgeSearchResult searchKnowledge(KnowledgeSearchRequest request) {
        long startTime = System.currentTimeMillis();
        
        try {
            // 查询重写
            List<String> rewrittenQueries = rewriteQuery(request.getQuery());
            
            // 多查询检索
            List<Document> allResults = new ArrayList<>();
            for (String query : rewrittenQueries) {
                List<Document> results = vectorStore.similaritySearch(
                    SearchRequest.builder()
                        .query(query)
                        .topK(request.getTopK())
                        .similarityThreshold(request.getSimilarityThreshold())
                        .filterExpression(request.getFilterExpression())
                        .build()
                );
                allResults.addAll(results);
            }
            
            // 结果去重和重排序
            List<Document> deduplicatedResults = deduplicateDocuments(allResults);
            List<Document> rerankedResults = rerank(request.getQuery(), deduplicatedResults);
            
            // 限制结果数量
            List<Document> finalResults = rerankedResults.stream()
                .limit(request.getTopK())
                .toList();
            
            return KnowledgeSearchResult.builder()
                .query(request.getQuery())
                .results(finalResults)
                .totalFound(allResults.size())
                .responseTime(System.currentTimeMillis() - startTime)
                .build();
                
        } catch (Exception e) {
            logger.error("Knowledge search failed", e);
            throw new KnowledgeSearchException("搜索失败", e);
        }
    }
    
    /**
     * 知识库质量评估
     */
    public KnowledgeQualityReport assessKnowledgeQuality() {
        // 统计知识库基本信息
        long totalDocuments = countTotalDocuments();
        Map<String, Long> documentsByType = countDocumentsByType();
        Map<String, Long> documentsBySource = countDocumentsBySource();
        
        // 评估覆盖度
        double coverageScore = assessCoverage();
        
        // 评估质量
        double qualityScore = assessQuality();
        
        // 评估时效性
        double freshnessScore = assessFreshness();
        
        return KnowledgeQualityReport.builder()
            .totalDocuments(totalDocuments)
            .documentsByType(documentsByType)
            .documentsBySource(documentsBySource)
            .coverageScore(coverageScore)
            .qualityScore(qualityScore)
            .freshnessScore(freshnessScore)
            .overallScore((coverageScore + qualityScore + freshnessScore) / 3)
            .assessmentTime(Instant.now())
            .build();
    }
}

3. 业务工具集成

@Component
public class CustomerServiceTools {
    
    private final OrderService orderService;
    private final UserService userService;
    private final RefundService refundService;
    private final LogisticsService logisticsService;
    
    /**
     * 查询订单信息
     */
    @ToolFunction(
        name = "query_order",
        description = "查询用户的订单信息,包括订单状态、商品详情、物流信息等"
    )
    public OrderInfo queryOrder(OrderQueryRequest request, ToolContext context) {
        String userId = (String) context.get("user_id");
        
        // 权限检查
        if (!hasOrderAccess(userId, request.getOrderId())) {
            throw new ToolExecutionException("无权访问该订单信息");
        }
        
        try {
            Order order = orderService.findById(request.getOrderId());
            if (order == null) {
                return OrderInfo.notFound(request.getOrderId());
            }
            
            // 获取物流信息
            LogisticsInfo logistics = logisticsService.getLogisticsInfo(order.getLogisticsNumber());
            
            return OrderInfo.builder()
                .orderId(order.getId())
                .status(order.getStatus())
                .items(order.getItems())
                .totalAmount(order.getTotalAmount())
                .createTime(order.getCreateTime())
                .logistics(logistics)
                .build();
                
        } catch (Exception e) {
            throw new ToolExecutionException("查询订单失败", e);
        }
    }
    
    /**
     * 处理退款申请
     */
    @ToolFunction(
        name = "process_refund",
        description = "处理用户的退款申请,需要验证订单状态和退款条件"
    )
    public RefundResult processRefund(RefundRequest request, ToolContext context) {
        String userId = (String) context.get("user_id");
        
        try {
            // 验证订单
            Order order = orderService.findById(request.getOrderId());
            if (order == null || !order.getUserId().equals(userId)) {
                return RefundResult.failed("订单不存在或无权操作");
            }
            
            // 检查退款条件
            RefundEligibility eligibility = refundService.checkEligibility(order);
            if (!eligibility.isEligible()) {
                return RefundResult.failed(eligibility.getReason());
            }
            
            // 创建退款申请
            RefundApplication application = RefundApplication.builder()
                .orderId(request.getOrderId())
                .userId(userId)
                .amount(request.getAmount())
                .reason(request.getReason())
                .type(request.getType())
                .build();
            
            RefundApplication savedApplication = refundService.createApplication(application);
            
            // 自动审核(小额退款)
            if (request.getAmount().compareTo(new BigDecimal("100")) <= 0) {
                refundService.autoApprove(savedApplication.getId());
                return RefundResult.success("退款申请已自动通过,预计3-5个工作日到账");
            } else {
                return RefundResult.pending("退款申请已提交,将在24小时内审核");
            }
            
        } catch (Exception e) {
            logger.error("Refund processing failed", e);
            return RefundResult.failed("退款处理失败,请联系人工客服");
        }
    }
    
    /**
     * 更新用户信息
     */
    @ToolFunction(
        name = "update_user_profile",
        description = "更新用户的个人信息,如地址、电话等"
    )
    public UpdateResult updateUserProfile(UserUpdateRequest request, ToolContext context) {
        String userId = (String) context.get("user_id");
        
        try {
            // 数据验证
            validateUserUpdateRequest(request);
            
            // 更新用户信息
            User user = userService.findById(userId);
            if (user == null) {
                return UpdateResult.failed("用户不存在");
            }
            
            // 应用更新
            if (request.getPhone() != null) {
                user.setPhone(request.getPhone());
            }
            if (request.getAddress() != null) {
                user.setAddress(request.getAddress());
            }
            if (request.getEmail() != null) {
                user.setEmail(request.getEmail());
            }
            
            userService.save(user);
            
            return UpdateResult.success("用户信息更新成功");
            
        } catch (ValidationException e) {
            return UpdateResult.failed("输入信息格式错误:" + e.getMessage());
        } catch (Exception e) {
            logger.error("User profile update failed", e);
            return UpdateResult.failed("更新失败,请稍后重试");
        }
    }
    
    /**
     * 智能推荐商品
     */
    @ToolFunction(
        name = "recommend_products",
        description = "基于用户历史和偏好推荐相关商品"
    )
    public RecommendationResult recommendProducts(RecommendationRequest request, ToolContext context) {
        String userId = (String) context.get("user_id");
        
        try {
            // 获取用户画像
            UserProfile profile = userService.getUserProfile(userId);
            
            // 获取推荐商品
            List<Product> recommendations = recommendationService.getRecommendations(
                RecommendationQuery.builder()
                    .userId(userId)
                    .category(request.getCategory())
                    .priceRange(request.getPriceRange())
                    .limit(request.getLimit())
                    .userProfile(profile)
                    .build()
            );
            
            return RecommendationResult.builder()
                .products(recommendations)
                .reason("基于您的购买历史和偏好推荐")
                .confidence(0.85)
                .build();
                
        } catch (Exception e) {
            logger.error("Product recommendation failed", e);
            return RecommendationResult.empty("暂时无法提供推荐");
        }
    }
}

安全与合规实现

1. 内容安全防护

@Component
public class ContentSecurityService {
    
    private final SensitiveWordDetector sensitiveWordDetector;
    private final PersonalInfoDetector personalInfoDetector;
    private final ToxicityClassifier toxicityClassifier;
    
    /**
     * 输入内容安全检查
     */
    public SecurityCheckResult checkInputSecurity(String content, String userId) {
        SecurityCheckResult.Builder resultBuilder = SecurityCheckResult.builder()
            .content(content)
            .userId(userId);
        
        // 1. 敏感词检测
        SensitiveWordResult sensitiveResult = sensitiveWordDetector.detect(content);
        if (sensitiveResult.hasSensitiveWords()) {
            return resultBuilder
                .safe(false)
                .reason("包含敏感词汇")
                .sensitiveWords(sensitiveResult.getWords())
                .build();
        }
        
        // 2. 个人信息检测
        PersonalInfoResult personalInfoResult = personalInfoDetector.detect(content);
        if (personalInfoResult.hasPersonalInfo()) {
            // 记录但不阻止,用于脱敏处理
            resultBuilder.personalInfo(personalInfoResult.getInfoTypes());
        }
        
        // 3. 毒性内容检测
        ToxicityResult toxicityResult = toxicityClassifier.classify(content);
        if (toxicityResult.getToxicityScore() > 0.7) {
            return resultBuilder
                .safe(false)
                .reason("内容可能包含不当信息")
                .toxicityScore(toxicityResult.getToxicityScore())
                .build();
        }
        
        return resultBuilder.safe(true).build();
    }
    
    /**
     * 输出内容安全处理
     */
    public String sanitizeOutput(String content) {
        String sanitized = content;
        
        // 1. 个人信息脱敏
        sanitized = personalInfoDetector.mask(sanitized);
        
        // 2. 敏感信息过滤
        sanitized = sensitiveWordDetector.filter(sanitized);
        
        // 3. 格式化处理
        sanitized = formatOutput(sanitized);
        
        return sanitized;
    }
    
    /**
     * 用户行为风险评估
     */
    public RiskAssessmentResult assessUserRisk(String userId, String content) {
        // 获取用户历史行为
        UserBehaviorHistory history = getUserBehaviorHistory(userId);
        
        // 计算风险分数
        double riskScore = calculateRiskScore(history, content);
        
        RiskLevel riskLevel;
        if (riskScore < 0.3) {
            riskLevel = RiskLevel.LOW;
        } else if (riskScore < 0.7) {
            riskLevel = RiskLevel.MEDIUM;
        } else {
            riskLevel = RiskLevel.HIGH;
        }
        
        return RiskAssessmentResult.builder()
            .userId(userId)
            .riskScore(riskScore)
            .riskLevel(riskLevel)
            .factors(analyzeRiskFactors(history, content))
            .recommendations(generateSecurityRecommendations(riskLevel))
            .build();
    }
}

2. 数据隐私保护

@Component
public class PrivacyProtectionService {
    
    private final EncryptionService encryptionService;
    private final AuditLogService auditLogService;
    
    /**
     * 敏感数据加密存储
     */
    public void storeSensitiveData(String userId, String data, DataType dataType) {
        try {
            // 加密数据
            String encryptedData = encryptionService.encrypt(data);
            
            // 存储加密数据
            SensitiveDataRecord record = SensitiveDataRecord.builder()
                .userId(userId)
                .encryptedData(encryptedData)
                .dataType(dataType)
                .createdAt(Instant.now())
                .build();
            
            sensitiveDataRepository.save(record);
            
            // 记录审计日志
            auditLogService.logDataAccess(
                AuditEvent.builder()
                    .userId(userId)
                    .action("STORE_SENSITIVE_DATA")
                    .dataType(dataType.name())
                    .timestamp(Instant.now())
                    .build()
            );
            
        } catch (Exception e) {
            logger.error("Failed to store sensitive data", e);
            throw new PrivacyProtectionException("数据存储失败", e);
        }
    }
    
    /**
     * 用户数据删除(GDPR合规)
     */
    @Transactional
    public void deleteUserData(String userId, DataDeletionRequest request) {
        try {
            // 验证删除权限
            if (!hasDataDeletionPermission(userId, request)) {
                throw new UnauthorizedException("无权删除数据");
            }
            
            // 删除各类数据
            if (request.isDeleteConversations()) {
                conversationRepository.deleteByUserId(userId);
            }
            
            if (request.isDeleteProfile()) {
                userProfileRepository.deleteByUserId(userId);
            }
            
            if (request.isDeleteSensitiveData()) {
                sensitiveDataRepository.deleteByUserId(userId);
            }
            
            // 记录删除操作
            auditLogService.logDataDeletion(
                AuditEvent.builder()
                    .userId(userId)
                    .action("DELETE_USER_DATA")
                    .details(request.toString())
                    .timestamp(Instant.now())
                    .build()
            );
            
        } catch (Exception e) {
            logger.error("User data deletion failed", e);
            throw new PrivacyProtectionException("数据删除失败", e);
        }
    }
    
    /**
     * 数据访问日志记录
     */
    @EventListener
    public void handleDataAccessEvent(DataAccessEvent event) {
        auditLogService.logDataAccess(
            AuditEvent.builder()
                .userId(event.getUserId())
                .action("DATA_ACCESS")
                .resourceType(event.getResourceType())
                .resourceId(event.getResourceId())
                .accessType(event.getAccessType())
                .ipAddress(event.getIpAddress())
                .userAgent(event.getUserAgent())
                .timestamp(event.getTimestamp())
                .build()
        );
    }
}

性能优化策略

1. 缓存策略

@Configuration
@EnableCaching
public class CacheConfiguration {
    
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        
        // 对话缓存 - 短期缓存
        cacheManager.registerCustomCache("conversations",
            Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(Duration.ofMinutes(30))
                .recordStats()
                .build());
        
        // 知识检索缓存 - 中期缓存
        cacheManager.registerCustomCache("knowledge-search",
            Caffeine.newBuilder()
                .maximumSize(5000)
                .expireAfterWrite(Duration.ofHours(2))
                .recordStats()
                .build());
        
        // 用户上下文缓存 - 长期缓存
        cacheManager.registerCustomCache("user-context",
            Caffeine.newBuilder()
                .maximumSize(50000)
                .expireAfterWrite(Duration.ofHours(24))
                .recordStats()
                .build());
        
        return cacheManager;
    }
    
    @Bean
    public CacheMetricsRegistrar cacheMetricsRegistrar(MeterRegistry meterRegistry) {
        return new CacheMetricsRegistrar(meterRegistry);
    }
}

@Service
public class CachedKnowledgeService {
    
    @Cacheable(value = "knowledge-search", key = "#query + '_' + #topK")
    public List<Document> searchKnowledge(String query, int topK) {
        return vectorStore.similaritySearch(query, topK);
    }
    
    @Cacheable(value = "user-context", key = "#userId")
    public UserContext getUserContext(String userId) {
        return userContextService.loadUserContext(userId);
    }
    
    @CacheEvict(value = "user-context", key = "#userId")
    public void invalidateUserContext(String userId) {
        // 缓存失效
    }
}

2. 异步处理

@Configuration
@EnableAsync
public class AsyncConfiguration {
    
    @Bean(name = "chatTaskExecutor")
    public TaskExecutor chatTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(200);
        executor.setThreadNamePrefix("chat-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
    
    @Bean(name = "knowledgeTaskExecutor")
    public TaskExecutor knowledgeTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("knowledge-");
        executor.initialize();
        return executor;
    }
}

@Service
public class AsyncChatService {
    
    @Async("chatTaskExecutor")
    public CompletableFuture<String> processMessageAsync(String message, String userId) {
        // 异步处理消息
        String response = chatClient.prompt(message).call().content();
        
        // 异步保存对话历史
        saveConversationAsync(userId, message, response);
        
        return CompletableFuture.completedFuture(response);
    }
    
    @Async("knowledgeTaskExecutor")
    public CompletableFuture<Void> updateKnowledgeAsync(List<Document> documents) {
        // 异步更新知识库
        vectorStore.add(documents);
        return CompletableFuture.completedFuture(null);
    }
}

3. 连接池优化

@Configuration
public class HttpClientConfiguration {
    
    @Bean
    public RestClient restClient() {
        return RestClient.builder()
            .requestFactory(clientHttpRequestFactory())
            .build();
    }
    
    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory() {
        HttpComponentsClientHttpRequestFactory factory = 
            new HttpComponentsClientHttpRequestFactory();
        
        // 连接池配置
        PoolingHttpClientConnectionManager connectionManager = 
            new PoolingHttpClientConnectionManager();
        connectionManager.setMaxTotal(200);
        connectionManager.setDefaultMaxPerRoute(50);
        
        // HTTP客户端配置
        CloseableHttpClient httpClient = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .setDefaultRequestConfig(RequestConfig.custom()
                .setConnectTimeout(5000)
                .setSocketTimeout(30000)
                .build())
            .build();
        
        factory.setHttpClient(httpClient);
        return factory;
    }
}

监控与运维

1. 健康检查

@Component
public class AiHealthIndicator implements HealthIndicator {
    
    private final ChatModel chatModel;
    private final VectorStore vectorStore;
    private final MeterRegistry meterRegistry;
    
    @Override
    public Health health() {
        Health.Builder builder = Health.up();
        
        try {
            // 检查AI模型连接
            checkChatModel(builder);
            
            // 检查向量数据库
            checkVectorStore(builder);
            
            // 检查系统指标
            checkSystemMetrics(builder);
            
        } catch (Exception e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
        
        return builder.build();
    }
    
    private void checkChatModel(Health.Builder builder) {
        try {
            long startTime = System.currentTimeMillis();
            String response = chatModel.call(new Prompt("Health check")).getResult().getOutput().getContent();
            long responseTime = System.currentTimeMillis() - startTime;
            
            builder.withDetail("chatModel", Map.of(
                "status", "UP",
                "responseTime", responseTime + "ms",
                "lastCheck", Instant.now()
            ));
            
        } catch (Exception e) {
            builder.withDetail("chatModel", Map.of(
                "status", "DOWN",
                "error", e.getMessage()
            ));
        }
    }
    
    private void checkVectorStore(Health.Builder builder) {
        try {
            List<Document> results = vectorStore.similaritySearch("health check", 1);
            
            builder.withDetail("vectorStore", Map.of(
                "status", "UP",
                "documentCount", results.size(),
                "lastCheck", Instant.now()
            ));
            
        } catch (Exception e) {
            builder.withDetail("vectorStore", Map.of(
                "status", "DOWN",
                "error", e.getMessage()
            ));
        }
    }
}

2. 自定义指标

@Component
public class BusinessMetrics {
    
    private final Counter conversationCounter;
    private final Timer responseTimer;
    private final Gauge activeUsersGauge;
    private final DistributionSummary satisfactionScore;
    
    public BusinessMetrics(MeterRegistry meterRegistry) {
        this.conversationCounter = Counter.builder("business.conversations.total")
            .description("Total number of conversations")
            .register(meterRegistry);
        
        this.responseTimer = Timer.builder("business.response.time")
            .description("AI response time")
            .register(meterRegistry);
        
        this.activeUsersGauge = Gauge.builder("business.users.active")
            .description("Number of active users")
            .register(meterRegistry, this, BusinessMetrics::getActiveUserCount);
        
        this.satisfactionScore = DistributionSummary.builder("business.satisfaction.score")
            .description("User satisfaction scores")
            .register(meterRegistry);
    }
    
    public void recordConversation(String userId, String channel) {
        conversationCounter.increment(
            Tags.of(
                "user_type", getUserType(userId),
                "channel", channel
            )
        );
    }
    
    public void recordResponseTime(Duration duration, String model) {
        responseTimer.record(duration, Tags.of("model", model));
    }
    
    public void recordSatisfactionScore(double score) {
        satisfactionScore.record(score);
    }
    
    private double getActiveUserCount() {
        // 实现活跃用户统计逻辑
        return activeUserService.getActiveUserCount();
    }
}

部署与运维最佳实践

1. 容器化部署

# Dockerfile
FROM openjdk:17-jdk-slim

# 安装必要工具
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*

# 创建应用目录
WORKDIR /app

# 复制应用文件
COPY target/intelligent-customer-service-*.jar app.jar

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

# 启动应用
ENTRYPOINT ["java", "-jar", "app.jar"]
# docker-compose.yml
version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=docker
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - DATABASE_URL=jdbc:postgresql://postgres:5432/customerservice
      - REDIS_URL=redis://redis:6379
    depends_on:
      - postgres
      - redis
      - pgvector
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped

  postgres:
    image: postgres:15
    environment:
      POSTGRES_DB: customerservice
      POSTGRES_USER: ${DB_USER}
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  pgvector:
    image: pgvector/pgvector:pg15
    environment:
      POSTGRES_DB: vectorstore
      POSTGRES_USER: ${VECTOR_DB_USER}
      POSTGRES_PASSWORD: ${VECTOR_DB_PASSWORD}
    volumes:
      - vector_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3

volumes:
  postgres_data:
  vector_data:
  redis_data:

2. Kubernetes部署

# k8s-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: intelligent-customer-service
  labels:
    app: intelligent-customer-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: intelligent-customer-service
  template:
    metadata:
      labels:
        app: intelligent-customer-service
    spec:
      containers:
      - name: app
        image: intelligent-customer-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "kubernetes"
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: ai-secrets
              key: openai-api-key
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10

---
apiVersion: v1
kind: Service
metadata:
  name: intelligent-customer-service
spec:
  selector:
    app: intelligent-customer-service
  ports:
  - port: 80
    targetPort: 8080
  type: LoadBalancer

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: intelligent-customer-service-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: intelligent-customer-service
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

总结与展望

通过这个完整的企业级智能客服系统案例,我们展示了Spring AI在实际项目中的应用:

核心价值

  1. 开发效率:通过Spring AI的抽象和自动配置,大大简化了AI应用的开发
  2. 企业级特性:内置的安全、监控、缓存等特性满足生产环境需求
  3. 可扩展性:模块化的设计让系统易于扩展和维护
  4. 技术栈统一:与Spring生态无缝集成,降低学习成本

最佳实践总结

  1. 分层架构:清晰的分层设计,职责分离
  2. 安全第一:多层次的安全防护机制
  3. 性能优化:缓存、异步、连接池等优化策略
  4. 可观测性:全方位的监控和指标收集
  5. 容器化部署:现代化的部署和运维方式

未来展望

Spring AI作为一个快速发展的框架,未来可能会在以下方面继续演进:

  • 多模态能力增强:更好的图像、音频处理支持
  • 边缘计算支持:本地模型部署和推理
  • 更多AI模型集成:支持更多的开源和商业模型
  • 智能化运维:基于AI的自动化运维能力

系列文章结语

经过十章的深入剖析,我们完整地探索了Spring AI的设计哲学、核心架构和实践应用。从最初的项目概览,到最后的企业级实战,我们见证了一个优秀框架的诞生和成长。

Spring AI不仅仅是一个技术框架,更是Java生态拥抱AI时代的重要里程碑。它让Java开发者能够用熟悉的方式构建AI应用,让企业能够更容易地将AI能力集成到现有系统中。

希望这个系列能够帮助你:

  • 深入理解Spring AI的设计思想
  • 掌握企业级AI应用的开发技巧
  • 建立完整的AI应用架构思维
  • 在实际项目中应用这些知识

您可能感兴趣的与本文相关的镜像

Qwen3-14B

Qwen3-14B

文本生成
Qwen3

Qwen3 是 Qwen 系列中的最新一代大型语言模型,提供了一整套密集型和专家混合(MoE)模型。基于广泛的训练,Qwen3 在推理、指令执行、代理能力和多语言支持方面取得了突破性进展

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THMAIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值