OpenAI Chat

Spring AI 支援來自 OpenAI 的各種 AI 語言模型,OpenAI 是 ChatGPT 背後的公司,由於其創建了業界領先的文字生成模型和嵌入模型,因此在激發人們對 AI 驅動的文字生成興趣方面發揮了重要作用。

先決條件

您需要使用 OpenAI 建立 API 才能存取 ChatGPT 模型。在 OpenAI 註冊頁面 建立帳戶,並在 API 金鑰頁面 上產生權杖。Spring AI 專案定義了一個名為 spring.ai.openai.api-key 的組態屬性,您應將其設定為從 openai.com 取得的 API 金鑰 值。匯出環境變數是設定該組態屬性的一種方法

export SPRING_AI_OPENAI_API_KEY=<INSERT KEY HERE>

新增儲存庫和 BOM

Spring AI 成品發佈在 Spring Milestone 和 Snapshot 儲存庫中。請參閱 儲存庫 章節,將這些儲存庫新增至您的建置系統。

為了協助進行依賴性管理,Spring AI 提供了 BOM(物料清單),以確保在整個專案中使用一致版本的 Spring AI。請參閱 依賴性管理 章節,將 Spring AI BOM 新增至您的建置系統。

自動組態

Spring AI 為 OpenAI Chat Client 提供 Spring Boot 自動組態。若要啟用它,請將以下依賴性新增至專案的 Maven pom.xml 或 Gradle build.gradle 建置檔案

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
dependencies {
    implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
}
請參閱 依賴性管理 章節,將 Spring AI BOM 新增至您的建置檔案。

聊天屬性

重試屬性

前綴 spring.ai.retry 用作屬性前綴,可讓您設定 OpenAI 聊天模型的重試機制。

屬性 描述 預設值

spring.ai.retry.max-attempts

最大重試次數。

10

spring.ai.retry.backoff.initial-interval

指數退避策略的初始睡眠持續時間。

2 秒。

spring.ai.retry.backoff.multiplier

退避間隔乘數。

5

spring.ai.retry.backoff.max-interval

最大退避持續時間。

3 分鐘。

spring.ai.retry.on-client-errors

如果為 false,則擲回 NonTransientAiException,且不嘗試重試 4xx 用戶端錯誤代碼

false

spring.ai.retry.exclude-on-http-codes

不應觸發重試的 HTTP 狀態代碼清單(例如,擲回 NonTransientAiException)。

spring.ai.retry.on-http-codes

應觸發重試的 HTTP 狀態代碼清單(例如,擲回 TransientAiException)。

連線屬性

前綴 spring.ai.openai 用作屬性前綴,可讓您連線至 OpenAI。

屬性 描述 預設值

spring.ai.openai.base-url

要連線的 URL

api.openai.com

spring.ai.openai.api-key

API 金鑰

-

spring.ai.openai.organization-id

您可以選擇性地指定要用於 API 請求的組織。

-

spring.ai.openai.project-id

您可以選擇性地指定要用於 API 請求的專案。

-

對於屬於多個組織的使用者(或透過其舊版使用者 API 金鑰存取其專案),您可以選擇性地指定用於 API 請求的組織和專案。來自這些 API 請求的使用量將計為指定組織和專案的使用量。

組態屬性

前綴 spring.ai.openai.chat 是屬性前綴,可讓您設定 OpenAI 的聊天模型實作。

屬性 描述 預設值

spring.ai.openai.chat.enabled

啟用 OpenAI 聊天模型。

true

spring.ai.openai.chat.base-url

spring.ai.openai.base-url 屬性的選用覆寫,以提供聊天專用 URL。

-

spring.ai.openai.chat.completions-path

要附加到基本 URL 的路徑。

/v1/chat/completions

spring.ai.openai.chat.api-key

spring.ai.openai.api-key 的選用覆寫,以提供聊天專用 API 金鑰。

-

spring.ai.openai.chat.organization-id

您可以選擇性地指定要用於 API 請求的組織。

-

spring.ai.openai.chat.project-id

您可以選擇性地指定要用於 API 請求的專案。

-

spring.ai.openai.chat.options.model

要使用的 OpenAI 聊天模型名稱。您可以從模型中選擇,例如:gpt-4ogpt-4o-minigpt-4-turbogpt-3.5-turbo 等等。請參閱 模型 頁面以取得更多資訊。

gpt-4o

spring.ai.openai.chat.options.temperature

