AI-LECTURER 速報課|2026-08-23|約 25 分鐘

Codex CLI 接 AWS Bedrock 帳單暴增 10 倍:一個「快取斷點」漏掉的代價

取材:Codex on AWS bedrock bug causing 10x charges(Hacker News(AI 高人氣))
📍 真實場景
陳雅婷,接案後端工程師,正在幫一家客戶公司用 Codex CLI 做整批自動化程式碼重構

客戶規定所有 AI 工具都要走公司自己的 AWS 帳號計費,於是她把 Codex CLI 接到公司的 AWS Bedrock 帳號,跑了一整週的 agentic coding 重構任務

😖 卡住的地方:月底一看 AWS 帳單,光是這個模型的費用就暴增到平常的 8-10 倍,但 Codex CLI 操作過程完全正常、沒有跳出任何錯誤訊息,她完全查不出錢花去哪
💡 這堂課會拆解「提示詞快取」的計費邏輯,讓她看懂帳單裡「快取寫入」跟「快取命中」的差別,以後不管用哪種 AI 編碼工具,都能自己抓出帳單爆增的根本原因

🧭 這到底是什麼(白話版)

如果你用過 Codex CLI 這種「Agentic coding(代理式編碼)讓 AI 自己連續執行多個步驟、呼叫工具、修改檔案來完成任務,而不是一問一答」的工具,你會發現每一次呼叫,AI 其實都要重新讀一遍很長的「系統指令」和「工具說明書」——這些內容通常一整個工作階段都不會變。為了不要每次都重算這一大段,模型服務商設計了「提示詞快取 (prompt caching)讓 AI 模型記住一段先前處理過的內容,之後遇到一模一樣的內容,不用重新計算,改用比較便宜的價錢重複使用」機制:第一次要把這段內容存起來,這叫「快取寫入 (cache write)第一次把某段內容存進快取要付的費用,通常比一般輸入貴一些」;之後同一段內容再出現,就能改用便宜很多的「快取命中 (cache read)之後同樣內容再出現時,用便宜很多的價錢直接讀取已經存好的結果,不用重新計算」。

問題出在「怎麼告訴系統要存快取」這件事。模型代理層需要一個明確的訊號,叫「快取斷點 (cache breakpoint)在提示詞裡標記「這裡是穩定不變的內容結尾」,告訴代理層這一段可以存起來重複使用」,插在「系統指令、工具說明書」結尾的地方。這篇文章的 issue 指出,Codex CLI 在直接呼叫 OpenAI 或 Anthropic 官方 API 時,這個機制運作正常;但當它改走 AWS Bedrock 這個「API 代理層 (provider layer)夾在你的程式和模型公司伺服器中間,負責轉送、驗證、計費的服務,例如 AWS Bedrock」時,程式碼裡卻少了塞進快取斷點標記的那段邏輯——等於每次都在跟代理層說「這是全新的內容」,快取機制形同虛設。

結果反映在帳單上:8/5 到 8/8 這 4 天,3,656 次呼叫裡,光是「快取寫入」就燒掉了約 1,182 美元,佔了整體估算費用的 85%。正常情況下,快取寫入應該只發生一次,接下來大量重複呼叫都應該走便宜的「快取命中」;但這裡幾乎每次都在付最貴的「寫入」價,代表快取從頭到尾沒有真的發揮省錢效果——而使用者從 Codex CLI 的操作介面上完全看不出任何錯誤訊息,直到月底看帳單才會發現不對勁。

🎯 為什麼值得你花時間

帳單正常≠系統正常Codex CLI 操作起來完全順暢、沒有跳出任何錯誤,問題只會出現在月底的 AWS 帳單上。用 agentic coding 工具搭配雲端代理計費時,「介面沒報錯」不能當作「沒有問題」的證據。
重複利用的設計,也可能反過來變成懲罰提示詞快取的設計本來是要獎勵「重複使用穩定內容」,讓大量重複呼叫變便宜;但只要中間少一個標記欄位,同樣的重複使用模式反而會變成「每次都付最貴的寫入價」,設計立意跟實際結果完全相反。
多一層代理,就多一個對不齊的風險點同一套工具(Codex CLI)接官方 API 沒事,接 AWS Bedrock 就出包,因為 Bedrock 這層代理目前只開放連線和驗證設定,沒開放請求內容的客製化。選擇透過雲端代理層串接 AI 服務時,功能對齊永遠可能慢半拍,要有心理準備主動驗證。

