コンテンツにスキップ

8.5.3 プロジェクト:RAG+微調整の統合システム

  • 「RAG だけ」や「微調整だけ」では足りないことがある理由を理解する
  • 分野別Q&Aシステムを RAG 層と微調整層に分けて考えられるようになる
  • 説明可能な RAG+微調整プロジェクト案を設計する
  • 最小限の組み合わせ型プロジェクトの骨組みを動かす

RAG と微調整を組み合わせる前に、学習関連の用語を分けて理解しておきましょう。

用語初学者向けの意味何を解決するものか
fine-tuning基盤モデルに対して、タスク例で追加学習すること振る舞い、形式、分野らしい表現を安定させる
SFTSupervised Fine-Tuning。人が作成または整理した入力・出力例で学習する方法良い回答の形をモデルに教える
LoRALow-Rank Adaptation。小さな adapter 重みだけを学習する軽量な微調整方法学習コストを下げながらモデルの振る舞いを調整する
QLoRA量子化したモデル読み込みと LoRA を組み合わせる方法小さめのハードウェアでも微調整を試しやすくする
domain adaptation特定の分野や業務文脈にシステムを合わせること分野知識と、その分野らしい答え方の両方が必要になる
eval set固定の評価質問と期待チェック項目のセット1つの良さそうな例だけで改善を判断しないため

実務上のルールはこうです。頻繁に変わる文書を fine-tuning で覚えさせようとしない。変わる知識は RAG に任せ、安定した振る舞いや形式は fine-tuning や SFT 例で補います。


一、なぜ RAG と微調整を組み合わせるのか?

Section titled “一、なぜ RAG と微調整を組み合わせるのか?”

RAG の強み:

  • 知識を更新しやすい
  • 出典を引用できる
  • モデルを再学習しなくてよい

ただし、限界もあります。

  • モデルがあなたの分野の表現を理解しているとは限らない
  • 検索できても、業務の形式に合った答え方になるとは限らない
  • 複雑なタスクでは、モデルの「答え方の癖」が安定しないことがある

微調整の強み:

  • モデルを特定のタスク形式によりよく合わせられる
  • 出力スタイルが安定しやすい
  • 指示への追従が業務に合いやすい

ただし、限界もあります。

  • 新しい知識の更新にあまり柔軟ではない
  • 微調整だけで細かい文書すべてを覚えさせるのは難しい
  • コストが高くなりやすい

だからこそ、両者は補完関係になりやすい

Section titled “だからこそ、両者は補完関係になりやすい”

まずは一言で覚えましょう。

RAG は知識を補い、微調整は振る舞いを補う。

これが組み合わせ型システムの核となる考え方です。

RAG と微調整の役割分担図


二、このプロジェクトでは何をするのか?

Section titled “二、このプロジェクトでは何をするのか?”

目標を、たとえば分野別Q&Aアシスタントに設定します。

  • 社内ポリシー文書向け
  • 回答時に出典を安定して引用する
  • 出力形式を必ず統一する
  • 一部の質問には固定の業務ルールで答える

つまり、このシステムは次の両方を満たす必要があります。

  • 知識を見つけられる
  • しかもその分野らしい答え方ができる

flowchart LR
A["ユーザーの質問"] --> B["検索器"]
B --> C["関連文書チャンク"]
C --> D["微調整済み回答モデル"]
D --> E["整形済み出力"]
style A fill:#e3f2fd,stroke:#1565c0,color:#333
style B fill:#fff3e0,stroke:#e65100,color:#333
style C fill:#f3e5f5,stroke:#6a1b9a,color:#333
style D fill:#e8f5e9,stroke:#2e7d32,color:#333
style E fill:#ffebee,stroke:#c62828,color:#333

重要なのは「部品が多いこと」ではなく、役割がはっきりしていることです。

  • 検索器は資料を探す
  • 微調整モデルは業務に合う形で答えをまとめる

これによって、システムはより説明しやすくなり、改善もしやすくなります。


最小限のナレッジベースと検索器

Section titled “最小限のナレッジベースと検索器”
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
kb = [
{"id": "doc1", "text": "返金ポリシー:購入後 7 日以内かつ学習進捗が 20% 未満なら返金可能です。"},
{"id": "doc2", "text": "修了証ポリシー:プロジェクトを完了しテストに合格すると修了証を取得できます。"},
{"id": "doc3", "text": "カスタマーサポート対応規範:回答時は、まずポリシーの根拠を示し、その後に結論を述べます。"}
]
vectorizer = TfidfVectorizer(analyzer="char", ngram_range=(2, 4))
doc_vectors = vectorizer.fit_transform([item["text"] for item in kb])
def retrieve(query, top_k=2):
query_vec = vectorizer.transform([query])
scores = cosine_similarity(query_vec, doc_vectors)[0]
top_idx = scores.argsort()[::-1][:top_k]
return [kb[i] for i in top_idx]
print(retrieve("返金条件は何ですか"))