要使用的取樣溫度,其控制產生的完成項目的表面創造力。較高的值會使輸出更隨機,而較低的值會使結果更集中和確定。不建議為相同的完成項目請求修改 temperaturetop_p,因為這兩個設定的互動難以預測。

0.8

spring.ai.openai.chat.options.frequencyPenalty

介於 -2.0 和 2.0 之間的數字。正值會根據新權杖在目前文字中的現有頻率來懲罰新權杖,從而降低模型逐字重複相同行的可能性。

0.0f

spring.ai.openai.chat.options.logitBias

修改指定權杖出現在完成項目中的可能性。

-

spring.ai.openai.chat.options.maxTokens

(已棄用,改用 maxCompletionTokens)聊天完成項目中要產生的最大權杖數。輸入權杖和產生的權杖的總長度受模型的上下文長度限制。

-

spring.ai.openai.chat.options.maxCompletionTokens

完成項目可以產生的權杖數上限,包括可見的輸出權杖和推理權杖。

-

spring.ai.openai.chat.options.n

為每個輸入訊息產生多少個聊天完成選項。請注意,您將根據所有選項中產生的權杖數來收費。將 n 保留為 1 以最大程度地降低成本。

1

spring.ai.openai.chat.options.presencePenalty

介於 -2.0 和 2.0 之間的數字。正值會根據新權杖是否出現在目前文字中來懲罰新權杖,從而增加模型談論新主題的可能性。

-

spring.ai.openai.chat.options.responseFormat.type

GPT-4oGPT-4o miniGPT-4 Turbo 以及所有新於 gpt-3.5-turbo-1106GPT-3.5 Turbo 模型相容。JSON_OBJECT 類型啟用 JSON 模式,保證模型產生的訊息是有效的 JSON。JSON_SCHEMA 類型啟用 結構化輸出,保證模型將符合您提供的 JSON 結構描述。JSON_SCHEMA 類型也需要設定 responseFormat.schema 屬性。

-

spring.ai.openai.chat.options.responseFormat.name

回應格式結構描述名稱。僅適用於 responseFormat.type=JSON_SCHEMA

custom_schema

spring.ai.openai.chat.options.responseFormat.schema

回應格式 JSON 結構描述。僅適用於 responseFormat.type=JSON_SCHEMA

-

spring.ai.openai.chat.options.responseFormat.strict

回應格式 JSON 結構描述遵循嚴格性。僅適用於 responseFormat.type=JSON_SCHEMA

-

spring.ai.openai.chat.options.seed

此功能處於 Beta 測試階段。如果指定,我們的系統將盡最大努力確定性地取樣,以便使用相同種子和參數的重複請求應傳回相同的結果。

-

spring.ai.openai.chat.options.stop

最多 4 個序列,API 將停止產生更多權杖。

-

spring.ai.openai.chat.options.topP

一種替代使用溫度的取樣方法,稱為核取樣,其中模型會考慮具有 top_p 概率質量的權杖結果。因此,0.1 表示僅考慮包含前 10% 概率質量的權杖。我們通常建議修改此項或 temperature,但不要同時修改兩者。

-

spring.ai.openai.chat.options.tools

模型可以呼叫的工具清單。目前,僅支援函數作為工具。使用此選項可提供模型可能為其產生 JSON 輸入的函數清單。

-

spring.ai.openai.chat.options.toolChoice

