1. 引言:为什么选择 Spring AI?
在人工智能浪潮席卷全球的今天,大语言模型(LLM)已成为企业数字化转型的核心驱动力。然而,对于 Java 后端开发者而言,如何将大模型能力无缝集成到现有系统中,面临着诸多挑战:API 调用复杂、模型切换困难、提示工程繁琐、成本控制不易等。
Spring AI 应运而生------这是 Spring 官方推出的 AI 集成框架,旨在为 Java 开发者提供统一、声明式的 AI 应用开发体验。它基于 Spring 生态的成熟设计理念,将大模型能力抽象为可插拔的组件,让开发者能够像使用数据库、消息队列一样轻松集成 AI 能力。
本文将深入探讨 Spring AI 的架构设计、核心组件、工程实践,并通过完整案例展示如何在企业级 Java 后端系统中落地大模型集成。
2. Spring AI 核心架构解析
2.1 分层架构设计
Spring AI 采用经典的分层架构,从上到下分为:
- 应用层:业务逻辑与 AI 能力的结合点
- 服务层 :
AiClient接口与具体实现 - 适配层:模型提供商适配器(OpenAI、Azure、Anthropic 等)
- 传输层:HTTP/REST 或 SDK 调用
#mermaid-svg-Pi2yvJcVA9nYjPBx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Pi2yvJcVA9nYjPBx .error-icon{fill:#552222;}#mermaid-svg-Pi2yvJcVA9nYjPBx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Pi2yvJcVA9nYjPBx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .marker.cross{stroke:#333333;}#mermaid-svg-Pi2yvJcVA9nYjPBx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Pi2yvJcVA9nYjPBx p{margin:0;}#mermaid-svg-Pi2yvJcVA9nYjPBx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .cluster-label text{fill:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .cluster-label span{color:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .cluster-label span p{background-color:transparent;}#mermaid-svg-Pi2yvJcVA9nYjPBx .label text,#mermaid-svg-Pi2yvJcVA9nYjPBx span{fill:#333;color:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .node rect,#mermaid-svg-Pi2yvJcVA9nYjPBx .node circle,#mermaid-svg-Pi2yvJcVA9nYjPBx .node ellipse,#mermaid-svg-Pi2yvJcVA9nYjPBx .node polygon,#mermaid-svg-Pi2yvJcVA9nYjPBx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .rough-node .label text,#mermaid-svg-Pi2yvJcVA9nYjPBx .node .label text,#mermaid-svg-Pi2yvJcVA9nYjPBx .image-shape .label,#mermaid-svg-Pi2yvJcVA9nYjPBx .icon-shape .label{text-anchor:middle;}#mermaid-svg-Pi2yvJcVA9nYjPBx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .rough-node .label,#mermaid-svg-Pi2yvJcVA9nYjPBx .node .label,#mermaid-svg-Pi2yvJcVA9nYjPBx .image-shape .label,#mermaid-svg-Pi2yvJcVA9nYjPBx .icon-shape .label{text-align:center;}#mermaid-svg-Pi2yvJcVA9nYjPBx .node.clickable{cursor:pointer;}#mermaid-svg-Pi2yvJcVA9nYjPBx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .arrowheadPath{fill:#333333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Pi2yvJcVA9nYjPBx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Pi2yvJcVA9nYjPBx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Pi2yvJcVA9nYjPBx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Pi2yvJcVA9nYjPBx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .cluster text{fill:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx .cluster span{color:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Pi2yvJcVA9nYjPBx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Pi2yvJcVA9nYjPBx rect.text{fill:none;stroke-width:0;}#mermaid-svg-Pi2yvJcVA9nYjPBx .icon-shape,#mermaid-svg-Pi2yvJcVA9nYjPBx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Pi2yvJcVA9nYjPBx .icon-shape p,#mermaid-svg-Pi2yvJcVA9nYjPBx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Pi2yvJcVA9nYjPBx .icon-shape .label rect,#mermaid-svg-Pi2yvJcVA9nYjPBx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Pi2yvJcVA9nYjPBx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Pi2yvJcVA9nYjPBx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Pi2yvJcVA9nYjPBx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 传输层
HTTP/REST
WebSocket
gRPC
适配层
OpenAI Adapter
Azure AI Adapter
Anthropic Adapter
Local Model Adapter
服务层
AiClient Interface
ChatClient
EmbeddingClient
应用层
Controller
Service
Prompt Templates
2.2 核心组件详解
2.2.1 AiClient 接口
AiClient 是 Spring AI 的核心抽象,定义了统一的 AI 操作接口:
java
public interface AiClient {
String generate(String prompt);
AiResponse generate(AiRequest request);
// 流式响应支持
Flux<String> stream(String prompt);
}
2.2.2 Prompt 模板引擎
Spring AI 内置强大的提示模板引擎,支持变量替换、条件逻辑和函数调用:
java
@Bean
public PromptTemplate promptTemplate() {
return new PromptTemplate("""
你是一个专业的{role}助手。
请根据以下上下文回答问题:
上下文:{context}
问题:{question}
要求:{requirement}
""");
}
2.2.3 向量存储集成
Spring AI 与主流向量数据库(Redis、PgVector、Milvus 等)深度集成:
java
@Bean
public VectorStore vectorStore(EmbeddingClient embeddingClient) {
return new RedisVectorStore(embeddingClient);
}
3. 环境搭建与项目初始化
3.1 依赖配置
xml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
<version>1.0.0</version>
</dependency>
3.2 配置文件
yaml
# application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4-turbo
temperature: 0.7
max-tokens: 2000
vectorstore:
pgvector:
enabled: true
host: localhost
port: 5432
database: ai_db
username: postgres
password: ${DB_PASSWORD}
3.3 基础配置类
java
@Configuration
@EnableAiClients
public class AiConfig {
@Bean
public ChatClient chatClient(OpenAiChatClient openAiClient) {
return openAiClient;
}
@Bean
public EmbeddingClient embeddingClient(OpenAiEmbeddingClient openAiEmbeddingClient) {
return openAiEmbeddingClient;
}
@Bean
public PromptTemplate systemPromptTemplate() {
return new PromptTemplate("""
系统角色:{systemRole}
当前任务:{task}
用户输入:{userInput}
请按照以下格式回复:
{format}
""");
}
}
4. 核心功能实现
4.1 智能问答系统
java
@Service
@Slf4j
public class QaService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
private final PromptTemplate qaPromptTemplate;
public QaService(ChatClient chatClient,
VectorStore vectorStore,
@Qualifier("qaPromptTemplate") PromptTemplate qaPromptTemplate) {
this.chatClient = chatClient;
this.vectorStore = vectorStore;
this.qaPromptTemplate = qaPromptTemplate;
}
public AiResponse answerQuestion(String question, String context) {
// 1. 构建提示词
Map<String, Object> promptVariables = Map.of(
"question", question,
"context", context,
"currentTime", LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
);
Prompt prompt = qaPromptTemplate.create(promptVariables);
// 2. 调用 AI 服务
AiResponse response = chatClient.call(prompt);
// 3. 记录日志
log.info("QA request - Question: {}, Context length: {}, Response tokens: {}",
question, context.length(), response.getGeneration().getText().length());
return response;
}
public Flux<String> streamAnswer(String question) {
return chatClient.stream(question)
.doOnNext(chunk -> log.debug("Received chunk: {}", chunk))
.doOnError(error -> log.error("Stream error: ", error))
.doOnComplete(() -> log.info("Stream completed"));
}
}
4.2 文档智能处理
java
@Service
public class DocumentService {
private final EmbeddingClient embeddingClient;
private final VectorStore vectorStore;
public void processDocument(MultipartFile file) {
try {
// 1. 提取文本内容
String content = extractText(file);
// 2. 分块处理
List<TextChunk> chunks = chunkText(content, 1000);
// 3. 生成向量
List<Embedding> embeddings = embeddingClient.embed(chunks.stream()
.map(TextChunk::getText)
.collect(Collectors.toList()));
// 4. 存储到向量数据库
List<Document> documents = new ArrayList<>();
for (int i = 0; i < chunks.size(); i++) {
Document doc = new Document(
chunks.get(i).getText(),
Map.of(
"filename", file.getOriginalFilename(),
"chunkIndex", i,
"timestamp", System.currentTimeMillis()
)
);
doc.setEmbedding(embeddings.get(i));
documents.add(doc);
}
vectorStore.add(documents);
} catch (IOException e) {
throw new RuntimeException("文档处理失败", e);
}
}
public List<Document> searchSimilar(String query, int topK) {
// 生成查询向量
Embedding queryEmbedding = embeddingClient.embed(query);
// 相似度搜索
return vectorStore.similaritySearch(
SimilaritySearchRequest.builder()
.queryEmbedding(queryEmbedding)
.topK(topK)
.build()
);
}
}
4.3 函数调用与工具集成
java
@Service
public class FunctionCallingService {
@AiFunction(name = "getWeather", description = "获取指定城市的天气信息")
public String getWeather(@AiParam("city") String city) {
// 调用外部天气 API
return weatherApiClient.getWeather(city);
}
@AiFunction(name = "calculate", description = "执行数学计算")
public String calculate(
@AiParam("expression") String expression,
@AiParam("precision") int precision) {
try {
double result = evaluateExpression(expression);
return String.format("%." + precision + "f", result);
} catch (Exception e) {
return "计算失败: " + e.getMessage();
}
}
public AiResponse callWithFunctions(String userInput) {
List<FunctionCallback> callbacks = List.of(
FunctionCallbackWrapper.builder(new WeatherFunction())
.withName("getWeather")
.withDescription("获取天气信息")
.withResponseConverter((response) -> response.toString())
.build(),
FunctionCallbackWrapper.builder(new CalculatorFunction())
.withName("calculate")
.withDescription("执行计算")
.build()
);
return chatClient.call(
new Prompt(userInput),
ChatOptions.builder()
.withFunctionCallbacks(callbacks)
.build()
);
}
}
5. 企业级架构设计
5.1 微服务架构下的 AI 集成
#mermaid-svg-4GObQHNh8sl1Sb6e{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4GObQHNh8sl1Sb6e .error-icon{fill:#552222;}#mermaid-svg-4GObQHNh8sl1Sb6e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4GObQHNh8sl1Sb6e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4GObQHNh8sl1Sb6e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4GObQHNh8sl1Sb6e .marker.cross{stroke:#333333;}#mermaid-svg-4GObQHNh8sl1Sb6e svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4GObQHNh8sl1Sb6e p{margin:0;}#mermaid-svg-4GObQHNh8sl1Sb6e .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e .cluster-label text{fill:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e .cluster-label span{color:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e .cluster-label span p{background-color:transparent;}#mermaid-svg-4GObQHNh8sl1Sb6e .label text,#mermaid-svg-4GObQHNh8sl1Sb6e span{fill:#333;color:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e .node rect,#mermaid-svg-4GObQHNh8sl1Sb6e .node circle,#mermaid-svg-4GObQHNh8sl1Sb6e .node ellipse,#mermaid-svg-4GObQHNh8sl1Sb6e .node polygon,#mermaid-svg-4GObQHNh8sl1Sb6e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4GObQHNh8sl1Sb6e .rough-node .label text,#mermaid-svg-4GObQHNh8sl1Sb6e .node .label text,#mermaid-svg-4GObQHNh8sl1Sb6e .image-shape .label,#mermaid-svg-4GObQHNh8sl1Sb6e .icon-shape .label{text-anchor:middle;}#mermaid-svg-4GObQHNh8sl1Sb6e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4GObQHNh8sl1Sb6e .rough-node .label,#mermaid-svg-4GObQHNh8sl1Sb6e .node .label,#mermaid-svg-4GObQHNh8sl1Sb6e .image-shape .label,#mermaid-svg-4GObQHNh8sl1Sb6e .icon-shape .label{text-align:center;}#mermaid-svg-4GObQHNh8sl1Sb6e .node.clickable{cursor:pointer;}#mermaid-svg-4GObQHNh8sl1Sb6e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4GObQHNh8sl1Sb6e .arrowheadPath{fill:#333333;}#mermaid-svg-4GObQHNh8sl1Sb6e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4GObQHNh8sl1Sb6e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4GObQHNh8sl1Sb6e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4GObQHNh8sl1Sb6e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4GObQHNh8sl1Sb6e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4GObQHNh8sl1Sb6e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4GObQHNh8sl1Sb6e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4GObQHNh8sl1Sb6e .cluster text{fill:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e .cluster span{color:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-4GObQHNh8sl1Sb6e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4GObQHNh8sl1Sb6e rect.text{fill:none;stroke-width:0;}#mermaid-svg-4GObQHNh8sl1Sb6e .icon-shape,#mermaid-svg-4GObQHNh8sl1Sb6e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4GObQHNh8sl1Sb6e .icon-shape p,#mermaid-svg-4GObQHNh8sl1Sb6e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4GObQHNh8sl1Sb6e .icon-shape .label rect,#mermaid-svg-4GObQHNh8sl1Sb6e .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4GObQHNh8sl1Sb6e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4GObQHNh8sl1Sb6e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4GObQHNh8sl1Sb6e :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 模型服务层
数据服务层
AI 核心服务
AI 网关服务
API Gateway
负载均衡
限流熔断
Prompt 管理服务
模型路由服务
向量检索服务
缓存服务
向量数据库
关系数据库
缓存集群
OpenAI
Azure AI
本地模型
备用模型
5.2 配置中心与动态切换
java
@Configuration
@RefreshScope
public class DynamicAiConfig {
@Value("${spring.ai.provider:openai}")
private String aiProvider;
@Bean
@Primary
public ChatClient chatClient(
OpenAiChatClient openAiClient,
AzureOpenAiChatClient azureClient,
AnthropicChatClient anthropicClient) {
return switch (aiProvider.toLowerCase()) {
case "azure" -> azureClient;
case "anthropic" -> anthropicClient;
case "openai", default -> openAiClient;
};
}
@Bean
public ModelRouter modelRouter() {
return new ModelRouter(Map.of(
"gpt-4", openAiChatClient,
"claude-3", anthropicChatClient,
"llama-3", localModelClient
));
}
}
5.3 监控与可观测性
java
@Configuration
public class MonitoringConfig {
@Bean
public MeterRegistryCustomizer<MeterRegistry> aiMetrics() {
return registry -> {
Timer.builder("ai.request.duration")
.description("AI 请求耗时")
.tag("provider", "openai")
.register(registry);
Counter.builder("ai.request.total")
.description("AI 请求总数")
.tag("status", "success")
.register(registry);
};
}
@Bean
public AiClientInterceptor metricsInterceptor(MeterRegistry meterRegistry) {
return new AiClientInterceptor() {
@Override
public AiResponse intercept(AiRequest request, AiClientExecution execution) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
AiResponse response = execution.execute(request);
sample.stop(Timer.builder("ai.request.duration")
.tag("status", "success")
.register(meterRegistry));
meterRegistry.counter("ai.request.total",
"status", "success").increment();
return response;
} catch (Exception e) {
sample.stop(Timer.builder("ai.request.duration")
.tag("status", "error")
.register(meterRegistry));
meterRegistry.counter("ai.request.total",
"status", "error").increment();
throw e;
}
}
};
}
}
6. 性能优化与最佳实践
6.1 缓存策略
java
@Service
@CacheConfig(cacheNames = "aiResponses")
public class CachedAiService {
private final ChatClient chatClient;
@Cacheable(key = "#prompt + '|' + #options.hashCode()",
unless = "#result == null")
public AiResponse getCachedResponse(String prompt, ChatOptions options) {
return chatClient.call(new Prompt(prompt, options));
}
@CacheEvict(allEntries = true)
public void clearCache() {
// 清理所有缓存
}
@Scheduled(fixedRate = 3600000) // 每小时清理一次
public void scheduledCacheEviction() {
clearCache();
}
}
6.2 批量处理与并发控制
java
@Service
public class BatchAiService {
private final ChatClient chatClient;
private final ExecutorService executorService;
@Async("aiTaskExecutor")
public CompletableFuture<List<AiResponse>> batchProcess(
List<String> prompts,
int batchSize) {
List<CompletableFuture<AiResponse>> futures = new ArrayList<>();
// 分批处理
for (int i = 0; i < prompts.size(); i += batchSize) {
int end = Math.min(i + batchSize, prompts.size());
List<String> batch = prompts.subList(i, end);
CompletableFuture<AiResponse> future = CompletableFuture.supplyAsync(() -> {
// 合并提示词
String combinedPrompt = String.join("\n---\n", batch);
return chatClient.call(combinedPrompt);
}, executorService);
futures.add(future);
}
// 等待所有任务完成
return CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
).thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
}
6.3 错误处理与重试机制
java
@Configuration
public class RetryConfig {
@Bean
public RetryTemplate aiRetryTemplate() {
return RetryTemplate.builder()
.maxAttempts(3)
.exponentialBackoff(1000, 2, 10000)
.retryOn(OpenAiHttpException.class)
.retryOn(SocketTimeoutException.class)
.notRetryOn(IllegalArgumentException.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);
}
})
.build();
}
@Bean
public CircuitBreakerFactory aiCircuitBreakerFactory() {
return new Resilience4JCircuitBreakerFactory();
}
}
7. 安全与合规考虑
7.1 敏感信息过滤
java
@Component
public class SecurityFilter implements AiClientInterceptor {
private final SensitiveDataFilter sensitiveDataFilter;
@Override
public AiResponse intercept(AiRequest request, AiClientExecution execution) {
// 1. 过滤敏感信息
String filteredPrompt = sensitiveDataFilter.filter(request.getPrompt());
// 2. 记录审计日志
auditLogger.logRequest(filteredPrompt, request.getOptions());
// 3. 执行请求
AiRequest filteredRequest = new AiRequest(filteredPrompt, request.getOptions());
AiResponse response = execution.execute(filteredRequest);
// 4. 过滤响应中的敏感信息
String filteredResponse = sensitiveDataFilter.filter(response.getGeneration().getText());
return new AiResponse(filteredResponse, response.getMetadata());
}
}
7.2 访问控制与权限管理
java
@RestController
@RequestMapping("/api/ai")
@PreAuthorize("hasRole('AI_USER')")
public class AiController {
@PostMapping("/chat")
@RateLimit(limit = 10, duration = 60) // 每分钟10次
@CostLimit(maxCost = 10.0) // 单次调用成本限制
public ResponseEntity<AiResponse> chat(
@RequestBody ChatRequest request,
@AuthenticationPrincipal User user) {
// 检查用户权限
if (!user.hasPermission("ai.chat")) {
throw new AccessDeniedException("无权限访问AI聊天功能");
}
// 检查额度
if (!quotaService.hasEnoughQuota(user.getId(), request.estimatedCost())) {
throw new QuotaExceededException("额度不足");
}
AiResponse response = chatService.chat(request);
// 扣减额度
quotaService.deductQuota(user.getId(), response.actualCost());
return ResponseEntity.ok(response);
}
}