引言

Spring AI 是一个创新的框架,它将人工智能能力无缝集成到 Spring 生态系统中。通过提供统一的 API 抽象层,Spring AI 让开发者能够轻松地在应用中集成各种大型语言模型(LLM),而无需关心底层实现的复杂性。本文将深入介绍 Spring AI 的使用方法,并分享实践中常见问题的解决方案。

1. Spring AI 核心概念

1.1 项目架构

Spring AI 采用了分层架构设计:

  • API 层​:提供统一的编程接口

  • 抽象层​:定义核心概念和契约

  • 实现层​:支持多种 AI 服务提供商

1.2 组件结构

1.2.1 核心组件类图

1.2.2 组件结构

1.2.3 配置加载流程

1.2.4 请求处理流程

1.2.5 异常处理机制

1.2.6 依赖关系图

2. 快速开始

2.1 环境配置

Maven 依赖配置:​

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.ai.version>1.1.0-M2</spring.ai.version>
        <spring.ai.open.ai.version>1.0.0-M6</spring.ai.open.ai.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring.ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
            <version>${spring.ai.open.ai.version}</version>
        </dependency>
    </dependencies>

application.yml 配置:​

spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      base-url: https://api.openai.com/v1
    # 其他提供商配置
    azure:
      openai:
        api-key: ${AZURE_OPENAI_API_KEY}
        endpoint: ${AZURE_OPENAI_ENDPOINT}

2.2 基础使用示例

@RestController
public class AIController {
    
    private final ChatClient chatClient;
    
    public AIController(ChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return chatClient.call(message);
    }
    
    @PostMapping("/prompt")
    public Response prompt(@RequestBody Prompt prompt) {
        return chatClient.call(prompt);
    }
}

3. 高级功能详解

3.1 提示词工程

@Service
public class AdvancedAIService {
    
    private final ChatClient chatClient;
    
    public AdvancedAIService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    public String generateContentWithTemplate(String topic, String style) {
        PromptTemplate template = new PromptTemplate("""
            请以{style}的风格,写一篇关于{topic}的文章。
            要求:结构清晰,内容生动有趣。
            """);
        
        Prompt prompt = template.create(
            Map.of("style", style, "topic", topic)
        );
        
        return chatClient.call(prompt).getResult().getOutput().getContent();
    }
}

3.2 流式响应处理

@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
    return chatClient.stream(message)
        .map(response -> response.getResult().getOutput().getContent());
}

3.3 函数调用集成

@Bean
public FunctionCallback weatherFunction() {
    return FunctionCallback.builder()
        .name("getWeather")
        .description("获取指定城市的天气信息")
        .inputType(WeatherRequest.class)
        .function((request) -> getWeatherData(request.getCity()))
        .build();
}

@Bean
public AiClient aiClientWithFunctions() {
    return new OpenAiChatClient(openAiApi, List.of(weatherFunction()));
}

4. 常见问题与解决方案

4.1 配置相关问题

问题1:API 密钥配置错误
错误信息:401 Unauthorized 或 Invalid API Key
解决方案:​纠正API 密钥配置,确保没有多余的空格
# 正确配置方式
spring:
  ai:
    openai:
      api-key: sk-your-actual-key-here
      # 确保没有多余的空格
问题2:依赖冲突
错误信息:NoSuchBeanDefinitionException 或 ClassNotFoundException
解决方案:​使用 BOM 管理版本
<!-- 使用 BOM 管理版本 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

4.2 运行时问题

问题3:超时设置不当
解决方案:设置合适的超时时间
@Configuration
public class AIConfig {
    
    @Bean
    public OpenAiApi openAiApi() {
        return OpenAiApi.builder()
            .apiKey("your-api-key")
            .connectTimeout(Duration.ofSeconds(30))
            .readTimeout(Duration.ofSeconds(60))
            .build();
    }
}
问题4:内存溢出(处理大文本时)
解决方案:分块处理大文档​
@Service
public class MemorySafeAIService {
    
    public String processLargeDocument(String document) {
        // 分块处理大文档
        List<String> chunks = splitDocument(document, 1000);
        List<String> results = new ArrayList<>();
        
        for (String chunk : chunks) {
            String result = chatClient.call(chunk);
            results.add(result);
            // 添加延迟避免速率限制
            try { Thread.sleep(100); } catch (InterruptedException e) {}
        }
        
        return String.join("\n", results);
    }
    
    private List<String> splitDocument(String document, int chunkSize) {
        // 实现文档分块逻辑
        return Arrays.asList(document.split("(?<=\\G.{" + chunkSize + "})"));
    }
}

4.3 性能优化问题

问题5:响应速度慢
解决方案:
  • 使用集成诊断和监控工具
         性能监控配置:
