Spring AI 完全指南:从入门到实战问题解决
Spring AI 是一个创新的框架,它将人工智能能力无缝集成到 Spring 生态系统中。通过提供统一的 API 抽象层,Spring AI 让开发者能够轻松地在应用中集成各种大型语言模型(LLM),而无需关心底层实现的复杂性。本文将深入介绍 Spring AI 的使用方法,并分享实践中常见问题的解决方案。
引言
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;
}
});
}
}
总结
响应速度慢的解决方案需要从多个层面综合考虑:
-
诊断定位 - 使用监控工具确定瓶颈位置
-
连接优化 - 调整超时和连接池参数
-
缓存策略 - 实现多级缓存减少API调用
-
异步处理 - 使用异步和非阻塞调用
-
批量操作 - 合并请求减少网络开销
-
模型优化 - 选择合适的模型和参数
-
提示词优化 - 设计高效的提示词
-
重试降级 - 实现智能重试和熔断机制
-
基础设施 - 优化网络和部署架构
-
持续监控 - 建立完整的监控告警体系
通过组合使用这些策略,可以显著提升 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 集成能力。通过本文的介绍,您应该能够:
-
正确配置和使用 Spring AI;
-
实现高级功能如流式响应和函数调用;
-
解决常见的配置和运行时问题;
-
应用生产环境的最佳实践。
随着 AI 技术的快速发展,Spring AI 也在不断演进。建议持续关注官方文档和更新,以获得最新的功能和改进。
扩展资源:
希望本文能帮助您顺利地在项目中使用 Spring AI,构建智能化的应用程序!
更多推荐
所有评论(0)