OpenAI Chat Completions API 是一種廣泛實作、以 JSON over HTTP 為基礎的對話式語言模型介面。用戶端會傳送依序排列的訊息清單與生成選項;伺服器則回傳一則或多則包含中繼資料與權杖用量的 assistant 訊息。
其基本結構如下:
HTTP client
-> POST /v1/chat/completions
-> request JSON containing messages and options
-> model inference
<- response JSON containing choices and usage許多閘道與模型伺服器即使底層模型並非由 OpenAI 託管,也會實作 OpenAI 相容端點。因此,這種格式可作為事實上的互通 API,但它不是像 HTTP、JSON-RPC 或 MCP 那樣獨立的正式標準。不同的相容提供者可能支援不同的欄位、模型、限制、錯誤格式或擴充功能。
Spring AI 在這種線上格式之上,為 Java 應用程式提供與提供者無關的模型。應用程式程式碼使用 Prompt、Message、ChatOptions 和 ChatResponse;OpenAI 或 OpenAI 相容介面卡負責在這些物件與提供者 JSON 之間進行轉換。
HTTP 請求 #
簡化後的請求如下:
POST /v1/chat/completions HTTP/1.1
Host: api.example.com
Authorization: Bearer ...
Content-Type: application/json
{
"model": "gpt-4.1",
"messages": [
{
"role": "system",
"content": "Answer concisely."
},
{
"role": "user",
"content": "Explain optimistic locking."
}
],
"temperature": 0.3,
"max_tokens": 1000,
"stream": false
}model 和 messages 用來識別應執行的模型,以及模型應該看到的對話內容。其餘欄位則控制生成行為。有些閘道會透過設定、部署名稱、URL 或標頭選取模型,而不是要求請求本文包含 model。
訊息 #
messages 是有順序的。其順序是提示的一部分:
[
{
"role": "system",
"content": "You are a support assistant."
},
{
"role": "user",
"content": "Where is order 42?"
},
{
"role": "assistant",
"content": "I need to look up that order."
},
{
"role": "user",
"content": "It belongs to account 7."
}
]常見角色如下:
| Role | Meaning |
|---|---|
system | 應用程式指示與行為 |
developer | 較新模型 API 支援的開發者指示 |
user | 使用者提供的輸入 |
assistant | 先前的或新生成的模型輸出 |
tool | 應用程式工具呼叫的結果 |
文字通常以字串表示。支援多模態的模型可能接受由具類型內容部分組成的陣列,其中包含文字、影像、音訊或其他媒體。
端點通常是無狀態的。呼叫端僅僅重複使用對話 ID,並不會讓模型記住先前的 HTTP 請求。若要繼續對話,用戶端必須再次傳送所選取的先前訊息:
load conversation history
-> append current user message
-> send all selected messages
-> receive assistant message
-> save new user and assistant messages為了保持在模型的上下文限制內,歷史記錄可以被截斷、摘要化,或從外部儲存中擷取。
生成選項 #
常見的請求欄位包括:
| Field | Purpose |
|---|---|
model | 模型或部署識別碼 |
temperature | 控制取樣隨機性 |
top_p | 核心取樣閾值 |
max_tokens | 在舊版或相容 API 中限制生成的權杖數 |
max_completion_tokens | 部分模型使用的新式 completion 權杖數上限 |
n | 候選選項的數量 |
frequency_penalty | 抑制重複使用頻率較高的權杖 |
presence_penalty | 鼓勵引入尚未使用的權杖 |
stop | 在設定的序列處停止生成 |
stream | 選擇回傳一個完整回應或串流區塊 |
response_format | 在支援時要求 JSON 或受 schema 約束的輸出 |
tools | 描述模型可以要求的函式 |
tool_choice | 限制是否可以選取工具,以及可選取哪個工具 |
提供者的支援範圍與語意可能不同。某個相容端點接受的欄位,在另一個端點可能被拒絕、忽略、重新命名,或以不同方式解讀。即使是同一端點背後的不同模型版本,也可能存在差異。
取樣選項也會互相影響。應用程式通常會調整 temperature 或 top_p 其中之一,而不是未經評估就同時修改兩者。
HTTP 回應 #
簡化後的非串流回應如下:
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1784778339,
"model": "gpt-4.1",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Optimistic locking detects concurrent updates..."
},
"finish_reason": "stop",
"logprobs": null
}
],
"usage": {
"prompt_tokens": 25,
"completion_tokens": 18,
"total_tokens": 43
}
}主要欄位如下:
| Field | Meaning |
|---|---|
id | 提供者生成的請求或 completion 識別碼 |
object | 回應類型,通常為 chat.completion |
created | 提供者指派的 Unix 時間戳 |
model | 產生結果的模型 |
choices | assistant 輸出候選項 |
usage | 輸入、輸出與總權杖用量 |
system_fingerprint | 可選的提供者端模型設定識別碼 |
每個 choice 通常包含 index、assistant message、finish_reason 和可選的對數機率。常見的結束原因包括:
stop:正常完成;length:達到輸出或上下文限制;tool_calls:模型要求一個或多個工具;以及content_filter:安全性篩選器停止生成。
HTTP 回應成功並不代表答案正確。它只表示提供者接受了請求,並產生了語法有效的模型結果。
提供者擴充功能 #
OpenAI 相容並不代表逐位元完全相同。例如:
- Azure 相容回應可能包含
prompt_filter_results與content_filter_results。 - 閘道可能新增延遲檢查點、路由詳細資料、快取統計或服務層級中繼資料。
- 權杖詳細資料可能區分推理、音訊、快取、已接受預測或已拒絕預測權杖。
- 不同提供者的錯誤回應與速率限制標頭可能不同。
在可能的情況下,應用程式應忽略未知欄位,並避免讓其領域模型繫結至每一個提供者擴充功能。若某項擴充功能是必要的,應在提供者專用設定或中繼資料中明確表示。
Schema 中的工具呼叫 #
請求可以包含工具定義:
{
"model": "gpt-4.1",
"messages": [
{
"role": "user",
"content": "What is the status of order 42?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "find_order",
"description": "Look up an order by ID",
"parameters": {
"type": "object",
"properties": {
"order_id": {
"type": "string",
"description": "Canonical order ID"
}
},
"required": [
"order_id"
]
}
}
}
]
}模型不會執行函式。它可以回傳包含結構化請求的 assistant 訊息:
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_123",
"type": "function",
"function": {
"name": "find_order",
"arguments": "{\"order_id\":\"42\"}"
}
}
]
}主機應用程式會驗證引數、呼叫本地或遠端操作,並在後續請求中傳送結果:
{
"role": "tool",
"tool_call_id": "call_123",
"content": "{\"status\":\"SHIPPED\"}"
}因此,通常的流量如下:
model request with tool schemas
<- assistant tool-call request
application executes tool
-> model request with tool result
<- final assistant answer工具呼叫通常至少需要兩次模型往返。授權、驗證、冪等性、逾時與副作用仍由應用程式負責,而不是由模型負責。
串流回應 #
當 "stream": true 時,伺服器通常會回傳 Server-Sent Events,其資料包含增量的 chat.completion.chunk 物件:
data: {"object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant"}}]}
data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":"Optimistic"}}]}
data: {"object":"chat.completion.chunk","choices":[{"delta":{"content":" locking"}}]}
data: [DONE]區塊包含差異,而不是完整訊息。用戶端必須聚合內容、工具呼叫片段、結束原因,以及回應提供時的權杖用量。串流可以改善首次可見輸出的時間,但不一定會降低總延遲或權杖成本。
Spring AI 的物件模型 #
Spring AI 將面向應用程式的聊天概念與提供者線上協定 DTO 分離。主要型別如下:
| Spring AI type | Responsibility |
|---|---|
ChatClient | 用於提示、Advisor、工具和輸出轉換的流暢應用程式 API |
ChatModel | 呼叫聊天模型的提供者無關介面 |
Prompt | 有序訊息與請求選項 |
Message | 表示單一對話項目的通用介面 |
SystemMessage | system 角色指示 |
UserMessage | user 角色輸入 |
AssistantMessage | assistant 文字與可選的工具呼叫 |
ToolResponseMessage | 與 assistant 工具呼叫關聯的結果 |
ChatOptions | 可移植的請求選項 |
OpenAiChatOptions | 使用 OpenAI 介面卡時的 OpenAI 專用選項 |
ChatResponse | 包含生成結果與中繼資料的提供者無關回應 |
Generation | 一個 assistant 候選項 |
ChatResponseMetadata | 模型、用量、速率限制與提供者中繼資料 |
Usage | 輸入、輸出與總權杖數 |
低階應用程式流程如下:
ChatOptions options = OpenAiChatOptions.builder()
.model("gpt-4.1")
.temperature(0.3)
.maxTokens(1000)
.build();
Prompt prompt = new Prompt(
List.of(
new SystemMessage("Answer concisely."),
new UserMessage("Explain optimistic locking.")
),
options
);
ChatResponse response = chatModel.call(prompt);
AssistantMessage answer = response.getResult().getOutput();ChatClient 提供更高階的流暢式等效 API:
ChatResponse response = chatClient.prompt()
.system("Answer concisely.")
.user("Explain optimistic locking.")
.options(options)
.call()
.chatResponse();call() 是同步終端操作。提示建構方法只會設定請求;call() 會呼叫 Advisor 鏈與模型介面卡,並等待完成。相較之下,stream() 會回傳反應式串流。
Spring AI 如何序列化請求 #
Spring AI 通常不會將 Prompt.toString() 序列化為 HTTP 本文。提供者介面卡會執行對映:
Spring AI Prompt
messages:
SystemMessage
UserMessage
AssistantMessage
ToolResponseMessage
options:
ChatOptions
|
v
OpenAI adapter
OpenAI request DTOs
role/content conversion
option and tool-schema conversion
|
v
JSON serializer
|
v
POST /v1/chat/completions對於 Spring AI OpenAI 整合,傳輸型別會在 OpenAiApi 下表示,其中包含請求、訊息、choice 與 completion 結構。應用程式通常應使用可移植型別,而不是直接建構這些傳輸 DTO。
回應會沿著相反路徑處理:
HTTP response JSON
-> OpenAI response DTOs
-> AssistantMessage
-> Generation
-> ChatResponseMetadata and Usage
-> ChatResponse這個邊界解釋了為何記錄 ChatClientRequest 時可能顯示 Java record 或 Prompt 表示,而 HTTP 線上記錄則顯示具體的 OpenAI 相容 JSON。前者是轉換前的提供者無關請求;後者則是轉換後序列化的傳輸本文。
Advisor 與對話記憶 #
ChatMemory 不屬於 OpenAI 線上 schema,而是應用程式端的抽象。記憶 Advisor 會在模型呼叫前載入訊息,並在呼叫後儲存新訊息:
ChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
ChatClient client = builder
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(memory).build()
)
.build();每個請求都會提供對話鍵:
ChatResponse response = client.prompt()
.user(question)
.advisors(spec -> spec.param(
ChatMemory.CONVERSATION_ID,
conversationId
))
.call()
.chatResponse();從概念上看,Advisor 會執行:
ChatMemory.get(conversationId)
-> add historical messages to Prompt
-> ChatModel.call(Prompt)
-> ChatMemory.add(current user and assistant messages)因此,序列化後的 messages 陣列可能包含所選取對話視窗的完整內容。對話 ID、儲存、保留策略與存取控制屬於主機應用程式;Chat Completions API 並未定義這些內容。
Spring AI 的工具序列化 #
Spring AI 使用 ToolCallback 表示可執行能力。帶有註解的 Java 方法可以被探索並轉換為回呼:
public class OrderTools {
@Tool(description = "Look up an order by ID")
public OrderStatus findOrder(
@ToolParam(description = "Canonical order ID") String orderId) {
return orderService.find(orderId);
}
}註冊該物件:
String answer = chatClient.prompt()
.user("Where is order 42?")
.tools(orderTools)
.call()
.content();會使 Spring AI:
- 檢查帶有註解的方法;
- 建立
ToolCallback; - 為其引數推導 JSON Schema;
- 將回呼定義對映到提供者的
tools陣列; - 將回傳的
tool_calls對映為AssistantMessage.ToolCall值; - 啟用自動工具執行時,在主機應用程式中呼叫回呼;以及
- 將結果序列化為下一次模型請求的工具訊息。
LLM 只能看到名稱、描述和 schema。它無法反射 JVM,也無法呼叫任意方法。
記錄兩種表示 #
Spring AI Advisor 記錄與 HTTP 線上記錄回答的是不同問題。
SimpleLoggerAdvisor 等 Advisor 可以顯示:
- 先前的 Advisor 修改請求後的 Spring AI 訊息;
ChatOptions;- 對話 ID 等 Advisor 上下文;以及
- 提供者無關的
ChatResponse。
請求看起來像 Java toString() 輸出,是因為記錄的是 ChatClientRequest,而不是最終的 HTTP 本文。
HTTP 用戶端記錄可以顯示:
- URL 與標頭;
- 最終序列化的 JSON 請求;
- 提供者特有的欄位與預設值;
- 原始 JSON 回應;以及
- 串流區塊。
線上記錄發生在提供者介面卡建立傳輸 DTO 之後,因此適合診斷序列化與提供者相容性。但它也具有風險:訊息、擷取的文件、工具結果、授權標頭、Cookie 與個人資料都可能出現在記錄中。只有在受控的開發環境中才應啟用完整負載記錄,並套用遮罩與保留期限限制。
相容性邊界 #
OpenAI 相容端點應被視為提供者契約,而不是行為完全相同的證明。請驗證:
- 端點路徑與驗證;
- 支援的訊息角色與內容型別;
- 模型命名與部署選取;
- 請求選項名稱與限制;
- 工具呼叫與結構化輸出支援;
- 串流事件格式;
- 權杖用量計算;
- 安全性與拒絕欄位;
- 錯誤本文與 HTTP 狀態碼;以及
- 提供者特有的擴充功能。
Spring AI 透過將這些細節保留在模型介面卡中,降低應用程式耦合;但它無法讓不相容的提供者行為變得一致。請將每個受支援的閘道與模型作為獨立的執行路徑進行測試。
請參閱 Spring AI,了解更廣泛的應用程式模型,包括 Advisors、RAG、向量儲存、記憶儲存庫、可觀測性與正式環境架構。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)