mcpField
C
cost-accrual-tracker

cost-accrual-tracker

github.com/curiositech開発257164 回インストール2 日前に更新
リポジトリ

cost-accrual-tracker について

LLM実行中にAPIコストを積算する実装スキルです。実行パイプラインやコスト表示を対象に、中断時の部分コスト記録と予算超過時の自動停止方法を扱います。

主な機能

  1. 01トークン使用量からコストを算出
  2. 02実行中にコストを段階的に積算
  3. 03中断時の部分コストを記録
  4. 04予算しきい値による自動停止
  5. 05複数の実行ノードのコストを集計

活用シーン

  1. 01実行画面でリアルタイムコストを表示
  2. 02中断時に発生済みコストを保持
  3. 03予算超過時にタスクを停止
  4. 04DAG内の複数ノードのコストを集計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/curiositech/some_claude_skills --skill cost-accrual-tracker

インストール後、Claude Code で/cost-accrual-trackerを入力して読み込まれたか確認します。

同梱ファイル.claude-plugin/plugin.jsonreferences/claude-api-pricing.md

詳細

コスト累積トラッカー

LLM の実行中に API コストをリアルタイムで追跡し、中断時には部分的なコストにも対応します。

使用するタイミング

✅ 次の場合に使用します:

  • 実行中のリアルタイムコスト追跡を実装する
  • 実行が中断された場合に部分的なコストを取得する
  • 実行 UI 用のコスト表示ウィジェットを構築する
  • 実行パイプラインにトークンカウントを統合する
  • 自動停止機能付きの予算しきい値を追加する

❌ 次の場合には使用しません:

  • 実行前のコスト見積もり(料金計算ツールを使用してください)
  • 課金システムの設計(billing-system スキルを使用してください)
  • 価格帯の管理や割引
  • 過去のコスト分析ダッシュボード

主なパターン

1. トークンベースのコスト計算
interface TokenUsage {
  inputTokens: number;
  outputTokens: number;
  cacheReadTokens?: number;   // Prompt caching hits
  cacheWriteTokens?: number;  // Prompt caching misses
}

interface CostCalculation {
  inputCostUsd: number;
  outputCostUsd: number;
  cacheSavingsUsd?: number;
  totalCostUsd: number;
}

function calculateCost(usage: TokenUsage, model: string): CostCalculation {
  const pricing = MODEL_PRICING[model];

  const inputCostUsd = (usage.inputTokens / 1_000_000) * pricing.inputPerMTok;
  const outputCostUsd = (usage.outputTokens / 1_000_000) * pricing.outputPerMTok;

  return {
    inputCostUsd,
    outputCostUsd,
    totalCostUsd: inputCostUsd + outputCostUsd,
  };
}
2. 増分累積パターン

完了時だけでなく、コストが発生するたびに追跡します:

class CostAccrualTracker {
  private totalInputTokens = 0;
  private totalOutputTokens = 0;
  private accruedCostUsd = 0;
  private readonly model: string;

  constructor(model: string) {
    this.model = model;
  }

  /**
   * Called after each API response (streaming or complete)
   */
  recordUsage(usage: TokenUsage): void {
    this.totalInputTokens += usage.inputTokens;
    this.totalOutputTokens += usage.outputTokens;

    const cost = calculateCost(usage, this.model);
    this.accruedCostUsd += cost.totalCostUsd;
  }

  /**
   * Get current accrued cost (for real-time display)
   */
  getCurrentCost(): number {
    return this.accruedCostUsd;
  }

  /**
   * Finalize on completion or abort
   */
  finalize(reason: 'completed' | 'aborted' | 'failed'): CostReport {
    return {
      totalInputTokens: this.totalInputTokens,
      totalOutputTokens: this.totalOutputTokens,
      totalCostUsd: this.accruedCostUsd,
      completionReason: reason,
      finalizedAt: Date.now(),
    };
  }
}
3. 中断対応のコスト取得

重要:中断時には、必ず部分的なコストを取得してください:

// In execution handler
const tracker = new CostAccrualTracker(model);

try {
  for await (const chunk of executeStream(request)) {
    if (abortSignal.aborted) {
      // CRITICAL: Capture cost BEFORE throwing
      const partialCost = tracker.finalize('aborted');
      onCostUpdate(partialCost);
      throw new AbortError('Execution aborted');
    }

    tracker.recordUsage(chunk.usage);
    onCostUpdate(tracker.getCurrentCost());
  }

  return tracker.finalize('completed');
} catch (error) {
  if (error instanceof AbortError) {
    throw error; // Already handled
  }
  return tracker.finalize('failed');
}
4. 予算しきい値パターン

予算を超過したら実行を自動停止します:

interface BudgetConfig {
  maxCostUsd: number;
  warnAtPercentage: number;  // e.g., 0.8 for 80%
  onWarn?: (current: number, max: number) => void;
  onExceed?: (current: number, max: number) => void;
}

