0
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でマルチシートExcelとWordを壊さず扱うために、構造化前処理とChunkingを実装してみた

0
Posted at

初めに

RAGでPDFを扱う記事はかなり増えました。

一方、実際の業務では、次のようなOffice文書もよく使われます。

  • 複数のsheetを持つExcel
  • 一覧表と作業手順が混在するExcel
  • 見出し、本文、箇条書き、表が混在するWord
  • 数式、結合セル、非表示sheetを含むExcel

これらを単純にplain textへ変換してから1,000文字ごとに分割すると、見た目はテキストになっていても、質問に答えるための関係が壊れてしまいます。

そこで今回は、ExcelとWordをRAGへ取り込むときに、文書構造を残したまま「検索可能な証拠単位」へ変換する方法を整理します。

対象読者は、実際にOffice文書を使ったRAGを構築・改善したい方です。

単なる考え方だけではなく、失敗しやすいところ、データ構造、サンプル文書の生成コード、変換後のJSONL、chunk分割のコードまで紹介します。

この記事に登場するファイル名、商品、コマンド、データは、すべて説明用の架空データです。

結論

先に結論を書くと、Office文書のRAGでは、次の順番が安定します。

1. ファイル形式ごとに構造を読む
2. Excelはsheetごとに「表形式」と「手順形式」を判定する
3. Wordは見出し階層と本文・表の出現順を保持する
4. 出典情報付きのEvidence Blockへ統一する
5. Evidence Blockの境界でchunkを確定する
6. chunk確定後にEmbedding、検索、Rerankを行う

重要なのは、最初から文字数だけでchunkを作らないことです。

Excelの1行、作業手順の1ステップ、Wordの1節、Word表の1行など、「これ以上分けると意味が壊れる単位」を先に作ります。

1. なぜ単純なテキスト変換では足りないのか

ここで解決したいこと

よくある取り込み方法は、ExcelやWordから文字列を取り出し、すべて連結してから固定長で分割する方法です。

text = extract_all_text("document.xlsx")
chunks = [text[i:i + 1000] for i in range(0, len(text), 1000)]

実装は簡単ですが、業務文書では次の問題が起きます。

- sheet名が消え、同じ列名を持つ表を区別できない
- 表のヘッダーと値が別chunkになる
- 作業コマンドと確認方法が別chunkになる
- Wordの見出しと本文が離れる
- Word本文と、その直後にある表の順番が崩れる
- 数式、更新日時、行番号などの根拠情報が落ちる
- 回答が正しくても、元の場所へ戻れない

例えば、次の作業手順を考えます。

No. 作業項目 コマンド/確認ポイント 備考
1.1 事前確認 samplectl service status runningを確認
samplectl disk check 空き容量を確認

1行目と2行目を別々のchunkにすると、「何を実行するか」は検索できても、「何を確認するか」が検索結果に入らない可能性があります。

LLMへ渡す前に文書構造が壊れていると、EmbeddingやRerankのモデルを改善しても限界があります。

2. まず共通のEvidence Blockを決める

ExcelとWordでは読み方が違います。

ただし、後段の処理まで別々にすると、Embedding、検索、引用、評価の実装が複雑になります。

そこで、Preprocessの出口だけを共通化します。

{
  "source_file": "sample_office_docs.xlsx",
  "source_type": "excel",
  "block_type": "table_row",
  "sheet": "Products",
  "section_path": ["商品一覧"],
  "row_start": 2,
  "row_end": 2,
  "fields": {
    "商品ID": "ITEM-001",
    "商品名": "ノートPCスタンド",
    "単価": 3200,
    "在庫数": 12
  }
}

最低限、次のmetadataを残します。

項目 目的
source_file 元ファイルを特定する
source_type Excel、Word、CSVなどを区別する
block_type 表の1行、手順、Word節、Word表の1行を区別する
sheet Excelの出典sheetへ戻る
section_path 上位見出しを含めて意味を補う
row_start / row_end 元の行、または文書内位置へ戻る
content 文章・手順向けの本文
fields 表形式データの列と値の対応

空の項目は出力しません。

これにより、ExcelとWordの違いを残しながら、後段では同じJSONLとして扱えます。

3. マルチシートExcelはsheetごとに読む

ここで解決したいこと

Excelでは、workbook全体を1つの表として扱わないことが重要です。

sheetごとに役割、列構成、密度が違うからです。

実装では、次の順番で処理します。

