【Dify】ナレッジパイプライン調査レポート2/3 - 文脈を理解した「チャンク分割の自動化」

に公開

はじめに

LLMアプリケーション開発プラットフォーム「Dify」にver.1.9.0で登場したナレッジパイプライン」について、その新機能を深掘りします。

前回の記事では、ナレッジパイプラインにおけるWordやPowerPoint内の「画像」の扱いに焦点を当てました。

今回のテーマは、「チャンク分割」です。

高精度なRAGを構築するには、文書の文脈を理解した単位(文脈単位)でのチャンク分割が欠かせません。検索精度を向上させるためのチャンク分割最適化には、多くの場合、試行錯誤が求められます。例えば、分割トリガーとなる識別子の調整、チャンク長の変更、文書データへの明示的な区切り文字(デリミタ)の挿入といったチューニングが必要です。
これらの作業は一定の知識と工数が必要であり、ナレッジ構築に慣れていない方にとってはハードルが高いタスクといえます。

本記事では、LLMを組み込んだ「AIによるチャンク分割パイプライン」で、このタスクを容易にする方法とその効果を検証・解説します。

体感できる効果
▲本記事で目指すAIによるチャンク分割結果。LLMを活用することで1章1チャンクに分割可能


チャンク分割における「品質」と「手間」のジレンマ

RAG開発におけるチャンク分割には、「品質」と「工数」のトレードオフという課題があります。

1. 手作業による分割:品質は高いが、スケーラビリティに欠ける

基本的には、人が文脈を解釈し「章」や「節」などの意味単位で分割する方法がとられます。具体的には、「人が対象のドキュメントを読み、文脈を理解しながら識別子を挿入し、Dify上で識別子を設定する」という作業が必要です。この作業には一定の工数と専門知識を要するうえ、ドキュメントの増加や更新に伴いメンテナンスコストが発生するという課題を抱えています。

2. 固定長分割:効率的だが、品質に劣る

上記の手間を省くためには、文字数などで機械的に区切る「固定長分割」が最も簡単です。「ナレッジメニュー->ナレッジベースを作成」にて単純にファイルをアップロードするだけでよく、手間はかかりません。しかし、デフォルト設定(チャンク識別子:/n/n,最大チャンク長:1024characters)のままでは、文書の構造や文脈が考慮されません。そのため文脈単位での分割ができず、下図のように章の途中で文章が分断される問題が発生します。これではRAGが文脈を正しく理解できず、回答の精度が低下してしまいます。

意図しない分割
▲固定長分割をすると、意図しない箇所でチャンクが分断されてしまう。

今回は、ナレッジパイプラインを使うことで生成AIが文書構造を解析し、最適な分割点に「識別子」を挿入することで、「低工数」で「高品質」なナレッジを作成できるかを検証しました。

イメージ


検証の概要

検証内容

「ナレッジパイプライン」を使い、以下の3つのファイル形式の処理の違いを確認します。

  • Word (.docx)
  • PDF (.pdf)
  • Excel (.xlsx)

本稿ではWordファイルを例に説明します。

検証環境

本検証の環境は以下の通りです。

環境 Difyバージョン
Community版 1.9.1
SaaS版 1.9.1

検証手順

本検証は、「①データ準備」「②パイプラインの構築」「③チャットボットでの効果検証」の3ステップで実施します。

手順1: データの準備

まず、ナレッジの元となるファイルを用意します。
今回は社内規則を想定したWordファイル.docx)を準備しました。

▲項目間の空行により、単純な分割では1章1チャンクに分割できない。

手順2: ナレッジパイプラインを構築してデータを格納

次に、Difyの「ナレッジ」メニューから、「ナレッジパイプラインから作成」→「空白のナレッジパイプライン」を選択し、以下のノードを接続してパイプラインを構築します。

  1. File: 検証したい社内規則ファイル(.docx)をアップロードするソースノード。
  2. Dify Extractor: アップロードされたファイルからテキスト情報を抽出するノード。
  3. LLM: 抽出されたテキストの分割点をAIが判断し、識別子を挿入するノード。
  4. General Chunker: LLMが挿入した識別子に基づき、テキストをチャンクに分割するノード。
  5. 知識ベース: 完成したチャンクを格納するノード。

パイプラインフロー画像
▲今回構築したパイプラインの全体像。ExtractorとChunkerの間にLLMノードを挟むのがポイント。

特に重要なのはLLMノードとGeneral Chunkerノードの設定です。

  • LLMノードの設定
    LLMノードにはDify Extractorから抽出されたテキスト全文(text変数)を入力します。
    プロンプトには以下のような指示を記述しています(抜粋)。
    1. **テキストの全体構造を把握する:** 入力テキストを読み、章・節・段落の構造と主要なトピックを理解する
    2. **意味的な境界を特定する:** 話題やテーマが切り替わる箇所、段落の境界、リストや表の範囲を特定する
    3. **分割ポイントを決定する:** 文脈を維持しながら、各チャンクが自己完結するように分割ポイントを決定する
    4. **チャンクを生成する:** 決定した分割ポイントでテキストを分割し、`--------`で区切って出力する
    5. **品質確認:** 各チャンクが意味的に完結し、元のテキストの内容が完全に保持されていることを確認する
    
