第3章 アプリケーション開発 / 想定学習時間:30〜40分 / 最終確認:2026年8月

3-4. プロンプトオーグメンテーション(検索結果の組み込み)

🎯 この節の学習目標

1. RAG の「A」:検索結果を根拠としてプロンプトに埋め込む

RAG(Retrieval-Augmented Generation)の3文字のうち、R(検索)は 3-3 で、G(生成)は 3-1 で扱いました。この節のテーマは真ん中の A(Augmented=拡張)、つまり検索で得たチャンクをプロンプトに組み込み、LLM に「その根拠に基づいて」回答させる工程です。

LLM は本来、学習時の知識(パラメトリック知識)から回答します。プロンプトオーグメンテーションは、そこに実行時の外部知識を注入することで、次を実現します。

ただし、チャンクをただ雑に連結して渡すだけでは効果は半減します。プロンプトの構造が品質を左右します。

2. RAG プロンプトの基本構造:指示+コンテキスト+質問

RAG プロンプトは次の3ブロックで構成するのが定石です。

ブロック内容ポイント
① 指示(Instruction)「以下のコンテキストのみに基づいて回答する」「コンテキストに答えがなければ『わかりません』と答える」ハルシネーション抑制の中核。回答の根拠をコンテキストに限定する
② コンテキスト(Context)検索で得たチャンク群。区切り文字(--- や XMLタグ風の記法など)で本文と明確に区別するチャンクごとにソース名などのメタデータを添えると出典表示が可能になる
③ 質問(Question)ユーザーの質問をそのまま置くコンテキストの後に置き、「何に答えるべきか」を最後に明示する

💡 具体例:RAG プロンプトテンプレート

あなたは社内文書に基づいて回答するアシスタントです。
以下のルールに従ってください。
- 回答は下記のコンテキストに書かれている情報のみに基づくこと
- コンテキストに答えが含まれない場合は「提供された文書からはわかりません」と答えること
- 回答の末尾に、根拠としたチャンクの出典(source)を挙げること

コンテキスト:
---
{context}
---

質問: {question}

区切り文字 --- でコンテキストの範囲を明示することで、モデルが「どこまでが参照資料で、どこからが質問か」を取り違えるのを防ぎます。

📝 試験のポイント

「RAG アプリでモデルが知識にない質問に対して事実をでっち上げる(ハルシネーション)。プロンプト側の対策は?」→ 「コンテキストのみに基づいて回答し、無ければ『わかりません』と答えるよう指示する」が正解の型です。temperature を上げる、コンテキストを削る、といった選択肢は逆効果で誤りです。

3. 出典表示:チャンクと一緒にメタデータを渡す

「回答の根拠となった文書を示してほしい」という要件(第1章のケースでも登場)は、検索時にメタデータ列を取得し、チャンク本文と一緒にプロンプトへ渡すことで実現します。

  1. 3-3 の similarity_search(または retriever)で columnssource などのメタデータ列を含める
  2. コンテキスト整形時に「[出典: ○○規程.pdf] チャンク本文…」の形式で各チャンクに出典を添える
  3. 指示ブロックで「回答に出典を含めること」を明示する

モデルはコンテキストに書かれていない出典名を作り出すことがあるため、出典は「モデルに書かせる」だけでなく、アプリ側で検索結果のメタデータから機械的に表示する設計も併用すると堅牢です。

4. LangChain での実装:RAG チェーン全体

3-1 の骨格と 3-3 の retriever を組み合わせ、チャンク整形関数を挟んだ完全な RAG チェーンは次のとおりです。

💡 具体例:RAG チェーンの全体コード

from databricks_langchain import ChatDatabricks, DatabricksVectorSearch
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# 検索段(3-3):Databricks AI Search(旧 Vector Search) を retriever に
vector_store = DatabricksVectorSearch(
    index_name="main.rag.docs_index",
    columns=["chunk_text", "source"],
)
retriever = vector_store.as_retriever(search_kwargs={"k": 3})

# 検索結果(Documentのリスト)を出典付きテキストに整形
def format_docs(docs):
    return "\n\n".join(
        f"[出典: {d.metadata.get('source', '不明')}] {d.page_content}"
        for d in docs
    )

prompt = ChatPromptTemplate.from_template(
    "以下のコンテキストのみに基づいて回答してください。"
    "答えがなければ「提供された文書からはわかりません」と答えてください。"
    "回答の末尾に出典を挙げてください。\n\n"
    "コンテキスト:\n---\n{context}\n---\n\n質問: {question}"
)

