Cloudflare 透過精細化最佳化,將推理成本削減 68%
Cutting our inference bill by 68% by being obsessive about optimization
Cloudflare 透過精細化模型選擇、快取最佳化與流程調整,將 AI 程式碼審核成本從 2.43 美元降至 0.77 美元,成本下降 68%。
- 成本:平均審核費用降至 $0.77,比 8 月的 $2.43 降 68%。
透過精細化模型選擇與快取最佳化,Cloudflare 成功將 AI 審核成本降低 68%,同時縮短審核時間至 1 分 32 秒,為大規模 AI 應用提供可復用成本控制方法。
當我們在四月之前 部落格寫過我們的 AI 程式碼審核工具 時,平均審核成本為 1.19 美元。到八月底,這個數字已飆升至約 2.46 美元。我們並未做任何重大架構調整:協調者仍然把工作交給同一組專業審核員,並且同樣的風險等級決定我們提供多深度的審核。
在九月中旬,我們指派代理人找出那些代幣的去向並大幅削減。將程式碼品質設定為高強度後,平均審核成本降至僅 0.77 美元,較我們開始前兩週平均 2.43 美元下降 68%。我們的主要目標是削減成本,但在此過程中也優化了審核器,使中位審核時間從 4 分 09 秒縮短至僅 1 分 32 秒。
約 40% 的節省來自於審核器程式碼的變更與最佳化,其餘則來自於實驗不同模型及其投入的工作量。
審核成本翻倍的原因
下圖顯示自三月以來每個工作日平均審核成本。圖表固定了審核等級組合,因此日子中若有更多大型審核不會扭曲資料。
每次審核成本穩步上升,從 1.22 美元(三月)→ 1.44 美元(六月)→ 1.74 美元(七月),最後升至 2.32 美元(八月)。其中一小部分原因是更多審核落入最昂貴的等級(三月 59% 的審核,八月 65%)。我們認為原因很簡單:模型變得更好、更有能力。自然,工程師產生更大、更複雜的變更,導致更多審核進入「完整審核」等級。我們發現,成本上升的大部分實際來自於這些月份模型的變更。
八月初,我們將所有審核器切換到 Anthropic 模型,導致每個工作成本從 1.38 美元升至 2.03 美元。我們之所以作此選擇,是因為在評估過程中,Anthropic 模型在此特定工作負載上表現優於其他模型。
我們的內部 標準審核器,Codex 最終選擇 Claude Opus 4.7,原因是其效能與可靠性兼具。這使其成本從每次審核約 0.25 美元升至八月的 0.88 美元,甚至超過了協調代理本身的成本。
快取命中率也提升,從四月的 85.7% 上升至九月的 97%,同時每次審核的代幣數量超過三倍,從 91 萬增加到 299 萬。即使快取讀取成本低,三倍代幣數量下的高命中率仍然成本更高。
自上次發布以來還有什麼變化?
最大的變化之一是審核器現在執行於 OpenCode v2,這是從 v1 完全重寫的 從零開始的重構。在將所有審核從 v1 轉移至 v2 之前,我們在每個 canary 倉庫中以比較式影子模式同時執行 v1 與 v2 的所有審核。兩者在我們測試的 98% 合併請求中達成相同的批准決策,於十月初一次性完成轉移並移除 v1 程式碼。使用相同模型比較兩天,v2 上的完整等級審核完成速度快約 13%。我們目前還在實驗其他方式執行內部 AI 審核器,包括僅使用 Durable Objects 搭配 pi-durable。希望不久後會有更多更新,敬請留意!
那麼,代幣究竟被用到了哪裡?
在我們開始專注於成本最佳化之前,我們的 AI 審核支出分佈為:輸出 tokens 約佔 23%,cache 寫入成本 43%,cache 讀取成本 34%。這意味著大部分金額都花在模型已經看到或即將再次看到的上下文上。
在檢視單一會話時,這點完全合理。程式碼品質審核者平均每次審核進行 16.6 次大型語言模型(LLM)呼叫,並在這些呼叫中寫入 72k tokens 到快取,並讀回 1.08m tokens,因為每次呼叫都會將整段對話作為上下文傳送。會話成本隨呼叫次數與上下文大小雙重增長,而在過去幾個月中我們慢慢提高了兩者。
值得花幾分鐘閱讀 Anthropic's prompt cache 的工作原理。它的工作方式是將已被大型語言模型(LLM)計算過的提示片段快取起來。這些片段稱為字首(prefix),因為它們僅適用於對話開始時的訊息,而不適用於中間或結尾。這意味著未來的請求可以重複使用較早的字首,直到對話中與已快取內容不同的部分。對我們而言,測試工具、系統提示以及 AI 審核會話開始時的訊息很少變動,因而是字首快取的理想物件。字首僅在快取斷點結束時被儲存。寫入快取的成本為正常輸入價格的 1.25 倍,讀取成本為 0.1 倍,且若無人讀取,專案在五分鐘後失效。
這意味著我們有兩件事需要處理:讓每個會話的首次請求從快取讀取,而不是寫入;以及減少每個會話的呼叫次數。
在每個倉庫之間共享快取字首
從 9 月 14 日起,審核者記錄了每位審核者首次 LLM 呼叫的 token 使用量,以及以完整方式傳送給供應商的第一個系統區塊的雜湊值。
資料顯示,會話的首次請求僅在 14% 至 26% 的時間內找到快取字首。每個會話都以相同的工具定義和相同的代理提示開始,包含 11k 至 21k 個 token,這些內容在我們審核的每個倉庫中都是相同的,因此幾乎所有情況下都應該是快取命中。
問題最終出在 OpenCode v1 構建系統提示的方式:它將代理提示、隨後的工作目錄和日期環境區塊合併為一個區塊,並將唯一的快取斷點設定在該區塊的結尾。共享部分從未單獨儲存,導致在一個倉庫中的審核無法重複使用另一個倉庫中審核者所寫的內容。
9 月 16 日,我們在每個代理提示的結尾加入了一個標記,並新增一個外掛掛鉤,在該標記處將區塊拆分為兩段。第一段現在以所有工作共用的位元組結束,並擁有自己的快取斷點。為了保持此狀態,我們必須移除前置的任何倉庫特定內容,因而停用了可用技能列表,並且工具說明無法在不同工作之間變更。
同一天,我們還將內部標準(Codex)審核者的參考索引(內部標準表、技能檔案及參考檔案清單)移入提示中。此之前,每個會話都在前三回合使用工具呼叫閱讀這些檔案。其每次執行的 LLM 呼叫次數從 15.9 降至 9.6,成本從 $1.12 降至 $0.72。
這些改動將每次審核成本從 $2.47 降至 $2.02,且下降在我們啟動發布時就已明顯。
呼叫更少,金額更少
下一組變更於 9 月 21 日與 23 日推出。單獨來看並不特別顯著,但合併後每次審核節省約 $0.20。
變更 | 發生了什麼 | 變更效果 |
移除審核者無法使用的工具 | 子審核者能看到發布最終審核評論的工具。在 57% 至 76% 的會話中,他們嘗試呼叫該工具,因伺服器檢查被拒絕,並額外耗費一次呼叫來恢復。 | 一個浪費的呼叫被消除,每次子審核者請求中工具定義的 token 減少約 2.9k。 |
並行讀取檔案 | 在 64% 的程式碼品質審核者回閤中,模型只進行了一次工具呼叫,通常是讀取單一檔案。 | 審核者在第一次回覆中閱讀共享上下文及所有修補程式,最多 150 KB。 |
跳過重新審核的審核者 | 我們的工作中約有三分之二是同一合併請求的第二次或之後審核,且常有三位審核者無新內容可檢視。 | 在完整層級的重新審核中,AGENTS.md 審核被跳過 56%,效能審核 23%,檔案審核 13%。 |
審核發布後停止 | OpenCode 將結果傳回模型進行一次額外回合,模型再次閱讀整段對話以撰寫一份沒有人看到的摘要。 | 每個工作節省約 $0.07 |
將慢速審核者與失效提供者區分處理 | 達到時間限制的審核者被視為提供者失敗,並以相同時間限制重新啟動。 | 加長時間限制並降低努力後,安全審核者的逾時率從 1.8% 降至幾乎零。 |
取消已取消的工作 | GitLab 的 Kubernetes 執行器在工作取消時不會向工作指令碼傳送訊號。在 446 個已取消工作樣本中,358 個工作仍繼續執行直到審核完成。 | 工作每 15 秒檢查一次自身狀態,若被取消則停止。 |
最複雜的變更是三位審核者共用一個字首。程式碼品質、安全與效能審核者現在以相同工具、共享規則檔案傳送相同的第一個請求,接著一次性傳送合併請求及所有修補程式。每位審核者的個別指示在此之後,且我們確保其中一位寫入快取,其餘兩位從中讀取。
快取專案在寫入後才可被讀取,且三位審核者同時啟動。因此第二和第三位在傳送自己的第一個請求前,會等待第一位的回應開始,最多 20 秒,這略微減慢他們的速度,但每位審核者可節省約九分之一元。
更換模型與努力
下一張圖表顯示自九月初以來的同一條線,並標示每一次變更。
將期間以每次發布與每次設定變更為分割點,程式碼變更佔總節省的較小部分。
程式碼變更佔總節省 $1.67 的 $0.65,模型與努力變更佔 $1.06。每一步比較變更前後的工作日小時,因此並非統計上健全的受控實驗。
協調員自八月以來一直執行三種模型與努力組合。在同一模型上,使用中等努力的全層級工作比率提前 20%,因此我們放棄了其他兩種。在後續的隨機分割中,Opus 5.5 在中等努力下與 Opus 5 相比,審查結果或關鍵發現沒有可測量的變化,因此我們切換。Opus 5.5 在高努力下的成本高於中等努力,卻沒有產生有意義的發現增長。
Opus 5.5 的每個 token 價格低於 Opus 5,但價格只佔下降的約 $0.20。其餘部分來自於傳送與產生更少的 token。自九月初以來,每次審查的快取讀取下降 82%,快取寫入下降 54%,輸出下降 67%。
最近的變更是新模型。10 月 3 日,我們將每位 Sonnet 審查員 50/50 分配到 Sonnet 5 與 Sonnet 5.5。兩天半後,Sonnet 5.5 每次執行的程式碼品質成本比 Sonnet 5 低 62%,效能低 58%,安全性低 47%。兩個模型每個 token 的成本相同,因此全部差異來自於使用更少的 token。在全層級審查中,5.5 審查員發現的關鍵與警告問題數量與 Sonnet 5 相近,於 10 月 5 日我們將所有審查員轉至 5.5。
在 Sonnet 5.5 中等努力下,程式碼品質審查員每次執行只做 1.7 次 LLM 呼叫,而非 6.2 次,且超過一半的執行在一次呼叫中完成,表示它們不會開啟倉庫中的其他檔案。在高努力下,它每次執行做 3.4 次呼叫。我們擔心這會使審查變得淺薄,以下品質章節將說明。
為了檢視更多努力是否能在更大層級帶回發現,程式碼品質審查員自 10 月 5 日起在 5.5 上 50/50 分配到中等與高努力。一天後,高努力的成本比每次執行多 61%,並且發現 58% 更多的關鍵與警告問題。這相當於每次審查約兩分錢,以下品質章節顯示這些額外發現值得擁有,因此我們將程式碼品質轉至高努力。本文中的成本數字假設了這一點。
延遲
我們並未旨在加速審查,但較少的呼叫也意味著等待更少,進而使審查更快、工程師更滿意。
最慢的審查提升最大。在 9 月 14 日前的四週中,37.9% 的審查耗時超過五分鐘,而現在僅 0.7%。今天的 99 百分位比 4 月時的審查快了四分之三。每個層級隨時間加速,平均全層級審查從 5 分 16 秒降至 1 分 54 秒,lite 從 2 分 58 秒降至 1 分 14 秒,trivial 從 77 秒降至 30 秒。
至八月,平均審查比我們在四月報告的慢 14%,99 百分位慢 48%,因此這項工作的一大部分只是讓我們回到原來的位置。
審查品質
如果更便宜的審查同時發現更少問題,那就不值得,這裡的第一張圖看起來就是這樣。
在 Sonnet 5.5 高努力下,程式碼品質審查員的關鍵與警告發現比八月底少 40%。安全與效能(保持中等努力)分別少 56% 與 71%。嚴重發現的下降本身無法告訴你是否真的漏掉了問題或是噪音被削減,我們從兩個角度進行檢視。
首先,我們檢查作者是否對審查者標示的專案採取行動。針對嚴重和警告性發現,我們將被審查的提交與作者的下一次推送進行比較,若指出的行號有變更則視為已處理。每個發現前後 30 至 40 行在各期間約有 15% 會發生變動,這提供了偶然編輯的基準。
在變更前,作者對程式碼品質審查者嚴重發現的 34% 進行了處理。現在為 59%。 審查者標示的專案較少,但其標示的專案更可能導致變更,因此每千次審查中導致變更的發現數量大致相同。我們現在看到 132 對比 145,落在誤差範圍內。四位審查者總體上,導致變更的嚴重發現從每千次審查 229 增至 321。
第二,我們請兩個不同供應商的模型對三位 Sonnet 審查者的 657 個發現進行盲判,提供每個發現所參照的差異並不透露其所屬時期。若有分歧則交由第三個模型。每個發現被標記為重要、次要、重複、錯誤或無法驗證,並將其嚴重程度標為合理、過高或過低。
大部分消失的專案是次要發現,還有一些重複專案以及審查者無法從差異中驗證的發現。每次審查的重要發現數從 0.28 增至 0.30,區間重疊。較大的變化出現在嚴重程度上。之前,審查者將每次審查 0.46 個嚴重或警告發現評為比實際更嚴重。現在降至 0.14,下降 70%,是研究中最清晰的結果。大部分嚴重發現的下降是因為審查者不再將次要專案稱為嚴重。
因此答案取決於審查者。對於高努力級別的程式碼品質,消失的主要是噪音與過度嚴重評估,仍然能發現與以前相近的實際問題。中等努力級別則失去了實際發現。在隨機分割中,中等努力級別的程式碼品質導致的已處理發現僅為高階別的約四分之一,這也是我們將其升級為高階別的原因。效能同樣失去實際發現:審查者每次審查發現的實質效能問題約減少 64%,實際上每 24 次完整級別審查少一個重要發現。安全性下降約四分之一,儘管統計上不顯著。兩者仍維持在中等努力級別,下一步將以高階別測試。
兩種方法都不是完美的。作者改動被標示的行號顯示他們採取了行動,但並不代表發現是正確的。審查者本身也是模型,且每組僅有 35 到 66 個發現。'現在' 時間視窗也只有一天。儘管如此,兩種方法仍相互一致,且都未顯示程式碼品質發現較少實際問題。
可靠性也保持穩定。未達成結論的審查率從 0.79% 降至 0.27%,安全審查者重試率從 3.5% 降至 0.04%。
這讓我們處於什麼位置
平均審查現在的成本為 0.77 美元,取代原本的 2.43 美元,且中位審查完成時間為 1 分 32 秒,取代原本的 4 分 9 秒。最大的一項節省來自於檢查每個會話第一個請求實際送達供應商的內容,並找出一種方式,將這些訊息以盡可能一致的順序排列,跨越提交、合併請求與倉儲。其餘的成本節省則來自於不斷實驗不同模型與努力組合,同時確保將對特定模型的任何先入為主偏見拋開。
較低成本的審查並不自動意味著品質下降,也不自動意味著品質良好。在高努力下,程式碼品質仍能發現與以前相同數量的實際問題,且噪音大幅減少,嚴重程度被高估的情況也大幅下降。在中等努力下,效能發現的實際問題減少,安全性也可能同樣受影響,因此下一步是以高努力測試兩者。若您在任何規模上執行代理,首要檢查的兩件事是:每個會話的第一次請求是否真的從快取讀取,以及您的審查者在您所付費的努力水平下,是否仍能發現實際問題。
如果您在舊金山的 Cloudflare Connect,我將在我的會議中說明我們如何建構審查者,而非購買,現在是建造而非購買的時候,時間為 10 月 20 日星期二,12:30 PM PDT。我也會於 1:10 PM 在講者連線休息室,若您想詢問本帖中的任何數字。