今回使用したLLMのプロンプト全文

JinjaをONにし、入力変数にはDify Extractorの出力を設定します。
SYSTEM

input:
  data: "分割対象のテキスト"  {{ text }}
  
prompt: |
  1. **テキストの全体構造を把握する:** 入力テキストを読み、章・節・段落の構造と主要なトピックを理解する
  2. **意味的な境界を特定する:** 話題やテーマが切り替わる箇所、段落の境界、リストや表の範囲を特定する
  3. **分割ポイントを決定する:** 文脈を維持しながら、各チャンクが自己完結するように分割ポイントを決定する
  4. **チャンクを生成する:** 決定した分割ポイントでテキストを分割し、`--------`で区切って出力する
  5. **品質確認:** 各チャンクが意味的に完結し、元のテキストの内容が完全に保持されていることを確認する

context:
  domain: "テキスト分割・チャンク化"
  role: "高度なナレッジベースを構築する専門家"
	
goal:
  objective: "ユーザーからの質問に対して、システムが最も的確で過不足のない情報を参照できるように、テキストを分割する"
  constraints: |
    - **元のテキストの完全性を維持する:** テキストの内容を一切、変更・要約・削除してはいけません。
    - **区切り文字の厳守:** チャンクの区切りには、必ず半角のハイフン8つ `--------` のみを使用してください。
    - **適切な分割粒度:**
      - **意味的なまとまり:** 話題やテーマが大きく切り替わる箇所で分割してください。章・節・項や、段落ごとの主題を意識することが重要です。
      - **文脈の維持:** 文の途中や、意味のつながりが強い段落の間など、文脈が途切れる場所で分割してはいけません。
      - **自己完結:** 各チャンクが、単体でもある程度の意味を成すようにしてください。
      - **リストと表:** 関連性の高い箇条書きや表は、分断せずに一つのチャンクに含めるようにしてください。
    - **余計な出力はしない:** `--------` で分割された
  
output:
  format: "チャンク分割されたテキスト(`--------`で区切り)"
  type: "テキスト"
  message: "入力テキストを意味的に最適なまとまりに分割し、`--------`で区切って出力してください"

USER

user:
    data:
    description: "RAGシステム向けの社内規則・文書の入力データ説明"
    examples:
        - "社内規則・規程・ポリシー文書"
        - "業務マニュアル・手順書"
        - "人事規定・就業規則"
        - "コンプライアンス関連文書"
        - "システム利用規約・ガイドライン"
        - "FAQ・よくある質問集"
        - "研修資料・教育コンテンツ"
  requirements:
        - "テキスト形式の社内文書"
        - "任意の長さの規則・規程"
        - "構造化された文書(章・節・条項等)"
        - "検索・参照が想定される内容"
  • General Chunkerノードの設定
    • チャンク識別子(Delimiter)には、LLMプロンプトで指定した--------(ハイフン8つ)を入力します。これにより、LLMが付けた"印"の通りに分割を実行できます。
    • 最大チャンク長(Maximum Chunk Length)は余裕を持たせて500に設定しています。

手順3: チャットボットで効果を検証

最後に、作成したナレッジをチャットボットに接続し、チャンク分割の品質が回答精度に与える影響を検証します。

  1. Difyでチャットボット(Chatflow)を新規作成します。
  2. 「知識取得」ノードを追加し、手順2で作成したナレッジを選択します。
  3. 開始ノード、知識取得ノード、LLMノード、回答ノードを接続すれば完成です。
LLMについて

本チャットフローは、作成したナレッジから正確な回答を生成できるかの検証が目的です。そのため、プロンプトは検証用にシンプルな構成にしました。

SYSTEM

