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

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

  1. 实现核心接口:实现 ChatModelEmbeddingModel
  2. 底层 API 客户端:封装提供商的 HTTP API
  3. 选项管理:每个模型有自己的 Options 类
  4. 密钥管理:通过 ApiKey 接口管理认证
  5. 重试机制:集成 Spring Retry
  6. 观察性:集成 Micrometer

3. 实现模式关系图

image.png

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 的大模型集成实现有几个亮点:

统一抽象,灵活实现。所有模型都实现相同的接口(ChatModelEmbeddingModel),但底层实现可以完全不同。这种设计让用户可以轻松切换模型,而不需要改业务代码。今天用 OpenAI,明天想换 DeepSeek?改个配置就行。

密钥管理统一化。通过 ApiKey 接口,所有模型使用相同的密钥管理方式。这简化了配置,也为未来的动态密钥刷新提供了扩展点。想从配置中心动态获取密钥?实现 ApiKey 接口就行。

选项合并机制ModelOptionsUtils.merge() 提供了灵活的选项合并机制,支持默认选项和运行时选项的合并,让配置既灵活又简单。默认配置在 application.yml,运行时可以动态调整。

观察性内置。所有实现都集成了 Micrometer,提供了完善的指标和追踪能力。这对于生产环境非常重要,可以监控模型调用次数、延迟、错误率等。

本地模型支持。Transformers 实现展示了如何在本地运行模型,这对于数据隐私要求高的场景非常有价值。数据不出本地,完全可控。

总的来说,Spring AI 的大模型集成实现既统一又灵活。统一的接口让代码简洁,灵活的实现让每个提供商都能发挥自己的优势。这种设计让 Spring AI 能够支持 20+ 种不同的模型提供商,同时保持代码的可维护性。

posted @ 2025-11-20 21:32  wasp  阅读(4)  评论(0)    收藏  举报