RAGの構造チャンク設計

構造化カッティング

Posted by LuochuanAD on January 31, 2026 本文总阅读量

はじめに

RAGシステム構築において、検索と生成結果の精度を向上させるための核心は、構造化されたChunksの設計にあります。

1. 一般的なChunks分割方法

1. 固定サイズChunk分割(Fixed-size Chunking)

from langchain.text_splitter import CharacterTextSplitter

text = "これはLangChainを使って固定サイズチャンクをテストするサンプルテキストです。chunk_sizeとchunk_overlapを設定することで、チャンクのサイズと内容をうまくコントロールできます。"
text_splitter = CharacterTextSplitter(
    separator="",      # 特定の文字で分割しない
    chunk_size=512,      # 各チャンクの文字数
    chunk_overlap=3,    # 重複する文字数
    length_function=len,
)
chunks = text_splitter.split_text(text)
print(chunks)

2. 文や段落、特定の句読点などに基づく分割

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import TextLoader

loader = TextLoader("../../data/xxx.txt")
docs = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    separators=["\n\n", "\n", "。", ",", " ", ""],  # 区切り文字の優先順位
    chunk_size=200,
    chunk_overlap=10,
)
chunks = text_splitter.split_text(docs)

3. セマンティックベースの分割

https://zhuanlan.zhihu.com/p/1924496550433919103

2. 構造化Chunks分割方法(推奨)

1. すでに構造化されている文書向け

例:タイトル、序論、第1章、第2章、結論などのタグに基づく直接的な分割。

https://zhuanlan.zhihu.com/p/1987591795891269696

2. 非構造化・半構造化文書向け

検索と生成結果の精度向上のため、私は全ての非構造化・半構造化文書を構造化Chunksに変換することを推奨します。異なる構造の文書には、それぞれ異なる構造化Chunks設計が必要です。

例:数百件の履歴書を分割し、ベクトル化してベクトルDBに保存する場合。各履歴書は異なるテンプレートを使用しています。従来のChunks分割方法では、検索・生成の精度が低下します。

私のアプローチ:

まず、人間の視点で考えてみます。テンプレートが異なっても、この部分が個人情報、あの部分がプロジェクト経験、その部分が資格情報と簡単に判断できます。これをコードでどう判別するか?

ステップ1:物理的な段落分割(意味は問わず、単に段落単位に分割)

blocks = re.split(r'\n\s*\n',text)

ステップ2:段落のタイプ分類。

分類は以下のように区別します:
基本情報(氏名、性別、生年月日、学歴など)
スキル(python, C++ など)
自己評価
資格情報
プロジェクト経験(1プロジェクト毎に1チャンク)

全ての履歴書の構造は以下のようになります:

resume_chunks = [
	basic_chunk,
	skills_chunk,
	introduction_chunk,
	certification_chunk,
	project_chunk_1,
	project_chunk_2,
	project_chunk_3,
	......
	other
]

(1) キーワード頻度アルゴリズムで判定。

type_keywords = {
	"basic_chunk" : ["姓名", "年龄", "年纪", "出生年月", "大学", "学院", ...],
	"skills_chunk" : ["python", "C++", "iOS", "Android", "Java", "PHP", ...],
	"introduction_chunk" : ["学習好き", "ストレス耐性", "高効率", "アジャイル開発", ...],
	"certification_chunk" : ["コンピューター二級", "大学英語四級", "大学英語六級", "IELTS", "AWS", ...],
	"project_chunk" : ["プロジェクト経験", "担当", "期間", ...]
}

スコア = ヒットしたキーワード数 / 段落長さ

(2) 日付パターン頻度で判定

(19|20)\d{2}年\s*\d{1,2}月

2回以上出現はプロジェクトまたは職務経験を示す

ステップ3:隣接段落の意味的統合(ソフトマージ)

1つのプロジェクトが3つの段落に分かれている問題を解決

	cos_sim(block[i], block[i+1]) > 0.85

これら3ステップを経て構造化Chunksが得られ、データをさらにクリーンアップ後、意味的に整った段落をベクトル化してベクトルDBに保存します。

利点:意味の連続性を最大限保持し、LLMを使わずコストを削減;検索と生成の精度が10倍向上。

project_chunk_1のjson例:

chunk = {
	"chunk_id": "resume_Louis_project_01",
	"resume_id": "resume_Louis",
	"section_type": "project",
	"title": "感情支援AIエージェント",
	"period":{
		"from": "2026-01",
		"to": "2026-02"
	},
	"role": ["設計", "開発", "テスト"],
	"skills": ["python", "js"],
	"content": "プロジェクト内容:感情支援AIエージェントを開発し、多くのプログラマーにサービスを提供...",
	"source":{
		"file": "履歴書(Louis).pdf",
		"page": [2,3]
	}
}

参考資料

https://developer.jdcloud.com/article/4408