{{#sys.query#}}に対して、社内規則の該当箇所を利用して回答を作成してください。

USER

{{#sys.query#}}:ユーザーの質問
{{#context#}}:ユーザーの質問に関連する社内規則文書の該当箇所

チャットフロー画像
▲作成したナレッジを参照するシンプルなRAGチャットボットのフロー図。

これで、AIが分割したナレッジに基づくRAGチャットボットが完成しました。

このボットに質問した結果、以下の回答が得られました。
チャットボット使用例
▲作成したRAGチャットボットによる回答。


検証結果:ファイル形式によらず、文脈に沿った分割ができた

ファイル形式 抽出されるテキスト形式 AIによる分割結果
Word (.docx) プレーンテキスト ⭕️ 「章」単位での分割に成功
PDF (.pdf) プレーンテキスト ⭕️ 「章」単位での分割に成功
Excel (.xlsx) Markdownテーブル形式 ⭕️ 意味のまとまりでの分割に成功

以下で、各形式の具体的な挙動を見ていきましょう。


1. Word (.docx) / PDF (.pdf) の場合:文書構造を理解し「章」で分割

WordとPDFファイルでは、Dify Extractorノードはファイルからテキスト情報をそのままの文章として抽出します。

このテキストがLLMノードに渡されると、プロンプトの指示(「...章・節・項...を意識することが重要です」)が効果を発揮しました。
AIはこの指示に従い、文書の構造(見出しや改行)を解析し、「第1章」「第2章」といった章の区切りが最大の文脈的なまとまりと判断し、その境界に識別子--------を挿入しました。その結果、パイプラインは1章1チャンクで分割を実行しました。

Word,PDFチャンク分割
▲Word, PDFともに、プロンプトの指示通り「章」単位でのチャンクを持つナレッジが生成された。

このナレッジを使用したチャットボットで期待する回答が得られました。

▲適切な箇所を参照して回答。


2. Excel (.xlsx) の場合:Markdown形式も理解し、一つのパイプラインで対応可能

Excelファイルの場合、挙動が少し異なります。Dify Extractorノードは、Excelシートの内容をMarkdownのテーブル形式のテキストとして抽出します。

注目すべきは、テキスト形式がWordやPDFと異なるにも関わらず、同一のLLMノード(同一プロンプト)がMarkdown構造を正しく解釈し、意味のまとまりで識別子を挿入した点です。

Excelチャンク
▲Excelでも文脈的単位で区切られた高品質なチャンクが生成された。

このナレッジを使用したチャットボットでも、期待する回答を得ることができました。

▲適切な箇所を参照して回答

これは、ファイル形式ごとにパイプラインを開発・保守する必要がなく、構造が異なるドキュメントでも単一のパイプラインで一貫した品質のナレッジ化が可能であることを示します。


比較:デフォルト設定での分割とナレッジパイプラインによる分割

「ナレッジメニュー->ナレッジベースを作成」にてデフォルト設定(チャンク識別子:/n/n,最大チャンク長:1024characters)で分割したナレッジと、自作パイプラインで作成したナレッジの違いは以下の通りです。

デフォルト設定:改行ごとにチャンク分割され、各チャンクで文脈の保持ができない。
自作パイプライン:章ごとに分割され、1章1チャンクに成形。

それぞれのナレッジを使用したチャットボットの回答は次のようになりました。

デフォルト設定:異なるチャンクから情報を取得し、回答として正しくない。
自作パイプライン:2章のチャンクから情報を取得し、正しく回答。

このように回答が分かれた理由は、ナレッジ格納時のチャンク分割にあります。
クエリを「第二章服務規律について教えて」としてナレッジの検索テストを行いました。

▲デフォルト設定で格納したナレッジのテスト結果
デフォルト設定で格納したナレッジは「第2章 服務規律」のみのチャンクScore:0.88で返され、それ以外はスコアの低いチャンクが返されました。


▲パイプラインで格納したナレッジのテスト結果
一見すると、自作パイプラインで格納したナレッジもデフォルト設定で格納したナレッジと同様の検索結果のように見えますが、Score:0.88で返されているチャンク内に「第2章 服務規律」の全内容が含まれています。

そのため、デフォルト設定で格納したナレッジは、取得したチャンクの情報が不足していたため正しく回答できませんでした。一方、自作パイプラインのナレッジは、回答に必要な情報がチャンク内に含まれていたため、正しく回答できました。


技術的なQ&A

Q1. どこまで自動化できる?

A1. パイプラインの構築は人間が行いますが、一度構築すればその後のチャンク分割ロジックを自動化できます。

ノードの接続、LLMプロンプト、チャンク設定といった初期設計は人間が行います。しかし、一度パイプラインが完成すれば、ファイルをアップロードするだけでAIが文脈を理解したチャンク分割から格納まで完了します。

Q2. 分割の精度は?

A2. 精度はプロンプト次第です。LLMノードに与えるプロンプトの品質が、チャンクの品質に直結します。

「章ごと」「節ごと」など、具体的なチャンク分割のステップを言語化し、プロンプトに落とし込む必要があります。


どんな場面で活躍する?

  • 社内規程・マニュアル管理 (人事・情報システム部門) 💬

    • 課題:規程更新のたびにドキュメントをナレッジ化する工数が発生。
    • 解決策:ナレッジパイプラインに更新された規定をアップロードすることで半自動でチャンク化することで、ナレッジ化にかかる工数を削減し、社内FAQボットを最新状態に維持します。

まとめ

今回の検証から、Difyのナレッジパイプラインは以下の特長を持つことが分かりました。

  • 拡張性: LLMノード等を自由に組み合わせ、独自のナレッジパイプラインを構築できます。
  • 自動化: これまで手作業が必須であったチャンク分割の作業を、ナレッジパイプラインで自動化できます。
  • 標準化: ナレッジ化の品質を標準化し、担当者のスキルに依存しないRAGを構築できます。
UPGRADE tech blog

Discussion