モデルルーティング:すべてに1つのモデルを使うのをやめよう
適切なタスクに最適なモデルを。
700億パラメータのモデルを走らせて200語のメールを要約するのは、もったいない。30億パラメータのモデルを使って本番環境のコードレビューを行うのは、無責任だ。ほとんどのシステムは、この中間的な位置にある。そこで登場するのが、モデルのルーティング(経路選択)である。
これは、タスクの複雑さとモデルの能力をマッチングさせる仕組みである。トレードオフは確かに存在するが、その分だけコスト削減効果も得られる。

ルーティングの課題
多くの場合、最初は1つのモデルを使い続けるところから始める。しかし、コストやレイテンシ(遅延)、あるいはその両面に気付き始めた頃、この方法では限界が訪れる。代替策は、どのリクエストをどのモデルが処理するかを決定する「ルーター」を構築することだ。
実務で機能する戦略は4つある:
- 能力ベース — モデルが何ができるかでルーティング
- コスト考慮型 — 予算(使用金額)に応じてルーティング
- レイテンシ考慮型 — 必要な処理速度に応じてルーティング
- ハイブリッド — 上記を組み合わせたもの
それぞれが最適化する対象は異なる。どれを選ぶかは、通常、どの問題(コスト、遅延、品質など)が最も深刻かという判断に帰着する。
能力ベースのルーティング
最もシンプルなアプローチである。タスクを分類し、それに対応できるモデルに送る。
| タスク | モデルサイズ | 例 |
|---|---|---|
| 分類、タグ付け | 10-30億パラメータ | Qwen2.5-1.5B, Gemma-2-2B |
| 要約、情報抽出 | 30-70億パラメータ | Qwen2.5-7B, Llama-3.1-8B |
| コード生成 | 70-140億パラメータ | Qwen2.5-Coder-7B, DeepSeek-Coder-V2 |
| 複雑な推論 | 140-320億パラメータ | Qwen2.5-32B, Llama-3.1-70B |
| クリエイティブな執筆、分析 | 320億パラメータ以上 | Qwen2.5-72B, Claude, GPT-4 |
タスクに大規模モデルが不要なら、使わない。15億パラメータのモデルでも感情分類は十分にこなせる。ただし、 coherent(一貫性のある)なエッセイを書くことはできない。
実装は straightforward(直截的)である:
ROUTING_RULES = {
"classify": {"model": "qwen2.5-1.5b", "max_tokens": 100},
"summarize": {"model": "qwen2.5-7b", "max_tokens": 500},
"code_review": {"model": "qwen2.5-coder-7b", "max_tokens": 2000},
"reason": {"model": "qwen2.5-32b", "max_tokens": 4000},
"creative": {"model": "claude-sonnet-4", "max_tokens": 8000},
}
def route_request(task_type: str) -> dict:
return ROUTING_RULES.get(task_type, ROUTING_RULES["reason"])
ただし、落とし穴は分類そのものにある。タスクの種類を誤判定すると、間違ったモデルへルーティングされてしまう。コードレビューを「要約」と誤分類し、品質が静かに低下するケースを私は目にしたことがある。
コスト考慮型のルーティング
ローカル推論はこの分野で真価を発揮する。ハードウェアのコスト償却後、ローカルモデルは実質的に無料と言える。RTX 5080なら、API利用が中程度であれば約6ヶ月で元が取れる計算だ。
| モデル | 入力コスト ($/M tokens) | 出力コスト ($/M tokens) | ローカル推論のコスト/時 |
|---|---|---|---|
| GPT-4o | $2.50 | $10.00 | — |
| Claude Sonnet 4 | $3.00 | $15.00 | — |
| Qwen2.5-72B (API) | $0.50 | $2.00 | — |
| Qwen2.5-32B (ローカル) | $0.00 | $0.00 | ~$0.10 |
| Qwen2.5-7B (ローカル) | $0.00 | $0.00 | ~$0.05 |
1セッションで数千件のリクエストを処理する場合、電力コスト$0.05でも、$15/M tokens(百万トークンあたり15ドル)のAPIコストより断然安い。
予算ベースのルーティングは、支出に応じてフェイルオーバーする:
class CostAwareRouter:
def __init__(self, budget_per_session: float = 0.10):
self.budget = budget_per_session
self.spent = 0.0
self.models = {
"cheap": {"model": "qwen2.5-7b", "cost": 0.0},
"medium": {"model": "qwen2.5-32b", "cost": 0.0},
"expensive": {"model": "claude-sonnet-4", "cost": 0.000015},
}
def route(self, task: str) -> str:
ratio = self.spent / self.budget
if ratio < 0.5:
return self.models["expensive"]["model"]
elif ratio < 0.8:
return self.models["medium"]["model"]
return self.models["cheap"]["model"]
フェイルオーバーが進むにつれて品質は低下する。最初はClaudeを使い、次にQwen-32B、そしてQwen-7Bへと切り替わる。長時間のセッション後半には、出力品質の劣化が顕著になる。それが問題になるかどうかは、構築しているシステム次第である。
レイテンシ考慮型のルーティング
インタラクティブなツールには、最初のトークン生成までの高速性が求められる。バッチ処理は待たせてもよい。その違いは、通常、モデルサイズで5倍ほどの差となる。
| 使用ケース | 最初のトークン | 完了まで | 最大モデルサイズ |
|---|---|---|---|
| リアルタイムチャット | < 200ms | < 2s | < 70億パラメータ |
| インタラクティブツール | < 500ms | < 5s | < 140億パラメータ |
| バッチ処理 | < 1s | < 30s | 制限なし |
| 研究/分析 | < 2s | < 60s | 制限なし |
ユーザーへトークンをストリーミング配信する場合、ユーザーが感じる遅延は「最初のトークン」までの時間である。320億パラメータのモデルが起動に半秒かかるのは、瞬発的に動作する15億パラメータのモデルと比較して、もっさりした印象を与える。
class LatencyAwareRouter:
def __init__(self):
self.model_latencies = {
"qwen2.5-1.5b": {"first_token": 0.05, "complete": 0.5},
"qwen2.5-7b": {"first_token": 0.15, "complete": 2.0},
"qwen2.5-32b": {"first_token": 0.5, "complete": 10.0},
"claude-sonnet-4": {"first_token": 0.3, "complete": 5.0},
}
def route(self, target_latency: float) -> str:
for model, latencies in sorted(
self.model_latencies.items(),
key=lambda x: x[1]["complete"]
):
if latencies["complete"] <= target_latency:
return model
return "qwen2.5-1.5b"
レイテンシの数値は概算であり、ハードウェア、量子化(quantization)、バッチサイズに依存する。自社の環境で計測するのが重要だ。
フェイルオーバー戦略
モデルは失敗する。APIはレート制限がかかる。タイムアウトも発生する。機能するパターンは、最も望ましいものから最も信頼性の高いものへと順に並べられた「フェイルオーバーチェーン」である:
class FallbackRouter:
def __init__(self):
self.fallback_chain = [
{"model": "claude-sonnet-4", "timeout": 30},
{"model": "qwen2.5-72b", "timeout": 60},
{"model": "qwen2.5-32b", "timeout": 120},
{"model": "qwen2.5-7b", "timeout": 300},
]
def route_with_fallback(self, prompt: str) -> str:
for config in self.fallback_chain:
try:
return self.call_model(
config["model"], prompt,
timeout=config["timeout"]
)
except (TimeoutError, APIError) as e:
log.warning(f"Model {config['model']} failed: {e}")
continue
raise RuntimeError("All fallback models failed")
チェーンの最後のモデルはローカルモデルにするべきだ。遅いかもしれないが、ネットワークの問題やAPIキーの失敗によって動作しなくなることはない。
ルーティングが有効な場合
ルーティングは、ワークロードが混在している場合に理にかなっている。同じシステム内で分類、要約、推論を行っている場合、ルーターはコストとレイテンシを節約できる。
すべてのタスクが同じ複雑さである場合は、ルーティングは理にかなわない。そのタスクに強いモデルを単に使い続けるだけだ。ルーティングは不要な複雑さを追加するだけとなる。
プロトタイピングの初期段階も、ルーティングを避ける理由の一つである。まずは1つのモデルでタスクが動作することを検証し、コストやレイテンシが実際に問題となった時点でルーティングを追加する。
トレードオフ
すべてのルーティング戦略は、何かを最適化すると同時に、何かを犠牲にする:
- 単一モデル — 最もシンプル、最も高コスト、品質は一定
- 能力ベース — コスト効率良好、タスクごとに高品質、複雑さは適度
- コスト考慮型 — 最も低コスト、品質は変動、複雑さは適度
- レイテンシ考慮型 — 最も高速、品質を犠牲にする可能性あり、複雑さは適度
- ハイブリッド — すべてを兼ね備えるが、実装が最も複雑
プロダクション環境のシステムは、通常、ハイブリッド型に収束する。能力ベースのルーティングから始め、請求額が気になるようになったらコスト考慮型を追加し、ユーザーから遅さへの苦情が出たらレイテンシ考慮型を追加していく。
関連記事
- コスト最適化 for LLM Systems — トークン予算管理、キャッシング、フェイルオーバーモデル
- 実践 LLM ガードレール — 入力検証、出力フィルタリング、安全性
- マルチモデルシステム設計 — 複数モデルのためのアーキテクチャ
- LLM アーキテクチャ — システム設計の柱:ルーティング、コスト、ガードレール、オーケストレーション