7月24日、Towards Data Scienceが「Loop Engineering for RAG Generation: An LLM Cascade from a Cheap Local Model Up to a Hosted Flagship」と題した記事を公開した。RAG生成においてローカルの安価なモデルから始め、検証失敗時にのみ上位モデルへエスカレーションする「LLMカスケード」の設計と実装手法について詳しく紹介されている。
なぜ今「LLMカスケード」が注目されるか
LLMを本番パイプラインに組み込む際、最大の障壁のひとつがAPIコストのコントロールだ。GPT-4クラスのフラッグシップモデルは精度が高い反面、フィールド数の多い文書処理や大量バッチ処理では費用が急騰する。2023〜2024年にかけてRAGアーキテクチャが急速に普及したことで、「全クエリを高精度モデルへ投げる」設計の限界がより顕在化してきた。
この背景から、業界ではモデルの能力を用途に応じて使い分ける「LLMルーティング」や「モデルカスケード」という設計パターンへの関心が高まっている。安いモデルで処理できるものは安いモデルで済ませ、難しいケースにのみ高コストモデルを投入するという発想だ。本記事が扱うLLMカスケードは、この思想を決定的な検証ループで実装したものといえる。
「全フィールドをGPT-4に送る」のコストをどう削るか
保険文書から金額や日付、カバレッジ上限などを抽出するパイプラインを想像してほしい。愚直な実装では全フィールドをフラッグシップモデル(GPT-4クラスのAPI)に投げる。動くが、費用が大きい。
かといって小さいモデルに丸ごと置き換えると、型付きの出力が壊れたり、変換で躓いたりして、誰も気づかないまま誤った値が下流に流れる。
記事が提案する解は「LLMカスケード」だ——安いモデルから始め、検証が通れば終了、失敗したときだけ上位モデルに再投する。コスト削減と正確性の両立を、推測ではなく決定的な検証ループで実現する設計である。
ベンチマークが示す2つの不都合な事実
設計の根拠として、数百フィールド・数十文書を対象に20本のローカルモデルを1本のホスト型フラッグシップと比較したベンチマーク結果が示されている。
「大きいほど良い」は成立しない
qwen3:4b(4B)とmistral:7b(7B)がローカル勢のトップgemma3:12b(12B)とphi4:14b(14B)はそれらを下回る- 同ファミリーの2倍サイズモデルが同スコアで並ぶケースもある
パラメータ数ではなく、モデルファミリーと学習方法が精度を左右する。これがカスケードで「単純にサイズ順に登る」設計を採らない根拠だ。
また、ホスト型フラッグシップはフィールドあたり約1.4秒で応答したのに対し、ローカルの7B〜14Bは同一GPUで約2.6〜7.6秒かかった。安価なローカルモデルはコスト削減とデータのローカル保持をもたらすが、レイテンシの改善にはならない点に注意が必要だ。
最大のレバーはモデルではなくプロンプトの中身
業務用語の定義(「deductible per claimはretentionとも呼ばれる」など)をプロンプトに追加するだけで、全モデルの精度が大幅に向上した。具体的には、小モデルが**38%→62%、フラッグシップが62%→100%**へと改善している。
フラッグシップでさえ語彙定義なしでは62%止まりで、追加して初めて100%に達した。この語彙定義は検索(Retrieval)フェーズでも使うものを再利用できるため、どの段のモデルにも等しくコストゼロで恩恵をもたらす。
カスケードの3つの仕組み
1. 開始モデルを基準で選ぶ
最小モデルから始めるのではなく、3つの基準で「適切な下限」を設定する。
- 機密性: 機密文書はホストAPIに送れないため、強制的にローカルモデルを選択
- 型付き出力の信頼性: 小さいモデルは
currencyフィールドを落としたり、found=Falseにすべき箇所で値を捏造したりする。型出力を使う場合は必ず検証とセットにする - 変換複雑度: 単純な抽出か、複雑な変換が必要かで扱いを分ける(後述)
class GenerationPlan(BaseModel):
model: str
needs_validation: bool
needs_step_split: bool
max_escalations: int = 2
def plan_generation(doc, shape):
if doc.confidential:
return local_plan(shape)
return tier_plan(shape)
2. 検証し、失敗時のみエスカレーション
def generate_with_escalation(question, context, plan, ladder):
for model in ladder.from_(plan.model, up_to=plan.max_escalations):
answer = generate(question, context, model=model)
if validate(answer, context): # 型チェック・引用範囲確認等
return answer
return NotAnswered()
安価なモデルで生成し、検証(型の整合性・引用箇所の存在確認・テキストの一致)を実施。通れば終了。失敗すれば1段上のモデルで再試行。ループはmax_escalationsで上限を設け、どんな異常フィールドでも無限ループにならないよう制御する。最上位モデルでも失敗した場合は誤値ではなく「回答不能」を返す。
3. タスク分割という第3の選択肢
エスカレーション以外のエスケープハッチとして、タスクの分割がある。
例えば「3Mを3000000に変換する」という処理は、小さいモデルには認識とスケーリングを一発でこなすのが難しい。ここで上位モデルに投げるのではなく、「モデルはソース行を指し示すだけ、数値変換はコードが行う」と分割すると、各ステップが単純になる。
ただし、記事はこの分割が常に有利ではないと明言している。ベンチマークでは、フル分割アーキテクチャは「能力あるモデルが一発抽出」より精度が落ちた。正しい行を指し示す作業自体が難しく、指し示しに失敗するとコード変換の入力が壊れるためだ。分割は機密性の制約がある場合や、モデルが繰り返し失敗する特定の変換処理に限定して使うべきで、採用する際は精度トレードオフを必ず実測することが推奨されている。
実装の参考資料
記事ではGitHub(doc-intel/notebooks-vol1)で動作するノートブックを公開している。同一フィールドをカスケードに通し、段ごとのフィールド単位コストと検証結果を出力し、安価なローカルモデルが誤答したフィールドでのみエスカレーションが発火する様子を確認できる。
詳細はLoop Engineering for RAG Generation: An LLM Cascade from a Cheap Local Model Up to a Hosted Flagshipを参照していただきたい。