7月22日、Towards Data Scienceが「Prompt Engineering Isn't Enough: How Four Bricks of Context Engineering Stop RAG Hallucinations」と題した記事を公開した。この記事では、RAGのハルシネーション(幻覚)がプロンプトではなく上流の4つのコンポーネント(ブリック)に起因するという実証と、それぞれの対処法について詳しく紹介されている。
「プロンプトを書き直せば直る」——RAG(Retrieval-Augmented Generation)開発者が最初に試みるこの反射的対応は、多くの場合、問題の本質を外している。本記事が主張するのは、自信に満ちた誤回答の原因はほぼ常にプロンプトより上流にあるという点だ。
記事はこの問題を「コンテキストエンジニアリング」という枠組みで整理している。コンテキストエンジニアリングとは、モデルに渡すコンテキスト全体の品質を設計・制御する実践のことで、Andrej Karpathyらが「プロンプトエンジニアリング」の後継概念として提唱したアプローチだ。プロンプトの文言を調整するのではなく、モデルが受け取る情報そのものを正しく構築することに焦点を当てる。
ナイーブなRAGはどこで壊れるか
記事は「ナイーブなRAG」(最初期に誰もが作る素朴な実装)と、著者らが構築した改良版パイプラインを、同一ドキュメント・同一質問で比較したものだ。対象は実際の業務文書——World Bankのコモディティ価格レポート、NIST SP 800-207(ゼロトラストアーキテクチャ)、NIST Cybersecurity Framework 2.0など。
ナイーブなRAGの構成はシンプルだ。PDFをフラットテキストに変換し、固定サイズでチャンク分割、キーワードマッチでページを取得し、モデルに回答させる。短くクリーンな文章のドキュメントならこれで十分機能する。だが企業文書——表、独自語彙、長尺、複雑な構造——が相手になった途端、4つのブリック(構成要素)それぞれで異なる理由で壊れる。
記事が提示する4つの失敗は意図的に作り込んだ失敗例ではない。再現可能な実際の実行結果であり、GitHubのノートブックで誰でも検証できる。
ブリック1:パーシング——表がノイズに変わる
最も見落とされがちで、かつ破壊力が大きい失敗がこれだ。
World Bankのコモディティ価格レポートに対して「2025年の米国天然ガス(Henry Hub)の年間平均価格予測は?」と質問した場合、ナイーブなパイプラインは get_text() でPDFをフラットテキストに変換し、固定サイズでチャンク分割する。
価格表はグリッド構造だ。フラット化するとその行列関係が失われ、固定チャンクの境界でさらにバラバラになる。「Henry Hub」というラベルが1つのチャンクに、対応する「3.5」という数値が別のチャンクに入る。モデルが受け取るのは、両者を結びつける情報を持たないチャンクであり、正直に回答する——「これらの行には記載されていません」、信頼度 0.00。
ハルシネーションではない。数値は確かにドキュメントにあった。パーサーがラベルとセルの対応関係を破壊したのが原因だ。
改良版の対処:フラットテキストの代わりに line_df(バウンディングボックス付きの行単位データフレーム)を返すリレーショナルパーシングを採用する。表の行構造が保持され、モデルは「Henry Hub」と「3.5」を同一行で読む。結果:「$3.5 per mmbtu」、信頼度 0.99。
ブリック2:質問パーシング——ドキュメントにない単語で検索する
NIST SP 800-207に「ゼロトラストアーキテクチャのpillars(柱)は何か?」と質問した場合、ナイーブなパイプラインは「pillars」という単語でページを検索する。
問題は、このドキュメントが "pillars" という単語を一切使っていないことだ。NIST SP 800-207が使う用語は "tenets"(原則)だ。スコアはゼロ、モデルは「特定のピラーは記載されていません」と答える(信頼度 0.20)。答えはドキュメントにある。ただし別の名前で。
これはtop-kを増やしても解決しない。ランキングの問題ではなく、語彙の問題だ。
改良版の対処:検索前に質問を正規化・拡張する。pillars → tenets, principles のようなドメイン同義語マッピングを行い、ドキュメントが実際に使っている語彙で検索する。結果:7つのtenetがすべて列挙、信頼度 0.95。
ブリック3:リトリーバル——答えがカットオフ以下に沈む
NIST CSF 2.0(32ページ、目次あり)に「CSF 2.0でProfileはどう定義されているか?」と質問した場合、「Profile」という単語はドキュメント全体に散在する。キーワードや余弦類似度でのランキングでは、どのページも同程度に「関連度が高い」と判定され、定義が書かれたページはtop-kのカットオフ以下に沈む。ナイーブな実装の信頼度は 0.10。
改良版の対処:目次を小規模LLMで解析し、該当セクションに直接アンカリングしてリトリーバルを実行する構造ルーティングを採用する。400ページ超のNIST SP 800-53に対してAU-2コントロールを検索した場合、ナイーブな実装は類似した数百の兄弟コントロールに埋もれて信頼度 0.00、改良版は直接AU-2を特定して信頼度 0.98 を達成した。
ブリック4:生成——自己チェック機構のない自信満々な回答
World Bankレポート(2024年4月版、予測は2025年まで)に「2026年のBrent原油の年間平均価格予測は?」と質問した。
この場合、両パイプラインとも同じ正しいコンテキストを取得する。ただしドキュメントに2026年の行は存在しない。ナイーブなパイプラインの生成ブリックは自由テキストでモデルに回答を求める。モデルは質問に答えようとして、最も近い数値(2025年の$79)を取得し、「2026年のBrent予測は$79/バレル」と回答する。流暢で自信に満ちているが、完全な誤りだ。
これこそが読者が「ハルシネーション」と報告するもので、正しいコンテキストを手元に持ちながら最後のブリックで発生した。
改良版の対処:自由テキストではなく、型付きスキーマで回答を要求する。complete_answer_found フィールド、エビデンススパン(引用根拠)、信頼度の3点をモデルが必ず設定しなければならない構造だ。値がドキュメントに存在しない場合、モデルは complete_answer_found: false をセットし「2026年の予測は提供されていない。最新は2025年だ」と返す。OpenAIのStructured Outputsを活用した実装だ。
4つのブリック、1つの根本原因
記事の結論は明快だ。4つの失敗はすべて同じ構造を持っている——モデルは渡されたコンテキストに忠実に答えた。コンテキストが間違っていただけだ。
- パーサーが表を破壊した
- 質問の語彙がドキュメントの語彙と一致しなかった
- 答えのページがカットオフ以下に沈んだ
- 存在しない値を埋めることを止めるものがなかった
「ハルシネーション」という言葉はモデルを指すが、原因は上流のブリックにある。そして修正すべきはプロンプトではなく、各ブリックのコントラクト(契約)だ。記事はこれをコンテキストエンジニアリングと呼ぶ——プロンプトの文言ではなく、モデルに渡すコンテキスト全体の品質を設計するという実践だ。
一方で、記事は正直な注釈も付けている。ナイーブなRAGが常に間違うわけではない。短くクリーンな文書では十分に機能する。問題が顕在化するのは、表・独自語彙・長尺・複雑な構造を持つエンタープライズ文書を扱うときだ。
詳細はPrompt Engineering Isn't Enough: How Four Bricks of Context Engineering Stop RAG Hallucinationsを参照していただきたい。