控制模型呼叫哪個(如果有)函數。none 表示模型不會呼叫函數,而是產生訊息。auto 表示模型可以在產生訊息或呼叫函數之間選擇。透過 {"type: "function", "function": {"name": "my_function"}} 指定特定函數會強制模型呼叫該函數。當沒有函數存在時,none 是預設值。如果存在函數,則 auto 是預設值。

-

spring.ai.openai.chat.options.user

代表您的最終使用者的唯一識別碼,可以幫助 OpenAI 監控和偵測濫用行為。

-

spring.ai.openai.chat.options.functions

函數清單,以其名稱識別,可在單個提示請求中啟用函數呼叫。具有這些名稱的函數必須存在於 functionCallbacks 登錄中。

-

spring.ai.openai.chat.options.stream-usage

(僅適用於串流)設定為新增一個額外的區塊,其中包含整個請求的權杖使用統計資訊。此區塊的 choices 欄位是一個空陣列,所有其他區塊也將包含一個 usage 欄位,但值為 null。

false

spring.ai.openai.chat.options.parallel-tool-calls

是否在工具使用期間啟用 平行函數呼叫

true

spring.ai.openai.chat.options.http-headers

要新增至聊天完成請求的選用 HTTP 標頭。若要覆寫 api-key,您需要使用 Authorization 標頭金鑰,並且必須以 `Bearer ` 前綴作為金鑰值的前綴。

-

spring.ai.openai.chat.options.proxy-tool-calls

如果為 true,Spring AI 將不會在內部處理函數呼叫,而是將其代理到用戶端。然後,用戶端有責任處理函數呼叫、將其分派到適當的函數,並傳回結果。如果為 false(預設值),Spring AI 將在內部處理函數呼叫。僅適用於支援函數呼叫的聊天模型

false

您可以覆寫 ChatModelEmbeddingModel 實作的通用 spring.ai.openai.base-urlspring.ai.openai.api-key。如果設定了 spring.ai.openai.chat.base-urlspring.ai.openai.chat.api-key 屬性,則它們優先於通用屬性。如果您想要針對不同的模型和不同的模型端點使用不同的 OpenAI 帳戶,這非常有用。
spring.ai.openai.chat.options 為前綴的所有屬性都可以在執行階段透過將請求特定的 執行階段選項 新增至 Prompt 呼叫來覆寫。

執行階段選項

OpenAiChatOptions.java 類別提供模型組態,例如要使用的模型、溫度、頻率懲罰等。

在啟動時,可以使用 OpenAiChatModel(api, options) 建構函式或 spring.ai.openai.chat.options.* 屬性來組態預設選項。

在執行階段,您可以透過將新的、請求特定的選項新增至 Prompt 呼叫來覆寫預設選項。例如,若要覆寫特定請求的預設模型和溫度

ChatResponse response = chatModel.call(
    new Prompt(
        "Generate the names of 5 famous pirates.",
        OpenAiChatOptions.builder()
            .withModel("gpt-4-o")
            .withTemperature(0.4)
        .build()
    ));
除了模型特定的 OpenAiChatOptions 之外,您還可以使用可攜式 ChatOptions 實例,使用 ChatOptionsBuilder#builder() 建立。

函數呼叫

您可以向 OpenAiChatModel 註冊自訂 Java 函數,並讓 OpenAI 模型智慧地選擇輸出 JSON 物件,其中包含呼叫一個或多個已註冊函數的引數。這是一種將 LLM 功能與外部工具和 API 連接的強大技術。閱讀更多關於 OpenAI 函數呼叫 的資訊。

多模態

多模態是指模型同時理解和處理來自各種來源的資訊的能力,包括文字、影像、音訊和其他資料格式。提供多模態支援的 OpenAI 模型包括 gpt-4gpt-4ogpt-4o-mini。請參閱 Vision 指南以取得更多資訊。

OpenAI 使用者訊息 API 可以將 base64 編碼的影像或影像 URL 清單與訊息合併。Spring AI 的 Message 介面透過引入 Media 類型來促進多模態 AI 模型。此類型包含訊息中媒體附件的資料和詳細資訊,利用 Spring 的 org.springframework.util.MimeTypeorg.springframework.core.io.Resource 作為原始媒體資料。

以下程式碼範例摘錄自 OpenAiChatModelIT.java,說明如何使用 gpt-4o 模型將使用者文字與影像融合。

var imageResource = new ClassPathResource("/multimodal.test.png");

var userMessage = new UserMessage("Explain what do you see on this picture?",
        new Media(MimeTypeUtils.IMAGE_PNG, this.imageResource));

ChatResponse response = chatModel.call(new Prompt(this.userMessage,
        OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_O.getValue()).build()));
GPT_4_VISION_PREVIEW 將繼續僅向 2024 年 6 月 17 日開始的此模型的現有使用者提供。如果您不是現有使用者,請使用 GPT_4_O 或 GPT_4_TURBO 模型。更多詳細資訊請參閱 此處

或使用 gpt-4o 模型的等效影像 URL

var userMessage = new UserMessage("Explain what do you see on this picture?",
        new Media(MimeTypeUtils.IMAGE_PNG,
                "https://spring-docs.dev.org.tw/spring-ai/reference/_images/multimodal.test.png"));

ChatResponse response = chatModel.call(new Prompt(this.userMessage,
        OpenAiChatOptions.builder().withModel(OpenAiApi.ChatModel.GPT_4_O.getValue()).build()));
您也可以傳遞多個影像。

範例顯示模型將 multimodal.test.png 影像作為輸入

Multimodal Test Image

以及文字訊息「Explain what do you see on this picture?」,並產生類似這樣的回應

