深入解析 Spring AI 系列:解析OpenAI介面對接

努力的小雨發表於2025-01-14

今天我們將主要探討OpenAI是如何進行介面對接的,雖然我們不打算深入細節,但會對整體流程進行一個大概的瞭解。後續會逐步分析其中的具體細節,大家可以耐心等待,逐步展開。好的,現在讓我們開始,下面是我簡單繪製的一張圖示,旨在幫助大家更好地理解接下來的分析流程。

image

OpenAiApi

我們第一步將直接檢視 OpenAIApi 類,這是與介面最為密切相關的核心類。首先,我們會關注它的構造器部分,因為在構造器中,基本包含了與介面互動所需的最主要依賴和配置資訊。透過這段程式碼,我們可以瞭解該類如何初始化並準備好與 OpenAI 介面進行通訊。接下來,大家可以看到下面這段程式碼:

public OpenAiApi(String baseUrl, String apiKey, MultiValueMap<String, String> headers, String completionsPath,
    String embeddingsPath, RestClient.Builder restClientBuilder, WebClient.Builder webClientBuilder,
    ResponseErrorHandler responseErrorHandler) {

this.completionsPath = completionsPath;
this.embeddingsPath = embeddingsPath; 
Consumer<HttpHeaders> finalHeaders = h -> {
    h.setBearerAuth(apiKey);
    h.setContentType(MediaType.APPLICATION_JSON);
    h.addAll(headers);
};
this.restClient = restClientBuilder.baseUrl(baseUrl)
    .defaultHeaders(finalHeaders)
    .defaultStatusHandler(responseErrorHandler)
    .build();

this.webClient = webClientBuilder
    .baseUrl(baseUrl)
    .defaultHeaders(finalHeaders)
.build(); 
}

這段建構函式程式碼相對簡單,主要負責建立一個包含認證資訊和內容型別的HTTP頭配置,並透過這些配置初始化RestClient和WebClient,從而為後續的網路請求提供基礎支援。

RestClient

RestClient主要用於處理非流式的請求和響應,程式碼如下:

public ResponseEntity<ChatCompletion> chatCompletionEntity(ChatCompletionRequest chatRequest,
        MultiValueMap<String, String> additionalHttpHeader) {

    return this.restClient.post()
        .uri(this.completionsPath)
        .headers(headers -> headers.addAll(additionalHttpHeader))
        .body(chatRequest)
        .retrieve()
        .toEntity(ChatCompletion.class);
}

WebClient

WebClient主要就是處理流式的請求和響應,程式碼看下:

    public Flux<ChatCompletionChunk> chatCompletionStream(ChatCompletionRequest chatRequest,
            MultiValueMap<String, String> additionalHttpHeader) {

        AtomicBoolean isInsideTool = new AtomicBoolean(false);

        return this.webClient.post()
            .uri(this.completionsPath)
            //此處省略部分程式碼

這部分程式碼引數很多,我們就看下核心邏輯即可。

類屬性

這樣,在理解了主要流程之後,你就能更清晰地理解每個引數在具體實現中的角色,以及它們如何影響整體功能的執行。接下來,我們來一起看一下這段關鍵程式碼:

public static final OpenAiApi.ChatModel DEFAULT_CHAT_MODEL = ChatModel.GPT_4_O;
public static final String DEFAULT_EMBEDDING_MODEL = EmbeddingModel.TEXT_EMBEDDING_ADA_002.getValue();
private static final Predicate<String> SSE_DONE_PREDICATE = "[DONE]"::equals;
private final String completionsPath;
private final String embeddingsPath;
private final RestClient restClient;
private final WebClient webClient;
private OpenAiStreamFunctionCallingHelper chunkMerger = new OpenAiStreamFunctionCallingHelper();

在去掉了向量的相關屬性之後,剩下的部分就是chunkMerger,不過這個部分涉及的是處理流式響應的邏輯,暫時我們可以先不關注它。為了簡化分析,當前我們主要關注的是最基本的、正常的阻塞式請求處理流程,因為這種模式更加直觀易懂,便於我們理解和除錯。

ChatCompletionRequest

接下來,我們將繼續深入分析之前提到的阻塞請求 chatCompletionEntity。該請求的引數包括一個 ChatCompletionRequest 物件和一個包含額外頭資訊的 Map 結構。由於我們關注的重點是請求的核心內容,因此我們將主要分析 ChatCompletionRequest 的實現。

image

這部分內容相信大家一定很熟悉,它實際上就是介面請求的引數部分。具體來說,它是一個記錄類,用於封裝介面請求所需的各項資訊。透過檢視原有介面平臺上展示的引數列表,我們可以很清楚地看到這個記錄類是如何對映到實際介面請求中的各個欄位的。如圖所示:

image

ChatCompletion

他的返回引數是ResponseEntity,他會將返回資訊包裝成一個ChatCompletion實體,猜一下也是介面返回相關的引數封裝。如圖所示:

image

image

usage

在之前我們說過usage這個類,他其實就是計算token用的。如果不看統計類的資訊,也沒啥大用。

image

這裡就不拿官方介面做對比了,結果是一樣的。

總結

透過今天的分析,我們初步瞭解了OpenAI介面對接的整體流程。雖然我們沒有深入細節,但透過對OpenAiApi類、RestClient、WebClient及相關請求引數的分析,大家應該對介面的工作原理有了一個大致的認識。後續,我們將繼續細化具體實現,逐步揭示每個部分的功能與邏輯。希望大家耐心等待,跟隨我們一起深入探索更多的技術細節。這一過程將幫助我們更好地理解如何與OpenAI的介面進行高效對接與互動。


我是努力的小雨,一個正經的 Java 東北服務端開發,整天琢磨著 AI 技術這塊兒的奧秘。特愛跟人交流技術,喜歡把自己的心得和大家分享。還當上了騰訊雲創作之星,阿里雲專家博主,華為云云享專家,掘金優秀作者。各種徵文、開源比賽的牌子也拿了。

💡 想把我在技術路上走過的彎路和經驗全都分享出來,給你們的學習和成長帶來點啟發,幫一把。

🌟 歡迎關注努力的小雨,咱一塊兒進步!🌟

相關文章