0.3 OpenAI 相容 API
OpenAI 相容 API 是本地 LLM 生態能夠快速繁榮的關鍵基礎建設。OpenAI 在 2023 年定義的 POST /v1/chat/completions 介面成為事實標準後,後來幾乎所有本地推論伺服器(Ollama、LM Studio、llama.cpp、vLLM、oMLX)都實作同一份 API 規格;介面層工具只要支援這個規格,就能「不改一行程式」切換本地與雲端。
這個相容性決定了你的選擇空間。理解它的意義後,看到任何工具寫「支援 OpenAI 相容 API」時,你會知道這句話真正承諾的是什麼、不承諾的是什麼。
本章目標
讀完本章後,你應該能:
- 看懂
apiBase: http://localhost:11434/v1這類設定背後在做什麼。 - 判斷一個介面層工具是否支援本地 LLM。
- 知道「OpenAI 相容」承諾的範圍與邊界。
- 用 curl 直接打本地 LLM 的 API 驗證它在跑。
API 形狀的核心:chat completions
OpenAI 在 2023 年定義的 chat completions API 核心是這個請求格式:
1curl http://api.openai.com/v1/chat/completions \
2 -H "Authorization: Bearer $OPENAI_API_KEY" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "model": "gpt-5",
6 "messages": [
7 {"role": "system", "content": "You are a helpful assistant."},
8 {"role": "user", "content": "寫一個 Python function 計算費氏數列"}
9 ],
10 "stream": true
11 }'回應是一連串 server-sent events(SSE、伺服器把回應切成小封包陸續推給 client、而不是等整段算完才一次回)、每個 event 包含一個 token chunk。
本地推論伺服器實作同樣的 endpoint 形狀,只是 host 換成 localhost、API key 不檢查或檢查 dummy 值:
1curl http://localhost:11434/v1/chat/completions \
2 -H "Content-Type: application/json" \
3 -d '{
4 "model": "gemma4:31b-coding-mtp-bf16",
5 "messages": [
6 {"role": "system", "content": "You are a helpful assistant."},
7 {"role": "user", "content": "寫一個 Python function 計算費氏數列"}
8 ],
9 "stream": true
10 }'差別只有三點:
- host:從
api.openai.com換成localhost:11434。 - model:從
gpt-5換成gemma4:31b-coding-mtp-bf16。 - Authorization:本地通常不檢查 API key,或接受任意值。
請求與回應的 JSON schema 完全一樣。這就是「OpenAI 相容」的字面意義。
為什麼這個相容性這麼重要
如果沒有 OpenAI 相容 API,每個介面層工具要支援新的伺服器就得寫專屬整合:Continue.dev 要為 Ollama 寫一份、為 LM Studio 寫一份、為 llama.cpp 寫一份、為雲端 OpenAI 寫一份、為 Anthropic 寫一份。每多一個工具就 N×M 的整合成本。
OpenAI 相容把這個成本拆成「介面層支援標準 API 一次 + 伺服器層實作標準 API 一次」、整合工作從 N×M 降到 N+M。後果是新伺服器(如 2024 年才出現的 oMLX)只要實作這份 API、馬上能被既有的所有介面層用上。
這也是為什麼幾乎所有 IDE plugin、CLI 工具、Web UI 都選擇 OpenAI 相容做 first-class citizen。Anthropic 自己的 API 形狀(messages、不同 streaming 格式)反而成為次要選項,介面層工具通常要為 Anthropic 寫額外的 adapter。
接本地 LLM 的最小設定
實際使用上,把任一個介面層工具切到本地 LLM 通常只要改三個欄位:
| 欄位 | 雲端 OpenAI 預設 | 切到本地 Ollama 後 |
|---|---|---|
| API base | https://api.openai.com/v1 | http://localhost:11434/v1 |
| API key | sk-xxxxxxx | 任意字串,常用 ollama 或 not-needed |
| Model name | gpt-5、gpt-4o | Ollama 本地的 model tag,如 gemma4:31b |
三個欄位的延伸判讀:API base 改成 localhost:11434 表示請求送到本機 11434 port、不走網路;API key 本地通常不檢查、但介面層工具可能仍要求填一個值才能初始化;Model name 要去伺服器看當前已下載的 model tag、Ollama 用 ollama list 查、LM Studio 在 Discover 分頁查。
接近真實的例子是 Continue.dev 的 config.json:
1{
2 "models": [
3 {
4 "title": "Gemma 4 31B (local)",
5 "provider": "ollama",
6 "model": "gemma4:31b-coding-mtp-bf16",
7 "apiBase": "http://localhost:11434"
8 }
9 ]
10}Continue.dev 內部會把 provider: ollama 翻成 OpenAI 相容請求送到 apiBase。如果你想用通用 OpenAI provider:
1{
2 "models": [
3 {
4 "title": "Local LLM (via OpenAI-compatible)",
5 "provider": "openai",
6 "model": "gemma4:31b-coding-mtp-bf16",
7 "apiBase": "http://localhost:11434/v1",
8 "apiKey": "not-needed"
9 }
10 ]
11}兩種寫法都會工作。provider: ollama 多一些 Ollama 特有功能(如 model auto-pull),provider: openai 比較通用、可以接任何 OpenAI 相容伺服器。
「OpenAI 相容」承諾什麼、不承諾什麼
相容承諾的是 API 形狀 —— request schema、response schema、streaming 格式、錯誤碼大致一致。不承諾的是:
- 模型能力:本地 Gemma 4 31B 跟雲端 GPT-5 都能用同一套 API 呼叫,但回答品質天差地遠。
- 效能特性:本地的 TTFT、生字速度跟雲端完全不同,介面層感覺不到差別不代表速度一樣。
- 進階參數:OpenAI 自己的新功能(function calling 進階模式、structured output 強制 JSON 輸出、reasoning effort 控制推理深度等)不一定被本地伺服器完整支援。寫 code 場景常見問題是設定了
tools參數但本地模型不會主動呼叫。模組四會展開這些進階特性、見 4.3 Tool use 原理。 - 模型清單:呼叫
GET /v1/models回的清單、本地是你已下載的模型、雲端是 OpenAI 提供的模型;介面層要把兩邊清單視為各自獨立的資料。
接近真實的意外事件:
- 設定
response_format: { type: "json_object" }強制 JSON 輸出,本地某些舊模型不認,會直接回普通文字。 - 設定
tool_choice: "required"強制使用工具,本地許多模型不支援,行為退化成普通對話。 - 設定
seed想拿確定性輸出,本地伺服器多半實作了,但雲端 OpenAI 並不保證每個 model 都尊重。
陷阱是把「相容」當成「等價」。在依賴進階參數的場景下、寫程式時值得先假設本地伺服器可能不支援最新功能、預先準備降級處理(例如先試 tool_choice: "required"、伺服器忽略時 fallback 到 prompt-based 工具呼叫)。
用 curl 驗證本地 LLM 在跑
啟動 Ollama 並 pull 一個模型後,最快確認它在跑的方式是直接 curl:
1curl http://localhost:11434/v1/chat/completions \
2 -H "Content-Type: application/json" \
3 -d '{
4 "model": "gemma4:e4b",
5 "messages": [{"role": "user", "content": "Say hi in three languages."}],
6 "stream": false
7 }'如果回的是 JSON 包含 choices[0].message.content,伺服器層正常。介面層連不上的時候,先用這個 curl 確認問題是介面層、伺服器層,還是模型本身。
需要驗證 streaming:
1curl http://localhost:11434/v1/chat/completions \
2 -H "Content-Type: application/json" \
3 -d '{
4 "model": "gemma4:e4b",
5 "messages": [{"role": "user", "content": "Count from 1 to 5."}],
6 "stream": true
7 }'正常應該看到一連串 data: {...} 行,每行是一個 token chunk。
多伺服器並存:同時跑 Ollama 與 LM Studio
OpenAI 相容讓你可以同時在同一台 Mac 上跑多個伺服器,只要 port 不撞。常見配置:
| 伺服器 | 預設 port | 用途 |
|---|---|---|
| Ollama | 11434 | 日常寫 code 主力 |
| LM Studio | 1234 | 探索新模型、不影響主 server |
| llama.cpp | 8080 | 進階測試、特殊量化 |
| oMLX | 8000 | 長 context coding agent 場景 |
Port 衝突的徵兆是啟動伺服器時報 address already in use。用 lsof -i :<port> 找佔用方、確認是舊版伺服器就 pkill -f 終止、或改用其他 port 啟動。詳細的 port 與 listen address 判讀見 Port 與 Localhost 卡片。
Continue.dev 的 config.json 可以同時列多個 model、每個 model 指向不同伺服器、UI 上下拉切換。這個能力讓「主力模型穩定跑、實驗模型隔離測試」變得直接。
不是 OpenAI 相容的本地工具
少數本地工具不走 OpenAI 相容,要特別注意:
- MLX 原生 Python API:Apple 的 MLX framework 本身是 Python library、不是 HTTP server。需要自己 wrap 或用
mlx_lm.server(次要產品、功能不全)。完整的 MLX / MTP / oMLX 區別見 0.4 章節。 - 早期 llama.cpp:在 OpenAI 相容前就存在,原生 API 形狀不同;新版加上
/v1/chat/completions後跟主流相容。 - 某些研究專案:直接 wrap PyTorch / Transformers,沒有 HTTP 層,要當 library 用。
遇到這類工具時、值得先評估「該不該為它寫 adapter」。判讀訊號:模型唯一性(這個工具是否提供其他伺服器拿不到的模型?)vs 整合成本(寫 adapter 與長期維護的時間投入)。模型唯一性高時值得投資、模型可在主流伺服器找到替代時、選 OpenAI 相容的主流伺服器(Ollama、LM Studio)能省下大量整合成本。
下一章
下一章:0.4 MLX / MTP / oMLX,澄清三個常被混為一談的術語,避開網路上最常見的本地 LLM 認知陷阱。