重複検出・最新版選択システム 仕様書¶
作成日: 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万件以上)の場合は分割処理を検討
制限事項¶
- 画像内テキスト: OCRは非対応
- 暗号化PDF: 読み取り不可
- マクロ有効Excel: 非対応(xlsmは除外)
- ファイル日時: 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/データ化が先決課題 |
日報パイプラインの課題(参考)¶
- 手書き日報のデータ化: OCR精度、フォーマット統一
- 重複検出スキップ:
--exclude-patternオプション追加、またはフォルダ分離 - 時系列クエリ対応: 「先月の日報を集計」などの検索UI
→ 日報のデータ化は別ラインで進行。RAGへの統合は次フェーズ。
関連ドキュメント¶
- data-ingestion-v5.md - マルチフォーマットインジェスト
- knowledge-inventory.md - ナレッジデータ一覧