過去一年裡,讓 Claude Code 或 Codex 寫遊戲程式碼,已經從新鮮嘗試變成了日常習慣。重構、整套系統的實作、著色器和玩法的調整,代理產出 diff 的速度不是人能比的。現在的問題不再是這個 diff 能不能跑,而是它是不是還一樣快。
AI 寫程式碼時代的瓶頸
寫程式碼的工夫一降下來,瓶頸就從「寫」挪到了「確認」。功能能不能跑,測試會自己給出答案。可這段程式碼有沒有把實機影格時間悄悄抬高幾毫秒,一片綠的測試是不會告訴你的。
一旦代理一天提交幾十個 diff,逐個用手去剖析就不切實際了。算繪執行緒上不知不覺多了一次配置,更新迴圈的計算量悄悄變差,GC 觸發得更頻繁。這些都能通過單元測試,也躲得過肉眼的程式碼審查,直到放上實機才作為體感上的劣化顯現出來。
用代理在寫這一頭提上來的速度,把確認這道工序變成了新的瓶頸。
所以不能再把效能確認留成一道靠審查者的警覺、靠老手直覺的工序。它得變成一個和代理同速運轉、由機器給出的判定。
把回歸關卡放到機器這一側
要做的事很簡單。給每個建置收集效能遙測,和上一個已知良好的建置相比,一旦超過閾值變差,就讓 CI 擋下。不是由人盯著數字來判斷,而是由機器判定是否發生回歸,並作為管線的通過/失敗回傳。
Framedash 的 framedash perf-diff 會給出兩個建置在影格時間、記憶體、GPU 時間上的 P50 和 P95。加上 --fail-on-regression,當候選建置的 P50 超過閾值變差時,它會以結束碼 1 擋下(關卡按 P50 的比較來判定,P95 作為參考一併列出,不參與判定)。
framedash perf-diff --baseline "$BASE_SHA" --candidate "$GITHUB_SHA" \
--threshold 5 --fail-on-regression
比較的對象除了影格時間、記憶體、GPU 時間,還包括地圖載入時間(load_time_ms)和磁碟 IO(io.*),都按越小越好的指標處理。你可以用 --metric 把關卡收窄到某個指標,或者用 --map 和 --platform 限定比較範圍。
這裡的關鍵是作為比較單位的 build_id。如果一個數字屬於哪個建置都定不下來,建置之間的比較本身就無從談起。
工作流程全貌
我們來跟一遍代理寫的程式碼走到這道回歸關卡前的流程。
關卡一擋,代理就自己讀取遙測資料來縮小原因範圍。
在 CI 裡把這條流程一把跑完的是 framedash run-profile-test。這條命令會寫出自動工作階段用的 FRAMEDASH_* 環境變數,啟動你指定的剖析建置,等它的遙測被收集進來,再對著基準跑 perf-diff 關卡。
framedash run-profile-test \
--command "./Build/Game.exe -nullrhi -ExecCmds='Automation RunTest Perf'" \
--scenario nightly --api-key-file ci-read.key \
--baseline "$BASE_SHA" --threshold 5 --fail-on-regression
被啟動的遊戲這一側,在自動測試進入點處呼叫一次自動工作階段 API。當 SDK 呼叫 BeginAutomatedSessionFromEnvironment(),它會讀取 run-profile-test 寫出的 FRAMEDASH_BUILD_ID / FRAMEDASH_GIT_BRANCH / FRAMEDASH_GIT_COMMIT / FRAMEDASH_TEST_SCENARIO,此後每個事件都自動帶上 CI 建置以及它的分支、提交、情境。不需要逐個事件加標籤的程式碼。
TelemetrySDK.Instance.BeginAutomatedSessionFromEnvironment();
// ... 執行剖析情境 ...
TelemetrySDK.Instance.EndAutomatedSession();
這裡有一個維運上的注意點:短建置或無頭執行可能在 SDK 的定期刷新之前就結束。請讓執行一直存活到遙測真正送出為止(各 SDK 的 CI 與無頭執行指南裡有做法);隨後 run-profile-test 會等遙測被收集進來,再執行關卡。
build_id 記為頂層欄位,分支、提交、情境則掛在 ci.branch / ci.commit / ci.scenario 屬性上。這樣,代理提交的 build_id 就直接成了回歸比較的候選。
只有金鑰的區分要留意。關卡用 analytics:read 金鑰讀取遙測,被啟動的遊戲則用另一把 events:write 收集金鑰傳送遙測。為避免名稱衝突,關卡那把金鑰用 --api-key-file 傳,而不是 FRAMEDASH_API_KEY 環境變數,讓 FRAMEDASH_API_KEY 留給遊戲當收集金鑰。
關卡擋了,該看哪裡
perf-diff 擋下時,你能知道的只到哪個指標變差了、差了多少。在哪裡變差,光憑這個數字看不出來。
這時候用的是效能熱區圖。它把 FPS、影格時間、GPU 時間、記憶體用量按格子疊加在你的遊戲地圖上。你可以按裝置或建置設定檔篩選,於是能把回歸建置裡哪張地圖哪一帶變重了,定位成地圖上的一個位置。
地圖上的分布,是由帶位置的事件畫出來的。當你的剖析情境把玩家位置連同一個已註冊的地圖 ID 一起回報,SDK 就會給這些事件自動附上攝影機朝向(偏航和俯仰)。有了這些,「build 1042 的 desert_ruins,朝北看的一個角落裡 P95 會跳」這種粒度你就能追下去。回歸就此成了可重現的調查對象。
讓代理自己讀取遙測
上面這條流程裡還剩一處有人:打開熱區圖、縮小原因、寫下修復程式碼。
這道工序也能交給代理。Framedash 的 MCP 伺服器把針對遙測的唯讀工具和資源開放給 LLM。熱區圖網格資料、儀表板 KPI,乃至原始 SQL 查詢,代理都能從一句自然語言指示直接拉出來。
claude mcp add framedash \
-e FRAMEDASH_API_KEY=fd_xxx \
-e FRAMEDASH_PROJECT_ID=your-project-uuid \
-- npx -y @framedash/mcp-server
這樣就能搭出一個閉環:讓製造了回歸的代理自己去讀它弄壞的那處,再把它修好。用 get_heatmap 俯瞰地圖上變重的地方,用按 build_id 過濾的 query 把候選建置的熱點單獨摘出來,形成對原因的判斷,然後改程式碼。工具是唯讀的,所以代理不會改寫或刪除你的遙測。改好的程式碼有沒有真正消除回歸,會在下一個建置裡由同一道 CI 關卡再客觀判定一次。
判定交給機器,讀原因交給代理。留給人的,是閾值劃在哪裡,以及最後看一眼修法是否穩妥。
如何開始
你需要做的,只是給遊戲接入 SDK、從 CI 傳入 build_id,再往管線裡加一道關卡。SDK 接入幾行就夠,支援的引擎是 Unity、UE5、Godot (C#)。
claude plugin marketplace add crane-valley/framedash-claude-plugin
claude plugin install framedash@framedash