AI活用11分で読めます

LLMアプリのコスト最適化設計|キャッシュ・モデル振り分け・トークン削減の実装と外注判断

コセケン

コセケン

テクラル合同会社

#LLMコスト最適化#プロンプトキャッシュ#モデルルーティング#トークン削減#生成AI#AI活用#API設計#コスト管理
LLMアプリのコスト最適化設計|キャッシュ・モデル振り分け・トークン削減の実装と外注判断

LLMアプリのコストは、プロンプトキャッシュ・モデル振り分け・トークン削減・API上限設計という4つの施策を組み合わせることで、機能を落とさずに構造的に抑えられます。コストが暴走するLLMアプリには、利用が増えるほど料金が線形(あるいはそれ以上)に膨らむ「設計上の欠陥」が共通して存在します。逆に言えば、暴走は気合や節約意識ではなく設計で止めるべき問題です。

本記事では、コストが膨らむ構造を分解したうえで、各施策の実装方法と効果、そして本番運用に乗せる際の「内製で持つか、設計ごと外注するか」の判断軸までを、実装の解像度で解説します。料金の具体値は陳腐化しやすいため、必ず各社の公式料金ページで最新値を確認してください(本記事の数値は2026年6月時点の公開情報に基づきます)。

LLMアプリのコストが暴走する3つの構造的原因

コスト暴走の原因は、ほぼ「入力トークンの肥大化」「過剰なモデル選定」「無制限なリトライ・ループ」の3つに集約されます。施策に入る前に、自分のアプリがどこで出血しているかを構造で特定することが先決です。

第一に、入力トークンの肥大化です。LLMの料金は入力・出力のトークン量に比例します。会話履歴をすべて毎回送る、RAGで検索したドキュメントを丸ごと詰め込む、長大なシステムプロンプトを毎リクエスト送る、といった実装は、1リクエストあたりの入力トークンを際限なく押し上げます。多くのモデルで入力より出力のほうが単価が高い一方、実運用では入力トークンの総量が支配的になりやすく、ここが最大の削りどころです。

第二に、過剰なモデル選定です。分類・要約・定型抽出のような単純タスクにまで最上位モデルを使うと、単価の高さがそのまま積み上がります。タスクの難易度とモデルの能力を合わせる「振り分け」がないと、すべてのリクエストが最高単価で処理されます。

第三に、無制限なリトライ・エージェントループです。ツール呼び出しを繰り返すエージェントや、失敗時に自動リトライする処理は、上限を設けないと1タスクが何十回ものAPI呼び出しに膨らみます。1件の異常リクエストが想定の数十倍のコストを出すのは、たいていここが原因です。

構造的原因 主な発生箇所 効く対策
入力トークンの肥大化 会話履歴・RAG文脈・固定プロンプト プロンプトキャッシュ/トークン削減
過剰なモデル選定 単純タスクへの最上位モデル使用 モデル振り分け(ルーティング)
無制限なループ・リトライ エージェント・自動リトライ API上限設計(task budget・max_tokens)

プロンプトキャッシュ:固定文脈の再課金を止める

プロンプトキャッシュは、毎回同じ内容を送っている「固定の前置き」部分の再計算・再課金を止める施策で、入力トークンが大きいアプリでは最も費用対効果が高いレバーです。システムプロンプト、few-shotの例、RAGで添付する固定ドキュメントなど、リクエストをまたいで変わらない先頭部分をキャッシュし、2回目以降は大幅に割り引いた単価で読み出します。

主要プロバイダーの料金構造は次の通りです。Anthropic Claudeでは、キャッシュ読み出しは通常の入力単価の約0.1倍、キャッシュ書き込みは5分TTLで1.25倍、1時間TTLで2倍です。つまり5分キャッシュなら2回読み出すだけで元が取れます。OpenAIではプロンプトキャッシュが自動で適用され、コード変更なしで1,024トークンを超えるプロンプトの先頭一致部分にキャッシュ割引が効きます。