workbook
  ├─ visible sheetを列挙
  ├─ 除外対象sheetを除く
  ├─ sheetごとにヘッダー候補を探す
  ├─ 表形式 / 手順形式を判定する
  └─ sheet名付きEvidence Blockへ変換する

3.1 読み込むsheetを明示する

すべてのsheetを無条件に取り込むと、評価用の回答、計算用の中間sheet、古いバックアップ、非表示設定などが知識ベースへ混ざることがあります。

次の項目は設定可能にしておく方が安全です。

excluded_sheets = {"QA", "Answer", "Evaluation"}
include_hidden = False

除外名をコードの各所へ埋め込むのではなく、変換関数の引数または設定ファイルから渡します。

convert_excel_to_text_document(
    file_path,
    excluded_sheets=("QA", "Answer", "Evaluation"),
    include_hidden=False,
    mode="auto",
    max_block_chars=3000,
)

ここで大事なのは、複数sheetを読めることだけではありません。

「どのsheetを、なぜ読まなかったか」をログへ残すと、後で検索漏れを調査しやすくなります。

3.2 .xlsxは数式と保存済み計算結果の両方を読む

openpyxlでは、同じworkbookを2回開きます。

from openpyxl import load_workbook

formula_book = load_workbook(path, data_only=False)
value_book = load_workbook(path, data_only=True)
  • data_only=False: 数式を取得する
  • data_only=True: Excelが保存した計算結果を取得する

基本方針は次です。

value = cached_value if cached_value is not None else formula

保存済み計算結果があれば値を優先し、存在しなければ数式を残します。

なお、openpyxl自体はExcelの数式計算エンジンではありません。生成直後などで計算結果が保存されていない場合は、数式文字列が出力されます。

3.3 表形式と手順形式を分ける

同じExcelでも、次の2種類はchunkの作り方が違います。

表形式

商品ID | 商品名 | 単価 | 在庫数 | 更新日時
ITEM-001 | ノートPCスタンド | 3200 | 12 | ...
ITEM-002 | USBハブ | 4800 | 8 | ...

表形式では、1データ行を1つのEvidence Blockにします。

{
  "block_type": "table_row",
  "sheet": "Products",
  "row_start": 2,
  "row_end": 2,
  "fields": {
    "商品ID": "ITEM-001",
    "商品名": "ノートPCスタンド",
    "単価": 3200,
    "在庫数": 12
  }
}

これにより、商品ID、商品名、価格、在庫数の関係を同じchunkに保持できます。

手順形式

No. | 作業項目       | コマンド/確認ポイント
1   | 月次メンテナンス |
1.1 | 事前確認       | samplectl service status
    |                | samplectl disk check
1.2 | バックアップ   | samplectl backup create --name monthly
    |                | samplectl backup verify --name monthly

手順形式では、1行を1chunkにしてはいけません。

「番号列と作業項目列の両方に値がある行」を次の手順の開始位置として、次の開始位置の直前までを同じEvidence Blockへまとめます。

{
  "block_type": "procedure_step",
  "sheet": "Maintenance",
  "section_path": ["月次メンテナンス", "事前確認"],
  "row_start": 3,
  "row_end": 4,
  "content": "コマンド: samplectl service status | 備考: runningを確認\nコマンド: samplectl disk check | 備考: 空き容量を確認"
}

上位の作業名も各子ステップへ繰り返して入れます。

そうすると、「月次メンテナンスの事前確認は何か」のような質問でも、検索対象のchunk単体で意味が通ります。

3.4 自動判定は「ヘッダー+密度+手順境界」で行う

自動判定の実装例です。

from statistics import median


def detect_sheet_mode(rows, header_index, active_columns):
    if has_number_and_title_boundaries(
        rows,
        header_index,
        active_columns,
    ):
        return "procedure"

    sample_rows = [
        row
        for row in rows[header_index + 1:]
        if any(value not in (None, "") for value in row)
    ][:50]

    if not sample_rows:
        return "table"

    fill_ratios = [
        sum(
            col < len(row) and row[col] not in (None, "")
            for col in active_columns
        ) / len(active_columns)
        for row in sample_rows
    ]

    return "table" if median(fill_ratios) >= 0.60 else "procedure"

この例では、最大50件の非空行を調べ、利用列の中央値が60%以上埋まっていれば表形式とみなします。

60%は絶対的な正解ではありません。

