コンテンツにスキップ

重複検出・最新版選択システム 仕様書

作成日: 2025年12月17日
バージョン: 1.0
ファイル: ingest/deduplicate_chunks.py

概要

雑多に格納されたドキュメントデータから、重複を検出し最新版を自動選択するシステム。 ファイル名だけでなく、コンテンツの類似度、文書内日付、パス情報を総合的に判断する。

設計思想

「差分はファイル名だけでなく、中身を見て判別」
「差分とファイルの更新日時を確認し、新しい方を活かす」
「文書内に日付があれば新しいほうを活かす」
「判断に迷った場合はアラートを出して判断を仰ぐ」

処理フロー

┌─────────────────────────────────────────────────────────────┐
│ 入力: チャンクJSONL(source_path, contentを含む)            │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 1: 重複候補の検出                                       │
├─────────────────────────────────────────────────────────────┤
│ 1. コンテンツハッシュ(MD5)が一致 → 確実に重複              │
│ 2. ファイル名類似度 > 60% かつ コンテンツ類似度 > 70%        │
│    → 重複候補                                                │
│ ※事業所別ファイル(久慈/豊洲/北沼/本社)は別ファイル扱い    │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 2: スコアリング                                         │
├─────────────────────────────────────────────────────────────┤
│ 総合スコア = パス優先度スコア + ファイル日時スコア           │
│            + 文書内日付スコア                                │
│                                                              │
│ ■ パス優先度スコア(0-110点)                               │
│   - 2025.6更新版/PDF: 110点                                  │
│   - 2025.6更新版: 100点                                      │
│   - 最新2025.4更新用: 90点                                   │
│   - 令和2年版: 33点 > 令和元年版: 28点                       │
│   - 旧版フォルダ: -8点                                       │
│   - (2)ファイル: -10点                                       │
│   - 「旧」ファイル: -15点                                    │
│                                                              │
│ ■ ファイル日時スコア(0-30点)                              │
│   - ファイル名の日付(YYYYMMDD)を解析                       │
│   - 2020年基準で新しいほど加点                               │
│                                                              │
│ ■ 文書内日付スコア(0-30点)                                │
│   - 令和/平成/西暦の日付パターンを抽出                       │
│   - 最新日付を採用、新しいほど加点                           │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ STEP 3: 判定ロジック                                         │
├─────────────────────────────────────────────────────────────┤
│ ■ 自動採用条件                                              │
│   1. 同一ハッシュ → パス優先度最高を採用                    │
│   2. スコア差 > 閾値(デフォルト20点)→ 最高スコアを採用    │
│   3. 文書内日付が異なる → 新しい日付を採用                  │
│   4. パス内年度が異なる → 新しい年度を採用                  │
│                                                              │
│ ■ 要判断条件                                                │
│   - 上記いずれにも該当しない                                 │
│   - コンテンツが異なりスコア差も小さい                       │
└─────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────┐
│ 出力                                                         │
├─────────────────────────────────────────────────────────────┤
│ 1. chunks_merged.jsonl: 重複除去済みチャンク                 │
│ 2. dedup_report.md: 処理レポート                             │
│    - 採用ファイル一覧                                        │
│    - 除外ファイル一覧(理由付き)                            │
│    - 要判断グループ(手動選択用)                            │
└─────────────────────────────────────────────────────────────┘

パス優先度パターン

パターン 優先度 説明
2025.6.*更新版/*.pdf 110 最新更新版のPDF
2025.6.*更新版/変更届/*/*.pdf 108 変更届内のPDF
2025.6.*更新版/更新*.docx 105 更新版Word
2025.6.*更新版 100 2025.6更新版全般
2025.6変更届用 95 2025.6変更届
最新.*2025.4.*更新用/PDF 92 2025.4 PDF
令和 2年版 33 税務手引き(令和2年)
令和元年版 28 税務手引き(令和元年)
過去データ 10 過去データフォルダ

ペナルティパターン

パターン ペナルティ 説明
(2) または (2) -10 重複コピーの可能性
で終わる -15 旧バージョン
/旧版/ -15 旧版フォルダ
作成中 -5 作業中フォルダ

日付抽出パターン

対応フォーマット

形式 優先度
令和年月日 令和6年4月1日
令和年月 令和6年4月
令和年 令和6年
西暦年月日 2024年4月1日, 2024/4/1
ファイル名内日付 20240730 参考

和暦→西暦変換

和暦 西暦
令和元年 2019年
令和2年 2020年
令和7年 2025年

コンテンツ類似度

Jaccard係数

類似度 = |A ∩ B| / |A ∪ B|

A: ファイル1の単語集合
B: ファイル2の単語集合
  • 閾値: 0.7(70%以上で重複とみなす)
  • 日本語対応: \w+ パターンで単語分割

使用方法

# ドライラン(レポートのみ)
python ingest/deduplicate_chunks.py input.jsonl --dry-run --report report.md

# 重複除去実行
python ingest/deduplicate_chunks.py input.jsonl -o output.jsonl --report report.md

