從官方 API 或其他 OpenAI 相容服務遷移:換地址和 Key,分批切流量,留好回滾
遷移到土土金 API,程式碼通常只改 base_url、api_key 和模型名;上線前要核對模型列表、流式輸出、錯誤碼和限流的差異,再按小流量、半量、全量分批切換,舊服務保留到穩定為止。本文給出遷移清單、按比例分流的程式碼和回滾辦法。
遷移到土土金 API,程式碼通常只改三處:base_url 換成 https://www.tutujin.com/v1,api_key 換成在控制台建立的 Key,model 換成本站模型廣場裡的名字。真正要花時間的是上線前的核對和上線後的切換:先用模型列表介面確認模型都在,再核對流式輸出、錯誤碼和限流的差異,然後按小流量、半量、全量分批把請求切過來。原來的服務一直保留到全量穩定為止,分流比例放在配置裡,出問題時調回 0 就是回滾。
遷移前先確認
| 要確認的 | 怎麼確認 |
|---|---|
| 用到的介面本站支援 | 對照下面的介面表;用 Claude 或 Gemini 原生 SDK 的,也有對應的原生介面 |
| 模型都有,名字對得上 | 用新 Key 呼叫模型列表介面,見下文第 2 步 |
| 費用心裡有數 | 在模型與價格查單價,估算方法見大模型 API 價格怎麼算 |
| 分組選好了 | 分組決定線路和價格,見如何選分組 |
本站支援的介面:
| 介面 | 方法與路徑 | 狀態 | 模型數 |
|---|---|---|---|
| Chat Completions | POST/v1/chat/completions | 已實測 | 681 |
| Responses | POST/v1/responses | 原樣轉發 | 5 |
| Claude Messages | POST/v1/messages | 已實測 | 49 |
| Gemini generateContent | POST/v1beta/models/{model}:generateContent | 已實測 | 63 |
| 影像生成 | POST/v1/images/generations | 原樣轉發 | 54 |
| 影像編輯 | POST/v1/images/edits | 原樣轉發 | 31 |
| Embeddings 向量 | POST/v1/embeddings | 已實測 | 18 |
| Rerank 重排序 | POST/v1/rerank | 原樣轉發 | 6 |
| 語音合成 TTS | POST/v1/audio/speech | 原樣轉發 | 8 |
| 語音識別 STT | POST/v1/audio/transcriptions | 原樣轉發 | 1 |
| Midjourney 繪圖 | POST/mj/submit/imagine | 原樣轉發 | 13 |
| Suno 音樂 | POST/suno/submit/music | 原樣轉發 | 2 |
「已實測」表示我們在本站完成過真實呼叫與扣費核對;「原樣轉發」表示請求按原格式轉發給模型服務,參數以對應模型的官方說明為準。模型數為當前目錄中支援該介面的模型數量。
第 1 步:建立 Key,改地址
在控制台「令牌」頁建立一把遷移專用的 Key,設好額度上限和分組。然後按你原來用的方式改配置:
| 原來的呼叫方式 | 要改的地方 | 改成 |
|---|---|---|
| OpenAI SDK,或其他 OpenAI 相容服務 | base_url / baseURL,或環境變數 OPENAI_BASE_URL | https://www.tutujin.com/v1 |
| Anthropic SDK | base_url / baseURL | https://www.tutujin.com |
| Google Gen AI SDK | http_options 裡的 base_url | https://www.tutujin.com |
| 直接發 HTTP 請求 | 完整地址 | 例如 https://www.tutujin.com/v1/chat/completions |
api_key 一律換成本站的 Key。只改環境變數就能切過來的專案:
export OPENAI_BASE_URL="https://www.tutujin.com/v1"
export OPENAI_API_KEY="sk-你的Key"三種請求頭的寫法和各 SDK 的完整配置,見認證與 Base URL。
第 2 步:核對模型名
用新 Key 呼叫模型列表介面,返回的就是這把 Key 能呼叫的模型:
curl https://www.tutujin.com/v1/models \
-H "Authorization: Bearer $TUTUJIN_API_KEY"把程式碼裡用到的每個模型名和返回結果逐個對上。同一個模型在不同平臺的叫法可能不一樣,以本站的名字為準;對不上的,在程式碼裡做一張對映表。返回裡沒有某個模型,多半是 Key 的分組不提供它,在模型詳情頁查它有哪些分組,見模型列表介面。
第 3 步:核對行為差異
介面格式相容,但有幾處行為要按本站的情況處理:
| 方面 | 本站的情況 | 程式碼裡要做的 |
|---|---|---|
| 餘額不足 | 返回 403,錯誤碼 insufficient_user_quota;OpenAI 官方返回的是 429 | 把 403 單獨處理:提醒充值,不要放進重試佇列 |
| 分組裡沒有這個模型 | 返回 503,錯誤碼 model_not_found | 不要無限重試,換模型名或換分組 |
| 錯誤體 | error 物件裡的 code 經常是空字串,message 末尾帶 request id | 按 HTTP 狀態碼判斷,記錄完整錯誤響應,排查時把 request id 發給客服 |
| 限流 | 429,額度隨線路和負載變化,不公佈固定數字 | 退避重試,設併發上限,見限流說明 |
| 流式輸出 | Chat Completions 的流式輸出已在本站實測;資料開始返回後出錯,表現為流提前結束、收不到 data: [DONE] | 把沒收到結束標記的流當作失敗處理 |
| 參數支援 | 非 OpenAI 模型對 response_format、tools、reasoning_effort 等參數的支援各不相同 | 不支援的參數可能被忽略或返回 400,換模型後先用少量請求驗證 |
| 用量欄位 | 響應的 usage 返回輸入、輸出 tokens,命中快取時有快取 tokens | 在你自己的系統裡按 usage 記帳,再和控制台核對 |
完整的狀態碼對照見錯誤碼與重試,計費欄位的說明見計費與用量。
第 4 步:分批切換
不要一次全量切換。按比例分流,每一階段都看過資料再進下一階段:
| 階段 | 發到本站的比例 | 看什麼 | 進入下一階段的條件 |
|---|---|---|---|
| 離線驗證 | 不接線上流量 | 用一批真實的歷史請求對比輸出質量、延遲和花費 | 結果可以接受 |
| 小流量 | 5% 左右 | 錯誤率、延遲、單次請求的花費 | 穩定執行一段時間,沒有新增的錯誤類型 |
| 半量 | 50% | 同上,再看高峰時段有沒有 429 | 高峰期也穩定 |
| 全量 | 100% | 同上 | 穩定後再下線原來的服務 |
分流按使用者或會話固定:同一個使用者始終走同一邊,對比效果、排查問題都更清楚。比例放在環境變數或配置中心裡,調整和回滾都不用改程式碼:
import os
import zlib
from openai import OpenAI
tutujin = OpenAI(base_url="https://www.tutujin.com/v1", api_key=os.environ["TUTUJIN_API_KEY"])
legacy = OpenAI(base_url=os.environ["OLD_BASE_URL"], api_key=os.environ["OLD_API_KEY"])
# 原服务的模型名 → 本站的模型名,比如原平台的名字带厂商前缀
MODEL_MAP = {"openai/gpt-4.1-mini": "gpt-5.4-mini"}
def rollout() -> float:
# 0 = 全部走原服务,1 = 全部走本站;每次读取,改配置立即生效
return float(os.environ.get("TUTUJIN_ROLLOUT", "0"))
def chat(user_id: str, model: str, messages: list):
bucket = zlib.crc32(user_id.encode()) % 100 # 同一个用户永远落在同一个桶
if bucket < rollout() * 100:
return tutujin.chat.completions.create(model=MODEL_MAP.get(model, model), messages=messages)
return legacy.chat.completions.create(model=model, messages=messages)import OpenAI from 'openai'
import { crc32 } from 'node:zlib'
const tutujin = new OpenAI({ baseURL: 'https://www.tutujin.com/v1', apiKey: process.env.TUTUJIN_API_KEY })
const legacy = new OpenAI({ baseURL: process.env.OLD_BASE_URL, apiKey: process.env.OLD_API_KEY })
// 原服务的模型名 → 本站的模型名,比如原平台的名字带厂商前缀
const MODEL_MAP = { 'openai/gpt-4.1-mini': 'gpt-5.4-mini' }
// 0 = 全部走原服务,1 = 全部走本站;每次读取,改配置立即生效
const rollout = () => Number(process.env.TUTUJIN_ROLLOUT ?? '0')
export function chat(userId, model, messages) {
const bucket = crc32(userId) % 100 // 同一个用户永远落在同一个桶
if (bucket < rollout() * 100) {
return tutujin.chat.completions.create({ model: MODEL_MAP[model] ?? model, messages })
}
return legacy.chat.completions.create({ model, messages })
}Python 和 Node.js 兩個版本對同一個使用者算出的桶相同,兩種語言的服務混用也能保持一致。每個階段都在控制台「操作日誌」裡按令牌名稱篩選這把遷移 Key,核對請求數、錯誤和花費,再和你自己系統裡按 usage 記的帳對一遍。
回滾
- 發現問題,先把分流比例調回 0,請求立刻全部回到原來的服務,不用改程式碼、不用重新部署。
- 全量之後,原來服務的帳號和 Key 先別登出,保留到新服務穩定執行一段時間。
- 回滾之後,用操作日誌裡的時間、模型和錯誤資訊定位原因;需要我們協助時,把錯誤裡的 request id 發給客服微信
JsnonoChat。
切換完成後
- 給生產 Key 設額度上限和 IP 白名單,見 API Key 安全最佳實踐。
- 關鍵業務給 Key 配多個分組、開啟跨分組重試,見大模型 API 高可用。
- 每月按 Key 匯出用量對帳,見成本分攤與對帳。
常見問題
遷移要改多少程式碼?
用 OpenAI SDK 的專案,一般只改 base_url、api_key 和模型名。另外要單獨處理一個差異:餘額不足時本站返回 403,不是 OpenAI 的 429,不能放進重試佇列。
遷移期間能同時用原來的服務和土土金 API 嗎?
能。分批切換時兩邊並行:按比例把一部分請求發到本站,其餘照舊,確認穩定後再逐步提高比例。
模型名和原平臺不一樣怎麼辦?
以本站模型廣場和模型列表介面返回的名字為準,在程式碼裡維護一張模型名對映表,切換時按表替換。
本站的限流額度是多少?
不公佈固定數字,額度隨線路和負載變化。按退避重試和併發上限來設計呼叫;長期需要更高併發,加客服微信 JsnonoChat 說明模型、分組和請求規模。
出了問題怎麼回滾?
把分流比例調回 0,請求就全部回到原來的服務。分流比例放在環境變數或配置中心裡,回滾不用改程式碼、不用重新部署。