Prompt Caching:讓 Claude 別再重讀同一份文件
那天在調一個客服問答的小工具。一份 48 頁的產品手冊塞進 system prompt,使用者每問一句,我就把整包手冊加問題丟給 API。功能是會動,但兩件事很快就不對勁。
第一,帳單。同一份手冊,一天被重複送進去幾百次,每次都照全額計費。第二,延遲。使用者按下送出到看見第一個字,中間那三四秒的空白,長到讓人以為連線斷了。
我一開始的判斷是「模型太慢」。於是我做了兩件很直覺、但後來證明都搞錯方向的事。
先講我試錯的兩條路
第一條路:把 prompt 縮短。我開始刪手冊,砍掉我覺得「應該用不到」的章節。結果呢?使用者一問到被我砍掉的那段,答案就開始亂編。原來那些內容全都有用,只是我沒本事判斷哪句話哪天會被問到。刪 prompt 等於賭運氣,而且我一定賭輸。
第二條路:換小模型。想說便宜的模型跑得快、算得省。但手冊本身沒變短,該讀的 token 一個都沒少,小模型只是讀得更吃力、答得更心虛。延遲沒改善多少,準確率倒是先掉了。
繞了一大圈才發現,我盯錯了地方。真正貴的、真正慢的,不是「模型思考」這一步,是「每次都把那 48 頁重新算一遍」這一步。
打個比方。你去便利商店影印一份 48 頁的合約,印一百份。影印機第一次得把每一頁掃描進感光鼓,這一步慢、耗電;但第二份、第三份、第一百份,機器根本不重掃,直接拿掃好的影像印出來。真正花力氣的是第一次掃描,後面全是「重印」。
我的程式當時在做的事,等於是每印一份就重掃一次全部 48 頁。荒謬,但我沒意識到。
快取切進來的位置
Prompt Caching 要解的就是這件事——讓那份長文件「掃描一次,之後重讀」。
機制其實不難懂。你在 prompt 裡某個穩定內容的結尾,插一個叫做快取斷點的標記:
1 | system=[ |
cache_control 就是那個標記,ephemeral 是它的類型(意思是「短暫的、會過期的」)。
第一次帶著這個標記呼叫,API 會把斷點前的所有內容算過一遍,然後把結果存進快取——這一步叫快取寫入,會多收一點錢,大約是原價的 1.25 倍。聽起來像虧,但它是投資。
從第二次呼叫開始,只要斷點前的內容一模一樣,API 就不重算了,直接讀快取。這一步叫快取命中讀取,價格大約只有原價的 0.1 倍。48 頁手冊的成本,從「每次全額」變成「第一次 1.25 倍、後面每次一折」。延遲也跟著掉,因為省掉的正是那個最慢的重算步驟。
重算變重讀。開場那兩個問題,一次解決。
但這裡有個非講不可的坑
影印機的比喻很好用,可是它藏了一個關鍵細節,不講清楚你一定會踩。
影印機認的是「這一頁跟剛剛那頁一不一樣」。快取更嚴格:它認的是前綴,而且是逐位元組地認。
什麼意思?API 組 prompt 的順序是固定的:工具定義 → system prompt → 對話訊息。快取比對是從最前面開始一路往後對,一旦某個位置的內容變了,那個位置之後的快取就全部作廢,得重算。
所以最經典的災難是這樣的:有人在 system prompt 開頭寫「現在時間是 2026-07-14 09:30:12」。這行字每一秒都在變。它排在最前面,於是後面那 48 頁手冊,無論你標記下得多漂亮,永遠對不上前一次的前綴,永遠在重算。你付了快取寫入的錢,卻一次都沒命中讀取。
心智圖記這一句就夠了:穩定的放前面,會變的放後面。 凍結不動的 system prompt、固定的工具清單、那份長文件,通通排在斷點之前;時間戳、每次都不同的使用者問題、隨機 ID,一律丟到斷點之後去。
還有幾個邊界,一次講完:
- 太短的內容不會快取。門檻依模型不同,大約落在 1024 到 4096 token 之間,低於門檻就算你下了標記也不會生效,而且不報錯——它就是靜靜地不快取。
- 快取會過期。預設存活 5 分鐘,每次命中會續命。流量有長間隔時,可以把它延長到 1 小時,但延長的寫入成本會變成原價 2 倍,得多幾次命中才划算。
- 一次最多下 4 個斷點。
- 別在對話中途換模型或改工具清單。工具排在最前面,動一根寒毛,整個快取從頭作廢。
怎麼確認它真的在動
這是我學到最實在的一課:不要用「感覺帳單好像有變少」來判斷。回傳結果裡有一個欄位 usage.cache_read_input_tokens,它老實告訴你這次呼叫從快取讀了多少 token。
第一次呼叫,這個數字是 0,cache_creation_input_tokens(寫入)會是一大包——那是掃描費。第二次開始,如果一切正確,讀取數字應該接近你手冊的大小,寫入變成 0。
如果你重複呼叫、前綴照理說沒變,這個讀取數字卻一直是 0,那就別懷疑了,一定有個「隱形破壞者」躲在前綴裡偷偷改東西。這時候最笨也最有效的辦法:把兩次呼叫實際送出去的 prompt 印出來,逐字元 diff,那個一直在變的傢伙自己會現形。通常就是某個 datetime.now(),或是一段沒排序、每次鍵順序都不同的 JSON。
什麼時候該用,什麼時候別用
拆到這裡,判斷其實收斂成很乾淨的兩條線:這段內容夠不夠大、會不會重複用。
兩個都成立,就快取——長 system prompt、幾十頁的文件、一長串固定的工具定義、few-shot 範例,這些是天生的快取對象,重複呼叫越多次,省得越兇。
有任何一個不成立,就別碰。內容每次都從頭不一樣的(比如每次貼一段全新的短文請它摘要),沒有可重用的前綴,下了標記只會白付那 1.25 倍寫入費,一次都讀不回來。內容太短、低於門檻的,省下來的也是零頭,不值得為它增加程式複雜度。
回到開場那個客服工具。現在我會怎麼做?手冊凍結成穩定的 system 區塊,結尾下一個斷點;使用者的問題,放在斷點後面的訊息裡,愛怎麼變怎麼變。第一個使用者付掃描費,之後每個人都在吃第一次留下的快取。帳單掉下來,那三四秒的空白也縮回一秒內。
我當初繞的那兩條冤枉路——縮 prompt、換小模型——錯的不是方法本身,是我連問題出在哪都沒定位。複雜度一定來自某個地方,你得先找出那個地方,而不是對著症狀亂投藥。這個工具真正的瓶頸從來不是「模型慢」,是「同一份東西被重算了幾百遍」。看懂這一句,剩下的都只是把斷點放對位置而已。



























