実データで誤判定したsheetをログへ残し、sheet単位でtableまたはprocedureを上書きできるようにしておくのが現実的です。

3.5 Excelで落としやすい情報

実装時は、次も確認します。

対象 処理
空行 chunkを作らない
日付・時刻 ISO 8601形式へ統一する
重複ヘッダー 列記号を付けて区別する
空ヘッダー column_Cのような名前を付ける
結合セル 論理ブロック内だけ値を引き継ぐ
非表示sheet 初期値では読み込まない
数式 保存済み値を優先し、なければ数式を残す
.xls xlrdなど別readerを使う
CSV UTF-8に加えてCP932も考慮する

重複ヘッダーの例です。

担当 | 担当

このままdictへ変換すると、後の値で上書きされます。

担当
担当__H

のように列記号で区別すると、元の列位置も分かります。

4. Wordは見出しと出現順を残す

ここで解決したいこと

Wordでは、見出し、本文、箇条書き、表が交互に登場します。

見出し
  本文
  箇条書き
  表
  本文
次の見出し

python-docxdocument.paragraphsdocument.tablesを別々に処理すると、本文と表の出現順が分からなくなります。

そのため、文書bodyのXML要素を順番に走査します。

from docx.oxml.table import CT_Tbl
from docx.oxml.text.paragraph import CT_P
from docx.table import Table
from docx.text.paragraph import Paragraph


def iter_word_items(document):
    for child in document.element.body.iterchildren():
        if isinstance(child, CT_P):
            yield Paragraph(child, document)
        elif isinstance(child, CT_Tbl):
            yield Table(child, document)

4.1 Heading 1〜3をsection pathとして持つ

例えば、次のWord文書があります。

運用ガイド                       Heading 1
  バックアップ                   Heading 2
    バックアップを作成します。   本文
    実行前に空き容量を確認する。 箇条書き
  障害時の確認                   Heading 2
    ログを確認します。           本文

バックアップを作成します。だけをchunkへ入れるのではなく、次のように見出し階層を付けます。

{
  "source_file": "sample_operations_guide.docx",
  "source_type": "word",
  "block_type": "word_section",
  "section_path": ["運用ガイド", "バックアップ"],
  "content": "バックアップを作成します。\n- 実行前に空き容量を確認します。"
}

検索時には本文だけでなく、運用ガイド > バックアップもEmbedding対象へ含めます。

同じ「確認」という言葉が複数の章にあっても、意味を区別しやすくなります。

4.2 目次は本文として扱わない

Wordの目次には、本文と同じ見出し文字列が並びます。

TOC 1TOC 2などのstyleを本文として取り込むと、検索結果に目次だけが出ることがあります。

if style_name.casefold().startswith("toc"):
    continue

ただし、独自styleを使用している文書もあるため、style名はログで確認できるようにします。

4.3 Word表は1行ずつEvidence Blockにする

表は、先頭行をヘッダーとして、各データ行をfield mappingへ変換します。

{
  "source_file": "sample_operations_guide.docx",
  "source_type": "word",
  "block_type": "word_table_row",
  "section_path": ["運用ガイド", "バックアップ"],
  "table_index": 1,
  "row_start": 2,
  "row_end": 2,
  "fields": {
    "項目": "保存先",
    "確認方法": "空き容量が20%以上あること"
  }
}

表の各行へ現在のsection_pathを継承させるところがポイントです。

表だけを見ても、その表が何について説明しているか分かるようになります。

5. ChunkingはEvidence Blockの境界を優先する

ここで解決したいこと

Preprocessで正しく構造化しても、後段で再び1,000文字ごとに切ると意味がありません。

そこで、Evidence Blockの末尾へ固定delimiterを付けます。

{"block_type":"table_row", ...} <FIXED_DELIMITER>
{"block_type":"procedure_step", ...} <FIXED_DELIMITER>
{"block_type":"word_section", ...} <FIXED_DELIMITER>

splitter側は、このdelimiterがある場合、通常のchunk_sizechunk_overlapよりもBlock境界を優先します。

FIXED_DELIMITER = "<FIXED_DELIMITER>"


def split_fixed_blocks(text: str) -> list[str]:
    if FIXED_DELIMITER not in text:
        return recursive_character_split(text)

    return [
        block.strip()
        for block in text.split(FIXED_DELIMITER)
        if block.strip()
    ]

これで、原則として次の関係になります。

1 Evidence Block = 1 Chunk