@Configuration
@EnableAspectJAutoProxy
public class MonitoringConfig {
    
    @Bean
    public AIServiceMonitor aiServiceMonitor() {
        return new AIServiceMonitor();
    }
}

@Aspect
@Component
@Slf4j
public class AIServiceMonitor {
    
    @Around("execution(* org.springframework.ai.client..*(..))")
    public Object monitorAICalls(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        String methodName = joinPoint.getSignature().getName();
        
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - startTime;
            
            log.info("AI调用 - 方法: {}, 耗时: {}ms", methodName, duration);
            
            // 记录到指标系统
            Metrics.counter("ai.call.duration")
                  .tag("method", methodName)
                  .record(duration);
                  
            return result;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("AI调用失败 - 方法: {}, 耗时: {}ms, 错误: {}", 
                     methodName, duration, e.getMessage());
            throw e;
        }
    }
}
  • 优化连接和超时
       连接池配置:
spring:
  ai:
    openai:
      # 连接池配置
      connection-timeout: 10s
      read-timeout: 60s
      max-connections: 100
      max-connections-per-route: 20
      keep-alive: 30s
    # HTTP客户端优化
    http-client:
      enable-compression: true
      use-keep-alive: true
  •  自定义HTTP客户端
@Configuration
public class HttpClientConfig {
    
    @Bean
    public RestTemplate aiRestTemplate() {
        return new RestTemplateBuilder()
            .setConnectTimeout(Duration.ofSeconds(10))
            .setReadTimeout(Duration.ofSeconds(60))
            .rootUri("https://api.openai.com/v1")
            .additionalInterceptors(new LoggingInterceptor())
            .requestFactory(this::httpRequestFactory)
            .build();
    }
    
    private ClientHttpRequestFactory httpRequestFactory() {
        HttpComponentsClientHttpRequestFactory factory = 
            new HttpComponentsClientHttpRequestFactory();
        
        HttpClient httpClient = HttpClientBuilder.create()
            .setMaxConnTotal(100)
            .setMaxConnPerRoute(20)
            .setConnectionTimeToLive(30, TimeUnit.SECONDS)
            .evictIdleConnections(30, TimeUnit.SECONDS)
            .build();
        
        factory.setHttpClient(httpClient);
        return factory;
    }
}
  •  缓存策略
        多级缓存实现:
@Service
@Slf4j
public class CachedAIService {
    
    private final ChatClient chatClient;
    private final CacheManager cacheManager;
    
    // 短期缓存(内存)
    @Cacheable(value = "aiShortCache", key = "#prompt.hashCode()")
    public String getCachedResponse(String prompt) {
        return chatClient.call(prompt);
    }
    
    // 长期缓存(Redis)
    @Cacheable(value = "aiLongCache", key = "#prompt.hashCode()")
    public String getLongTermCachedResponse(String prompt) {
        return getCachedResponse(prompt);
    }
    
    // 向量语义缓存
    public String getSemanticCachedResponse(String prompt, double similarityThreshold) {
        // 使用向量相似度查找缓存
        return findSimilarCachedResponse(prompt, similarityThreshold)
            .orElseGet(() -> {
                String response = chatClient.call(prompt);
                cacheSemanticResponse(prompt, response);
                return response;
            });
    }
}
       Redis缓存配置:
spring:
  cache:
    type: redis
    redis:
      time-to-live: 1h
      cache-null-values: false
  redis:
    host: localhost
    port: 6379
    lettuce:
      pool:
        max-active: 20
        max-wait: 10s
  •  异步处理
        异步服务实现:
@Service
@Slf4j
@EnableAsync
public class AsyncAIService {
    
    private final ChatClient chatClient;
    private final ThreadPoolTaskExecutor aiTaskExecutor;
    
    @Bean
    public ThreadPoolTaskExecutor aiTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("ai-executor-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
    
    @Async("aiTaskExecutor")
    public CompletableFuture<String> processAsync(String prompt) {
        try {
            String response = chatClient.call(prompt);
            return CompletableFuture.completedFuture(response);
        } catch (Exception e) {
            return CompletableFuture.failedFuture(e);
        }
    }
    
    // 批量异步处理
    public CompletableFuture<List<String>> processBatchAsync(List<String> prompts) {
        List<CompletableFuture<String>> futures = prompts.stream()
            .map(this::processAsync)
            .collect(Collectors.toList());
            
        return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
            .thenApply(v -> futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList()));
    }
}
  • 批量处理优化
      批量请求处理:
@Service
public class BatchAIService {
    
    private final ChatClient chatClient;
    private final int BATCH_SIZE = 10;
    private final Duration BATCH_TIMEOUT = Duration.ofSeconds(5);
    
