6月18日、Toward Data Scienceが「Dispatching the Parsed RAG Question: Chunk Strategy, Model Tier, Activations, Audit」と題した記事を公開した。この記事では、エンタープライズRAGシステムにおける質問パース後の意思決定フロー——チャンク戦略・モデル選択・アクティベーション制御・監査——を実装レベルで詳述している。
なぜ「質問パース後の処理」が重要なのか
RAGシステムで見落とされがちなのが、ユーザーの質問をパースした後のディスパッチ(dispatch)処理だ。「名前は何ですか?」という質問に対し、単純にキーワード name で全文検索しても、履歴書には「name」という単語は登場しない。人間であれば文書を一瞥して「これは履歴書だから、候補者名を聞いているのだ」と理解する。パーサーも同じ出発点が必要だ。
本記事は、エンタープライズRAGをパーシング・質問パース・検索・生成の4ブロックで構築するシリーズ「Enterprise Document Intelligence」の第6回cパートに当たる。シリーズの前回(第6回bパート)では、質問テキストから直接抽出できる情報——answer_shape(単一値か複数値か)、answer_type(金額・日付・フラグ等)、マッチしたコンセプト名——をパーサーが導出する手順を解説している。本稿(6cパート)では、それらの出力値を受け取り、文書プロファイルを参照してさらに決定する列(チャンク戦略・モデル選択・アクティベーション)を扱う。
※本記事はシリーズの一部として読むと理解が深まる。前回パート(6b)ではパーサーが質問から直接取り出す情報(形状・型・コンセプト)を扱っており、本稿の「3つのディスパッチ決定」はその出力を前提としている。
核心:3つのディスパッチ決定
パーサーは質問解析後に、以下の3つを決定する。すべて「コンセプト単位のオーバーライド → 形状/タイプのデフォルト → プロジェクト全体のフォールバック」という同一カスケードで解決される。
1. どれだけのコンテキストを読むか(answer_context)
StructuralHints 上の3フィールドがこれを制御する:
detection_context:正規表現による確認ゾーンの粒度。金額・日付なら"line"、叙述文なら"paragraph"answer_context:生成器に渡す周辺テキストの範囲。"line"/"paragraph"/"page"/"section"/"chapter"/"document"から選択needs_summary:答えが逐語引用に収まらない場合にTrue
「年間保険料はいくら?」は (single, amount) なので answer_context = "line"。「この契約の除外事項は?」は exclusions コンセプトに一致するため、concepts_df のオーバーライドで answer_context = "chapter" になる。
2. チャンクをどう処理するか(chunk_strategy)
ここが最もコスト効率に直結する部分だ。
- 単一の事実(金額・日付・IBAN・yes/no)は sequential:検索順位に従い1チャンクずつLLMに投入し、
answer_found=Trueになった時点で停止。k=3のとき、上位1チャンクに答えがあれば入力トークンを約⅔削減できる。 - 複数箇所から合成が必要な答え(除外条項のリスト、定義と脚注の組み合わせ)は combined:k個のチャンクを1回のLLM呼び出しにまとめる。
この判断はパーサーが一度行い、検索と生成は実行するだけ。エンタープライズスケール(数百万文書 × top-kチャンク)では、この差がLLMコストの大半を占めると記事は指摘する。
解決ロジックのコードは以下のとおり:
def resolve_chunk_strategy(
answer_shape: str,
matched_concept: str | None,
answer_shapes_df: pd.DataFrame,
concepts_df: pd.DataFrame,
) -> Literal["combined", "sequential"]:
"""Concept-level override > answer-shape default > hard default."""
if matched_concept is not None:
row = concepts_df[concepts_df["concept"] == matched_concept]
if not row.empty and pd.notna(row.iloc[0].get("default_chunk_strategy")):
return row.iloc[0]["default_chunk_strategy"]
row = answer_shapes_df[answer_shapes_df["shape"] == answer_shape]
if not row.empty:
return row.iloc[0]["default_chunk_strategy"]
return "combined"
3. どのモデルを呼ぶか(suggested_model)
1行の金額を抽出するのに gpt-4.1 を使うのは無駄だ。かといって3ページの法律文書を小さなモデルに投げると品質が落ちる。
設計では2層のサテライトテーブルを使う。サテライトテーブルとは、メインのロジックコードとは分離された参照用データテーブルのことで、ここではモデル情報をコードに埋め込まず外部データとして管理する手法を指す:
llm_model_tiers_df:nano / mini / standard / large の4段階の概念的グルーピング(ベンダー非依存)llm_models_df:プロジェクトが呼び出せる具体的なモデルを1行1モデルで管理
デフォルトはティアではなく具体的なモデル名(llm_models_df の外部キー)を指す。gpt-4.1 から gpt-4.5 への切り替えは、llm_models_df の1行更新と評価スイートの再実行だけで済み、コード変更は不要だ。
アクティベーション:文書プロファイルへの適応
文書が期待どおりの構造を持つとは限らない。
Word文書で「1ページ目に何が書いてありますか?」と聞かれた場合、PDFと違い「1ページ目」はフォントや画面幅・プリンタドライバによって変わる。extract_page_numbers=True をハードコードすると、高い確度で誤ったページ参照を返す。
記事では DocumentProfile モデルで文書の実際の性質を把握し、アクティベーションを適切に降格させる設計を示している:
class DocumentProfile(BaseModel):
format: Literal["pdf", "docx", "html", "txt", "xlsx"]
has_toc: bool = False
has_tables: bool = False
n_pages: int | None = None # フォーマットに実ページがない場合はNone
languages: list[str] = Field(default_factory=list)
is_scanned: bool = False # OCR済み、スペルノイズに注意
def parse_question(question, doc_profile) -> ParsedQuestion:
parsed = base_parse(question)
if doc_profile.format == 'docx':
parsed.activations.extract_page_numbers = False
if not doc_profile.has_toc:
parsed.activations.use_toc_navigation = False
return parsed
よくある落とし穴: アクティベーションフラグをドキュメントタイプに関係なくデフォルト値でハードコードすること。常に
extract_page_numbers=Trueを設定するパイプラインは、実際のページを持たない文書でもページ引用を生成してしまう。アクティベーションは文書の実際のプロパティから導出しなければならない。
parsing_notes フィールドには「パーサーが検知したが強制できなかった制限」が記録され、生成側の _meta ブロックに流れる。ユーザーは誤った確度の高い答えではなく、「このフォーマットではページ参照は近似値です」という注記付きの答えを受け取る。
監査(Audit):意思決定の追跡可能性
タイトルに掲げられた「Audit」は、ここまでの3つのディスパッチ決定すべてに横断する設計上の要件だ。
チャンク戦略・モデル選択・アクティベーションのいずれも、「なぜその値になったか」のトレースが ParsedQuestion オブジェクト内に記録される。どのサテライトテーブルのどの行が適用されたか、コンセプトオーバーライドが発動したかフォールバックで解決されたか——こうした決定の根拠がログとして残ることで、本番環境での挙動を事後に検証できる。
エンタープライズ用途では、「なぜそのモデルが選ばれたか」「なぜページ参照が省略されたか」を説明できることが、システムの信頼性と運用保守性を大きく左右する。コードではなくデータで制御する設計は、この監査要件とも表裏一体だ。
設計思想:コードではなくデータで管理する
本記事全体を貫く方針は、意思決定をコードではなくサテライトテーブルに持たせることだ。モデルの追加、デフォルトの変更、コンセプト単位のオーバーライドはすべてSQLの更新で完結する。評価スイートさえ回せば、コードレビューなしにデプロイできる。
この設計が有効なのは、チャンク戦略・モデル選択・アクティベーション・監査の4要素がいずれも「運用中に頻繁に調整が必要になる」性質を持つからだ。新しいLLMモデルのリリース、新たな文書フォーマットへの対応、特定コンセプトの品質改善——こうした変更をコード修正なしに吸収できる構造は、エンタープライズRAGの長期運用において実質的なコスト削減につながる。
詳細はDispatching the Parsed RAG Question: Chunk Strategy, Model Tier, Activations, Auditを参照していただきたい。