5.1 最大文字数はhard limitではなくsoft limitにする

1つのWord節や作業手順が極端に長い場合は、上限も必要です。

ただし、3,000文字になった瞬間に文字列を切るのではなく、次の境界だけで分割します。

- Wordの段落境界
- 箇条書きの項目境界
- Excel手順のコマンド/確認行の境界
- 表の行境界

表の1行が上限を超える場合でも、途中で分割すると列と値の対応が壊れます。

そのため、表の1行や単独コマンドはatomicな要素として扱い、soft limitを超えても保持する選択肢が必要です。

5.2 overlapを自動で付けない

固定長chunkでは、前後の文脈を補うためにoverlapを使います。

しかし、構造化Blockへ機械的にoverlapを付けると、別sheetや別手順のJSONが混ざる可能性があります。

Evidence Blockにsection_pathや上位タイトルを繰り返している場合、固定delimiterのchunkにはoverlapを付けない方が扱いやすくなります。

6. コピペして試せるサンプルOffice文書を作る

ここからは、架空のExcelとWordを生成します。

Excelには次のsheetを作ります。

sheet 内容 期待する処理
Products 商品一覧 表形式として1行1Block
Maintenance 月次作業 手順形式として複数行を1Block
QA 評価用データ 除外
HiddenConfig 内部設定 非表示のため除外

Wordには、Heading、本文、箇条書き、表を順番に配置します。

6.1 必要なライブラリ

pip install openpyxl python-docx

6.2 サンプル生成コード

from datetime import datetime
from pathlib import Path

from docx import Document
from openpyxl import Workbook


output_dir = Path("sample_data")
output_dir.mkdir(exist_ok=True)

# ------------------------------------------------------------
# Excel
# ------------------------------------------------------------
workbook = Workbook()

products = workbook.active
products.title = "Products"
products.append([
    "商品ID",
    "商品名",
    "単価",
    "在庫数",
    "更新日時",
    "在庫金額",
    "担当",
    "担当",
])
products.append([
    "ITEM-001",
    "ノートPCスタンド",
    3200,
    12,
    datetime(2026, 7, 1, 9, 0),
    "=C2*D2",
    "運用担当",
    "購買担当",
])
products.append([
    "ITEM-002",
    "USBハブ",
    4800,
    8,
    datetime(2026, 7, 2, 10, 30),
    "=C3*D3",
    "運用担当",
    "購買担当",
])

maintenance = workbook.create_sheet("Maintenance")
maintenance.append([
    None,
    "作業項目",
    "コマンド/確認ポイント",
    "備考",
])
maintenance.append([
    1,
    "月次メンテナンス",
    None,
    None,
])
maintenance.append([
    1.1,
    "事前確認",
    "samplectl service status",
    "状態がrunningであること",
])
maintenance.append([
    None,
    None,
    "samplectl disk check",
    "空き容量が20%以上あること",
])
maintenance.append([
    1.2,
    "バックアップ",
    "samplectl backup create --name monthly",
    "作成完了を確認する",
])
maintenance.append([
    None,
    None,
    "samplectl backup verify --name monthly",
    "検証結果がsuccessであること",
])
maintenance.merge_cells("A3:A4")
maintenance.merge_cells("B3:B4")
maintenance.merge_cells("A5:A6")
maintenance.merge_cells("B5:B6")

qa = workbook.create_sheet("QA")
qa.append(["Question", "Expected Answer"])
qa.append([
    "ITEM-001の在庫数は?",
    "このsheetは評価専用なので知識ベースへ入れない",
])

hidden = workbook.create_sheet("HiddenConfig")
hidden.append(["Key", "Value"])
hidden.append(["internal_mode", "sample"])
hidden.sheet_state = "hidden"

excel_path = output_dir / "sample_office_docs.xlsx"
workbook.save(excel_path)

# ------------------------------------------------------------
# Word
# ------------------------------------------------------------
document = Document()
document.add_heading("運用ガイド", level=1)
document.add_paragraph(
    "この文書はサンプルシステムの基本的な運用方法を説明します。"
)

document.add_heading("バックアップ", level=2)
document.add_paragraph(
    "月次作業では、保存先を確認してからバックアップを作成します。"
)
document.add_paragraph(
    "実行前に空き容量を確認します。",
    style="List Bullet",
)
document.add_paragraph(
    "作成後に検証結果を確認します。",
    style="List Bullet",
)

