OpenAI 相容 API 是本地 LLM 生態能夠快速繁榮的關鍵基礎建設。OpenAI 在 2023 年定義的 POST /v1/chat/completions 介面成為事實標準後,後來幾乎所有本地推論伺服器(Ollama、LM Studio、llama.cpp、vLLM、oMLX)都實作同一份 API 規格;介面層工具只要支援這個規格,就能「不改一行程式」切換本地與雲端。

這個相容性決定了你的選擇空間。理解它的意義後,看到任何工具寫「支援 OpenAI 相容 API」時,你會知道這句話真正承諾的是什麼、不承諾的是什麼。

本章目標

讀完本章後,你應該能:

  1. 看懂 apiBase: http://localhost:11434/v1 這類設定背後在做什麼。
  2. 判斷一個介面層工具是否支援本地 LLM。
  3. 知道「OpenAI 相容」承諾的範圍與邊界。
  4. 用 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  }'

差別只有三點:

  1. host:從 api.openai.com 換成 localhost:11434
  2. model:從 gpt-5 換成 gemma4:31b-coding-mtp-bf16
  3. 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 basehttps://api.openai.com/v1http://localhost:11434/v1
API keysk-xxxxxxx任意字串,常用 ollamanot-needed
Model namegpt-5gpt-4oOllama 本地的 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 格式、錯誤碼大致一致。不承諾的是:

  1. 模型能力:本地 Gemma 4 31B 跟雲端 GPT-5 都能用同一套 API 呼叫,但回答品質天差地遠。
  2. 效能特性:本地的 TTFT、生字速度跟雲端完全不同,介面層感覺不到差別不代表速度一樣。
  3. 進階參數:OpenAI 自己的新功能(function calling 進階模式、structured output 強制 JSON 輸出、reasoning effort 控制推理深度等)不一定被本地伺服器完整支援。寫 code 場景常見問題是設定了 tools 參數但本地模型不會主動呼叫。模組四會展開這些進階特性、見 4.3 Tool use 原理
  4. 模型清單:呼叫 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用途
Ollama11434日常寫 code 主力
LM Studio1234探索新模型、不影響主 server
llama.cpp8080進階測試、特殊量化
oMLX8000長 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 相容,要特別注意:

  1. MLX 原生 Python API:Apple 的 MLX framework 本身是 Python library、不是 HTTP server。需要自己 wrap 或用 mlx_lm.server(次要產品、功能不全)。完整的 MLX / MTP / oMLX 區別見 0.4 章節
  2. 早期 llama.cpp:在 OpenAI 相容前就存在,原生 API 形狀不同;新版加上 /v1/chat/completions 後跟主流相容。
  3. 某些研究專案:直接 wrap PyTorch / Transformers,沒有 HTTP 層,要當 library 用。

遇到這類工具時、值得先評估「該不該為它寫 adapter」。判讀訊號:模型唯一性(這個工具是否提供其他伺服器拿不到的模型?)vs 整合成本(寫 adapter 與長期維護的時間投入)。模型唯一性高時值得投資、模型可在主流伺服器找到替代時、選 OpenAI 相容的主流伺服器(Ollama、LM Studio)能省下大量整合成本。

下一章

下一章:0.4 MLX / MTP / oMLX,澄清三個常被混為一談的術語,避開網路上最常見的本地 LLM 認知陷阱。