    public List<String> processInBatches(List<String> prompts) {
        return Lists.partition(prompts, BATCH_SIZE).stream()
            .flatMap(batch -> processBatch(batch).stream())
            .collect(Collectors.toList());
    }
    
    private List<String> processBatch(List<String> batchPrompts) {
        // 使用并行流处理批次
        return batchPrompts.parallelStream()
            .map(prompt -> {
                try {
                    return chatClient.call(prompt);
                } catch (Exception e) {
                    log.warn("处理提示词失败: {}", prompt, e);
                    return "处理失败";
                }
            })
            .collect(Collectors.toList());
    }
}
  • 模型和参数优化
       模型选择优化:
@Configuration
public class ModelOptimizationConfig {
    
    @Bean
    @Primary
    public ChatClient optimizedChatClient(OpenAiApi openAiApi) {
        OpenAiChatOptions options = OpenAiChatOptions.builder()
            .withModel("gpt-3.5-turbo")  // 使用更快的模型
            .withTemperature(0.7)        // 降低随机性
            .withMaxTokens(500)          // 限制输出长度
            .withTopP(0.9)
            .build();
            
        return new OpenAiChatClient(openAiApi, options);
    }
    
    @Bean
    @Qualifier("qualityChatClient")
    public ChatClient qualityChatClient(OpenAiApi openAiApi) {
        // 用于质量要求高的场景
        OpenAiChatOptions options = OpenAiChatOptions.builder()
            .withModel("gpt-4")
            .withTemperature(0.3)
            .build();
            
        return new OpenAiChatClient(openAiApi, options);
    }
}
  • 提示词优化
  高效提示词设计
@Service
public class PromptOptimizationService {
    
    private final TokenCountEstimator tokenEstimator;
    
    public Prompt optimizePrompt(String userPrompt) {
        // 1. 令牌数量优化
        if (tokenEstimator.estimate(userPrompt) > 2000) {
            userPrompt = truncatePrompt(userPrompt, 1500);
        }
        
        // 2. 结构化提示词
        String optimizedPrompt = """
            请用简洁的语言回答以下问题,回答请控制在200字以内。
            
            问题:%s
            
            要求:
            - 直接回答问题要点
            - 避免冗长的介绍
            - 使用清晰的段落结构
            """.formatted(userPrompt);
            
        return new Prompt(optimizedPrompt);
    }
    
    private String truncatePrompt(String prompt, int maxTokens) {
        // 智能截断逻辑
        return prompt.substring(0, Math.min(prompt.length(), maxTokens));
    }
}
  • 重试和降级策略
       智能重试机制:
@Configuration
public class RetryConfig {
    
    @Bean
    public RetryTemplate aiRetryTemplate() {
        return RetryTemplate.builder()
            .maxAttempts(3)
            .exponentialBackoff(1000, 2, 5000)
            .retryOn(IOException.class)
            .retryOn(TimeoutException.class)
            .notRetryOn(AuthenticationException.class)
            .withListener(new RetryListener() {
                @Override
                public <T, E extends Throwable> void onError(
                    RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                    log.warn("AI调用重试: 次数={}, 错误={}", 
                            context.getRetryCount(), throwable.getMessage());
                }
            })
            .build();
    }
    
    @Bean
    public CircuitBreakerFactory aiCircuitBreakerFactory() {
        return new DefaultCircuitBreakerFactory();
    }
}
  • 基础设施优化
       CDN和网络优化:
@Configuration
public class NetworkOptimizationConfig {
    
    @Bean
    public OpenAiApi openAiApi() {
        return OpenAiApi.builder()
            .apiKey(apiKey)
            .baseUrl("https://api.openai.com/v1")
            // 使用更近的端点
            // .baseUrl("https://api.ap-southeast-1.openai.com/v1")
            .build();
    }
}
  • 监控和告警
       性能指标收集:
@Component
public class AIPerformanceMetrics {
    
    private final MeterRegistry meterRegistry;
    private final Timer aiCallTimer;
    private final Counter errorCounter;
    
    public AIPerformanceMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.aiCallTimer = Timer.builder("ai.call.duration")
            .description("AI调用耗时")
            .register(meterRegistry);
            
        this.errorCounter = Counter.builder("ai.call.errors")
            .description("AI调用错误次数")
            .register(meterRegistry);
    }
    
    public void recordCall(Runnable call, String operation) {
        aiCallTimer.record(() -> {
            try {
                call.run();
            } catch (Exception e) {
                errorCounter.increment();
                throw e;
            }
        });
    }
}
总结

