1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RAGチャンキング戦略7選の精度比較とパラメータ最適化の実践ガイド

1
Last updated at Posted at 2026-03-04

RAGチャンキング戦略7選の精度比較とパラメータ最適化の実践ガイド

RAGシステムの検索精度を左右する最大の要因は、テキストをどう分割するか、すなわちチャンキング戦略です。2025〜2026年に公開された複数のベンチマーク研究により、「高度なAI駆動手法がシンプルな手法を上回るとは限らない」という意外な結果が明らかになりました。

この記事では、7つのチャンキング戦略を実データのベンチマークで比較し、チャンクサイズ・オーバーラップ率などのパラメータ最適化手法を、MLEが自分のRAGパイプラインにすぐ適用できる形で解説します。

この記事でわかること

  • 7つのチャンキング戦略(Fixed-size〜Late Chunking)の精度比較と使い分け
  • チャンクサイズ・オーバーラップ率の最適値とドメイン別推奨パラメータ
  • 「チャンクサイズはクエリ依存」という2025年の発見とマルチスケールアプローチ
  • LangChain・LlamaIndexでの実装例とパラメータチューニングの手順
  • 実運用で陥りやすい落とし穴と対処法

対象読者

  • 想定読者: RAGシステムを構築・運用しているMLE
  • 必要な前提知識:
    • Embeddingモデル(OpenAI text-embedding-3-small 等)の基本的な使い方
    • Pythonの基礎文法
    • RAG(Retrieval-Augmented Generation)の基本概念

結論・成果

2026年2月のVectaベンチマーク(学術論文50本、7戦略比較)によると、Recursive Character Splitting(512トークン)が精度69%で全手法中1位を達成しています。一方、Semantic Chunkingは過度な断片化(平均43トークン/チャンク)によりドキュメントレベルF1が0.42まで低下しました。

また、医療文書ドメインではAdaptive Chunkingが完全正確率50%を達成し、Fixed-sizeの13%を大幅に上回ったと報告されています(PMCの査読済み論文、p=0.001)。

パラメータの出発点として、256〜512トークン、オーバーラップ10〜20% が複数のベンチマークで推奨されています。

チャンキング戦略7選を精度ベンチマークで比較する

「どのチャンキング手法を使うべきか?」はRAG構築で最初に直面する設計判断です。ここでは、2026年2月にVecta社が公開したベンチマークを軸に、各手法の特性と精度を比較します。

ベンチマーク結果の全体像

Vecta社は学術論文50本に対して7つのチャンキング戦略を適用し、約2,000トークンのコンテキスト予算で公平に比較しました。

戦略 精度 平均トークン/チャンク Doc F1 Page F1
Recursive 512 69% 397 0.86 0.92
Fixed 512 67% 401 0.85 0.88
Fixed 1024 61% 658 0.88 0.72
Page-per-Chunk 57%
Semantic 54% 43 0.42 0.91
Doc-Structure 52%
Proposition 51% 17 0.27 0.97

出典: We Benchmarked 7 Chunking Strategies on Real-World Data(Vecta, 2026年2月)

注目すべきは、Semantic Chunkingが平均43トークンまで断片化し、ページレベルでは高精度(F1=0.91)ながらドキュメントレベルの検索精度(F1=0.42)が壊滅的に低下した点です。同様にProposition Chunkingも平均17トークンまで分割され、Doc F1=0.27と低迷しています。

注意: このベンチマークは学術論文というドメインに特化した結果です。ドメインが異なれば最適な戦略も変わるため、自分のデータで必ず検証してください。

各手法の特性と使い分け

ここからは、各手法の仕組みと適用場面を見ていきましょう。

Fixed-size Chunking

最もシンプルな手法で、固定トークン数で機械的にテキストを分割します。

# Fixed-size Chunkingの基本実装
from langchain.text_splitter import CharacterTextSplitter