table = document.add_table(rows=3, cols=2)
table.rows[0].cells[0].text = "項目"
table.rows[0].cells[1].text = "確認方法"
table.rows[1].cells[0].text = "保存先"
table.rows[1].cells[1].text = "空き容量が20%以上あること"
table.rows[2].cells[0].text = "検証結果"
table.rows[2].cells[1].text = "statusがsuccessであること"

document.add_heading("障害時の確認", level=2)
document.add_paragraph(
    "エラーが発生した場合は、時刻とエラーコードを記録してログを確認します。"
)

word_path = output_dir / "sample_operations_guide.docx"
document.save(word_path)

print(excel_path)
print(word_path)

samplectlは説明用の架空コマンドです。

6.3 期待するExcel変換結果

Productsの2行目は、次のようなBlockになります。

{
  "source_file": "sample_office_docs.xlsx",
  "source_type": "excel",
  "block_type": "table_row",
  "sheet": "Products",
  "row_start": 2,
  "row_end": 2,
  "fields": {
    "商品ID": "ITEM-001",
    "商品名": "ノートPCスタンド",
    "単価": 3200,
    "在庫数": 12,
    "更新日時": "2026-07-01T09:00:00",
    "在庫金額": "=C2*D2",
    "担当": "運用担当",
    "担当__H": "購買担当"
  }
}

このファイルはPythonで生成した直後なので、Excelによる数式の再計算結果は保存されていません。

そのため、在庫金額には数式が残る想定です。

Maintenanceの3〜4行目は、1つの作業Blockになります。

{
  "source_file": "sample_office_docs.xlsx",
  "source_type": "excel",
  "block_type": "procedure_step",
  "sheet": "Maintenance",
  "section_path": [
    "月次メンテナンス",
    "事前確認"
  ],
  "row_start": 3,
  "row_end": 4,
  "content": "セクション: 月次メンテナンス\ncolumn_A: 1.1 | 作業項目: 事前確認 | コマンド/確認ポイント: samplectl service status | 備考: 状態がrunningであること\nコマンド/確認ポイント: samplectl disk check | 備考: 空き容量が20%以上あること"
}

実行コマンドと確認条件が同じchunkに入っています。

6.4 期待するWord変換結果

バックアップ節の本文と箇条書きです。

{
  "source_file": "sample_operations_guide.docx",
  "source_type": "word",
  "block_type": "word_section",
  "section_path": [
    "運用ガイド",
    "バックアップ"
  ],
  "row_start": 4,
  "row_end": 6,
  "content": "月次作業では、保存先を確認してからバックアップを作成します。\n- 実行前に空き容量を確認します。\n- 作成後に検証結果を確認します。"
}

直後の表は、同じ見出し階層を継承します。

{
  "source_file": "sample_operations_guide.docx",
  "source_type": "word",
  "block_type": "word_table_row",
  "section_path": [
    "運用ガイド",
    "バックアップ"
  ],
  "table_index": 1,
  "row_start": 2,
  "row_end": 2,
  "body_index": 6,
  "fields": {
    "項目": "保存先",
    "確認方法": "空き容量が20%以上あること"
  }
}

7. JSONLへ出力して後段へ渡す

Evidence Blockを1行1JSONで保存します。

import json
from pathlib import Path


FIXED_DELIMITER = "<FIXED_DELIMITER>"


def write_evidence_document(blocks, output_path):
    output = Path(output_path)
    output.parent.mkdir(parents=True, exist_ok=True)

    with output.open("w", encoding="utf-8") as stream:
        for block in blocks:
            json_line = json.dumps(
                block,
                ensure_ascii=False,
                separators=(",", ":"),
            )
            stream.write(
                f"{json_line} {FIXED_DELIMITER}\n"
            )

出力イメージです。

{"source_file":"sample_office_docs.xlsx","source_type":"excel","block_type":"table_row","sheet":"Products","row_start":2,"row_end":2,"fields":{"商品ID":"ITEM-001","商品名":"ノートPCスタンド","単価":3200,"在庫数":12}} <FIXED_DELIMITER>
{"source_file":"sample_office_docs.xlsx","source_type":"excel","block_type":"procedure_step","sheet":"Maintenance","section_path":["月次メンテナンス","事前確認"],"row_start":3,"row_end":4,"content":"..."} <FIXED_DELIMITER>
{"source_file":"sample_operations_guide.docx","source_type":"word","block_type":"word_section","section_path":["運用ガイド","バックアップ"],"content":"..."} <FIXED_DELIMITER>