function createBudgetGuard(config: BudgetConfig) {
  return {
    check(currentCostUsd: number): 'ok' | 'warn' | 'exceed' {
      const percentage = currentCostUsd / config.maxCostUsd;

      if (percentage >= 1.0) {
        config.onExceed?.(currentCostUsd, config.maxCostUsd);
        return 'exceed';
      }

      if (percentage >= config.warnAtPercentage) {
        config.onWarn?.(currentCostUsd, config.maxCostUsd);
        return 'warn';
      }

      return 'ok';
    }
  };
}

アンチパターン

中断時に失われるコスト

初心者の考え:「中断時にエラーを投げればよい」

実際には:中断前にコストを取得しないと、次の情報が失われます:

  • 実行途中までのトークン使用量データ
  • 課金の正確なコスト報告
  • デバッグ用の監査証跡

経緯:以前から存在する問題ですが、高価なモデル(GPT-4、Claude Opus)の登場で深刻になりました。

正しい方法:中断エラーを投げる前に、部分データを指定して必ず finalize() を呼び出してください。

デバウンスなしのポーリング

初心者の考え:「リアルタイム更新のため、コストエンドポイントを100ミリ秒ごとにポーリングする」

実際には:

  • 帯域幅と CPU を浪費します
  • コストが更新されるのは API の応答後だけです
  • 応答レートを上回る頻度のポーリングには意味がありません

正しい方法:1~2秒間隔でポーリングするか、実行ストリームからのイベント駆動型更新を使用してください。

プロンプトキャッシュを無視する

初心者の考え:「トークン数にトークン単価を掛ければよい」

実際には:Claude のプロンプトキャッシュによってコストモデルが変わります:

  • キャッシュ読み取りは90%安くなります
  • 初回使用時にはキャッシュ書き込みに追加料金がかかります
  • キャッシュを無視すると、コストが不正確になります

経緯:

  • 2024年以前:キャッシュなし。計算はシンプルでした
  • 2024年以降:Claude のプロンプトキャッシュには個別の追跡が必要です

正しい方法:cache_read_input_tokens と cache_creation_input_tokens を個別に追跡してください。

リクエストごとのコストオブジェクト

初心者の考え:「リクエストごとに新しいトラッカーを作成する」

実際には:複数のノードを含む DAG の実行では、次のことが必要です:

  • すべてのノードのコストを集計する
  • 特定のノードにコストを割り当てる
  • 親実行に対してコストを集計する

正しい方法:階層型の追跡を使用し、ノードごとのトラッカーから実行レベルへ集計します。

状態の流れ

                    ┌─────────────────────────────────────────┐
                    │           CostAccrualTracker            │
                    └─────────────────────────────────────────┘
                                        │
              ┌─────────────────────────┼─────────────────────────┐
              │                         │                         │
              ▼                         ▼                         ▼
    ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
    │  recordUsage()  │     │ getCurrentCost()│     │   finalize()    │
    │                 │     │                 │     │                 │
    │ After each API  │     │ For real-time   │     │ On completion,  │
    │ response        │     │ display         │     │ abort, or fail  │
    └─────────────────┘     └─────────────────┘     └─────────────────┘
              │                         │                         │
              │                         │                         │
              ▼                         ▼                         ▼
    ┌─────────────────────────────────────────────────────────────────┐
    │                        CostReport                               │
    │  { inputTokens, outputTokens, totalCostUsd, completionReason }  │
    └─────────────────────────────────────────────────────────────────┘

UI 表示パターン

実行 UI でコストをリアルタイムに表示する場合:

// Poll every 2 seconds while executing
useEffect(() => {
  if (status !== 'running') return;

  const interval = setInterval(async () => {
    const response = await fetch(`/api/execute/${executionId}`);
    const data = await response.json();
    setAccruedCost(data.cost.accruedUsd);
    setTokens({
      input: data.cost.inputTokens,
      output: data.cost.outputTokens,
    });
  }, 2000);

  return () => clearInterval(interval);
}, [executionId, status]);

// Display format
<div className="cost-display">
  <span className="cost-amount">${accruedCost.toFixed(4)}</span>
  <span className="token-count">
    {tokens.input.toLocaleString()} in / {tokens.output.toLocaleString()} out
  </span>
</div>

統合ポイント

コンポーネント担当
CostAccrualTracker実行ごとのトークンカウントとコスト計算
ExecutionManagerDAG 実行全体のコスト集計
BudgetGuardしきい値の監視と自動停止
/api/execute/:idポーリングによる現在のコストの公開
コスト表示ウィジェットリアルタイムの UI 描画

参考資料

現在の Claude API 料金については、/references/claude-api-pricing.md を参照してください。

コメント