响应速度慢的解决方案需要从多个层面综合考虑:

  1. 诊断定位​ - 使用监控工具确定瓶颈位置

  2. 连接优化​ - 调整超时和连接池参数

  3. 缓存策略​ - 实现多级缓存减少API调用

  4. 异步处理​ - 使用异步和非阻塞调用

  5. 批量操作​ - 合并请求减少网络开销

  6. 模型优化​ - 选择合适的模型和参数

  7. 提示词优化​ - 设计高效的提示词

  8. 重试降级​ - 实现智能重试和熔断机制

  9. 基础设施​ - 优化网络和部署架构

  10. 持续监控​ - 建立完整的监控告警体系

通过组合使用这些策略,可以显著提升 Spring AI 应用的响应速度。

问题6:令牌使用量过高
解决方案:估算令牌数量,简化提示词或分多次请求
@Component
public class TokenAwareService {
    
    private final ChatClient chatClient;
    private final TokenCountEstimator tokenEstimator;
    
    public String optimizePrompt(String userPrompt) {
        // 估算令牌数量
        int tokenCount = tokenEstimator.estimate(userPrompt);
        
        if (tokenCount > 4000) {
            // 简化提示词或分多次请求
            return processInChunks(userPrompt);
        }
        
        return chatClient.call(userPrompt);
    }
}

4.4 错误处理最佳实践

@ControllerAdvice
public class AIExceptionHandler {
    
    @ExceptionHandler(ApiException.class)
    public ResponseEntity<ErrorResponse> handleApiException(ApiException e) {
        return ResponseEntity.status(e.getStatusCode())
            .body(new ErrorResponse("AI服务调用失败: " + e.getMessage()));
    }
    
    @ExceptionHandler(TimeoutException.class)
    public ResponseEntity<ErrorResponse> handleTimeout(TimeoutException e) {
        return ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT)
            .body(new ErrorResponse("请求超时,请稍后重试"));
    }
    
    @Slf4j
    @Component
    public static class AIServiceWithRetry {
        
        private final ChatClient chatClient;
        private final RetryTemplate retryTemplate;
        
        public String callWithRetry(String prompt) {
            return retryTemplate.execute(context -> {
                try {
                    return chatClient.call(prompt);
                } catch (Exception e) {
                    log.warn("第{}次调用失败", context.getRetryCount(), e);
                    if (context.getRetryCount() >= 2) {
                        return "服务暂时不可用,请稍后重试";
                    }
                    throw e;
                }
            });
        }
    }
}

5. 生产环境最佳实践

5.1 监控和日志

@Aspect
@Component
@Slf4j
public class AIServiceMonitor {
    
    @Around("execution(* com.example.service..*.*(..))")
    public Object monitorAICalls(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            Object result = joinPoint.proceed();
            long duration = System.currentTimeMillis() - start;
            log.info("AI调用完成 - 方法: {}, 耗时: {}ms", 
                    joinPoint.getSignature().getName(), duration);
            return result;
        } catch (Exception e) {
            log.error("AI调用失败 - 方法: {}", joinPoint.getSignature().getName(), e);
            throw e;
        }
    }
}

5.2 安全考虑

@Configuration
public class SecurityConfig {
    
    @Bean
    public FilterRegistrationBean<PromptValidationFilter> promptValidationFilter() {
        FilterRegistrationBean<PromptValidationFilter> registrationBean = 
            new FilterRegistrationBean<>();
        registrationBean.setFilter(new PromptValidationFilter());
        registrationBean.addUrlPatterns("/api/ai/*");
        return registrationBean;
    }
}

@Component
public class PromptValidationFilter implements Filter {
    
    private final Set<String> blockedPatterns = Set.of(
        "敏感词1", "敏感词2", "恶意模式"
    );
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                       FilterChain chain) throws IOException, ServletException {
        
        String prompt = request.getParameter("prompt");
        if (containsBlockedContent(prompt)) {
            throw new SecurityException("提示词包含不允许的内容");
        }
        chain.doFilter(request, response);
    }
    
    private boolean containsBlockedContent(String prompt) {
        return blockedPatterns.stream().anyMatch(prompt::contains);
    }
}

6. 总结

Spring AI 为 Java 开发者提供了强大而灵活的 AI 集成能力。通过本文的介绍,您应该能够:

  1. 正确配置和使用 Spring AI;

  2. 实现高级功能如流式响应和函数调用;

  3. 解决常见的配置和运行时问题;

  4. 应用生产环境的最佳实践。

随着 AI 技术的快速发展,Spring AI 也在不断演进。建议持续关注官方文档和更新,以获得最新的功能和改进。

扩展资源:​

希望本文能帮助您顺利地在项目中使用 Spring AI,构建智能化的应用程序!

Logo

更多推荐