splitter = CharacterTextSplitter(
    separator="\n",       # 区切り文字
    chunk_size=512,       # チャンクサイズ(文字数)
    chunk_overlap=50,     # オーバーラップ
    length_function=len,
)

chunks = splitter.split_text(document_text)
# 各チャンクは約512文字、50文字のオーバーラップで分割される

適用場面: プロトタイピング、均質なテキスト(ログデータ等)
制約: 文の途中で切断されるリスクがあり、意味的一貫性は保証されません。

Recursive Character Splitting

階層的セパレータ(\n\n\n "")を段階的に適用し、文脈境界を尊重しつつチャンクサイズを制御します。2026年のベンチマークで総合1位を獲得しました。

# Recursive Character Splitting(推奨手法)
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=50,
    # デフォルトのセパレータ: ["\n\n", "\n", " ", ""]
    # 段落 → 改行 → スペース → 文字 の順で分割を試みる
    separators=["\n\n", "\n", "", "", " ", ""],  # 日本語対応版
    length_function=len,
)

chunks = splitter.split_text(document_text)

なぜこの手法が1位なのか:

  • 段落境界を優先的に保持するため、意味的な切断が少ない
  • チャンクサイズの制約を守りつつ、セパレータの階層でフォールバックできる
  • 計算コストが極めて低く、大規模データにも対応可能

注意点:

セパレータの設定は言語・ドメインに依存します。日本語テキストでは["。", "、", "\n"]を追加しないと句読点で分割されません。

Semantic Chunking

Embeddingの類似度を用いて意味的な境界を検出し、関連する文をグループ化します。

# Semantic Chunking(LlamaIndex実装)
from llama_index.core.node_parser import SemanticSplitterNodeParser
from llama_index.embeddings.openai import OpenAIEmbedding

embed_model = OpenAIEmbedding(model="text-embedding-3-small")

splitter = SemanticSplitterNodeParser(
    buffer_size=1,                # 前後の文を含めた類似度計算の窓サイズ
    breakpoint_percentile_threshold=95,  # 類似度の閾値(パーセンタイル)
    embed_model=embed_model,
)

nodes = splitter.get_nodes_from_documents(documents)

なぜベンチマークで低精度だったのか:

  • breakpoint_percentile_threshold の値次第で極度に細かく分割される
  • 平均43トークンのチャンクは、Embeddingモデルが十分な意味を捉えるには短すぎる
  • 「意味的に一貫した分割」と「検索に十分な情報量の確保」はトレードオフの関係にある

適用場面: 閾値パラメータを十分にチューニングできる場合、技術文書やナレッジベースで有効です。ただし、チューニングなしのデフォルト設定では断片化リスクが高いことを認識しておく必要があります。

Agentic Chunking

LLMにテキストを読ませ、意味的に適切な分割点をLLM自身に判断させる手法です。

# Agentic Chunkingの概念コード
# LLMに各文がどのチャンクに属するか判断させる
import openai

def agentic_chunk(text: str, client: openai.OpenAI) -> list[str]:
    """LLMに文書を意味単位で分割させる"""
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{
            "role": "system",
            "content": (
                "以下のテキストを意味的に一貫したセクションに分割してください。"
                "各セクションの区切りを '---SPLIT---' で示してください。"
                "各セクションは200-500単語を目安にしてください。"
            ),
        }, {
            "role": "user",
            "content": text,
        }],
    )
    chunks = response.choices[0].message.content.split("---SPLIT---")
    return [chunk.strip() for chunk in chunks if chunk.strip()]

制約条件:

  • LLM APIコールが必要なため、大規模データには不向き(コストと処理時間が線形に増加)
  • Vectaベンチマークの「Proposition Chunking」はこの手法の一種で、精度51%と低迷
  • 処理時間がRecursive Splittingの100倍以上になるケースがあり、バッチ処理パイプラインのボトルネックになる

適用場面: 少量の重要文書(契約書、医療文書等)で品質が最優先の場合

