CodeGym /課程 /ChatGPT Apps /釋出流程 — 版本、釋出說明、SDK/規格遷移、feature flags、回滾

釋出流程 — 版本、釋出說明、SDK/規格遷移、feature flags、回滾

ChatGPT Apps
等級 17, 課堂 3
開放

1. 為什麼 ChatGPT App 的釋出流程比一般部署更複雜

在上一部分,我們談了如何觀察 App 的品質與穩定性(日誌、指標、SLO)。現在來看,如何設計釋出流程,避免每次部署都把這些指標搞壞。

在一般 Web 應用中,事情相對單純:你部署了新的後端與前端——使用者重新整理頁面就跑在新版本上。若出問題,常常只要回滾部署就好。

在 ChatGPT App 的堆疊裡情況更微妙。至少有四個彼此節奏不同的層:

  • Manifest 與工具的 schema(MCP tools / OpenAPI);
  • 基礎設施:Next.js 應用與 MCP/Agents/ACP 伺服器;
  • System‑prompt 與其他提示詞;
  • 資料:product feed、設定、組態。

問題在於,模型活在自己的「心智世界」。聊天上下文可以延續數小時乃至數天。Manifest 與 tools 的描述會由 OpenAI 端載入 並會快取,且不會在所有現有對話裡即時更新。若你修改了工具的簽章(例如從 input‑schema 刪除欄位),在舊對話中模型仍會送出舊的 payload,而你的新後端會拒收。結果就是 400 錯誤、聊天中怪異的回覆,以及非常不開心的使用者。

因此,在 ChatGPT App 的世界裡,「釋出」不是「部署一個新的 Docker」那麼簡單。它是多個層面的協同變更,需要謹慎的版本管理、功能旗標,以及可隨時回滾的能力。

2. 版本矩陣:到底該對哪些東西版控

與其只看「App 1.3 的版本」,不如同時看各層的版本矩陣。以 GiftGenius 為例,大致如下。

層 要版控什麼 範例值 存放位置
App / Next.js 程式碼與建置
giftgenius-app 1.4.2
package.json、Git 標籤
MCP / API 介面 工具集合及其 schema
tools-schema v1.7
程式中的常數、註解
System / prompts System‑prompt、輔助提示、範例
prompt v3.1
獨立檔案 + 中繼資料
Commerce / ACP / feed product feed 與 ACP 合約的格式
feed v2, acp v1.3
資料儲存庫中的 schema

請注意,這是不同的維度。你可以發佈應用版本 1.4.2,其中 tools 的 schema 仍是 v1.7,而 prompt 升到 v3.2。這些應在日誌中可見;之後就更容易追查為什麼在 prompt v3.2 之後結帳轉換率突然下滑。

對於程式碼,建議採用語意化版本(SemVer):MAJOR.MINOR.PATCH。MAJOR=破壞性變更,MINOR=不造成破壞的新功能,PATCH=修補錯誤。對介面類的版本(schema、prompts)邏輯也相似:MAJOR 代表有 breaking‑change。

尤其要單獨管理工具介面的版本。對 LLM 來說這極其重要:若你變更了工具契約,而模型「以為」契約仍舊,災難就來了。因此常見政策是:在 MINOR 釋出中只新增選用欄位,不重新命名或刪除舊欄位;破壞性變更只能透過新工具,例如 suggest_gifts_v2。

現在我們把各層分出版本後,來看看這些版本在釋出週期中如何運作——從 dev 到 prod。

3. GiftGenius 的基礎釋出流程

先約定環境階段。你可能已經有不同環境(在部署模組裡提過),但這次改用釋出的視角來看。

通常會區分:

  • dev——本機開發、ChatGPT 的 Dev Mode、tunnel;
  • staging——盡量貼近生產:相同類型的資料庫、相同類型的 MCP/ACP,但使用測試資料與 sandbox 支付;
  • prod——生產環境,即連接正式版 ChatGPT App/商店上架頁的那個環境。