項目 Anthropic Claude OpenAI
キャッシュ読み出し単価 通常入力の約0.1倍 キャッシュ済み入力に割引(モデルにより50〜90%)
キャッシュ書き込み 5分TTLで1.25倍/1時間TTLで2倍 追加料金なし(自動)
最小キャッシュ長 モデル依存(Claude Haiku 4.5/Opus 4.8は約4,096トークン、Sonnet 4.6は約2,048トークン以上) 1,024トークン以上
有効化 cache_control を明示的に付与 自動(コード変更不要)

最重要の原則は「キャッシュは前方一致である」という点です。先頭からの連続したバイト列が一致して初めてキャッシュが効きます。先頭1バイトでも変わると、それ以降のキャッシュはすべて無効になります。したがって、安定した内容(凍結したシステムプロンプト、決定論的に並べたツール定義)を前に、揺れる内容(タイムスタンプ、リクエスト固有ID、毎回変わる質問)を後ろに置く設計が必須です。

Anthropic SDK(TypeScript)での明示的なキャッシュ指定は次のようになります(2026年6月時点)。

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();

const response = await client.messages.create({
  model: "claude-sonnet-4-6",
  max_tokens: 1024,
  system: [
    {
      type: "text",
      text: LARGE_SHARED_SYSTEM_PROMPT, // リクエストをまたいで不変
      cache_control: { type: "ephemeral" }, // 既定は5分TTL
    },
  ],
  messages: [
    // 揺れる内容(ユーザーの質問)は cache_control の後ろに置く
    { role: "user", content: userQuestion },
  ],
});

// キャッシュが効いているかは usage で必ず検証する
console.log(response.usage.cache_creation_input_tokens); // 書き込み(1.25倍)
console.log(response.usage.cache_read_input_tokens);     // 読み出し(約0.1倍)
console.log(response.usage.input_tokens);                // 非キャッシュ(全額)

実運用での落とし穴は「効いているつもりで効いていない」状態です。cache_read_input_tokens が同一プレフィックスの繰り返しリクエストで常にゼロのままなら、システムプロンプトに Date.now() を埋め込んでいる、JSONを毎回違う順序で直列化している、リクエストごとにツールセットを変えている、といった「サイレントな無効化」が起きています。導入後は必ずこの値を監視し、ヒット率で効果を確認してください。

モデル振り分け(ルーティング):タスクに能力を合わせる

モデル振り分けは、タスクの難易度に応じて使うモデルを切り替え、単純タスクを安価なモデルに流すことで全体の平均単価を下げる施策です。すべてのリクエストを最上位モデルで処理する必要はありません。分類・要約・定型抽出は小型モデルで十分な品質が出ることが多く、複雑な推論やコード生成だけを上位モデルに回せば、品質を保ったままコストを下げられます。

振り分けの基本パターンは2段構えです。まず軽量・安価なモデル(あるいはルールベースの判定)で「このタスクは難しいか」を判定し、簡単なら安価なモデルで処理を完結させ、難しい場合だけ上位モデルにエスカレーションします。

// タスクの複雑度でモデルを振り分ける最小例
type Tier = "cheap" | "premium";

function classifyComplexity(input: string): Tier {
  // 実務ではルールベース+小型モデルでの判定を組み合わせる
  const longInput = input.length > 4000;
  const needsReasoning = /なぜ|比較して|設計|手順を考え/.test(input);
  return longInput || needsReasoning ? "premium" : "cheap";
}

const MODEL_BY_TIER: Record<Tier, string> = {
  cheap: "claude-haiku-4-5",   // 分類・要約・定型抽出
  premium: "claude-opus-4-8",  // 複雑な推論・コード生成
};

async function routeAndRun(client: Anthropic, input: string) {
  const tier = classifyComplexity(input);
  return client.messages.create({
    model: MODEL_BY_TIER[tier],
    max_tokens: tier === "premium" ? 8000 : 1024,
    messages: [{ role: "user", content: input }],
  });
}

モデルの能力と単価には大きな開きがあります。下表は2026年6月時点のClaude各モデルの公開単価(100万トークンあたり)の一例です。最新値は必ず公式料金ページで確認してください。

