Databricks Certified Generative AI Engineer Associate 教科書
第3章 アプリケーション開発(Application Development, 30%)
🎯 この節の学習目標
RAG(Retrieval-Augmented Generation)の3文字のうち、R(検索)は 3-3 で、G(生成)は 3-1 で扱いました。この節のテーマは真ん中の A(Augmented=拡張)、つまり検索で得たチャンクをプロンプトに組み込み、LLM に「その根拠に基づいて」回答させる工程です。
LLM は本来、学習時の知識(パラメトリック知識)から回答します。プロンプトオーグメンテーションは、そこに実行時の外部知識を注入することで、次を実現します。
ただし、チャンクをただ雑に連結して渡すだけでは効果は半減します。プロンプトの構造が品質を左右します。
RAG プロンプトは次の3ブロックで構成するのが定石です。
| ブロック | 内容 | ポイント |
|---|---|---|
| ① 指示(Instruction) | 「以下のコンテキストのみに基づいて回答する」「コンテキストに答えがなければ『わかりません』と答える」 | ハルシネーション抑制の中核。回答の根拠をコンテキストに限定する |
| ② コンテキスト(Context) | 検索で得たチャンク群。区切り文字(--- や XMLタグ風の記法など)で本文と明確に区別する | チャンクごとにソース名などのメタデータを添えると出典表示が可能になる |
| ③ 質問(Question) | ユーザーの質問をそのまま置く | コンテキストの後に置き、「何に答えるべきか」を最後に明示する |
💡 具体例:RAG プロンプトテンプレート
あなたは社内文書に基づいて回答するアシスタントです。
以下のルールに従ってください。
- 回答は下記のコンテキストに書かれている情報のみに基づくこと
- コンテキストに答えが含まれない場合は「提供された文書からはわかりません」と答えること
- 回答の末尾に、根拠としたチャンクの出典(source)を挙げること
コンテキスト:
---
{context}
---
質問: {question}
区切り文字 --- でコンテキストの範囲を明示することで、モデルが「どこまでが参照資料で、どこからが質問か」を取り違えるのを防ぎます。
📝 試験のポイント
「RAG アプリでモデルが知識にない質問に対して事実をでっち上げる(ハルシネーション)。プロンプト側の対策は?」→ 「コンテキストのみに基づいて回答し、無ければ『わかりません』と答えるよう指示する」が正解の型です。temperature を上げる、コンテキストを削る、といった選択肢は逆効果で誤りです。
「回答の根拠となった文書を示してほしい」という要件(第1章のケースでも登場)は、検索時にメタデータ列を取得し、チャンク本文と一緒にプロンプトへ渡すことで実現します。
similarity_search(または retriever)で columns に source などのメタデータ列を含める[出典: ○○規程.pdf] チャンク本文…」の形式で各チャンクに出典を添えるモデルはコンテキストに書かれていない出典名を作り出すことがあるため、出典は「モデルに書かせる」だけでなく、アプリ側で検索結果のメタデータから機械的に表示する設計も併用すると堅牢です。
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 チェーンの完成形です。
コンテキストに入れるチャンク数(top-k)は多いほどよいわけではありません。2-4 で学んだコンテキストウィンドウ(トークン上限)との兼ね合いで調整します。
| 設定 | メリット | デメリット |
|---|---|---|
| k を大きくする | 答えを含むチャンクを取りこぼしにくい(recall 向上) | トークン消費・コスト・レイテンシ増。無関係なチャンクが混ざると回答品質が低下。上限超過のリスク |
| k を小さくする | 低コスト・低レイテンシ。ノイズが減る | 答えを含むチャンクが k 件に入らないと回答不能になる |
✅ この節のまとめ
{"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | parser が完成形。問1. RAG アプリケーションで、コンテキストに答えが含まれない質問に対してモデルがもっともらしい誤情報を生成してしまう。プロンプト設計での対策として最も適切なのはどれか。
正解:B
回答の根拠をコンテキストに限定し、答えられない場合の振る舞いを明示するのがハルシネーション抑制の定石です。Aの temperature 上昇はランダム性を増し逆効果、Cは外部知識への接地を放棄することになりハルシネーションが悪化します。Dの順序変更はこの問題の解決策ではありません。
問2. 「回答の根拠となった文書名をユーザーに示したい」という要件を満たす実装として最も適切なのはどれか。
正解:A
出典表示には、検索結果のメタデータ(source 等)をチャンクと一緒にコンテキストへ組み込む必要があります。Bは出典の捏造を指示しているに等しく、信頼性を損ないます。Cは誤りで、チャンク本文だけではモデルは由来を知り得ません。Dは根拠と無関係なファイル名の羅列であり、トークンを浪費するだけで出典の正確さは担保されません。
問3. RAG チェーン {"context": retriever | format_docs, "question": RunnablePassthrough()} | prompt | llm | parser における format_docs の役割はどれか。
正解:B
retriever の出力は Document オブジェクトのリストであり、そのままではプロンプトの {context} 変数に適した形ではありません。format_docs は各チャンクの本文(と出典メタデータ)を連結し、1つの文字列に整形します。質問の埋め込み(A)は検索基盤側が行い、出力のパース(C)はチェーン末尾のパーサーの役割、インデックスへの追加(D)は検索パイプライン構築(3-3)の話で、このチェーンでは行いません。
問4. RAG アプリで retriever の k(取得チャンク数)を 3 から 20 に増やしたところ、コストが増えただけでなく回答品質も下がった。原因として最も考えられるものはどれか。
正解:B
k を大きくすると recall は上がりますが、下位の低関連チャンクがコンテキストに混ざり、ノイズとトークン消費が増えて回答品質がむしろ下がることがあります。AとCとDはいずれも事実に基づかない説明です。「候補は広く取りたいがノイズは入れたくない」という状況への対処が、次節のリランキングです。