This is an image of a fruit bowl with a simple design. The bowl is made of metal with curved wire edges that
create an open structure, allowing the fruit to be visible from all angles. Inside the bowl, there are two
yellow bananas resting on top of what appears to be a red apple. The bananas are slightly overripe, as
indicated by the brown spots on their peels. The bowl has a metal ring at the top, likely to serve as a handle
for carrying. The bowl is placed on a flat surface with a neutral-colored background that provides a clear
view of the fruit inside.

結構化輸出

OpenAI 提供自訂 結構化輸出 API,確保您的模型產生的回應嚴格符合您提供的 JSON 結構描述。除了現有的 Spring AI 模型不可知 結構化輸出轉換器 之外,這些 API 還提供增強的控制和精確度。

目前,OpenAI 支援 JSON 結構描述語言格式的子集

組態

Spring AI 允許您以程式設計方式使用 OpenAiChatOptions 建構器或透過應用程式屬性來組態您的回應格式。

使用聊天選項建構器

您可以使用 OpenAiChatOptions 建構器以程式設計方式設定回應格式,如下所示

String jsonSchema = """
        {
            "type": "object",
            "properties": {
                "steps": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "properties": {
                            "explanation": { "type": "string" },
                            "output": { "type": "string" }
                        },
                        "required": ["explanation", "output"],
                        "additionalProperties": false
                    }
                },
                "final_answer": { "type": "string" }
            },
            "required": ["steps", "final_answer"],
            "additionalProperties": false
        }
        """;

Prompt prompt = new Prompt("how can I solve 8x + 7 = -23",
        OpenAiChatOptions.builder()
            .withModel(ChatModel.GPT_4_O_MINI)
            .withResponseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, this.jsonSchema))
            .build());

ChatResponse response = this.openAiChatModel.call(this.prompt);

與 BeanOutputConverter 公用程式整合

您可以利用現有的 BeanOutputConverter 公用程式,從您的網域物件自動產生 JSON 結構描述,然後將結構化回應轉換為網域特定的實例

  • Java

  • Kotlin

record MathReasoning(
    @JsonProperty(required = true, value = "steps") Steps steps,
    @JsonProperty(required = true, value = "final_answer") String finalAnswer) {

    record Steps(
        @JsonProperty(required = true, value = "items") Items[] items) {

        record Items(
            @JsonProperty(required = true, value = "explanation") String explanation,
            @JsonProperty(required = true, value = "output") String output) {
        }
    }
}

var outputConverter = new BeanOutputConverter<>(MathReasoning.class);

var jsonSchema = this.outputConverter.getJsonSchema();

Prompt prompt = new Prompt("how can I solve 8x + 7 = -23",
        OpenAiChatOptions.builder()
            .withModel(ChatModel.GPT_4_O_MINI)
            .withResponseFormat(new ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, this.jsonSchema))
            .build());

ChatResponse response = this.openAiChatModel.call(this.prompt);
String content = this.response.getResult().getOutput().getContent();

MathReasoning mathReasoning = this.outputConverter.convert(this.content);
data class MathReasoning(
	@get:JsonProperty(required = true, value = "steps") val steps: Steps,
	@get:JsonProperty(required = true, value = "final_answer") val finalAnswer: String) {

	data class Steps(@get:JsonProperty(required = true, value = "items") val items: Array<Items>) {

		data class Items(
			@get:JsonProperty(required = true, value = "explanation") val explanation: String,
			@get:JsonProperty(required = true, value = "output") val output: String)
	}
}

val outputConverter = BeanOutputConverter(MathReasoning::class.java)

val jsonSchema = outputConverter.jsonSchema;

val prompt = Prompt("how can I solve 8x + 7 = -23",
	OpenAiChatOptions.builder()
		.withModel(ChatModel.GPT_4_O_MINI)
		.withResponseFormat(ResponseFormat(ResponseFormat.Type.JSON_SCHEMA, jsonSchema))
		.build())

val response = openAiChatModel.call(prompt)
val content = response.getResult().getOutput().getContent()

val mathReasoning = outputConverter.convert(content)
請確保您使用 @JsonProperty(required = true,…​) 註解 (Kotlin 中使用 @get:JsonProperty(required = true,…​),以便在相關的 getter 上產生註解,請參閱 相關文件)。這對於產生將欄位準確標記為 required 的結構描述至關重要。雖然這對於 JSON 結構描述是選用的,但 OpenAI 強制要求它,以便結構化回應能正常運作。