⚙️ 它是怎麼運作的

1
組裝提示詞Codex 把「系統指令、工具定義」這些每次都一樣的部分,跟「這次要做的任務、對話紀錄」這些會變動的部分,兜成一份完整的請求內容。
2
標記快取斷點(正常流程應該做的事)在「系統指令、工具定義」結尾插入一個標記,告訴模型代理層:「這一段到此為止是穩定內容,可以存起來重複用」。
3
第一次呼叫:付快取寫入的錢把這段標記過的內容存進快取,這一次要多付一筆「快取寫入」費用,比一般輸入貴,但理論上只需要付這一次。
4
之後呼叫:改付快取命中的錢只要同一段穩定內容再出現,之後每次呼叫都能改用便宜很多的「快取命中」價格,通常只要原價的一到兩成。
5
Bedrock 路徑上少了「標記」這一步文章指出,Codex 送給 Bedrock 的請求裡沒有 prompt_cache_options 和 prompt_cache_breakpoint 這兩個欄位,代理層完全收不到「這段要存起來」的訊號。
6
代理層只好每次都當成全新內容沒有標記,Bedrock 沒辦法判斷哪一段是可以重複使用的,於是每次呼叫都重新寫入快取,導致這 85% 的帳單都花在本來應該只付一次的「寫入價」上。
這 4 天(8/5–8/8)Sol 模型的實際帳單拆解(依文章內 Cost Explorer 估算)
成本類別金額(美元)佔總費用比例
快取寫入(cache write)$1,182.09約 85%
其他(一般輸入、少量快取命中等)$204.37約 15%
總計$1,386.46100%

🔍 程式碼漫遊(點有 ● 的行看白話講解)

這段是 Codex 組請求(request body)送給 Bedrock 時的簡化示意,重點在對照「官方文件建議要加的兩個欄位」跟「Codex 實際上少加的部分」。

request_body = {
"model": "openai.gpt-5.6-sol",
"input": build_input_blocks(system_prompt, tool_defs, user_turns),
💬 把系統指令、工具定義(穩定不變)跟這次的任務內容兜成一份請求
"prompt_cache_key": session_id, # Codex 已經有帶這個
💬 這個欄位只是「幫同一個對話分組」,不等於「開啟快取」,容易被誤以為快取已經生效
# 下面兩個欄位才是真正「叫代理層存快取」的開關——
# "prompt_cache_options": {"type": "explicit"},
💬 缺這行:代理層不知道你要用「明確快取」模式
# "prompt_cache_breakpoint": True, # 標記在穩定前綴的結尾
💬 缺這行:代理層不知道「存到哪裡為止」
}
response = bedrock_client.invoke(request_body)
💬 少了上面兩行,Bedrock 每次都把整段內容當「全新的」,只能一直付最貴的寫入價

🛠️ 動手做:快取斷點模擬器:同一份提示詞,多送幾次要多少錢?

  1. 一開始「快取斷點」是打勾的,按下第一次「送出一次呼叫」,你會看到它一定要先付一次比較貴的「寫入」價——這就是快取第一次一定要先存一次的意思
  2. 接著連續按「送出一次呼叫」5-6 次,觀察後面每一次是不是都變成便宜的「快取命中」
  3. 按「重設」,把「快取斷點」取消打勾,再連續送出同樣次數,觀察累積花費差多少——這正是 Codex 在 Bedrock 上實際發生的情況:少了那個「斷點」標記,永遠停在最貴的「寫入」價
  4. 比較兩種情境下的「累積花費」數字,感受一下為什麼文章作者統計出「快取寫入佔了 85% 帳單」
👇 下面是活的,直接操作

🧠 工程思維透鏡(資深工程師看到的是什麼)