JSONLにしておくと、次の用途に使いやすくなります。

  • chunk単位の再処理
  • 出典metadataによるfilter
  • 変換前後のdiff
  • 検索失敗時の調査
  • Embeddingだけの再作成
  • Block単位の自動テスト

8. テストで確認すること

構造化Preprocessでは、変換できたかどうかだけでなく、「関係が壊れていないか」をテストします。

Excel

[ ] 複数のvisible sheetを読み込める
[ ] 除外sheetが知識ベースへ入らない
[ ] hidden sheetの初期値が除外になっている
[ ] 数式の保存値を優先し、なければ数式が残る
[ ] 日時がISO 8601になる
[ ] 重複ヘッダーが上書きされない
[ ] 結合セルを含む手順の継続行が同じBlockに入る
[ ] sheet名とrow_start / row_endが残る

Word

[ ] Heading 1〜3の階層を保持する
[ ] 目次styleを除外する
[ ] 本文、箇条書き、表の出現順を保持する
[ ] 表の各行が現在のsection_pathを継承する
[ ] 長文を段落境界で分割する
[ ] 表の1行を途中で分割しない

Chunk

最も重要なテストです。

chunks = split_fixed_blocks(evidence_text)

assert len(chunks) == len(evidence_blocks)
assert all(
    FIXED_DELIMITER not in chunk
    for chunk in chunks
)

さらに、次も確認します。

[ ] 1 Evidence Blockが1 chunkになる
[ ] 構造化Blockにoverlapが混入しない
[ ] すべてのchunkにsource_fileがある
[ ] Excel chunkにsheetと行番号がある
[ ] Word chunkにsection_pathがある
[ ] 評価用の正解データが混入していない

9. 実運用で失敗しやすいところ

9.1 回答データを知識ベースへ入れてしまう

検索精度が高く見えても、評価用の回答sheetを検索しているだけでは意味がありません。

評価データと知識データは、取り込み時点で分離します。

9.2 すべてのExcelを同じルールで読む

一覧表と作業手順では、意味の単位が違います。

workbook単位ではなく、sheet単位で判定します。

9.3 chunk_sizeだけを何度も調整する

800文字、1,000文字、1,500文字と変更しても、元の構造が壊れていれば改善しません。

まず、表の1行、手順の1ステップ、Wordの1節を正しく作ります。

9.4 元の場所へ戻れない

回答だけを表示できても、利用者が根拠を確認できないと業務では使いにくくなります。

source_filesheetsection_pathrow_startrow_endを最初から持たせます。

9.5 長いBlockを無条件に細かく切る

手順の実行内容と確認条件が分離すると、危険な回答になりやすくなります。

文字数上限より先に、意味の最小単位を決めます。

10. 実装時のチェックリスト

段階 確認すること
Input 原本を変更せず、別outputへ保存する
Excel 複数sheet、hidden、除外設定に対応する
Excel 表形式と手順形式をsheetごとに分ける
Excel 数式、日時、結合セル、重複列を扱う
Word Heading、本文、箇条書き、表の順番を保持する
Schema 出典metadata付きの共通Blockへ変換する
Split Block境界を通常の文字数分割より優先する
Search IDはText Search、意味検索はVector Searchも使う
Rerank 最終回答へ渡す根拠を絞る
Generation Contextにない内容は推測で補わない
Evaluation 正解ではなく、根拠Blockの到達も評価する

まとめ

Office文書をRAGで扱うとき、重要なのは「ExcelやWordをテキスト化できたか」ではありません。

重要なのは、質問に答えるための関係を壊さず、検索可能なEvidence Blockへ変換できたかです。

Excel:
  sheetを残す
  表は1行単位
  手順は1ステップ単位
  実行内容と確認条件をまとめる

Word:
  見出し階層を残す
  本文と表の出現順を残す
  表へsection pathを引き継ぐ

Chunking:
  文字数より構造境界を優先する
  1 Evidence Block = 1 Chunk
  出典metadataを落とさない

先にParserとChunkingを安定させると、Embedding、Hybrid Search、Rerank、LLM回答の改善を正しく評価できるようになります。

マルチシートExcelやWordをRAGへ入れるときは、まず1つの質問に必要な情報が「同じchunkに残っているか」を確認してみてください。

是非お試しください!

0
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
0
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?