成本、速率限制與錯誤處理
程式要真正拿來用,就得知道每次呼叫花多少錢、被限速時怎麼辦、出錯時怎麼不崩潰。這堂課教你從 token 估算費用,並替程式加上逾時、重試與錯誤處理。
前面五堂課,你已經能呼叫模型、維持對話、串流顯示、拿回結構化資料。這些程式在自己電腦上試跑沒問題,但只要開始處理大量資料或給別人使用,就會遇到三個現實問題:
- 這樣跑下去,要花多少錢?
- 一次送太多請求,被供應商擋下來怎麼辦?
- 網路斷線或伺服器忙碌時,程式會不會直接當掉?
這堂課就來一一處理。
費用從 token 算起
還記得第二堂看過的 usage 嗎?它就是帳單的依據。模型供應商依照 token 數收費,而且輸入和輸出的單價不同,輸出通常比較貴。
單價會隨模型和時間調整,請以供應商官方定價頁為準。
單價通常寫成「每百萬 token 多少美元」。去官方定價頁查到你使用的模型單價後,就能寫一個小函式來算:
# 到官方定價頁查詢後填入(單位:美元/每百萬 token)
INPUT_PRICE_PER_M = 0.0 # 輸入單價
OUTPUT_PRICE_PER_M = 0.0 # 輸出單價
def estimate_cost(usage) -> float:
return (
usage.input_tokens * INPUT_PRICE_PER_M
+ usage.output_tokens * OUTPUT_PRICE_PER_M
) / 1_000_000
每次呼叫完,把 estimate_cost(response.usage) 印出來或記錄到檔案,你就能清楚知道錢花在哪裡。
送出前先估算
如果要處理一大批文件,可以先算一下輸入有多少 token,不用真的送出請求:
from dotenv import load_dotenv
import anthropic
load_dotenv()
MODEL = "claude-sonnet-5-5" # 可以換成其他模型 ID
client = anthropic.Anthropic()
with open("report.txt", encoding="utf-8") as f:
text = f.read()
count = client.messages.count_tokens(
model=MODEL,
messages=[{"role": "user", "content": f"請摘要以下內容:\n\n{text}"}],
)
print("輸入 token 數:", count.input_tokens)
不同模型的 token 算法不完全一樣,所以要用你實際會使用的模型來計算。中文、程式碼和英文的 token 數也不一樣,與其猜,不如實際算一次。
省錢的三個習慣
- 先用少量資料試跑:例如先處理 5 份文件,看用量和結果都滿意,再跑全部。
- 只送需要的內容:長文件先挑出相關段落,不必整份貼進去。
- 選適合的模型:簡單的分類或抽取,較小、較便宜的模型可能就夠用。換模型只要改
MODEL那一行。
速率限制:一次別送太快
供應商會限制你每分鐘能送多少請求、多少 token,這叫速率限制(rate limit)。超過時,API 會回傳狀態碼 429,意思是「太多請求了,請稍後再試」。
這不是你的程式壞了,只是要放慢一點。帳號用量越高,額度通常也會跟著提高,詳細數字可以在主控台的限制頁面查到。
錯誤處理:分清楚「等一下」和「要修改」
重試只對暫時性問題有用。參數寫錯,重試一百次也是錯。
好消息是,Anthropic 的 SDK 預設就會自動重試暫時性錯誤幾次,而且每次等待時間會逐漸拉長。你可以在建立用戶端時調整:
client = anthropic.Anthropic(
max_retries=4, # 最多自動重試幾次,預設 2
timeout=60.0, # 單次請求最多等幾秒
)
自動重試之後還是失敗,SDK 會丟出例外。依照錯誤類型分別處理,程式就不會直接崩潰:
try:
response = client.messages.create(
model=MODEL,
max_tokens=16000,
messages=[{"role": "user", "content": "你好"}],
)
except anthropic.AuthenticationError:
print("金鑰無效:檢查 .env 裡的 ANTHROPIC_API_KEY")
except anthropic.NotFoundError:
print("找不到模型:檢查 MODEL 的拼字")
except anthropic.BadRequestError as e:
print("請求參數有誤:", e.message)
except anthropic.RateLimitError:
print("被限速了,自動重試後仍失敗,請稍後再跑")
except anthropic.APITimeoutError:
print("等太久沒有回應,可以調高 timeout 或改用串流")
except anthropic.APIConnectionError:
print("網路連線有問題")
except anthropic.APIStatusError as e:
print(f"API 錯誤(狀態碼 {e.status_code}):", e.message)
順序很重要:比較具體的錯誤寫在前面,最廣泛的 APIStatusError 放最後,否則它會先把所有錯誤都接走。
批次處理時的小技巧
處理一整個資料夾的檔案時,把 try 包在每一個檔案外面,而不是整個迴圈外面。這樣一個檔案失敗,其他檔案還能繼續,最後再列出哪些失敗、重跑那幾個就好。
其他供應商也有一樣的觀念:按 token 計費、有速率限制、會回傳 429 和 5xx。SDK 的例外名稱不同,但分辨「重試」還是「修改」的思路完全相同。
我有一支程式會用 Claude API 逐一摘要 docs 資料夾裡的 .txt 檔。請幫我加上:每個檔案各自的錯誤處理、失敗的檔名記錄到 failed.txt、每次呼叫的輸入與輸出 token 累加起來,最後印出總用量。單價請留成常數讓我自己填。
- 到你使用的供應商官方定價頁,查出你使用模型的輸入與輸出單價,填進
estimate_cost。 - 拿上一堂的訂單抽取程式,每次呼叫後印出估算費用,算算看處理 1000 筆訂單大約要多少錢。
- 用
count_tokens計算一份你手邊的長文件有多少 token。 - 故意把
MODEL改成一個不存在的名稱,確認你的錯誤處理有印出清楚的中文提示,而不是一大串錯誤訊息。
讀完了嗎?
標記完成後,單元與專案的進度會自動更新(只存在這台裝置的瀏覽器)。
留言與討論
卡關了、有心得,或發現內容需要更新?都歡迎留言。留言使用 GitHub 帳號登入。