Late Chunking

2025年から注目度が急上昇している手法です。従来のチャンキングは「分割してからEmbedding」ですが、Late Chunkingは文書全体をlong-contextエンベディングモデルに入力し、トークンレベルの埋め込みを取得してからチャンクに分割します。

なぜ注目されているのか:

  • 従来手法の根本的な課題「チャンク分割時の文脈喪失」を構造的に解決する
  • jina-embeddings-v2(8,192トークン)やjina-embeddings-v3等のlong-contextモデルの登場で実用的になった
  • チャンク境界の設定自体はシンプルな手法(固定長やRecursive)を併用可能

制約条件:

Long-contextエンベディングモデルが必須です。text-embedding-3-small(8,191トークン)でも文書が長い場合は複数回に分ける必要があります。また、文書全体を一度に処理するため、メモリ使用量は従来手法より大きくなります。

Parent-Child(階層的)Chunking

小さいチャンク(子)で検索精度を高め、実際にLLMに渡すのは大きいチャンク(親)という二段階の設計です。

# Parent-Child Chunkingの実装例
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 親チャンク: LLMに渡す単位(文脈が十分)
parent_splitter = RecursiveCharacterTextSplitter(
    chunk_size=2000,
    chunk_overlap=200,
)

# 子チャンク: 検索に使う単位(精度が高い)
child_splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,
    chunk_overlap=50,
)

parent_chunks = parent_splitter.split_text(document_text)

# 各親チャンクから子チャンクを生成し、親への参照を保持
for i, parent in enumerate(parent_chunks):
    children = child_splitter.split_text(parent)
    for child in children:
        # 子チャンクをベクトルDBに格納(parent_idをメタデータに)
        vector_store.add(
            text=child,
            metadata={"parent_id": i, "parent_text": parent},
        )

# 検索時: 子チャンクで検索 → 親チャンクをLLMに渡す

日本語のRAG実装で、チャンクサイズを1,000文字→2,000文字に拡大し、オーバーラップを200→500文字にしたところ、精度が73.3%から100%に向上したとの報告があります(Zennの実践記事)。同記事では、Parent-Childパターンも93.3%の精度を達成しています。

Contextual Retrieval

チャンクにタイトル・見出し・要約をプリペンド(前置き追加)してからEmbeddingする手法です。Anthropicが2024年に提案した手法で、チャンク単体では失われる文脈情報を補完します。

# Contextual Retrievalの実装概念
def add_context_to_chunk(chunk: str, document: str, client) -> str:
    """チャンクに文書全体の文脈情報を付加する"""
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=200,
        messages=[{
            "role": "user",
            "content": (
                f"<document>\n{document}\n</document>\n"
                f"<chunk>\n{chunk}\n</chunk>\n"
                "このチャンクが文書全体のどの部分にあたるか、"
                "簡潔なコンテキスト(1-2文)を生成してください。"
            ),
        }],
    )
    context = response.content[0].text
    # コンテキストをチャンクの先頭に付加
    return f"{context}\n\n{chunk}"

トレードオフ: LLMコールが各チャンクに必要なため処理コストが増加しますが、検索精度の向上が見込めます。プロンプトキャッシングを活用することでAPI呼び出しコストを最大90%削減可能です。

手法選択のフローチャート

チャンクサイズとオーバーラップ率を最適化する

チャンキング戦略を選んだ後の次のステップは、チャンクサイズとオーバーラップ率のパラメータチューニングです。2025年のarXiv論文が「チャンクサイズは固定の最適値を持たない」ことを実証し、パラメータ最適化の考え方が大きく変わりました。

チャンクサイズの影響:データセット別の実験結果

arXivの論文「Rethinking Chunk Size for Long-Document Retrieval」は、4つのQAデータセットで最適チャンクサイズが大きく異なることを示しました。

