AI業務活用 2026.05.01

Claude API暴走で$437請求された実例|費用爆発の原因と防止策5つ

タグ:Claude API / AI料金 / コスト管理 / エージェント / トークン計算

実際に起きた$437請求の原因分析

# ❌ これが$437請求を引き起こしたコードのパターン

import anthropic
import time

client = anthropic.Anthropic()

def bad_agent_loop(task: str):
    """やってはいけない実装例: 無限ループ+エラー時のリトライ"""
    messages = []
    
    while True:  # ← 終了条件なし
        try:
            response = client.messages.create(
                model="claude-sonnet-4-5",
                max_tokens=10000,  # ← 大きすぎる上限
                messages=messages + [{"role": "user", "content": task}]
            )
            messages.append({"role": "assistant", "content": response.content[0].text})
            # ループ継続...(終了条件なし)
            
        except Exception:
            time.sleep(1)
            continue  # ← エラーでも無限リトライ

防止策1: Usage Limits(最重要)

console.anthropic.com → Settings → Limits → Usage Limits

設定値の目安:
- 個人開発: Hard Limit $20/月、Soft Limit $10/月
- チーム開発: Hard Limit $100/月、Soft Limit $50/月
- 本番サービス: Hard Limit $500/月、Soft Limit $250/月

この設定だけで「一晩で$437」は防げます。

防止策2: 安全なエージェント実装

# safe_agent.py
# 費用暴走を防ぐ安全なエージェントの実装

import anthropic
import time
from datetime import datetime

client = anthropic.Anthropic()

DAILY_BUDGET = 5.0   # $5/日の上限
MAX_ITERATIONS = 20  # 最大20ループ
MAX_TOKENS = 1024    # 出力上限

PRICES = {
    "claude-sonnet-4-5": (3.00, 15.00),
    "claude-haiku-4-5-20251001": (0.80, 4.00),
}

class SafeAgent:
    def __init__(self, model: str = "claude-haiku-4-5-20251001"):
        self.model = model
        self.total_cost = 0.0
        self.iterations = 0
    
    def ask(self, message: str) -> str | None:
        """費用・ループ回数の上限チェック付きAPIコール"""
        
        # ① 予算チェック
        if self.total_cost >= DAILY_BUDGET:
            print(f"⛔ 予算上限${DAILY_BUDGET}に達しました。処理を停止します。")
            return None
        
        # ② ループ回数チェック
        if self.iterations >= MAX_ITERATIONS:
            print(f"⛔ 最大反復回数({MAX_ITERATIONS}回)に達しました。")
            return None
        
        try:
            response = client.messages.create(
                model=self.model,
                max_tokens=MAX_TOKENS,  # ③ 出力上限を必ず設定
                messages=[{"role": "user", "content": message}]
            )
            
            inp = response.usage.input_tokens
            out = response.usage.output_tokens
            inp_price, out_price = PRICES.get(self.model, (3.00, 15.00))
            cost = (inp * inp_price + out * out_price) / 1_000_000
            
            self.total_cost += cost
            self.iterations += 1
            
            print(f"[Iteration {self.iterations}] コスト: ${cost:.5f} | 累計: ${self.total_cost:.4f}")
            return response.content[0].text
            
        except Exception as e:
            print(f"[Error] {e}")
            time.sleep(5)  # ④ エラー時は5秒待ってから終了
            return None
    
    def summary(self):
        print(f"\n実行完了: {self.iterations}回 | 合計コスト: ${self.total_cost:.4f}")

# 使用例
agent = SafeAgent(model="claude-haiku-4-5-20251001")

for task in ["タスク1", "タスク2", "タスク3"]:
    result = agent.ask(task)
    if result is None:
        break  # ← 必ず中断する

agent.summary()

防止策3: Slackコスト通知

# cost_monitor.py
# APIコストをリアルタイムでSlackに通知する

import json
import urllib.request
from datetime import date
from pathlib import Path

SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
ALERT_THRESHOLD = 10.0  # $10超えたら通知

cost_log = Path(".api_cost_log.json")

def alert_slack(message: str):
    """Slackにアラートを送る"""
    payload = json.dumps({"text": message}).encode()
    req = urllib.request.Request(SLACK_WEBHOOK, data=payload,
                                  headers={"Content-Type": "application/json"})
    urllib.request.urlopen(req)

def check_daily_cost():
    """日次コストをチェックしてアラートを送る"""
    if not cost_log.exists():
        return
    
    log = json.loads(cost_log.read_text())
    today_cost = log.get(str(date.today()), {}).get("total", 0)
    
    if today_cost >= ALERT_THRESHOLD:
        alert_slack(f"⚠️ Claude API コスト警告: 本日 ${today_cost:.2f} を超えました。\n上限: ${ALERT_THRESHOLD}/日")
        print(f"Slackに通知送信: ${today_cost:.2f}")

check_daily_cost()

あわせて読みたい

参考ソース