モデル 入力($/1M) 出力($/1M) 主な用途
Claude Haiku 4.5 1.00 5.00 分類・要約・定型抽出
Claude Sonnet 4.6 3.00 15.00 標準的なアプリ・バランス重視
Claude Opus 4.8 5.00 25.00 複雑な推論・エージェント・コード

加えて、推論モデルでは「思考の深さ」自体がコストレバーになります。たとえばClaudeの effort パラメータ(lowmediumhigh)は思考に費やすトークン量を制御し、定型業務は低い設定、正確さが要る処理は高い設定、と使い分けることで品質とコストの折り合いをつけられます。「全リクエストに最大の思考を使う」のではなく、タスクごとに必要な深さを与えるのが要点です。

振り分けで注意すべきは、誤判定のコストです。簡単と誤判定して安価モデルに流し、品質が落ちて結局ユーザーが再質問すれば、トータルでは高くつきます。判定ロジックは「外したときの損失」まで含めて評価し、エスカレーションの閾値を実データで調整する運用が必要です。

トークン削減:履歴・文脈・出力を構造的に絞る

トークン削減は、1リクエストあたりの入力・出力トークンそのものを減らす施策で、キャッシュと並行して常に効かせるべき土台です。やみくもに切り詰めるのではなく、「会話履歴」「文脈(RAG)」「出力」の3か所を構造的に管理します。

会話履歴については、全履歴を毎回送るのをやめ、古いやり取りを要約して圧縮する、あるいは関連性の低い過去ターンを削除する、といった文脈管理を入れます。多くのSDKやプラットフォームに、文脈が長くなったら自動で要約・圧縮する仕組み(コンパクション)や、古いツール結果を削除する機能(コンテキスト編集)が用意されており、これを使えば会話が伸びても入力トークンの伸びを頭打ちにできます。

RAGの文脈については、検索結果を丸ごと詰め込まず、再ランキングで上位だけに絞る、チャンクを小さくする、重複を除く、といった前処理でトークンを削ります。「とりあえず全部入れる」は精度向上にもコスト削減にも逆効果になりがちです。

出力については、max_tokens を用途に合わせて適切に設定し、構造化出力(JSON Schema指定)で冗長な説明文を生成させない、といった制御が効きます。出力単価は入力より高いことが多いため、不要な長文出力を止める効果は小さくありません。

削減対象 主な手法 補足
会話履歴 要約圧縮・古いターン削除 コンパクション/コンテキスト編集の活用
RAG文脈 再ランキング・チャンク最適化・重複除去 「全部入れる」を避ける
出力 max_tokens 設定・構造化出力 出力単価は入力より高いことが多い

トークン量の見積もりには、各社の公式トークンカウント手段を使ってください。OpenAI互換のトークナイザー(tiktoken等)でClaudeのトークンを数えると1〜2割以上ずれることがあり、コスト試算の前提が崩れます。モデルに合ったカウント方法で測ることが、削減施策の効果検証の前提になります。

API上限設計:1リクエストの暴発を止める

API上限設計は、1リクエストや1タスクが想定外に膨らむのを構造的に止める安全装置で、コスト事故を防ぐ最後の砦です。前述の3施策が「平均単価を下げる」ものだとすれば、上限設計は「最悪値を抑える」ものです。