データセット 文書平均トークン 最適チャンクサイズ Recall@1
SQuAD 7,998 64トークン 64.1%
NewsQA 8,485 512トークン 55.9%
TechQA 7,597 1,024トークン 71.5%
NarrativeQA 51,830 1,024トークン 10.7%

よくある間違い: 「チャンクサイズ512が万能」と固定してしまうことです。この論文のoracle実験では、クエリごとに最適なチャンクサイズを選択した場合、固定サイズと比較して20〜40%の精度ギャップがあることが確認されています。つまり、固定サイズでは理論上の上限に大きな差があります。

クエリタイプ別の推奨チャンクサイズ

チャンクサイズの最適値は、回答に必要な文脈の広さに依存します。

クエリタイプ 推奨チャンクサイズ 具体例
エンティティ検索(事実ベース) 64〜256トークン 「Pythonの最新バージョンは?」
手順・方法の説明 256〜512トークン 「Dockerfileの書き方は?」
概念理解・要約 512〜1,024トークン 「マイクロサービスの設計原則は?」

オーバーラップ率の最適化

オーバーラップは「チャンク境界での情報損失」を防ぐために設定しますが、大きすぎるとインデックスの冗長性が増加します。

# オーバーラップ率の影響を比較する実験コード
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 実験パラメータ
configs = [
    {"chunk_size": 512, "chunk_overlap": 0,    "label": "0%"},
    {"chunk_size": 512, "chunk_overlap": 50,   "label": "~10%"},
    {"chunk_size": 512, "chunk_overlap": 100,  "label": "~20%"},
    {"chunk_size": 512, "chunk_overlap": 200,  "label": "~40%"},
]

for config in configs:
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=config["chunk_size"],
        chunk_overlap=config["chunk_overlap"],
    )
    chunks = splitter.split_text(document_text)
    print(f"Overlap {config['label']}: {len(chunks)} chunks")
    # → 各設定でチャンク数が変わる
    # → 多いほどインデックスサイズが増加、検索時の計算コストも増加

Chroma Researchのベンチマークでは、OpenAIのデフォルト設定(800トークン、400オーバーラップ=50%)が特に悪いRecall-Efficiencyトレードオフを示しました。さらに、ゼロオーバーラップがIoUスコアを改善するケースも確認されています。

推奨ガイドライン:

オーバーラップ率 適用場面 備考
0% 構造化文書(セクション区切りが明確) インデックスサイズ最小
10〜20% 汎用的な出発点(推奨) 情報損失と冗長性のバランス
20〜30% 法律文書・学術論文 文脈の連続性が重要
30%以上 通常は不要 冗長性過多でRecallが低下するリスク

ドメイン別推奨パラメータ一覧

実運用では、ドメインに応じた初期パラメータを設定し、そこからチューニングする方法が効率的です。

ドメイン チャンクサイズ オーバーラップ 推奨手法
技術マニュアル 300〜500単語 20〜30% Recursive Splitting
ニュース記事 200〜300単語 10〜15% Fixed-size or Recursive
法律文書 400〜600単語 25〜35% Recursive + Parent-Child
会話ログ 100〜200単語 15〜20% Fixed-size
学術論文 500〜700単語 20〜30% Doc-Structure or Recursive
医療文書 400〜600単語 20〜30% Adaptive Chunking

出典: RAGシステムにおける効果的なチャンキング戦略(Zenn)

ポイント: これらの値は出発点です。自分のデータセットとクエリパターンに対してA/Bテストを行い、Recall@K・Precision・F1を計測して最適化してください。

マルチスケールアプローチとEmbeddingモデルの選択

2025年以降、単一チャンクサイズの限界を克服するための新しいアプローチが登場しています。ここでは、マルチスケールインデキシングとEmbeddingモデルの特性について見ていきましょう。

マルチスケールインデキシング

AI21が提案したマルチスケールアプローチは、複数のチャンクサイズで同時にインデックスを構築し、検索時にReciprocal Rank Fusion(RRF)で結果を統合します。