llm = ChatDatabricks(endpoint="databricks-meta-llama-3-3-70b-instruct")

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

print(rag_chain.invoke("経費精算の締め日はいつですか?"))

流れ:質問文字列 → retriever が上位3チャンクを取得 → format_docs が出典付きテキストに整形して {context} へ、RunnablePassthrough が元の質問を {question} へ → プロンプト完成 → LLM が生成 → 文字列化。retriever | format_docs | prompt | llm | parser という並びが RAG チェーンの完成形です。

5. コンテキストの量と順序:top-k とトークン上限の調整

コンテキストに入れるチャンク数(top-k)は多いほどよいわけではありません。2-4 で学んだコンテキストウィンドウ(トークン上限)との兼ね合いで調整します。

設定メリットデメリット
k を大きくする答えを含むチャンクを取りこぼしにくい(recall 向上)トークン消費・コスト・レイテンシ増。無関係なチャンクが混ざると回答品質が低下。上限超過のリスク
k を小さくする低コスト・低レイテンシ。ノイズが減る答えを含むチャンクが k 件に入らないと回答不能になる

✅ この節のまとめ

練習問題

問1. RAG アプリケーションで、コンテキストに答えが含まれない質問に対してモデルがもっともらしい誤情報を生成してしまう。プロンプト設計での対策として最も適切なのはどれか。

  1. temperature を上げて、より多様な回答を生成させる
  2. 「以下のコンテキストのみに基づいて回答し、答えが含まれない場合は『わかりません』と答える」という指示を加える
  3. コンテキストをプロンプトから削除し、モデルの学習知識だけで回答させる
  4. 質問をコンテキストの前に移動する
解答と解説を見る

正解:B

回答の根拠をコンテキストに限定し、答えられない場合の振る舞いを明示するのがハルシネーション抑制の定石です。Aの temperature 上昇はランダム性を増し逆効果、Cは外部知識への接地を放棄することになりハルシネーションが悪化します。Dの順序変更はこの問題の解決策ではありません。

問2. 「回答の根拠となった文書名をユーザーに示したい」という要件を満たす実装として最も適切なのはどれか。

  1. 検索時にソース名などのメタデータ列も取得し、チャンク本文とともにプロンプトへ渡して出典として挙げさせる
  2. モデルに「もっともらしい文書名を推測して付けてください」と指示する
  3. チャンク本文のみを渡す。モデルはどの文書由来かを自動的に知っている
  4. 全文書のファイル名一覧を毎回プロンプトに含める
解答と解説を見る

正解:A

出典表示には、検索結果のメタデータ(source 等)をチャンクと一緒にコンテキストへ組み込む必要があります。Bは出典の捏造を指示しているに等しく、信頼性を損ないます。Cは誤りで、チャンク本文だけではモデルは由来を知り得ません。Dは根拠と無関係なファイル名の羅列であり、トークンを浪費するだけで出典の正確さは担保されません。

問3. RAG チェーン {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | parser における format_docs の役割はどれか。

  1. ユーザーの質問を埋め込みベクトルに変換する
  2. retriever が返した Document のリストを、プロンプトに挿入できる1つのテキストに整形する
  3. LLM の出力を JSON にパースする
  4. インデックスに新しいチャンクを追加する
解答と解説を見る

正解:B

retriever の出力は Document オブジェクトのリストであり、そのままではプロンプトの {context} 変数に適した形ではありません。format_docs は各チャンクの本文(と出典メタデータ)を連結し、1つの文字列に整形します。質問の埋め込み(A)は検索基盤側が行い、出力のパース(C)はチェーン末尾のパーサーの役割、インデックスへの追加(D)は検索パイプライン構築(3-3)の話で、このチェーンでは行いません。

問4. RAG アプリで retriever の k(取得チャンク数)を 3 から 20 に増やしたところ、コストが増えただけでなく回答品質も下がった。原因として最も考えられるものはどれか。

  1. k を増やすと埋め込みモデルの次元数が変わってしまうため
  2. 関連度の低いチャンクが多数コンテキストに混入し、ノイズが増えてモデルが重要情報に集中できなくなったため
  3. k は 3 の倍数でなければならないため
  4. AI Search は k が 10 を超えると精度が保証されないため
解答と解説を見る

正解:B

k を大きくすると recall は上がりますが、下位の低関連チャンクがコンテキストに混ざり、ノイズとトークン消費が増えて回答品質がむしろ下がることがあります。AとCとDはいずれも事実に基づかない説明です。「候補は広く取りたいがノイズは入れたくない」という状況への対処が、次節のリランキングです。