A
AIエージェントの仕組み
ch5-s3 · Chunking Strategy

チャンク分割戦略

約 15 分

この回のゴール

1. なぜチャンク分割が重要か

前回は短い FAQ(各 300〜500 文字)をそのまま 1 文書として扱いました。でも現実は:

これを 1 ベクトルで表現したら? - 意味が「平均化」されて個別トピックが検索ヒットしない - 埋め込みモデルの入力上限(512 トークンが多い)を超える

👉 適切なサイズに区切る = チャンク分割 が必要。

2. 4 つの戦略

戦略①: 固定長分割 (Fixed-size)

[あなた] ──── tools=[calculator] を付けて request ───▶ [Claude]
                                                        │
                                                        │ 「計算が必要だな、
                                                        │  calculator ツールを呼ぼう」
                                                        │
[あなた] ◀── tool_use ブロック (input={expression: "..."}) ── [Claude]
    │
    │ (あなたのコードで実行)
    │  eval("2+3*4") → 14
    │
[あなた] ──── tool_result (content="14") を付けて request ─▶ [Claude]
                                                              │
                                                              │ 「結果は 14 か、
                                                              │  これで答えられる」
                                                              │
[あなた] ◀── text: "答えは 14 です" ───── [Claude]

戦略②: 段落単位 (Paragraph)

response1 = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=1024,
    tools=[{
        "name": "calculator",
        "description": "数式を評価して結果を返す。",
        "input_schema": {
            "type": "object",
            "properties": {
                "expression": {
                    "type": "string",
                    "description": "Python の式として評価可能な数式。例: '2+3*4'"
                }
            },
            "required": ["expression"],
        }
    }],
    messages=[
        {"role": "user", "content": "734521 × 892143 はいくつ?"}
    ],
)

戦略③: 再帰的分割 (Recursive)

LangChain の RecursiveCharacterTextSplitter が有名。

response1.stop_reason  # "tool_use"
response1.content      # [ToolUseBlock(name="calculator", id="toolu_ABC", input={"expression": "734521 * 892143"})]

戦略④: セマンティック分割

3. Overlap(重複)の役割

チャンク間を 少し重ねる ことで、境界の情報欠落 を防ぎます:

tool_use_block = next(b for b in response1.content if b.type == "tool_use")
expression = tool_use_block.input["expression"]  # "734521 * 892143"
result = eval(expression)                        # 655199895003

質問「夏季休暇は?」で、どちらのチャンクでもヒット可能

overlap サイズの目安

4. チャンクサイズの選び方

サイズ 特徴 向く用途
100 トークン前後 ピンポイント、高精度 FAQ・短い事実
300〜500 トークン 実務の標準 技術ドキュメント
1000〜2000 トークン 文脈豊富 長文推論・物語

トレードオフ: - 小さい: 正確性↑ だが文脈が足りず LLM が答えにくい - 大きい: 文脈豊富だが検索精度↓・トークン料金↑

5. トークンか文字か

日本語では 文字とトークンが乖離 します(第 3 章):

実装の簡単さでは 文字数基準 が多いですが、厳密なら tiktoken で計測します。

6. 前処理の重要性

チャンク分割の前に:

を忘れると、埋め込みが「改行」や「空白」を重みに入れてしまい精度が落ちます。

7. メタデータを付ける

各チャンクに メタデータ を紐付けると検索の質が上がります:

response2 = client.messages.create(
    model="claude-haiku-4-5",
    max_tokens=1024,
    tools=[...],   # 同じ tools
    messages=[
        {"role": "user", "content": "734521 × 892143 はいくつ?"},
        {"role": "assistant", "content": response1.content},  # 👈 全部含める
        {
            "role": "user",
            "content": [{
                "type": "tool_result",
                "tool_use_id": tool_use_block.id,   # 👈 ID マッチング
                "content": str(result),
            }]
        }
    ],
)

検索時に 出典表示カテゴリフィルタ新しい文書を優先 などが可能。


まとめ

この回の限界(次への動機)

検索とチャンク分割は揃った。これらを Claude と統合してエンドツーエンドの RAG チャットを作る番。 👉 次回「Claude with RAG」で、完成形のパイプライン + citation(引用元表示)を実装します。

参考文献

📝 理解度クイズ (3 問) 💡 ログインすると進捗が保存されます

💬 このサブステップの Q&A

まだ質問はありません。最初の質問を投稿してみましょう。

質問の投稿にはログインが必要です。