RAG vs ファインチューニング vs ロングコンテキスト:2026年LLMアーキテクチャの選び方完全ガイド
LLMを使ったアプリケーションを作っていて、自社データを使う必要がある。社内ドキュメント、カスタマーサポートのチケット、法的契約書、商品カタログ。ベースモデルはこれらのデータを一切知りません。これ、AIエンジニアなら誰もが通る道なんですよね。
RAGを使うか、モデルをファインチューニングするか、それともコンテキストウィンドウに全部突っ込むか?
1年前なら、これは二択の問題でした。2026年は三択になっています。間違えると、不要なインフラにお金を燃やすか、自社データについてハルシネーションしまくるアプリをリリースすることになります。
このガイドで判断フレームワークをそっくりお見せします。曖昧な話はなし。実際のアーキテクチャ、リアルなコスト計算、本番コード、そして今日から使える意思決定ツリーまで全部入ってます。
3つのアプローチ概観
深掘りする前に、それぞれが何をするものか整理しておきましょう。
**RAG(Retrieval-Augmented Generation)**はクエリ時に関連するデータの断片を検索してプロンプトに注入します。モデルの重みは一切変わりません。毎回カンニングペーパーを渡しているイメージです。
ファインチューニングは自社データでモデルの重みそのものを修正します。知識がモデル内部に焼き込まれるんです。モデルに自社ドメインの言語をネイティブに話せるよう教える、と考えてください。
ロングコンテキストはデータセット全体(または大きな塊)をモデルのコンテキストウィンドウにそのまま投入します。検索パイプラインもなし、学習もなし。Claudeの100万トークン、Gemini 3.1の100万トークン時代だからこそ実現可能になったアプローチです。
┌──────────────────────────────────────────────────────────────────┐
│ 自社データ + LLM = 回答 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ RAG ファインチューニング ロングコンテキスト │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ クエリ→検索 │ │ 自社データで │ │ 全データを │ │
│ │ →上位K個 │ │ モデル学習 │ │ プロンプトに │ │
│ │ →プロンプトに │ │ →新しい │ │ 投入→質問 │ │
│ │ 注入→生成 │ │ 重み→生成 │ │ →生成 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ モデル変更なし モデル変更あり モデル変更なし │
│ データ外部保管 データ内在化 データはプロンプト内 │
│ 動的知識 静的知識 クエリごとに静的 │
│ インフラ必要 学習コスト必要 トークンコスト高い │
│ │
└──────────────────────────────────────────────────────────────────┘
それぞれ深掘りしていきましょう。
RAG:Retrieval-Augmented Generation
仕組み
RAGはパイプラインを検索と生成の2フェーズに分けます。
- インデキシング(オフライン):ドキュメントをチャンクに分割し、ベクトルに変換してベクトルDBに格納
- 検索(クエリ時):ユーザーのクエリをエンベディングし、意味的に最も類似したチャンクを取得
- 生成:取得したチャンクをコンテキストとしてプロンプトに注入し、LLMが根拠のある回答を生成
2026年のプロダクションRAGパイプライン
現代のRAGは「エンベッドして検索して終わり」じゃないんですよね。本番レベルのセットアップはこうなります:
import { OpenAIEmbeddings } from "@langchain/openai"; import { PGVectorStore } from "@langchain/community/vectorstores/pgvector"; import { ChatOpenAI } from "@langchain/openai"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; // 1. セマンティックを意識したチャンキング const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 512, chunkOverlap: 64, separators: ["\n## ", "\n### ", "\n\n", "\n", " "], }); const chunks = await splitter.splitDocuments(documents); // 2. メタデータ付きでエンベッド・保存 const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-large", dimensions: 1024, }); const vectorStore = await PGVectorStore.fromDocuments(chunks, embeddings, { postgresConnectionOptions: { connectionString: process.env.PG_URL }, tableName: "documents", }); // 3. ハイブリッド検索:ベクトル検索 + メタデータフィルタリング async function retrieve(query: string, filters?: Record<string, any>) { const results = await vectorStore.similaritySearchWithScore(query, 10, filters); const reranked = await rerank(query, results); return reranked.slice(0, 5); } // 4. 検索コンテキストで回答生成 async function generateAnswer(query: string) { const context = await retrieve(query); const contextText = context.map(([doc]) => doc.pageContent).join("\n\n---\n\n"); const llm = new ChatOpenAI({ model: "gpt-4.1", temperature: 0 }); const response = await llm.invoke([ { role: "system", content: `提供されたコンテキストに基づいて回答してください。 コンテキストに答えがない場合はそう伝えてください。 可能であればソースドキュメントを引用してください。 コンテキスト: ${contextText}`, }, { role: "user", content: query }, ]); return response.content; }
RAGが勝つ場面
- データが頻繁に変わる場合:商品カタログ、サポートチケット、ニュース、毎週更新されるドキュメント
- ソース帰属が重要な場合:法務、医療、コンプライアンス。回答の根拠がどこから来たか正確に示す必要がある
- データセットが大きい場合:数十万件のドキュメントからクエリごとに小さな関連断片だけ必要
- 正確さがスタイルより重要な場合:事実の正確性が回答のトーンより重い
- マルチテナントアプリ:ユーザーごとに異なるデータサブセットから回答が必要
RAGが苦手な場面
- 多数ドキュメントにまたがる複雑な推論:50以上のドキュメントを統合しないと答えられない場合、検索が重要なピースを逃す可能性がある
- スタイル・トーン・フォーマットの要件:RAGはモデルの話し方を変えません。クエリ時に知っていることだけを変える
- レイテンシに敏感なアプリ:検索ステップが各リクエストに100-500ms追加。サクサク動かしたいならこれは痛い
- 小さくて安定したデータセット:コンテキストウィンドウに収まりほとんど変わらないなら、RAGは過剰
RAGのコストプロファイル
| 項目 | 典型的なコスト |
|---|---|
| エンベディング(インデキシング) | ~$0.02 / 1Mトークン |
| ベクトルDBホスティング | $70-500/月 |
| エンベディング(クエリごと) | ~$0.02 / 1Mトークン |
| LLM生成 | モデル + 検索コンテキストサイズに依存 |
| 100万クエリの合計 | ~$500-2,000 |
隠れたコストがあります。エンジニアリング時間です。本番RAGパイプラインの構築と保守は正直地獄とまでは言いませんが、かなりのエンジニアリング投資になります。
ファインチューニング
仕組み
ファインチューニングは事前学習済みLLMをタスク固有のデータで追加学習し、モデルの重みを修正します。ドメイン知識、スタイル、行動パターンがモデルに内在化されます。
実践的なファインチューニング
OpenAI APIでのファインチューニングの実際のコード:
import openai import json # 1. JSONL形式の学習データ準備 training_data = [ { "messages": [ { "role": "system", "content": "あなたはAcme社のシニアセキュリティエンジニアです。" "セキュリティポリシーとインシデント対応手順について、" "正確で実行可能な言葉で回答してください。" "常に関連するポリシーセクション番号を引用してください。" }, { "role": "user", "content": "データ漏洩が疑われる場合の手順は?" }, { "role": "assistant", "content": "ポリシー§4.2.1 - データ漏洩対応に従います:\n\n" "1. **即時封じ込め**(15分以内):影響を受けたシステムを" "隔離してください。電源は切らないでください。" "フォレンジック証拠を保全してください。\n" "2. **エスカレーション**(30分以内):セキュリティ" "インシデント司令官に#security-incidentsのSlackで通知。\n" "3. **評価**(2時間以内):DREADモデル(§4.2.3)で" "範囲を判定。\n" "4. **通知**(24時間以内):PIIが関与する場合、" "§7.1 GDPRに基づき法務部への通知が必須。\n\n" "法務部の承認なしに外部へのコミュニケーションは禁止。" } ] }, # ... 数百の例が必要 ] with open("training_data.jsonl", "w") as f: for example in training_data: f.write(json.dumps(example) + "\n") # 2. アップロードしてファインチューニング開始 client = openai.OpenAI() file = client.files.create(file=open("training_data.jsonl", "rb"), purpose="fine-tune") job = client.fine_tuning.jobs.create( training_file=file.id, model="gpt-4.1-mini", hyperparameters={"n_epochs": 3, "learning_rate_multiplier": 1.0}, ) # 3. ファインチューニング済みモデルを使用 response = client.chat.completions.create( model="ft:gpt-4.1-mini:acme-corp:security-bot:abc123", messages=[{"role": "user", "content": "フィッシングインシデントはどう対応しますか?"}] ) # コンテキスト注入なしで、Acme社の口調でポリシーセクションを引用して回答
ファインチューニングが勝つ場面
- モデルの振る舞いを変える必要がある場合:特定の出力フォーマット、トーン、推論スタイル、ブランドボイス
- 知識が安定している場合:社内ポリシー、ドメイン専門知識、コーディング規約など、毎週変わらないもの
- レイテンシが重要な場合:検索ステップがないため、レスポンスが速い(推論のみ)
- 大規模でのコスト効率:安定した知識 + 高クエリ量なら、小さなファインチューニングモデルがRAGのクエリ当たりトークン膨張を回避
- 専門的推論パターン:医療診断、法的分析、コードレビューなどドメイン固有の推論を教える場合
ファインチューニングが苦手な場面
- データが頻繁に変わる:更新のたびに再学習が必要(時間 + コスト)
- 高品質な学習データが作れない:ゴミが入ればゴミが出ます
- Catastrophic Forgetting:狭いデータで激しく学習すると、汎用能力を「忘れる」リスクがある
- ソース帰属ができない:ファインチューニング済みモデルはどこで学んだか指し示せない
- 小規模チーム:データ準備、学習、評価、デプロイのMLエンジニアリングオーバーヘッドが大きい
ファインチューニングのコストプロファイル
| 項目 | 典型的なコスト |
|---|---|
| 学習(GPT-4.1-mini) | ~$5 / 1M学習トークン |
| 学習(GPT-4.1) | ~$25 / 1M学習トークン |
| 推論(ファインチューニング済み) | ベースモデル価格の~1.3倍 |
| データ準備 | エンジニアリング20-100時間 |
| プロジェクト合計 | $500-10,000+ |
隠れたコスト:データキュレーションです。高品質な例示会話が数百から数千件必要。このデータの作成・整理・検証がプロジェクトで最もコストがかかる部分であることが多いです。
ロングコンテキストウィンドウ
仕組み
もっともシンプルなアプローチです。ドキュメントを集めて連結し、モデルのコンテキストウィンドウにクエリと一緒にまるごと投入。エンベディングパイプラインなし、ベクトルDBなし、学習なし。
import Anthropic from "@anthropic-ai/sdk"; import { readFileSync, readdirSync } from "fs"; import { join } from "path"; const anthropic = new Anthropic(); function loadDocuments(dir: string): string { const files = readdirSync(dir).filter((f) => f.endsWith(".md")); return files .map((f) => { const content = readFileSync(join(dir, f), "utf-8"); return `--- ${f} ---\n${content}`; }) .join("\n\n"); } const allDocs = loadDocuments("./docs"); async function askQuestion(question: string) { const response = await anthropic.messages.create({ model: "claude-sonnet-4-20250514", max_tokens: 4096, messages: [{ role: "user", content: `全ドキュメントです:\n\n${allDocs}\n\n` + `上記ドキュメントに基づいて回答してください:${question}`, }], }); return response.content[0].text; }
以上です。チャンキングなし、エンベディングなし、ベクトルDBなし。ドキュメントと質問だけ。極めてシンプル。
2026年のコンテキストウィンドウサイズ
| モデル | コンテキストウィンドウ | 概算ページ数 |
|---|---|---|
| GPT-4.1 | 100万トークン | ~3,000ページ |
| Claude Sonnet 4.6 | 100万トークン | ~3,000ページ |
| Gemini 3.1 Pro | 100万トークン | ~3,000ページ |
| Llama 4 Scout | 1,000万トークン | ~30,000ページ |
ロングコンテキストが勝つ場面
- データセットが小〜中規模:~50万トークン以下(数百ページ)なら、最もシンプルな選択肢
- 今すぐ必要:セットアップするインフラがゼロ。数分でクエリ開始可能
- ドキュメント横断推論が必要:モデルが全体を一度に見るため、RAGが見落とす可能性のある情報の統合が可能
- プロトタイプ/MVP段階:まず動かす。アーキテクチャの最適化は後で
- クエリが少ない場合:1日数百件なら、クエリあたりのコストは許容範囲
ロングコンテキストが苦手な場面
- スケールでのコスト:50万トークンをクエリごとに送信すると、クエリあたり~15,000/日
- レイテンシ:50万トークンの処理は2千トークンのRAGプロンプトよりはるかに遅い
- 「干し草の中の針」問題:巨大コンテキストの中間に埋もれた情報をモデルが見落とす可能性("Lost in the Middle"現象)。これが地味に痛い
- データセットがウィンドウを超過:データが1,000万トークンでウィンドウが100万なら、不可能
ロングコンテキストのコストプロファイル
| 項目 | 典型的なコスト |
|---|---|
| インフラ | $0 |
| エンジニアリング時間 | 時間単位(週単位ではない) |
| クエリあたり(20万コンテキスト) | ~$0.30-0.60 |
| クエリあたり(50万コンテキスト) | ~$0.75-1.50 |
| 月10万クエリの合計 | $30,000-150,000 |
隠れたコスト:スケールしないことです。最も安い選択肢として始まったものが、ボリュームが増えると最も高くなります。
判断フレームワーク
比較マトリクス
| 項目 | RAG | ファインチューニング | ロングコンテキスト |
|---|---|---|---|
| セットアップ時間 | 日〜週 | 日〜週 | 分〜時間 |
| インフラ | ベクトルDB、エンベディング | 学習パイプライン | なし |
| データ鮮度 | リアルタイム | 再学習必要 | クエリごとに再読み込み |
| 低ボリュームコスト | 中 | 高(初期) | 低 |
| 高ボリュームコスト | 低〜中 | 低 | 非常に高い |
| レイテンシ | 中(+検索) | 低(推論のみ) | 高(長い入力) |
| ソース帰属 | 可能(ビルトイン) | 不可 | 可能(手動) |
| 行動変更 | 不可 | 可能 | 不可 |
| ハルシネーションリスク | 低(根拠あり) | 中 | 低(データあり) |
| エンジニアリング工数 | 高 | 高 | 低 |
実践的アーキテクチャパターン
パターン1:RAG(動的知識)+ ファインチューニング(行動)= ハイブリッド
最も強力なパターンです。ファインチューニングでどう振る舞うかを、RAGで何を知っているかを設定します。
例:臨床コミュニケーションガイドラインに合わせてファインチューニングしたヘルスケアチャットボットが、RAGで最新の医学文献と患者記録にアクセス。
パターン2:ロングコンテキストでプロトタイプ → プロダクションでRAG
ロングコンテキストでアプローチを検証してから、スケールが必要になったらRAGに移行。
パターン3:ティアードアーキテクチャ
3つを1つのシステムに統合し、クエリを最もコスト効率の良い方式にルーティング:
async function routeQuery(query: string, queryType: string) { switch (queryType) { case "factual_lookup": // シンプルなファクト検索:RAGが最安 return await ragPipeline(query); case "complex_analysis": // ドキュメント横断推論:ロングコンテキスト return await longContextAnalysis(query); case "formatted_report": // 特定フォーマット必要:ファインチューニングモデル + RAG return await fineTunedWithRAG(query); default: return await ragPipeline(query); } }
パターン4:エージェンティックRAG
2026年のRAGの進化形です。AIエージェントがどう検索するか、どのソースを使うか、マルチステップ検索をするか自律的に判断します:
import { ChatOpenAI } from "@langchain/openai"; import { createReactAgent } from "@langchain/langgraph/prebuilt"; const tools = [ vectorSearchTool, sqlQueryTool, webSearchTool, graphTraversalTool, calculatorTool, ]; const agent = createReactAgent({ llm: new ChatOpenAI({ model: "gpt-4.1" }), tools, messageModifier: `あなたはリサーチエージェントです。各クエリに対して: 1. 質問タイプに応じてどのツールを使うか判断 2. 必要なら複数ソースから情報を検索 3. 正確性のため結果をクロスチェック 4. 引用付きの包括的回答を合成`, });
よくある間違い
間違い1:何でもRAGにする
RAGが「安全な」選択肢になりましたが、常に正しいわけじゃないんですよね。安定したドキュメント50ページ + 1日100クエリなら、ロングコンテキストの方がシンプルで安く、しかも正確なことが多いです。
判断基準:データがコンテキストウィンドウに収まり、月1回未満しか変わらないなら、ロングコンテキストから始めましょう。
間違い2:RAGが必要なのにファインチューニングする
「モデルが自社製品を知らない」→ これは知識の問題であり、行動の問題ではありません。RAGで解決します。
判断基準:「モデルがXを知らない」→ RAG。「モデルがXのように話さない/考えない」→ ファインチューニング。
間違い3:「Lost in the Middle」問題の無視
モデルは非常に長いコンテキストの中間部分から情報を取り出すのがまだ苦手です。最も重要なコンテキストをプロンプトの最初と最後に配置してください。
間違い4:RAGのオーバーエンジニアリング
初日からGraphRAG + エージェンティック検索 + HyDE + マルチクエリ展開 + リランカーは不要です。基本的なベクトル検索から始めましょう。検索品質を計測しましょう。効果がデータで示されたときだけ複雑さを追加しましょう。
間違い5:検索品質を計測しない
RAG失敗の最も一般的な原因はLLMじゃないんです。検索が悪い。recall@kとprecision@kを計測していないなら、それは目隠しで走っているようなものです。
def evaluate_retrieval(test_queries, ground_truth_docs, retriever, k=5): recalls = [] for query, expected_doc_ids in zip(test_queries, ground_truth_docs): retrieved = retriever.retrieve(query, k=k) retrieved_ids = {doc.id for doc in retrieved} expected_ids = set(expected_doc_ids) recall = len(retrieved_ids & expected_ids) / len(expected_ids) recalls.append(recall) avg_recall = sum(recalls) / len(recalls) print(f"Recall@{k}: {avg_recall:.2%}") return avg_recall
コスト計算:具体的な例
具体的なシナリオで比較しましょう。月50,000クエリのカスタマーサポートボット、ナレッジベースは10,000件のFAQ記事(~200万トークン)。
オプションA:RAG
| 項目 | コスト |
|---|---|
| ベクトルDB(既存Postgresのpgvector) | $0/月(既存インフラ) |
| エンベディング(月5万クエリ × ~100トークン) | ~$0.10/月 |
| LLM呼び出し(5万 × ~2Kトークンプロンプト) | ~$300/月(GPT-4.1-mini) |
| エンジニアリングセットアップ | ~80時間(初回のみ) |
| 月額コスト | ~$300/月 |
オプションB:ファインチューニング + RAG(ハイブリッド)
| 項目 | コスト |
|---|---|
| ファインチューニング(初回のみ) | ~$200 |
| RAGパイプライン(上記と同じ) | ~$300/月 |
| 四半期ごとの再学習 | ~$200/四半期 |
| 月額コスト | ~$370/月 |
オプションC:ロングコンテキスト
| 項目 | コスト |
|---|---|
| インフラ | $0 |
| LLM呼び出し(5万 × ~50万トークン) | ~$37,500/月(Claude Sonnet) |
| 月額コスト | ~$37,500/月 |
スケールではRAGが100倍以上安いです。しかし1日50クエリのプロトタイプなら?ロングコンテキストは月~$60でインフラセットアップゼロです。
教訓:アーキテクチャを決める前に、必ず自社の規模に合ったコスト計算を実行してください。
2026年の変化
3つの大きな変化がこの判断を再形成しています。
1. コンテキストウィンドウは拡大し続けている
Llama 4 Scoutの1,000万トークンは、コードベース全体やドキュメントライブラリ丸ごとを保持できるモデルが来ていることを示しています。RAGを殺しはしませんが、RAGが厳密に必要な領域は縮小中です。モデルがネイティブにより多くのコンテキストを処理できるようになるにつれ、問いは「コンテキストに全部入れられるか?」から「入れるべきか?」に変わり、その答えはコストとレイテンシ次第です。
2. エージェンティックRAGの台頭
静的な検索-生成パイプラインが、AIエージェントがどう・どこから・マルチステップで検索するか自律判断するシステムに進化中です。RAGの精度とエージェントの柔軟性を組み合わせるこのアプローチは、AIエンジニアリングで最も重要なトレンドの一つです。
3. ファインチューニングがどんどん安く速くなっている
LoRA(Low-Rank Adaptation)やQLoRAなどの技法でコストが急減。70Bパラメーターモデルも単一GPUで数時間でファインチューニング可能です。これにより「安定した知識 + 行動変更」のユースケースが、複雑なRAGパイプラインと比較してますます魅力的になっています。
4. RAFT(Retrieval-Augmented Fine-Tuning)
検索コンテキストとうまく協調するようモデルをファインチューニングするハイブリッドアプローチが有力パターンとして台頭中。ノイズの多い検索結果から関連情報を抽出し、不要なものを無視する方法を学びます。RAFTは検索精度とアウトプット品質の両方が必要なシナリオに特に有望です。
結論
万能の「ベスト」なアプローチは存在しません。正しいアーキテクチャは、データ、スケール、レイテンシ要件、チームの能力次第なんですよね。
チートシート:
データが頻繁に変わる? → RAG
モデルの振る舞いを変えたい? → ファインチューニング
小さなデータセット、今すぐ必要? → ロングコンテキスト
スケールで最高品質? → ファインチューニングモデル + RAG
プロトタイピング? → ロングコンテキスト → 動いたらRAGに移行
これを宗教論争にしないでください。自社の規模でコスト計算を実行してください。検索品質を計測してください。シンプルに始めてください。データが必要だと示したときだけ複雑さを追加してください。
2026年に最高のLLMアプリを作っているエンジニアは、最も洗練されたパイプラインを持っている人ではありません。自分の具体的な問題に合ったアプローチを選んで、きちんと実行した人です。
関連ツールを見る
Pockitの無料開発者ツールを試してみましょう