最低限入れるべき制御は次の通りです。

  • 出力上限(max_tokens:1レスポンスの出力を上限で打ち切る。最も基本的なハードキャップです。
  • タスク予算(task budget):エージェントループ全体で使えるトークン総量をモデルに伝え、自律的にペース配分させる。エージェント型アプリで暴発を防ぐ要になります。
  • ループ回数の上限:ツール呼び出し・自動リトライの最大回数を明示し、無限ループを断つ。
  • タイムアウトとリトライ回数:ネットワーク再試行が無制限に走らないよう、リトライ回数と総待ち時間に上限を設ける。
// エージェントループにタスク予算とループ上限を設ける例(2026年6月時点・ベータ機能を含む)
const MAX_ITERATIONS = 5; // ループ回数のハードキャップ

let iterations = 0;
while (iterations < MAX_ITERATIONS) {
  const response = await client.beta.messages.create({
    model: "claude-opus-4-8",
    max_tokens: 8000, // 1レスポンスの出力上限
    betas: ["task-budgets-2026-03-13"],
    output_config: {
      effort: "high",
      // タスク全体のトークン予算をモデルに通知し自己制御させる
      task_budget: { type: "tokens", total: 64000 },
    },
    messages,
  });

  if (response.stop_reason === "end_turn") break;
  // ...ツール実行と結果の追記...
  iterations++;
}

運用面では、これらの上限に加えて「1ユーザー・1日あたりの利用上限」や「コストアラート」をアプリ側に実装し、異常な利用パターンを早期に検知・遮断する仕組みが要ります。プロバイダー側の制御だけでなく、自社アプリの認可レイヤーでレート制限を持つことが、想定外の請求を防ぐ実務上の鍵です。

効果の測り方とモニタリング

コスト最適化は「入れて終わり」ではなく、効果を継続的に測り続けて初めて成立します。施策を入れても、計測がなければ効いているか劣化していないかを判断できません。本番運用に乗せるなら、最低限以下を可視化してください。

  1. リクエストあたりの平均トークン数(入力・出力・キャッシュ別)。削減施策の効果はここに現れます。
  2. キャッシュヒット率cache_read_input_tokens の比率)。ゼロが続くならサイレントな無効化を疑います。
  3. モデル別のリクエスト分布とコスト。振り分けが意図通りか、上位モデルに過剰に流れていないかを確認します。
  4. タスクあたりの平均API呼び出し回数。エージェントの暴発はここで早期に見つかります。

これらをダッシュボード化し、機能追加やプロンプト変更のたびに退行していないかを監視する体制まで作って、ようやくコスト最適化は「運用に乗った」と言えます。一度きりの最適化は、プロンプトやモデルを更新した瞬間に崩れます。

内製で持つか、設計ごと外注するか

ここまでの施策は、どれも単体では難しくありません。難しいのは「4施策を品質を落とさずに組み合わせ、計測しながら本番運用で継続的に維持する」ことです。最後に、この設計を内製で持つか外注するかの判断軸を整理します。

内製が向くのは、社内に生成AIアプリの運用経験があり、トークン計測・キャッシュ設計・ルーティング・モニタリングを自走で回せるチームがある場合です。コスト構造は事業の根幹に関わるため、コア機能として内製で握れるならそれに越したことはありません。

一方で、外注(あるいは設計支援)を検討すべきなのは、次のような状況です。

  • LLM機能を出したものの採算が合わず、どこを削ればよいか構造的に切り分けられない
  • プロトタイプは動くが、本番のトラフィックに乗せるとコストが読めず、上限設計やモニタリングまで手が回らない
  • キャッシュやルーティングを入れたいが、品質を落とさずに組む設計判断(前方一致の崩れ・誤振り分けの損失)に自信がない

これらは「実装できるか」ではなく「設計判断を正しく下せるか」の問題です。コスト構造の診断と最適化設計は、LLMアプリの本番運用を数多く手がけたチームほど勘所を持っています。自社のLLM機能が採算ラインに乗らないと感じたら、機能を削る前に、まずコスト構造を設計の観点から見直す価値があります。最適化は節約術ではなく、利用が増えるほど効いてくる「設計への投資」だからです。


参考(一次情報・2026年6月時点):

この記事を書いた人

コセケン

コセケン

テクラル合同会社

スタートアップでのCTO経験を経て、現在はテクラル合同会社にてシステム開発全般を牽引しています。アプリおよびWebの開発から、バックエンド、インフラ構築に至るまで幅広い技術領域に対応可能です。スピード感を持った品質の高いシステム開発を得意としており、新規プロダクトの立ち上げを一気通貫で支援します。本ブログでは実践的な開発ノウハウを発信していきます。

関連記事