# オプション
#   --dry-run           レポートのみ生成
#   --base-dir DIR      ファイル日時取得用ベースディレクトリ
#   --similarity FLOAT  類似度閾値(デフォルト: 0.7)
#   --score-threshold N スコア差閾値(デフォルト: 5)

出力レポート形式

サマリー

| 項目 | 件数 |
|------|------|
| 重複なし(そのまま採用) | 580 |
| 重複あり → 最新版採用 | 93 |
| 重複あり → 除外 | 210 |
| ⚠️ 要判断 | 0 |

要判断グループ

### グループ 1

| # | ファイル | スコア | 文書内日付 | ファイル日時 | パス優先度 |
|---|----------|--------|-----------|-------------|-----------|
| 1 | `path/to/file1.pdf` | 58.0 | 2024-08-01 | - | 86 |
| 2 | `path/to/file2.pdf` | 54.0 | 2024-06-01 | - | 78 |

判定フローチャート

重複グループ検出
      ↓
┌─────────────────────┐
│ 全て同一ハッシュ?  │
└──────────┬──────────┘
           ↓ Yes
    パス優先度で決定 → 採用
           ↓ No
┌─────────────────────┐
│ スコア差 > 閾値?   │
└──────────┬──────────┘
           ↓ Yes
    最高スコアを採用 → 採用
           ↓ No
┌─────────────────────┐
│ 文書内日付で比較    │
│ 差があるか?        │
└──────────┬──────────┘
           ↓ Yes
    新しい日付を採用 → 採用
           ↓ No
┌─────────────────────┐
│ パス内年度で比較    │
│ 差があるか?        │
└──────────┬──────────┘
           ↓ Yes
    新しい年度を採用 → 採用
           ↓ No
┌─────────────────────┐
│ (2)ファイル除外     │
└──────────┬──────────┘
           ↓ Yes
    (2)なしを採用 → 採用
           ↓ No
┌─────────────────────┐
│ 整理済みフォルダ    │
│ (00_社内規則)優先   │
└──────────┬──────────┘
           ↓ Yes
    整理済みを採用 → 採用
           ↓ No
┌─────────────────────┐
│ 同一フォルダ内?    │
└──────────┬──────────┘
           ↓ Yes
    短いファイル名を採用 → 採用
           ↓ No
    要判断 → レポート出力

章番号による別文書判定

ファイル名に chap_XX-YY パターンがある場合、章番号が異なれば別文書として扱う。

例: - chap_06-11_利子所得の分離課税制度.pdf - chap_15-07_利子所得の分離課税制度.pdf

→ ファイル名は類似しているが、章番号(06-11 vs 15-07)が異なるため別文書

技術詳細

依存ライブラリ

  • Python 3.8+
  • 標準ライブラリのみ(外部依存なし)

計算量

  • 重複検出: O(n²) ※n = ファイル数
  • 類似度計算: O(m) ※m = 単語数

メモリ使用量

  • 全チャンクをメモリに読み込み
  • 大規模データ(10万件以上)の場合は分割処理を検討

制限事項

  1. 画像内テキスト: OCRは非対応
  2. 暗号化PDF: 読み取り不可
  3. マクロ有効Excel: 非対応(xlsmは除外)
  4. ファイル日時: Google Drive経由では取得不可の場合あり

今後の拡張案

  • [ ] 差分インジェスト対応(既存インデックスとの比較)
  • [ ] 並列処理によるパフォーマンス改善
  • [ ] Web UI による要判断ファイルの選択
  • [ ] 機械学習による類似度判定の高度化

データ種別による処理分離(設計方針)

背景

重複検出ロジックはデータの性質によって適切な処理が異なる。

データ種別 特性 重複検出
規程・マニュアル 最新版のみ有効 必要 - 最新を採用、旧を除外
日報・作業指示書 日付ごとに独立した文書 不要 - 全件採用

問題の例

日報20240730.pdf  ← 7月30日の日報
日報20240731.pdf  ← 7月31日の日報(内容がコピペで同一)

現在の重複検出ロジックでは、ファイル名から日付を除去して比較するため、 上記は「同一ファイルの別バージョン」と判定され、片方が除外される。 しかし日報の場合、両方とも必要な独立した文書である。

解決方針:パイプライン分離

規程・マニュアル:
  prepare_data_v5.py → deduplicate_chunks.py → upload

日報・作業指示書:
  prepare_data_v5.py → upload(重複検出なし)

実装ステータス

パイプライン 状態 備考
規程・マニュアル ✅ 完成 本ドキュメントの対象
日報・作業指示書 🔜 保留 手書きデータのOCR/データ化が先決課題

日報パイプラインの課題(参考)

  1. 手書き日報のデータ化: OCR精度、フォーマット統一
  2. 重複検出スキップ: --exclude-pattern オプション追加、またはフォルダ分離
  3. 時系列クエリ対応: 「先月の日報を集計」などの検索UI

→ 日報のデータ化は別ラインで進行。RAGへの統合は次フェーズ。

関連ドキュメント