流程可能如下:

flowchart TD
  A[Dev:分支 feature/*] --> B[PR → main]
  B --> C[CI: unit + contract + lint]
  C --> D[部署到 Staging]
  D --> E[Smoke/E2E + 人工檢查]
  E --> F[部署到 Prod]
  F --> G[依據指標與日誌觀察]

在每一步都加上「保險」。在 dev 環境,開發者可以自由嘗試,但每次合併到 main 都會觸發 CI,跑 unit/contract 測試。若一切正常——自動部署到 staging。在 staging 上跑一小套 E2E/煙霧測試:例如一次完整的禮物挑選流程,和在測試金流中的「假結帳」。

之後才按下部署到 prod 的按鈕。最好加上版本標記與 CHANGELOG 連結。進入生產後,持續觀察 p95、錯誤率與商業指標(從推薦到結帳的轉換率)。若釋出後有指標下滑——我們要有清楚的回滾計畫,稍後會談。

4. Release notes 與 changelog:要記錄什麼

如果沒有釋出筆記,一個月後你看著圖表:「為什麼三週前我們的結帳 p95 翻倍?」答案會是:「不清楚,但那時有個大釋出。」這不是好策略。

通常至少有兩種筆記。

內部 changelog——技術向的。用於開發者、SRE 與所有看程式的人。內容包含新增了哪些功能、修了哪些 bug、是否有 breaking changes。格式可採 Keep a Changelog 標準:分區 Added、Changed、Fixed、Removed。

外部 release notes——給使用者與商店看的。無需寫「migrate MCP SDK 0.4 → 0.5」;更好的是寫「新增感恩節的推薦」、「修正某些禮物無法加入購物車的罕見錯誤」。

GiftGenius 的 CHANGELOG.md 片段範例:

## [1.4.0] - 2025-11-20
### Added
- 新的工具 `suggest_gifts_v2`,支援興趣標籤。
- 新 system-prompt 的 A/B 測試(旗標:GG_PROMPT_V3)。

### Changed
- 改善 ACP checkout 的錯誤處理(訊息更友善)。

### Fixed
- 修正一個導致沒有圖片的商品會從推薦中被隱藏的錯誤。

把提示詞的版本也放在附近很有用:哪怕只是 system‑prompt 檔案的 commit‑hash,之後就能回想:「啊,更新到提示詞 b3f9c2d 後,使用者比較少按「購買」。」

5. SDK 與規格的遷移:如何在快速演進的生態裡求生

圍繞 Apps SDK、MCP、Agents 的生態變化很快:新的版本 SDK、MCP 協議變更、ACP 更新等等。這是好事(帶來新能力),但也痛(很容易壞)。

固定版本與「不要在釋出當天更新」

第一個簡單建議:鎖定相依版本。不要用 ^0.4.0,而是 0.4.0。對那些常破壞 API 的 SDK(MCP/Agents/Apps SDK)尤為關鍵。我研究過相關主題,文獻中特別提到「SDK fatigue」——因持續不相容更新而產生的疲勞,特別是讓相依採用浮動版本(例如 ^0.4.0)的人,某天突然在 npm install 後就遇上大量錯誤。

小例子:

// package.json(片段)
{
  "dependencies": {
    "@modelcontextprotocol/sdk": "0.4.2",
    "@openai/applications-sdk": "0.3.1"
  }
}

第二個建議:不要把商業功能與大型 SDK 遷移塞在同一個釋出裡。如果要把 MCP SDK 從 0.3.x 升到 0.4.x,最好做一次獨立的技術性釋出:先更新 SDK、跑測試、穩定之後,再推新的結帳流程。

SDK 遷移策略

合理的計畫通常如下:

在 dev 環境建立獨立分支 upgrade/mcp-sdk-0.4。更新相依、修正程式、跑所有 unit/contract 測試,並在本機透過 Dev Mode 跑 GiftGenius 的主要流程。

接著把這個分支部署到獨立的 staging‑URL,並對其跑 E2E/煙霧測試。甚至可以做小規模壓測:50–100 次連續呼叫 suggest_gifts。

若一切正常——合併到 main,部署到主要的 staging,再跑一次煙霧測試,最後才進 prod。

若不正常——回滾很明確:別合併此分支或把它還原。這就是把 SDK 遷移與產品釋出分開的意義。

工具與 API schema 的遷移

最棘手的是介面層變更:模型不會瞬間得知。研究特別強調一條規則:「只做擴充、不做破壞」(additive‑only)。若你要在 suggest_gifts 加入新參數 interests: string[],請把它做成選用,而不是必填;舊情境才能繼續運作。

Zod 輸入 schema 的演進示例:

// v1
const suggestGiftsInputV1 = z.object({
  recipientName: z.string(),
  budget: z.number()
});

// v1.1(新增選用欄位)
const suggestGiftsInputV1_1 = suggestGiftsInputV1.extend({
  interests: z.array(z.string()).optional(),
  occasion: z.string().optional()
});

注意:我們不動既有欄位,只做擴充。

若真的需要打破契約(例如把 budget 改成 minBudget + maxBudget),最好新增一個工具 suggest_gifts_v2,並在描述中標註這是改良版。舊 API 可標為已棄用並逐步關閉,待確定模型與使用者都已遷移。

資料遷移:product feed 與 ACP

前面談過 SDK 與工具 schema 的遷移。Product feed 也是一種契約。若你改了 SKU、幣別、在地化的格式,必須協調進行:同時更新 feed 的 schema、MCP/ACP 的處理,以及任何前置處理器。在 ChatGPT App 的 commerce 文件中指出,feed 的錯誤(欄位損壞、重複、奇怪的價格)即使程式完美也會毀掉推薦品質。

典型做法:

  1. 先在 feed 的 schema 新增欄位為選用,並讓 GiftGenius 在存在時加以使用。
  2. 然後更新產生 feed 的管線,讓它開始填入這些欄位。
  3. 跑 feed 驗證器(資料的 contract‑測試),之後才在邏輯中依賴這些欄位。

6. Feature flags:把部署和釋出解耦

功能旗標(feature flags)是 ChatGPT App 世界裡的生存利器。核心概念很簡單:你可以先部署程式碼,但不一定立刻開啟新功能。先把它「放在旗標下」——只對開發者開、或只對 1% 使用者開,甚至完全關閉。

以下情境特別重要:

  • 推出新的推薦演算法;
  • 更換 system‑prompt(可能劇烈改變模型行為);
  • 接上昂貴或緩慢的工具;
  • 嘗試新的結帳流程。

用環境變數實作的簡易旗標

最簡單的方式是用環境變數:

// lib/features.ts
export const isNewRecoEnabled =
  process.env.NEXT_PUBLIC_GG_NEW_RECO === "1";

接著在 MCP 工具的程式中這樣用:

if (isNewRecoEnabled) {
  return runNewRecoAlgorithm(input);
}
return runOldRecoAlgorithm(input);

好處是簡單。壞處是要在執行期切換旗標較麻煩:需要部署新的環境,或至少重新初始化。

集中化的 helper 與情境感知

再成熟一點的做法——有一個集中化的 helper,不只考慮全域旗標,還納入使用者情境(租戶、分群、A/B 群組)。

// lib/featureFlags.ts
type Feature = "new-reco" | "checkout-v2";

type Context = { userId: string; tenantId?: string };

export function isFeatureEnabled(
  feature: Feature,
  ctx: Context
): boolean {
  // 這裡可以放邏輯:環境變數、資料庫、外部旗標服務
  if (feature === "new-reco") {
    return process.env.GG_NEW_RECO === "1";
  }
  return false;
}

在 MCP handler:

const enabled = isFeatureEnabled("new-reco", { userId });
const result = enabled
  ? await runNewReco(input)
  : await runOldReco(input);

若將來接上外部旗標服務(LaunchDarkly、Statsig 等),只要更換 isFeatureEnabled 的實作,而非重寫整個程式。

GiftGenius 的實際情境

A/B 測試 system‑prompt。 你建立 PROMPT_V2,僅對特定名單中 10%、具有 tenantId 的使用者開啟。其間 MCP 工具與小工具不變,你比較轉換率。

昂貴工具的 kill‑switch。 假設你做了一個需要呼叫外部服務的工具(例如某個昂貴的推薦 ML 模型)。如果外部服務開始故障或成本暴增,你希望能一秒關掉它,而不必停掉 GiftGenius 整體。功能旗標是最簡單的方式。

逐步 rollout 新的結帳流程。 先只對公司員工與少數信任客戶開啟 Checkout v2。若指標良好——逐步擴大,直到全量開啟。

7. 回滾:當一切失火時如何快速回復

即便測試與旗標都做得很好,總會有東西壞掉。關鍵是要有明確策略:在發現錯誤尖峰或指標下滑後的前 5–15 分鐘,你要做什麼。

快速回滾程式碼

若問題是純技術性 bug(NPE、變數錯了、外部服務 URL 錯),通常只要回滾部署到前一版即可。像 Vercel 就有回滾到上一個 deployment 的即時功能。

你的任務是永遠知道哪個 deployment 對應哪個版本,以及如何回滾。理想情況下,這在 README(for on‑call)裡有寫:「若在 1.4.0 釋出後一切起火,回滾到 deployment X」。

第二個快速槓桿——功能旗標。若只是一個新功能(checkout-v2)掛了,與其立刻回滾整個釋出,不如先用旗標把它關掉。

高風險變更:manifests 與 schemas

Manifest 與 schema 更棘手。若你把新的、錯誤的工具 schema 上傳到 Store,且已被 OpenAI 核可,回滾可能要花上好幾天。原因很簡單:每次變更 manifest 也需要 OpenAI 審查。在各種 Store 的分析中都指出,manifest 與 schema 的變更是「高風險」釋出,必須特別小心地準備與測試。

因此最好區分:

  • 安全的釋出:MCP/Next.js 程式碼變更、提示詞修正、以旗標隱藏的新功能;
  • 高風險的釋出:tools 清單、input/output‑schema、ACP 契約的變更。

「高風險」釋出應單獨推出,做額外檢查,並可先經由 Dev Mode 與 staging‑App(不提交到 Store)驗證。

資料與 feed 的回滾

資料更棘手(也更痛)。若你把 product feed 遷到新 schema 並刪除了舊欄位,想回頭不一定容易。因此資料遷移應該可冪等且可逆:要嘛保留舊副本,要嘛切成兩步(先複寫資料,再切換讀取)。

簡單的做法是同時保留新舊欄位一段時間,並透過功能旗標在兩者間切換。若出問題——切回舊讀取。

8. GiftGenius 釋出流程的小型設計

把上述(版本、SDK 遷移、feature flags、回滾)組成一個實務情境。

假設你要做釋出 1.4.0,其中:

  • 新增工具 suggest_gifts_v2,輸入擴充;
  • 對部分使用者開啟新的 system‑prompt;
  • 把 MCP SDK 從 0.3 → 0.4;
  • 變更 product feed 格式(新增 tags 欄位)。

合理計畫如下。

先做獨立的技術性釋出 1.3.1:更新 MCP SDK + 最小幅度的程式修正,不改 schema 與功能。跑 CI、staging、煙霧測試。若一切穩定——先以此版本運行幾天。

接著建立分支 feature/reco-v2。在那裡新增 suggest_gifts_v2 作為新工具(舊的 suggest_gifts 保留)。其輸入 schema 只往「新增選用欄位」的方向擴充。準備新的 system‑prompt,但以旗標 GG_PROMPT_V3 包起來。在 ACP/feed 加入新欄位 tags 作為非必填,並讓讀取在缺少該欄位時依然能運作。

在 CI 增加一些新的 contract‑測試:確認 suggest_gifts_v2 可接受舊與新的 payload;確認含 tags 的 feed 有效,且缺少 tags 的舊資料也不會把伺服器弄掛。

合併到 main 後:

  • CI 跑 unit/contract;
  • 在 staging 透過新工具跑 1–2 個 E2E 腳本;
  • 透過功能旗標,只對測試用租戶開啟新的 prompt 與工具。

觀察指標:工具的 p95、錯誤率、結帳轉換率。若一切良好——擴大旗標覆蓋,直到開給更多使用者。只有在確認穩定後,才更新 Store 所需的 manifest(若需要)與應用的宣傳描述。

若過程中哪裡壞了——你知道怎麼回滾:要嘛手動關掉旗標、要嘛回滾 deployment,極端情況下才回退 manifest 版本(不過最好避免走到這一步)。

9. ChatGPT App 釋出流程中的常見錯誤

錯誤一:只有一個籠統的「App 版本」,而沒有版本矩陣。
若你只有「GiftGenius v1.4」,卻沒記錄 tools‑schema、提示詞與 product feed 的版本,就無法回答:「我們在什麼變更之後結帳下滑?」請把版本按層拆開,並在結構化日誌中記錄。

錯誤二:在工具上做了破壞性變更,卻沒有新的名稱/版本。
最痛的情況:你把 input‑schema 的欄位重新命名或刪除,卻不改工具名稱。在舊聊天中模型仍會送出舊的 payload,後端回傳 400,GPT 開始回「幻覺」,使用者也霧煞煞。任何破壞性變更請用新工具(foo_v2)或新 API 版本來做,且在過渡期保留舊介面。

錯誤三:在做新功能的途中順手升 SDK。
經典翻車法:你在新增業務功能的同時,順手把 @modelcontextprotocol/sdk 從 0.3 升到 0.5,「就想讓一切都很新」。結果如果壞了,搞不清是新程式、新 schema,還是新 SDK 惹的禍。SDK 遷移最好獨立成技術釋出,有明確測試計畫與回滾方案。

錯誤四:沒有功能旗標與即時 kill‑switch。
把新推薦演算法一次開給 100% 使用者很刺激,直到它產出怪結果或壓垮外部服務。沒有功能旗標,你唯一的槓桿就是整個釋出回滾,這可能牽連十來個無辜的小改進。至少用環境變數或小型設定檔做些簡單旗標吧。

錯誤五:以為 manifest 能「即時」更新。
常見誤解——以為一改了 tools 或 openapi.yaml,模型就會立刻知道新 schema。實務上 manifest 與工具描述會被快取,而在已開啟的聊天裡可能長時間存在。忽略這點會導致不明顯的 bug:新對話一切正常,舊對話卻在失敗。請在設計 schema 變更時考量此行為,並先透過 Dev Mode 與 staging 測試,再提交到 Store。

錯誤六:沒有清楚的回滾計畫與釋出文件。
若團隊裡沒人能回答「如何在 5 分鐘內回滾?」「要回到哪個版本?」「如何回到舊的 feed‑schema?」——就等於你們沒有回滾。On‑call 應該有一份簡短但具體的指引:按哪些按鈕、改哪些變數、到哪裡檢查回滾成功。

錯誤七:沒有 release notes、也沒有與指標關聯的「沉默釋出」。
沒有任何 changelog 就釋出,幾個月後你只能靠猜。當 p95 突然升高、轉換率下降時,你會想:「那時到底改了什麼?」養成至少寫最小限度釋出筆記、並把它與部署日期與版本連結的習慣,不只讓品質稽核容易,也讓整個團隊更輕鬆。

1
任務
ChatGPT Apps, 等級 17, 課堂 3
上鎖
Release Guard — 用於 release notes 與已鎖定依賴的腳本
Release Guard — 用於 release notes 與已鎖定依賴的腳本
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION