powered by TechFeed
表示モード
Deep Dive

RAGの精度はチャンク設計より「質問の分解」で決まる — ユーザー入力を型付きフィールドに変換して検索・生成を制御する手法

7月17日、Toward Data Scienceが「Context Engineering for RAG Question Parsing: From a Raw Question to Typed Fields That Steer Retrieval and Generation」と題した記事を公開した。この記事では、RAGパイプラインにおいてユーザーの質問を型付きフィールドに分解し、検索・生成の各ステップを制御する「質問側のコンテキストエンジニアリング」手法について詳しく紹介されている。

7月17日、Toward Data Scienceが「Context Engineering for RAG Question Parsing: From a Raw Question to Typed Fields That Steer Retrieval and Generation」と題した記事を公開した。この記事では、RAGパイプラインにおいてユーザーの質問を型付きフィールドに分解し、検索・生成の各ステップを制御する「質問側のコンテキストエンジニアリング」手法について詳しく紹介されている。


RAGの盲点:質問文そのものがコンテキストである

RAGの改善というと、チャンク設計やハイブリッド検索、リランキングなど「検索側」の話が中心になりがちだ。しかし記事が指摘するのは、ユーザーの質問文自体もLLMが受け取るコンテキストであり、同等の設計が必要だという点だ。

例として挙げられるのが、保険アナリストによる次の質問だ。

"What is the maximum coverage amount? Don't confuse it with the deductible, they're often listed together."

この一文には4つのシグナルが混在している。トピック、否定的手がかり、期待する回答の形式、文書構造のヒントだ。これをそのままコサイン類似度のtop-k検索に渡すと、埋め込みはdeductible(免責額)を含む行を拾い、生成は最初に見つかった数値を返す。これは検索の失敗でも生成の失敗でもなく、質問側のコンテキストエンジニアリングの失敗だと記事は位置付ける。


質問パーサーの二重の役割

記事が解説するのは、エンタープライズRAGの「質問パース(brick 2)」をLangChainが整理した4つのコンテキストエンジニアリング戦略(write / select / compress / isolate)で読み直す視点だ。

質問パーサーは、パイプライン中で唯一コンテキストの消費者であると同時に書き手でもあるブロックだ。

消費者としての側面では、パーサー自身のLLM呼び出しに渡すコンテキストウィンドウは以下の4スロットだけで構成される:

  • 固定のPARSE_QUESTION_SYSTEM_PROMPT(セッション全体でキャッシュ)
  • DocContextコンパクトJSON(doc_type、ページ数、典型フィールドなど約100トークン)
  • few-shotブロック(questionParsedQuestionのペア)
  • 生のユーザー質問(唯一の可変ペイロード)

ドキュメント本文も、検索結果も、メモリも一切含まない。パーサーは検索より前に位置するため、検索結果はまだ存在しないからだ。最小限に絞ること自体が設計である。

書き手としての側面では、パーサーはquestion_dfの1行と、下流の3つの呼び出しに渡される派生データを生成する。各フィールドは受け取り手が決まっている:

  • keywords → 検索内のキーワードマッチング検出器
  • intent → ディスパッチャー(モデルティア・チャンク戦略の決定)
  • pages_hint → アンカー検出器(ページ範囲の絞り込み)

誰も使わないフィールドは行に含めない。


4戦略 × 4つの型付きフィールド

記事の核心は、4つの戦略を質問パースの各出力に対応付けた以下のマッピングだ。

戦略 対応する出力 役割
Write ParsedQuestion(型付き行) 下流全体への契約
Compress 検索ブリーフ 検索に不要なフィールドを除去
Select 生成ブリーフ ディスパッチャーが決定済みの選択を渡す
Isolate ClarificationRequest 低品質な入力を下流に伝播させない

Write:ParsedQuestionはスキーマ契約

多くのRAGパイプラインはユーザーの質問を「文体を保ったリライト文字列」に変換する。記事が提示するアプローチは逆で、名前付きフィールドを持つ型付き行として書き出すことで、下流の全呼び出しが同じ構造を前提にできる。フィールド名がそのままテスト可能な契約になる——「pages_hintが存在するなら、検索ブリーフには必ずそれが含まれる」とアサートできる。

Compress:検索ブリーフはアーキテクチャ上の分離

検索ブリーフはParsedQuestionより小さいが、それはトークン節約が目的ではない。アンカー検出器がshapeフィールドを見られなければ、それに基づく誤った分岐が発生しない。省略されたフィールドは、決して形成されない結合(カップリング)だ。

Select:生成ブリーフはLLMへの"既決定事項"

生成ブリーフは「生成が必要なフィールド」ではなく、「ディスパッチャーがPythonで決定済みの事項」を渡すものだ。intentsection_hintlayout_hintpages_hintがそれぞれchunk_strategysuggested_model、3つのアクティベーションフラグに変換される。LLMはテンプレートを自分で選ばず、選択済みの結果を受け取って実行するだけだ。

Isolate:曖昧な質問を止める

パーサーがintentやscope(範囲)を確信を持って解決できない場合、最良の推測を転送しない。**ClarificationRequestを書き、パイプラインを停止し、ユーザーに確認を求める**。ユーザーの回答はデフォルトとしてキャッシュされ、同じ曖昧さが再度発生した際は自動で解決される。


実例:保険質問のパース結果

記事では5つの質問に対して実際のJSON出力が示されている。冒頭の保険質問のケースは以下のようになる:

{
  "keywords": ["maximum coverage amount", "deductible"],
  "intent": "factual",
  "retrieval": {
    "main_query": "maximum coverage amount",
    "rewrites": ["policy limit", "coverage limit"],
    "section_hint": null,
    "layout_hint": null
  }
}

ディスパッチ結果では全アクティベーションフラグがfalseとなり、ドキュメント全体を検索対象にするフルスキャンモードになる。

記事はまた、現スキーマの限界も正直に記している。否定的手がかり("Don't confuse with deductible")を表す専用フィールドが現時点では存在しない。そのためdeductibleは除外対象のヒントとして質問文中に登場するにもかかわらず、現状ではkeywords配列にmaximum coverage amountと並列で格納される。両語が同列に扱われるため、曖昧さの解消は検索エンジンとLLMの文脈読解に委ねられる形になる。negative_keywordsフィールドの追加はスキーマ進化の検討事項として言及されるが、現状の実装として誇張はない。


設計上の示唆:「質問の構造化」がパイプライン全体の品質を決める

記事全体を通じて浮かび上がるのは、RAGパイプラインの品質はインプット側の構造化によって大きく規定されるという視点だ。検索インデックスの精緻化や生成プロンプトの調整は、質問が適切に分解されていることを前提として初めて機能する。型付きフィールドへの変換はその前提を明示的に担保する仕組みであり、テスト可能・バージョン管理可能なスキーマ契約として機能する点で、単なるプロンプト改善とは一線を画す。コンパニオンコードはdoc-intel/notebooks-vol1で公開されており、5つの質問を自分でパースしてParsedQuestion JSONを確認できる。

詳細はContext Engineering for RAG Question Parsing: From a Raw Question to Typed Fields That Steer Retrieval and Generationを参照していただきたい。