透過應用程式屬性進行組態

或者,當使用 OpenAI 自動組態時,您可以透過以下應用程式屬性來組態所需的回應格式

spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.options.model=gpt-4o-mini

spring.ai.openai.chat.options.response-format.type=JSON_SCHEMA
spring.ai.openai.chat.options.response-format.name=MySchemaName
spring.ai.openai.chat.options.response-format.schema={"type":"object","properties":{"steps":{"type":"array","items":{"type":"object","properties":{"explanation":{"type":"string"},"output":{"type":"string"}},"required":["explanation","output"],"additionalProperties":false}},"final_answer":{"type":"string"}},"required":["steps","final_answer"],"additionalProperties":false}
spring.ai.openai.chat.options.response-format.strict=true

範例控制器

建立 新的 Spring Boot 專案,並將 spring-ai-openai-spring-boot-starter 新增至您的 pom (或 gradle) 依賴性。

src/main/resources 目錄下新增 application.properties 檔案,以啟用和組態 OpenAi 聊天模型

spring.ai.openai.api-key=YOUR_API_KEY
spring.ai.openai.chat.options.model=gpt-4o
spring.ai.openai.chat.options.temperature=0.7
api-key 替換為您的 OpenAI 憑證。

這將建立一個 OpenAiChatModel 實作,您可以將其注入到您的類別中。以下是一個簡單的 @RestController 類別範例,該類別使用聊天模型進行文字生成。

@RestController
public class ChatController {

    private final OpenAiChatModel chatModel;

    @Autowired
    public ChatController(OpenAiChatModel chatModel) {
        this.chatModel = chatModel;
    }

    @GetMapping("/ai/generate")
    public Map<String,String> generate(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        return Map.of("generation", this.chatModel.call(message));
    }

    @GetMapping("/ai/generateStream")
	public Flux<ChatResponse> generateStream(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
        Prompt prompt = new Prompt(new UserMessage(message));
        return this.chatModel.stream(prompt);
    }
}

手動組態

OpenAiChatModel 實作 ChatModelStreamingChatModel,並使用 低階 OpenAiApi 用戶端 連線至 OpenAI 服務。

spring-ai-openai 依賴性新增至您的專案 Maven pom.xml 檔案

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai</artifactId>
</dependency>

或新增至您的 Gradle build.gradle 建置檔案。

dependencies {
    implementation 'org.springframework.ai:spring-ai-openai'
}
請參閱 依賴性管理 章節,將 Spring AI BOM 新增至您的建置檔案。

接下來,建立一個 OpenAiChatModel 並使用它進行文字生成

var openAiApi = new OpenAiApi(System.getenv("OPENAI_API_KEY"));
var openAiChatOptions = OpenAiChatOptions.builder()
            .withModel("gpt-3.5-turbo")
            .withTemperature(0.4)
            .withMaxTokens(200)
            .build();
var chatModel = new OpenAiChatModel(this.openAiApi, this.openAiChatOptions);

ChatResponse response = this.chatModel.call(
    new Prompt("Generate the names of 5 famous pirates."));

// Or with streaming responses
Flux<ChatResponse> response = this.chatModel.stream(
    new Prompt("Generate the names of 5 famous pirates."));

OpenAiChatOptions 提供聊天請求的組態資訊。OpenAiChatOptions.Builder 是一個流暢的選項建構器。

低階 OpenAiApi 用戶端

OpenAiApi 為 OpenAI Chat API OpenAI Chat API 提供輕量級 Java 用戶端。

以下類別圖示說明了 OpenAiApi 聊天介面和建置區塊

OpenAiApi Chat API Diagram

以下是一個簡單的程式碼片段,展示如何以程式設計方式使用 API

OpenAiApi openAiApi =
    new OpenAiApi(System.getenv("OPENAI_API_KEY"));

ChatCompletionMessage chatCompletionMessage =
    new ChatCompletionMessage("Hello world", Role.USER);

// Sync request
ResponseEntity<ChatCompletion> response = this.openAiApi.chatCompletionEntity(
    new ChatCompletionRequest(List.of(this.chatCompletionMessage), "gpt-3.5-turbo", 0.8, false));

// Streaming request
Flux<ChatCompletionChunk> streamResponse = this.openAiApi.chatCompletionStream(
        new ChatCompletionRequest(List.of(this.chatCompletionMessage), "gpt-3.5-turbo", 0.8, true));

請遵循 OpenAiApi.java 的 JavaDoc 以取得更多資訊。

低階 API 範例