Spring AI 框架实战:Java 后端集成大模型的架构设计与工程落地
·
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 调用
2.2 核心组件详解
2.2.1 AiClient 接口
AiClient 是 Spring AI 的核心抽象,定义了统一的 AI 操作接口:
public interface AiClient {
String generate(String prompt);
AiResponse generate(AiRequest request);
// 流式响应支持
Flux<String> stream(String prompt);
}
2.2.2 Prompt 模板引擎
Spring AI 内置强大的提示模板引擎,支持变量替换、条件逻辑和函数调用:
@Bean
public PromptTemplate promptTemplate() {
return new PromptTemplate("""
你是一个专业的{role}助手。
请根据以下上下文回答问题:
上下文:{context}
问题:{question}
要求:{requirement}
""");
}
2.2.3 向量存储集成
Spring AI 与主流向量数据库(Redis、PgVector、Milvus 等)深度集成:
@Bean
public VectorStore vectorStore(EmbeddingClient embeddingClient) {
return new RedisVectorStore(embeddingClient);
}
3. 环境搭建与项目初始化
3.1 依赖配置
<!-- 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 配置文件
# 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 基础配置类
@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 智能问答系统
@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 文档智能处理
@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 函数调用与工具集成
@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 集成
5.2 配置中心与动态切换
@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 监控与可观测性
@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 缓存策略
@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 批量处理与并发控制
@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 错误处理与重试机制
@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 敏感信息过滤
@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 访问控制与权限管理
@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);
}
}
更多推荐
所有评论(0)