能杜孱 发表于 2025-11-20 22:50:01

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.java2. 通用实现模式

所有大模型实现都遵循相同的模式:

[*]实现核心接口:实现 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. 实现对比分析

特性OpenAIDeepSeekOCITransformersAPI 类型REST APIREST APIOCI 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 -> !"".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+ 种不同的模型提供商,同时保持代码的可维护性。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

村亢 发表于 2025-11-21 06:30:45

yyds。多谢分享

遑盲 发表于 2025-12-7 05:47:58

这个好,看起来很实用
页: [1]
查看完整版本: Spring AI 代码分析(三)--集成LLM