Spring AI 代码分析(三)--集成LLM
大模型集成实现分析
请关注微信公众号:阿呆-bot
1. 工程结构概览
Spring AI 支持多种大模型提供商,每个提供商都有独立的实现模块。虽然实现细节不同,但它们都遵循统一的抽象模式。下面我们来看看主要的模型实现:
models/
├── spring-ai-openai/ # OpenAI 实现
│ ├── OpenAiChatModel.java
│ ├── OpenAiEmbeddingModel.java
│ └── api/ # 底层 API 客户端
│ └── OpenAiApi.java
│
├── spring-ai-deepseek/ # DeepSeek 实现
│ ├── DeepSeekChatModel.java
│ └── api/
│ └── DeepSeekApi.java
│
├── spring-ai-oci-genai/ # Oracle OCI GenAI 实现
│ ├── OCICohereChatModel.java
│ └── OCIEmbeddingModel.java
│
└── spring-ai-transformers/ # 本地 Transformer 模型
└── TransformersEmbeddingModel.java
2. 通用实现模式
所有大模型实现都遵循相同的模式:
- 实现核心接口:实现
ChatModel或EmbeddingModel - 底层 API 客户端:封装提供商的 HTTP API
- 选项管理:每个模型有自己的 Options 类
- 密钥管理:通过
ApiKey接口管理认证 - 重试机制:集成 Spring Retry
- 观察性:集成 Micrometer
3. 实现模式关系图