# マルチスケールインデキシングの実装例
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 複数のチャンクサイズでインデックスを構築
chunk_sizes = [100, 200, 500, 1000, 2000]
all_chunks = {}

for size in chunk_sizes:
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=size,
        chunk_overlap=int(size * 0.1),  # 各サイズで10%オーバーラップ
    )
    all_chunks[size] = splitter.split_text(document_text)
    # 各サイズのチャンクを別々のコレクション or メタデータで管理
    for chunk in all_chunks[size]:
        vector_store.add(
            text=chunk,
            metadata={"chunk_size": size},
        )


def multi_scale_search(query: str, k: int = 5) -> list[str]:
    """複数スケールで検索し、RRFで統合"""
    results_per_scale = {}
    for size in chunk_sizes:
        # 各スケールのチャンクから検索
        results = vector_store.similarity_search(
            query,
            k=k,
            filter={"chunk_size": size},
        )
        results_per_scale[size] = results

    # Reciprocal Rank Fusion(RRF)でスコアを統合
    rrf_scores = {}
    for size, results in results_per_scale.items():
        for rank, doc in enumerate(results):
            doc_id = doc.metadata.get("doc_id", doc.page_content[:50])
            if doc_id not in rrf_scores:
                rrf_scores[doc_id] = {"score": 0, "doc": doc}
            rrf_scores[doc_id]["score"] += 1 / (60 + rank)  # RRFの標準式

    # スコア順にソートして返す
    sorted_results = sorted(
        rrf_scores.values(), key=lambda x: x["score"], reverse=True
    )
    return [r["doc"] for r in sorted_results[:k]]

AI21の報告では、一般的なデータセットで1〜3%の改善、特にTRECCOVIDデータセットでは**+36.7%の大幅改善**を達成しています。モデルの再訓練やリトリーバーの変更なしで適用可能な点が実用的です。

トレードオフ: インデックスサイズがチャンクサイズ数 × 元のサイズに増加します。5スケールで構築すると、ストレージと検索時の計算コストが約5倍になります。

Embeddingモデルとチャンクサイズの相性

見落とされがちですが、Embeddingモデルのアーキテクチャとチャンクサイズには相性があります。

モデルタイプ 代表例 推奨チャンクサイズ 最大コンテキスト
エンコーダーベース Snowflake Arctic-embed 小(128〜512トークン) 8,194トークン
デコーダーベース Stella 大(512〜2,000トークン) 130,000+トークン
Long-context対応 jina-embeddings-v3 Late Chunking向き 8,192トークン

arXivの論文によると、デコーダーベースのStellaモデルは大きいチャンクで5〜8%高い性能を発揮し、エンコーダーベースのSnowflakeモデルは小さいチャンクで優位とのことです。

よくある間違い: Embeddingモデルの最大コンテキスト長ギリギリまでチャンクサイズを大きくしてしまうことです。コンテキスト長の50〜70%程度が安全な範囲であり、末尾のトークンの品質が劣化するモデルもあります。

パラメータチューニングの実践手順を実装する

実際にRAGパイプラインのチャンキングパラメータをチューニングする際の、体系的な手順とコードを紹介します。

ステップ1: 評価データセットの準備

チューニングにはまず、質問と正解文書のペアが必要です。最低30〜50ペアを準備してください。

# 評価データセットの作成例
eval_dataset = [
    {
        "question": "Kubernetesのリソースリミットの設定方法は?",
        "expected_doc_id": "k8s_resource_management",
        "expected_chunk_text": "resources.limitsフィールドで...",
    },
    {
        "question": "PythonのGILとは何か?",
        "expected_doc_id": "python_concurrency",
        "expected_chunk_text": "Global Interpreter Lock...",
    },
    # ... 最低30ペア
]

ステップ2: グリッドサーチによるパラメータ探索

