在Spring AI中调用AI模型时,处理请求超时和错误需要结合Spring的异常处理机制和Spring AI自身的配置选项。以下是具体的实现方法和最佳实践:
1. 超时配置(预防超时)
首先通过配置避免无限制等待,Spring AI支持对不同AI服务设置超时参数:
全局超时配置
在application.properties
或application.yml
中设置:
# OpenAI 超时配置(毫秒)
spring.ai.openai.timeout=30000 # 30秒
# 更精细的超时控制(部分服务支持)
spring.ai.openai.chat.timeout=20000 # 聊天模型单独设置超时
spring.ai.openai.image.timeout=60000 # 图像生成超时更长
代码中动态设置超时
对个别请求需要特殊超时设置时,可通过RequestOptions
实现:
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.stereotype.Service;
@Service
public class AIService {
private final ChatClient chatClient;
public AIService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String generateWithTimeout(String prompt) {
// 为当前请求设置单独的超时(10秒)
OpenAiChatOptions options = OpenAiChatOptions.builder()
.withTimeout(10000)
.build();
return chatClient.prompt(
new Prompt(prompt, options) // 将选项传入prompt
).call()
.content();
}
}
2. 异常处理(捕获和处理错误)
Spring AI会抛出特定异常,可通过try-catch
捕获并处理:
常见异常类型
AiException
:所有Spring AI相关异常的父类
AiTimeoutException
:超时异常AiServiceException
:AI服务返回错误(如API密钥无效、模型不存在)AiResponseException
:响应格式错误或解析失败
同步调用的异常处理
import org.springframework.ai.AiException;
import org.springframework.ai.AiTimeoutException;
import org.springframework.ai.chat.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class AIService {
private final ChatClient chatClient;
public AIService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String safeGenerate(String userInput) {
try {
return chatClient.prompt()
.user(userInput)
.call()
.content();
} catch (AiTimeoutException e) {
// 处理超时:返回友好提示或执行重试
return "请求超时,请稍后再试";
} catch (AiException e) {
// 处理其他AI相关错误(如API密钥无效、模型错误)
log.error("AI调用失败: {}", e.getMessage());
return "服务暂时不可用,请稍后尝试";
} catch (Exception e) {
// 处理其他未知错误
log.error("未知错误: {}", e.getMessage());
return "系统错误,请联系管理员";
}
}
}
流式调用的异常处理(响应式)
流式调用返回Flux
,需通过响应式操作符处理错误:
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
@Service
public class StreamAIService {
private final ChatClient chatClient;
public StreamAIService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public Flux<String> safeStreamGenerate(String userInput) {
return chatClient.stream(new UserMessage(userInput))
.map(response -> response.getResult().getOutput().getContent())
// 响应式错误处理
.onErrorResume(AiTimeoutException.class, e -> {
// 超时后返回错误提示
return Flux.just("请求超时,请稍后再试");
})
.onErrorResume(AiException.class, e -> {
log.error("AI流式调用失败: {}", e.getMessage());
return Flux.just("服务异常,请稍后尝试");
})
.onErrorResume(e -> {
log.error("未知错误: {}", e.getMessage());
return Flux.just("系统错误,请联系管理员");
});
}
}
3. 高级策略:重试机制
结合Spring的@Retryable
实现失败重试(需添加spring-retry
依赖):
步骤1:添加依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
步骤2:启用重试
在启动类添加@EnableRetry
:
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableRetry
public class AiApplication {
public static void main(String[] args) {
SpringApplication.run(AiApplication.class, args);
}
}
步骤3:在服务方法上使用重试
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.stereotype.Service;
@Service
public class RetryAIService {
private final ChatClient chatClient;
public RetryAIService(ChatClient chatClient) {
this.chatClient = chatClient;
}
// 对超时和服务异常进行重试,最多3次,每次间隔1秒
@Retryable(
value = {AiTimeoutException.class, AiServiceException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000) // 重试间隔1秒
)
public String generateWithRetry(String input) {
return chatClient.prompt()
.user(input)
.call()
.content();
}
// 重试耗尽后执行的方法(可选)
@Recover
public String recover(AiException e, String input) {
log.error("重试耗尽,最终失败: {}", e.getMessage());
return "多次尝试失败,请稍后再试";
}
}
4. 错误日志与监控
建议记录详细错误日志,便于排查问题:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Service
public class AIService {
private static final Logger log = LoggerFactory.getLogger(AIService.class);
public String generate(String input) {
try {
// 调用逻辑
} catch (AiException e) {
// 记录错误详情(包括请求参数,注意脱敏)
log.error("AI调用失败,输入: {}, 错误: {}",
input.substring(0, 50), // 只记录部分输入,避免敏感信息
e.getMessage(),
e); // 打印完整堆栈
return "服务异常";
}
}
}
总结
处理超时和错误的核心流程:
- 预防:通过配置合理的超时参数,避免无限等待
- 捕获:使用
try-catch
(同步)或onErrorResume
(流式)捕获异常 - 恢复:返回友好提示,或通过重试机制尝试恢复
- 监控:记录详细日志,便于问题排查
根据业务场景调整策略,例如:
- 对实时性要求高的场景(如聊天):缩短超时时间,不重试
- 对成功率要求高的场景(如批量处理):延长超时,增加重试次数