4. 密钥管理机制
4.1 ApiKey 接口设计
Spring AI 使用 ApiKey 接口统一管理所有模型的密钥:
public interface ApiKey {
String getValue(); // 获取当前有效的密钥
}
这个设计的巧妙之处在于:
- 支持静态密钥:
SimpleApiKey直接存储密钥 - 支持动态刷新:可以实现自动刷新的密钥(比如 GCP 的短期密钥)
- 类型安全:所有模型都使用相同的接口
4.2 密钥使用方式
在 API 客户端中,密钥通过 HTTP Header 传递:
// OpenAiApi 中的密钥使用
this.restClient = restClientBuilder.clone()
.baseUrl(baseUrl)
.defaultRequest(requestHeadersSpec -> {
if (!(apiKey instanceof NoopApiKey)) {
requestHeadersSpec.header(HttpHeaders.AUTHORIZATION,
"Bearer " + apiKey.getValue());
}
})
.build();
每次请求都会调用 apiKey.getValue(),这样如果密钥需要刷新,实现类可以在内部处理。
4.3 密钥配置方式
密钥可以通过多种方式配置:
方式一:配置文件
spring.ai.openai.api-key=${OPENAI_API_KEY}
方式二:环境变量
export OPENAI_API_KEY=sk-xxx
方式三:程序化配置
OpenAiApi api = OpenAiApi.builder()
.apiKey(new SimpleApiKey("sk-xxx"))
.build();
5. 具体实现分析
5.1 OpenAI 实现
OpenAI 的实现是最完整的,支持同步和流式调用:
public class OpenAiChatModel implements ChatModel {
private final OpenAiApi openAiApi;
private final OpenAiChatOptions defaultOptions;
private final RetryTemplate retryTemplate;
private final ToolCallingManager toolCallingManager;
@Override
public ChatResponse call(Prompt prompt) {
// 1. 构建请求 Prompt(处理工具调用)
Prompt requestPrompt = buildRequestPrompt(prompt);
// 2. 调用底层 API
ResponseEntity<ChatCompletion> response = retryTemplate.execute(
ctx -> openAiApi.chatCompletion(toRequest(requestPrompt))
);
// 3. 转换响应
return toChatResponse(response);
}
@Override
public Flux<ChatResponse> stream(Prompt prompt) {
// 流式调用类似,但返回 Flux
return openAiApi.chatCompletionStream(toRequest(prompt))
.map(this::toChatResponse);
}
}
特点:
- 完整的工具调用支持
- 支持流式响应
- 完善的错误处理和重试
- 支持多模态(图像、音频)
5.2 DeepSeek 实现
DeepSeek 的实现与 OpenAI 类似,但更简洁:
public class DeepSeekChatModel implements ChatModel {
private final DeepSeekApi deepSeekApi;
private final DeepSeekChatOptions defaultOptions;
@Override
public ChatResponse call(Prompt prompt) {
Prompt requestPrompt = buildRequestPrompt(prompt);
ChatModelObservationContext context = ChatModelObservationContext.builder()
.prompt(requestPrompt)
.provider("DEEPSEEK")
.build();
return ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
.observation(...)
.observe(() -> {
ResponseEntity<ChatCompletion> response =
deepSeekApi.chatCompletion(toRequest(requestPrompt));
return toChatResponse(response);
});
}
}
特点:
- 结构清晰,代码简洁
- 支持观察性(Micrometer)
- 支持工具调用
5.3 OCI GenAI 实现
OCI 的实现使用了 Oracle 的官方 SDK:
public class OCICohereChatModel implements ChatModel {
private final GenerativeAiInference genAi; // OCI SDK 客户端
private final OCICohereChatOptions defaultOptions;
@Override
public ChatResponse call(Prompt prompt) {
// 使用 OCI SDK 调用
ChatRequest request = toCohereChatRequest(prompt, options);
ChatResponse ociResponse = this.genAi.chat(request);
return toGenerations(ociResponse, options);
}
}
特点:
- 使用官方 OCI SDK
- 需要配置 compartment 和 serving mode
- 支持 Cohere 模型
5.4 Transformers 实现(本地模型)
Transformers 实现比较特殊,它不调用远程 API,而是在本地运行 ONNX 模型:
public class TransformersEmbeddingModel extends AbstractEmbeddingModel {
private OrtSession session; // ONNX Runtime 会话
private HuggingFaceTokenizer tokenizer;
@Override
public EmbeddingResponse call(EmbeddingRequest request) {
// 1. Tokenize 输入文本
Encoding encoding = tokenizer.encode(request.getInstructions());
// 2. 运行 ONNX 模型
OnnxTensor inputTensor = OnnxTensor.createTensor(
ortEnvironment,
encoding.getIds()
);
OrtSession.Result result = session.run(
Map.of("input_ids", inputTensor)
);
// 3. 提取嵌入向量
float[] embedding = extractEmbedding(result);
return new EmbeddingResponse(List.of(new Embedding(embedding)));
}
}
特点:
- 完全本地运行:不需要 API 密钥
- 使用 ONNX Runtime:高性能推理引擎
- 支持 GPU 加速:可以配置 GPU 推理
- 模型缓存:自动下载和缓存模型文件
6. 实现对比分析
| 特性 | OpenAI | DeepSeek | OCI | Transformers |
|---|---|---|---|---|
| API 类型 | REST API | REST API | OCI SDK | 本地 ONNX |
| 密钥管理 | ApiKey 接口 | ApiKey 接口 | OCI 认证 | 无需密钥 |
| 流式支持 | ✅ | ✅ | ❌ | ❌ |
| 工具调用 | ✅ | ✅ | ✅ | N/A |
| 多模态 | ✅ | ❌ | ❌ | N/A |
| 重试机制 | ✅ | ✅ | ✅ | N/A |
| 观察性 | ✅ | ✅ | ✅ | ✅ |
7. 关键实现细节
7.1 请求构建模式
所有实现都遵循相同的请求构建模式:
// 1. 合并默认选项和运行时选项
ChatOptions requestOptions = ModelOptionsUtils.merge(
prompt.getOptions(),
this.defaultOptions,
ChatOptions.class
);
// 2. 转换为提供商特定的请求对象
ProviderRequest providerRequest = toProviderRequest(prompt, requestOptions);
// 3. 调用底层 API
ProviderResponse providerResponse = apiClient.call(providerRequest);
// 4. 转换为 Spring AI 响应
ChatResponse chatResponse = toChatResponse(providerResponse);
7.2 工具调用处理
支持工具调用的模型(如 OpenAI、DeepSeek)都实现了类似的工具调用流程:
// 1. 解析工具定义
List<ToolDefinition> toolDefinitions =
toolCallingManager.resolveToolDefinitions(options);
// 2. 将工具定义添加到请求中
request.setTools(toolDefinitions);
// 3. 调用模型
ChatResponse response = apiClient.call(request);
// 4. 检查是否有工具调用请求
if (response.hasToolCalls()) {
// 5. 执行工具调用
ToolExecutionResult toolResult =
toolCallingManager.executeToolCalls(prompt, response);
// 6. 将工具结果作为新的 Prompt 再次调用
return call(new Prompt(toolResult.getConversationHistory()));
}
7.3 流式处理实现
流式处理使用 Reactor 的 Flux:
@Override
public Flux<ChatResponse> stream(Prompt prompt) {
return webClient.post()
.uri("/v1/chat/completions")
.bodyValue(toRequest(prompt))
.retrieve()
.bodyToFlux(ServerSentEvent.class)
.filter(event -> !"[DONE]".equals(event.data()))
.map(this::toChatResponse);
}
8. 外部依赖
不同实现的依赖差异:
8.1 OpenAI/DeepSeek
- Spring Web:RestClient 和 WebClient
- Jackson:JSON 处理
- Spring Retry:重试机制
8.2 OCI GenAI
- OCI SDK:Oracle 官方 SDK
- Spring Web:HTTP 客户端(如果需要)
8.3 Transformers
- ONNX Runtime:ONNX 模型推理
- DJL:Deep Java Library(Tokenizer)
- 无网络依赖:完全本地运行
9. 工程总结
Spring AI 的大模型集成实现有几个亮点:
统一抽象,灵活实现。所有模型都实现相同的接口(ChatModel、EmbeddingModel),但底层实现可以完全不同。这种设计让用户可以轻松切换模型,而不需要改业务代码。今天用 OpenAI,明天想换 DeepSeek?改个配置就行。
密钥管理统一化。通过 ApiKey 接口,所有模型使用相同的密钥管理方式。这简化了配置,也为未来的动态密钥刷新提供了扩展点。想从配置中心动态获取密钥?实现 ApiKey 接口就行。
选项合并机制。ModelOptionsUtils.merge() 提供了灵活的选项合并机制,支持默认选项和运行时选项的合并,让配置既灵活又简单。默认配置在 application.yml,运行时可以动态调整。
观察性内置。所有实现都集成了 Micrometer,提供了完善的指标和追踪能力。这对于生产环境非常重要,可以监控模型调用次数、延迟、错误率等。
本地模型支持。Transformers 实现展示了如何在本地运行模型,这对于数据隐私要求高的场景非常有价值。数据不出本地,完全可控。
总的来说,Spring AI 的大模型集成实现既统一又灵活。统一的接口让代码简洁,灵活的实现让每个提供商都能发挥自己的优势。这种设计让 Spring AI 能够支持 20+ 种不同的模型提供商,同时保持代码的可维护性。

浙公网安备 33010602011771号