# チャンキングパラメータのグリッドサーチ
from itertools import product

from langchain.text_splitter import RecursiveCharacterTextSplitter

# 探索パラメータ
chunk_sizes = [128, 256, 512, 1024]
overlap_ratios = [0, 0.1, 0.2, 0.3]

results = []

for size, overlap_ratio in product(chunk_sizes, overlap_ratios):
    overlap = int(size * overlap_ratio)
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=size,
        chunk_overlap=overlap,
    )

    # 1. チャンクを生成
    chunks = splitter.split_text(all_documents_text)

    # 2. チャンクをEmbedding & インデックス構築
    index = build_index(chunks)

    # 3. 評価データセットで検索精度を計測
    recall_at_1 = 0
    recall_at_5 = 0
    for item in eval_dataset:
        retrieved = index.search(item["question"], k=5)
        if is_relevant(retrieved[0], item):
            recall_at_1 += 1
        if any(is_relevant(r, item) for r in retrieved):
            recall_at_5 += 1

    n = len(eval_dataset)
    results.append({
        "chunk_size": size,
        "overlap_ratio": overlap_ratio,
        "recall_at_1": recall_at_1 / n,
        "recall_at_5": recall_at_5 / n,
        "num_chunks": len(chunks),  # インデックスサイズの指標
    })

# 結果をRecall@5降順でソート
results.sort(key=lambda x: x["recall_at_5"], reverse=True)
for r in results[:5]:
    print(
        f"size={r['chunk_size']}, overlap={r['overlap_ratio']:.0%}: "
        f"R@1={r['recall_at_1']:.1%}, R@5={r['recall_at_5']:.1%}, "
        f"chunks={r['num_chunks']}"
    )

ステップ3: 結果の解釈と最終選択

パラメータ選択では、Recall@5とインデックスサイズ(チャンク数)のトレードオフを考慮します。

よくある問題と解決方法

問題 原因 解決方法
Recallは高いがLLMの回答品質が低い チャンクが短すぎてLLMに十分な文脈がない Parent-Childパターンの導入、またはチャンクサイズを拡大
検索速度が遅い チャンク数が多すぎる チャンクサイズを拡大、オーバーラップを削減、またはANN(近似最近傍)のパラメータを調整
特定クエリだけ精度が低い クエリタイプとチャンクサイズのミスマッチ マルチスケールインデキシングの導入
Semantic Chunkingで断片化 breakpoint_percentile_thresholdが高すぎる 閾値を下げる(95→85)、またはbuffer_sizeを増やす
日本語テキストで不自然な分割 セパレータに日本語の句読点が含まれていない separators"。""、"を追加

まとめと次のステップ

まとめ:

  • Recursive Character Splitting(512トークン)が汎用的に最高精度(Vecta 2026ベンチマーク: 69%)であり、RAG構築の出発点として最適
  • Semantic Chunkingは過度な断片化リスクがあり、デフォルト設定での採用は推奨されない
  • チャンクサイズの最適値はクエリ依存であり、固定値では理論上20〜40%の精度ギャップがある
  • 推奨パラメータの出発点: 256〜512トークン、オーバーラップ10〜20%
  • ドメイン特化(医療文書等)ではAdaptive Chunkingが有意に優位(p=0.001)
  • Embeddingモデルのアーキテクチャとチャンクサイズには相性がある(デコーダーベースは大チャンク向き)

次にやるべきこと:

  • 自分のデータセットで30〜50ペアの評価データを作成し、グリッドサーチでパラメータを探索する
  • Recursive Character Splitting(512トークン、オーバーラップ50)をベースラインとして設定し、そこからチューニングする
  • 精度が不足する場合、Parent-Childパターンまたはマルチスケールインデキシングを段階的に導入する

参考


注意: この記事はAI(Claude Code)により自動生成されました。内容の正確性については複数の情報源で検証していますが、実際の利用時は公式ドキュメントもご確認ください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?