期待される出力:

Terminal window
[{'id': 'doc1', 'text': '返金ポリシー:購入後 7 日以内かつ学習進捗が 20% 未満なら返金可能です。'}, {'id': 'doc3', 'text': 'カスタマーサポート対応規範:回答時は、まずポリシーの根拠を示し、その後に結論を述べます。'}]

日本語の例では analyzer="char"ngram_range=(2, 4) を使います。日本語は英語のように空白で単語が区切られないため、追加の分かち書きライブラリなしでも安定した教材用の結果にするためです。

この検索器自体は複雑ではありませんが、すでに組み合わせ型システムの前半部分になっています。


次に「微調整後の回答スタイル」をまねる

Section titled “次に「微調整後の回答スタイル」をまねる”

実際のプロジェクトでは、この部分はたとえば次のような方法で作られます。

  • 指示微調整
  • LoRA / QLoRA
  • 教師ありデータセットでの学習

ここでは、コードをそのまま動かせるように、ルールで「すでに学習済みの業務出力スタイル」をまねます。

def domain_answer_style(question, retrieved_docs):
evidence = " ".join(doc["text"] for doc in retrieved_docs)
if "返金" in question:
return {
"answer": "現行の返金ポリシーによると、購入後 7 日以内かつ学習進捗が 20% 未満のユーザーは返金を申請できます。",
"reasoning_style": "先にポリシー、後で結論",
"evidence": evidence
}
if "修了証" in question:
return {
"answer": "修了証ポリシーによると、プロジェクトを完了しテストに合格すると修了証を取得できます。",
"reasoning_style": "先にポリシー、後で結論",
"evidence": evidence
}
return {
"answer": "現在のところ、十分に一致する業務ルールが見つかりませんでした。",
"reasoning_style": "慎重に回答を保留",
"evidence": evidence
}

なぜこのシミュレーションに意味があるのか?

Section titled “なぜこのシミュレーションに意味があるのか?”

これによって、次のことが分かりやすくなるからです。

  • RAG が解決するのは「何を知っているか」
  • 微調整が解決するのは「どう答えるか」

def rag_plus_finetune_system(question):
docs = retrieve(question, top_k=2)
result = domain_answer_style(question, docs)
return {
"question": question,
"retrieved_docs": docs,
**result
}
result = rag_plus_finetune_system("返金条件は何ですか?")
print(result["question"])
print(result["answer"])
print("evidence:", result["evidence"])

期待される出力:

Terminal window
返金条件は何ですか?
現行の返金ポリシーによると、購入後 7 日以内かつ学習進捗が 20% 未満のユーザーは返金を申請できます。
evidence: 返金ポリシー:購入後 7 日以内かつ学習進捗が 20% 未満なら返金可能です。 カスタマーサポート対応規範:回答時は、まずポリシーの根拠を示し、その後に結論を述べます。

このシステムは何を示しているのか?

Section titled “このシステムは何を示しているのか?”

これで示されるのは次のことです。

組み合わせ型システムは、2つの技術を無理やり足し合わせるのではなく、それぞれが得意な部分を担当するようにすることだ。


本当のプロジェクトでは、何を微調整することが多いのか?

Section titled “本当のプロジェクトでは、何を微調整することが多いのか?”

「すべての文書を覚えさせる」ためではない

Section titled “「すべての文書を覚えさせる」ためではない”

初心者の方は、次のように考えがちです。

微調整したら、モデルがナレッジベースを全部暗記するはず

でも、もっと一般的で現実的な目的は次のようなものです。

  • 分野特有の用語の雰囲気を学ばせる
  • 出力形式を学ばせる
  • 業務用の回答テンプレートを学ばせる
  • ある種類のタスクの固定構造を学ばせる

モデルに次のようなことを学ばせたいかもしれません。

  • 「まずポリシーを引用してから結論を出す」
  • 「不確かな場合は明確に回答を保留する」
  • 「すべての回答を標準フィールドで出力する」

こうした能力は、微調整、または少なくとも強い教師あり学習で強化するのに向いています。


プロジェクト価値の高い分け方

Section titled “プロジェクト価値の高い分け方”
  • 文書のチャンク化
  • 検索
  • 出典引用
  • 知識更新
  • 回答スタイル
  • 出力形式
  • タスクテンプレート
  • 業務用語の理解

この役割分担をはっきりさせると、プロジェクトの保守性はかなり良くなります。


