RAGの検索精度を決める最も重要な要素のひとつが「チャンキング」です。どれほど高性能なエンベディングモデルやLLMを用意しても、入力となるチャンクの切り方が適切でなければ、検索結果は期待を裏切り続けます。本記事では、固定長・セマンティック・階層型という3つの主要なチャンキング手法を軸に、文書タイプ別の最適戦略、実装コード例、そして評価と最適化の方法までを体系的に整理します。
チャンキングがRAGの精度を左右する理由
チャンキングとは、検索対象となる文書を一定のルールで小さな断片(チャンク)に分割する処理を指します。RAGの検索はチャンク単位で行われるため、1チャンクに含まれる情報量と意味的まとまりが、そのまま検索の当たり外れを決定づけます。「チャンクを大きくすれば情報量は増えるが、複数トピックが混在してノイズになる」「チャンクを小さくすれば単一トピックに絞れるが、文脈が断ち切れて意味が伝わらない」というジレンマが常に付きまといます。
このバランスを取るために、チャンキングは文書の特性と質問の粒度に合わせてチューニングする必要があります。1チャンク1トピックを原則としつつ、そのトピックを理解するのに必要な文脈を失わない程度の大きさを保つ――この感覚を身につけることが、RAG設計者の基本スキルとなります。
【チャンクサイズと検索精度の関係】
検索精度
^
| ***
| ** **
| * *
| * *
| * *
| * *
+-----------------------> チャンクサイズ
小 中 大
小さい: 文脈喪失で曖昧な質問に弱い
中程度: 情報量と純度のバランスが取れた最適域
大きい: ノイズ混入で誤ヒット増加
※ 最適点は文書タイプ・質問パターンにより変動
※ 目安は200〜800トークン、評価で必ず検証すること
固定長チャンキング
固定長チャンキングは、文字数やトークン数を基準に機械的に分割する最も基本的な手法です。実装が容易で処理速度も速く、文書量が多いユースケースでも安定して動作するため、RAGの出発点として広く採用されています。LangChainのRecursiveCharacterTextSplitterは、段落→文→単語という優先順位で区切り文字を探し、指定サイズに収まるよう再帰的に分割してくれるため、実務でも使い勝手が良い選択肢です。
固定長チャンキングの精度を引き上げる小さな工夫が「オーバーラップ」です。隣接するチャンクの末尾と先頭を一部重複させることで、文の途中で切れてしまった場合の情報損失を緩和できます。オーバーラップ量はチャンクサイズの10〜20%程度が定石で、大きすぎるとインデックスが膨張し、検索コストと応答時間の両方に悪影響を与える点には注意しましょう。
# 固定長チャンキング(日本語向け設定)
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = open("manual.txt", encoding="utf-8").read()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=80,
length_function=len,
separators=["\n\n", "\n", "。", "、", " ", ""]
)
chunks = splitter.split_text(text)
print(f"生成チャンク数: {len(chunks)}")
print(f"最初のチャンク: {chunks[0][:100]}...")
セマンティックチャンキング
セマンティックチャンキングは、文同士の意味的な近さに応じて分割点を決める手法です。具体的には、文をエンベディングでベクトル化し、隣接文との類似度が急激に下がる地点を「話題の変わり目」として検出します。固定長の機械的な切断と違い、段落やセクションの自然な切れ目で分割できるため、1チャンクの意味的まとまりが保たれ、検索精度の向上が期待できます。
一方で、チャンク長が可変になるためコンテキスト長の管理がしにくく、エンベディング呼び出しコストも増える点は注意が必要です。セマンティックチャンキングは、長文のエッセイやレポート、議事録のように話題が段階的に推移する文書で特に威力を発揮します。
# セマンティックチャンキングの実装例
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
text = open("long_report.txt", encoding="utf-8").read()
chunker = SemanticChunker(
embeddings=OpenAIEmbeddings(model="text-embedding-3-small"),
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=90
)
docs = chunker.create_documents([text])
for i, doc in enumerate(docs[:3]):
print(f"--- Chunk {i} ({len(doc.page_content)}文字) ---")
print(doc.page_content[:120])
階層型チャンキング
階層型チャンキング(ペアレントチャイルドチャンキング)は、同じ文書に対して2種類の粒度でチャンクを作り、役割を分けて使う手法です。検索には短く純度の高い「子チャンク」を使い、LLMへの入力には文脈を保持した長い「親チャンク」を渡します。これにより、検索の精度と生成時の文脈の豊かさを両立できます。
例えば、長い契約書を段落単位(親)と文単位(子)の2階層に分割し、質問文に最も近い文を見つけたら、その文が含まれる段落全体をLLMに渡す、という使い方が典型です。複雑な構造の社内文書やナレッジベースで特に効果が高く、Advanced RAGの定番パターンとなっています。
# 階層型チャンキングの実装例
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader
docs = TextLoader("contract.txt", encoding="utf-8").load()
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1500)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=300)
vectorstore = Chroma(
collection_name="contracts",
embedding_function=OpenAIEmbeddings()
)
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
retriever.add_documents(docs)
文書タイプ別の最適チャンキング戦略
実務では単一の手法を全文書に適用するのではなく、文書タイプに応じて使い分けるのが定石です。以下に代表的な文書タイプと推奨戦略をまとめました。
| 文書タイプ | 推奨手法 | チャンクサイズ目安 | オーバーラップ | 注意点 |
|---|---|---|---|---|
| PDF(レポート・論文) | セマンティック + 階層型 | 800〜1200トークン | 10% | 表・図のキャプションも分離保持 |
| Markdown / 社内Wiki | セクション単位(見出しベース) | 300〜800トークン | 10〜15% | 見出し構造をメタデータ化 |
| HTML(Webページ) | 固定長 + HTMLクリーニング | 400〜600トークン | 15% | ナビ・広告を除去 |
| CSV / 表データ | 行単位 + ヘッダー付与 | 1行単位 | 無し | 列名を本文に埋め込む |
| コード | 関数・クラス単位 | 関数単位 | 無し | コメントと本体を分離しない |
| 会議議事録 | セマンティック | 400〜600トークン | 10% | 発言者名をメタデータ化 |
| FAQ | Q&A単位 | 200〜400トークン | 無し | Q-Aをペアで1チャンクに |
| 手法 | 精度 | 実装難易度 | 処理速度 | 適した文書タイプ |
|---|---|---|---|---|
| 固定長 | 中 | 低 | 高速 | 均質な短文・大量文書 |
| セマンティック | 高 | 中 | 中速 | 長文・議事録・レポート |
| 階層型 | 高 | 中〜高 | 中速 | 構造化された長文 |
| 文書構造ベース(Markdown等) | 高 | 低〜中 | 高速 | 見出しが明確な文書 |
特に社内ナレッジベースのように複数の文書形式が混在する場合、形式ごとに別のチャンキングパイプラインを用意しておき、それぞれの結果を同じベクトルDBに統合するアーキテクチャが有効です。メタデータに「文書種別」を含めておけば、検索時に絞り込みも可能となります。
チャンキングの評価と最適化
チャンキング戦略を決めたら、必ず定量評価で妥当性を確認しましょう。具体的には、代表的な質問20〜50件と「これがヒットすべき」という正解チャンクの対応表(ゴールデンデータセット)を用意し、Recall@KやMRRといった指標でスコアを取ります。
チャンクサイズ300 / 500 / 800、オーバーラップ0 / 10% / 20%のように複数組み合わせをA/Bテストし、スコアが最大となるパラメータを採用します。重要なのは「一度決めて終わり」にせず、文書更新や運用中に得られたユーザーフィードバックをもとに、半年〜1年おきに見直すサイクルを回すことです。チャンキングは運用と共に熟成させる生きたパラメータだと理解してください。
まとめ――チャンキングは「一度決めて終わり」ではない
- チャンキングはRAGの精度を決める最重要工程のひとつ
- 固定長・セマンティック・階層型の3手法を文書タイプで使い分ける
- 文書形式ごとに別パイプラインを用意し、メタデータで種別管理
- ゴールデンデータセットによる定量評価と定期的な見直しが必須
DE-STKでは文書構造分析からチャンキング戦略の設計、評価データセットの構築まで、RAG精度改善の実務を伴走支援しています。既存RAGの精度に課題を感じている場合は、まずチャンキング診断からご相談ください。
よくある質問(FAQ)
Q. RAGのチャンキングとは何ですか?
RAGで検索対象となる文書を、適切なサイズの断片(チャンク)に分割するプロセスです。分割方法とサイズがRAGの検索精度に大きく影響するため、文書タイプに応じた最適な戦略の選択が重要となります。固定長・セマンティック・階層型の3手法から、用途に合わせて選定することが推奨されます。
Q. チャンクサイズはどのくらいが最適ですか?
一般的には200〜1000トークンが目安ですが、文書タイプや用途により異なります。FAQ的な短い文書は200〜400トークン、技術文書や契約書は500〜1000トークンが適切な場合が多いです。必ず代表質問による評価テストで最適値を検証してください。経験則で決め打ちせず、自社データで実測することが精度改善の近道です。
Q. チャンキングのオーバーラップとは何ですか?
隣接するチャンク間で一部のテキストを重複させることです。例えば100トークンのオーバーラップを設定すると、チャンクの末尾100トークンが次のチャンクの先頭にも含まれます。文脈の断絶を防ぐ効果がありますが、インデックスサイズが増加する副作用もあります。チャンクサイズの10〜20%程度が定石です。