🔭 為什麼快取寫入要收比較貴的錢,而不是乾脆全部免費?
存快取需要模型代理層在背後把這段內容的運算狀態保留在記憶體或儲存中,這是有實質基礎設施成本的。用「寫入貴、讀取便宜」的定價,是把「你選擇讓內容重複使用」的成本轉嫁給使用方一次性付掉,換取之後大量重複呼叫時整體變便宜——這是一種「攤提」的定價邏輯,前提是你的用量模式真的有大量重複。像 Codex 這次一樣「每次都被迫重寫」,攤提就完全落空,反而變成純虧錢。
🔭 同樣是「相容官方 API 的雲端代理層」,為什麼 Bedrock 這條路會漏掉這個功能,而原生 API 不會?
這反映了「多層轉譯」的常見陷阱——Codex CLI 呼叫的不是模型公司自己的端點,而是先經過 AWS Bedrock 這層代理,Bedrock 再轉呼叫模型。每多一層轉譯,就多一個「這個欄位有沒有被正確傳遞下去」的風險點;Bedrock 的設定介面目前只開放連線和驗證層級的設定,沒開放請求內容要怎麼組的客製化,等於把「要不要用快取」這個決定權,卡在 Codex 官方要不要修這段程式碼,使用者自己完全沒有繞過的空間。這是選擇「用代理層省事」時要付出的隱性代價:功能對齊永遠慢半拍。
🔭 文章作者要求 Codex 在 telemetry 裡秀出每次呼叫的快取讀寫量,這個要求的工程價值是什麼?
這其實是在要「可觀測性 (observability)系統能不能讓開發者清楚看到內部實際發生了什麼,方便判斷問題出在哪裡」,而不是直接要「修好」——因為快取行為牽涉到很多因素(冷啟動、prompt 內容真的變了、對話被截斷重組等),不是每次「寫入」都算 bug。與其每次都用猜的,不如先讓系統「講清楚每次呼叫實際發生了什麼」,開發者才能自己判斷這次寫入是合理的還是不合理的。這是很典型的除錯優先順序:先讓問題「看得見」,才有辦法決定該怎麼「修」。

📝 隨堂考(點選答案,立即回饋)

Q1. 「快取寫入」跟「快取命中」相比,通常哪一個比較貴?
✅ 快取寫入是「第一次把內容存起來」的成本,快取命中則是「重複利用已存內容」,所以命中通常便宜很多。
Q2. 這次 Codex 在 AWS Bedrock 上出問題,最根本的原因是什麼?
✅ 文章指出 Codex 已經有帶 prompt_cache_key,但缺少 prompt_cache_options / prompt_cache_breakpoint,代理層因此拿不到「這段要存起來」的訊號。
Q3. 為什麼文章特別強調「快取寫入佔了帳單的 85%」?
✅ 正常有效使用快取時,寫入應該只發生一次、之後大量呼叫都走便宜的讀取;佔比高達 85% 代表幾乎沒有「讀取」在發生,快取形同虛設。
Q4. 如果你自己在串接 LLM API 時想避免類似的帳單意外,下面哪個做法最直接?
✅ 對照「寫入 vs 讀取」的實際量,是判斷快取到底有沒有生效最直接的方法,這正是文章作者要求 Codex 加上的 telemetry。

🃏 翻牌記憶卡(先想答案,再點開對答)

快取寫入(cache write)點我翻面
第一次把穩定內容存進快取要付的較高費用
快取命中(cache read)點我翻面
之後重複用同一段已存內容,改付的較低費用
快取斷點(cache breakpoint)點我翻面
標記「穩定內容到這裡結束」,告訴代理層這段可以存起來重複用的訊號
Agentic coding(代理式編碼)點我翻面
讓 AI 連續自主執行多步驟、呼叫工具完成任務的工作模式
這次 Bedrock bug 的根本原因點我翻面
Codex 送給 Bedrock 的請求少了標記快取斷點的欄位,代理層拿不到「可以存快取」的指示
為什麼要看快取讀寫量點我翻面
用來判斷快取到底有沒有真的省到錢,而不是只看有沒有錯誤訊息

✅ 離開前自測(全勾=這課真的學會了)

0%