この統合システムはどう評価するのか?

Section titled “この統合システムはどう評価するのか?”

「答えが自然か」だけでは足りない

Section titled “「答えが自然か」だけでは足りない”

少なくとも、2つの層を見てください。

  • 検索層:正しい文書を見つけられているか
  • 回答層:出力が業務要件に合っているか
eval_data = [
{"question": "返金条件は何ですか", "gold_doc": "doc1", "must_contain": "7 日以内"},
{"question": "修了証はどうやって取得しますか", "gold_doc": "doc2", "must_contain": "テストに合格"}
]
for item in eval_data:
result = rag_plus_finetune_system(item["question"])
hit = result["retrieved_docs"][0]["id"] == item["gold_doc"]
good_answer = item["must_contain"] in result["answer"]
print(item["question"], "retrieval_hit=", hit, "answer_ok=", good_answer)

期待される出力:

Terminal window
返金条件は何ですか retrieval_hit= True answer_ok= True
修了証はどうやって取得しますか retrieval_hit= True answer_ok= True

これだけでも、「ただデモがそれっぽく見えるか」を確認するより、ずっと前進しています。

小さなレイヤー診断ドリルを追加する

Section titled “小さなレイヤー診断ドリルを追加する”

組み合わせ型システムが失敗したら、まずどの層が担当すべき問題かを切り分けます。この小さな表が、実プロジェクトの振り返りの入口になります。

diagnostics = [
{"symptom": "正しい文書が top-2 に入らない", "likely_layer": "RAG", "next_step": "チャンク化、クエリ書き換え、検索戦略を改善する"},
{"symptom": "正しい文書は取れているが、回答形式が安定しない", "likely_layer": "微調整 / プロンプト", "next_step": "教師あり例を増やすか schema を厳しくする"},
{"symptom": "回答は A を引用しているが、事実は B から来ている", "likely_layer": "grounding", "next_step": "引用チェックと文単位の根拠検証を追加する"},
]
for row in diagnostics:
print(f"{row['likely_layer']}: {row['symptom']} -> {row['next_step']}")

期待される出力:

Terminal window
RAG: 正しい文書が top-2 に入らない -> チャンク化、クエリ書き換え、検索戦略を改善する
微調整 / プロンプト: 正しい文書は取れているが、回答形式が安定しない -> 教師あり例を増やすか schema を厳しくする
grounding: 回答は A を引用しているが、事実は B から来ている -> 引用チェックと文単位の根拠検証を追加する

RAG と微調整の統合結果図


知識更新の問題を微調整で解決しようとする

Section titled “知識更新の問題を微調整で解決しようとする”

これはたいてい効率がよくありません。

出力スタイルの安定化を RAG だけで無理に解決しようとする

Section titled “出力スタイルの安定化を RAG だけで無理に解決しようとする”

これも、いつも適切とは限りません。

自分で「どの層が何を担当しているのか」を説明できないと、後から調整がとても難しくなります。


このページを終えたら、この証拠カードを残します。

プロジェクト目標
ユーザーのタスクとビジネス境界
ベースライン
まずは最も簡単なプロンプト/RAG/app版
評価
固定ケース、検索証拠、回答品質、引用チェック
失敗ログ
少なくとも1件の失敗ケースと原因の可能性
成果物
README、実行コマンド、スクリーンショット/ログ、次の一手

この節で一番大事なのは、RAG と微調整という2つの言葉を並べることではありません。大事なのは、次を理解することです。

RAG+微調整の統合システムの価値は、「知識の取得」と「回答の振る舞い」を、それぞれ最も適した仕組みに任せられることにある。

これこそが、組み合わせ型 LLM システムの本当の設計思想です。


ポートフォリオ向け提出チェックリスト

Section titled “ポートフォリオ向け提出チェックリスト”

このプロジェクトをポートフォリオに入れるなら、「1つ質問したら1つ答える」だけを見せるのはもったいないです。RAG 層、回答層、評価層、そして振り返り資料までまとめて出す方がよいです。

提出物最低要件ポートフォリオ級の要件
ナレッジベースのサンプル少なくとも 3~5 個の文書断片元資料、チャンク結果、metadata フィールド、出典を示す
検索ログ命中した文書を表示できるクエリ、top-k、score、source、コンテキスト 長を保存する
回答出力答えを返せる結論、根拠、出典、「答えられない場合の保険」を含める
評価セット2~5 個のテスト質問20~50 問で、言い換え、境界条件、紛らわしい質問を含める
失敗サンプル簡単なエラー記録検索失敗、生成失敗、引用失敗、形式失敗を分けて記録する
README動かし方を説明できる構成図、実行コマンド、入力出力例、指標、今後の計画がある

この表のポイントは、プロジェクトを「技術デモ」から「説明可能な作品」に引き上げることです。見る人は、当たったかどうかだけでなく、なぜ当たったのか、なぜ外れたのか、どう改善するのかまで確認します。

最終成果物は、次のような構成にまとめるとよいです。

領域入れるファイル
ルートREADME.md
data/raw_docs/chunks.jsonleval_questions.csv
src/ingest.pyretrieve.pyanswer.pyevaluate.py
logs/retrieval_logs.jsonlfailure_cases.md
reports/baseline_result.mdimprovement_record.md

最初からすべてのファイルを埋める必要はありませんが、少なくとも次の 3 本の流れが見えるようにしましょう。資料がどうシステムに入るか、質問がどう文書に命中するか、答えがどう評価されるか、です。

ポートフォリオの README は、「このプロジェクトは RAG と微調整を使っています」と書くだけでは弱いです。大切なのは、全体の流れが見えることです。

README の項目何に答えるべきか
プロジェクト目標このシステムはどの分野の問題を解決するのか、なぜ RAG や微調整が必要なのか
システム構成ユーザーの質問がどう検索、文脈、回答、引用を通るのか
実行方法依存関係の入れ方、データ準備、Q&A 実行、評価の方法
出力例質問、命中文書、最終回答、出典引用
評価結果ベースライン の成績、改善後の成績、失敗サンプル
技術的な取捨選択なぜ RAG を使うのか、なぜ微調整を検討するのか、両者の境界は何か
今後の計画検索、回答スタイル、コスト、デプロイのうち何を次に改善するのか

とても小さいですが有効な出力例は、次のように書けます。

Terminal window
質問:返金条件は何ですか?
命中文書:doc1 返金ポリシー score=0.92
回答:返金ポリシーによると、購入後 7 日以内かつ学習進捗が 20% 未満なら返金を申請できます。
出典:doc1
評価:retrieval_hit=true, answer_ok=true, citation_ok=true

RAG+微調整プロジェクトで、エンジニアリング力が最もよく出るのは成功例ではなく失敗例です。少なくとも 3 種類は記録するのがおすすめです。

  • 検索失敗: 正しいポリシーが top-k に入りません。chunk、キーワード一致、embedding を確認し、チャンク調整、混合検索、クエリ書き換えを試します。
  • 回答失敗: 資料は取れているのに、答えから重要条件が抜けます。prompt 制約と回答テンプレートを確認し、出力形式と must_contain 検査を強化します。
  • 引用失敗: 答えの結論と引用部分が一致しません。引用の結合ミスとモデルの自由な創作を確認し、citation check と文ごとの根拠を求めます。
  • スタイル失敗: 事実は合っていますが、業務表現に合いません。微調整データと例を確認し、形式例や教師ありデータを増やします。

失敗サンプルをきちんと書くと、成功スクリーンショットだけを貼るよりずっと説得力が出ます。

バージョン目標提出の重点
基礎版最小限の流れを動かす入力できる、処理できる、出力できる、例を 1 セット残す
標準版見せられるプロジェクトにする設定、ログ、エラー処理、README、スクリーンショットを追加する
発展版ポートフォリオ品質に近づける評価、比較実験、失敗サンプル分析、次の改善計画を入れる

最初は基礎版を完成させることをおすすめします。いきなり大きく全部を作ろうとしないでください。1 段階進めるたびに、「何が増えたか」「どう確認したか」「まだ何が課題か」を README に書くようにしましょう。

  1. ナレッジベースに新しい文書を 2 つ追加し、検索結果がどう変わるか観察してみましょう。
  2. 自分なりの「分野別回答スタイル規則」を設計し、微調整層の動きをまねてみましょう。
  3. 考えてみましょう。もし検索はいつも正しいのに、回答形式だけがいつも乱れるなら、優先して改善すべきなのは RAG でしょうか、それとも微調整でしょうか?
  4. 自分の言葉で説明してみましょう。「RAG は知識を補い、微調整は振る舞いを補う」と言えるのはなぜでしょうか?
プロジェクト参考とレビュー観点
  1. 新しい文書は、query intent が合ったときだけ検索結果を変えるかを確認するために使います。ランダムに順位が揺れるだけなら改善とは言えません。
  2. style rules には、tone、section order、citation format、refusal boundary、domain terminology を含められます。
  3. 文書が正しく取れているのに形式だけが乱れるなら、まず behavior layer を改善します。prompt/schema を先に直し、パターンが安定してから fine-tuning を検討します。
  4. RAG は query 時に変化する domain knowledge を補います。fine-tuning は安定した response behavior、style、format を学ばせるのに向いています。