ふくふくHukuhuku Inc.
EP.14RAG 13分公開: 2026-09-01

更新が頻繁な文書を扱う ── 索引の鮮度をどう保つか

元の文書は更新されたのに、検索結果は古いまま。壊れているように見えないので、誤った回答が出続けます。更新の設計と、遅れの伝え方を扱います。

#RAG#運用#設計#データ品質
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

を運用し始めると、必ず出てくるのが鮮度の問題です。元の文書は更新されたのに、検索結果は古いまま

厄介なのは、壊れているように見えないことです。検索は成功し、それらしい回答が返ります。内容だけが古い。この回はその対処を扱います。

1. なぜ気づけないのか

── 元は更新されたのに索引に反映されていない状態 ── は、エラーとして現れません

壊れ方と、気づきやすさ
状態利用者から見て気づけるか
索引が壊れている検索できないすぐ気づく
索引が空何も出ないすぐ気づく
索引が古いそれらしい結果が出る気づけない
削除された文書が残る存在しない文書が出る指摘されるまで気づかない

3行目と4行目が問題です。動いているように見えて、内容が正しくない。この構造は 前処理の現場 EP.22 で扱った「静かに壊れる」と同じです。

削除の反映漏れが最も危険

4行目は特に危険です。廃止された規程、終了したサービスの説明が検索結果に出続ける。しかも出典として表示されるため、利用者は正しい情報だと受け取ります。

2. 更新の方式

方式は3つに整理できます。それぞれ得意な条件が違います

更新方式の比較
方式速さ確実さ削除の反映
全件を作り直す遅い最も確実確実
速い条件による漏れやすい
変更を検知して即時最速仕組み次第検知できれば確実

2行目の削除の反映漏れが、差分方式の最大の弱点です。「更新された文書」は検知できても、「消えた文書」は検知しにくい。元のシステムが削除の記録を残していなければ、気づけません。

実務的な組み合わせは、差分を基本にして、定期的に全件を作り直すことです。レガシー再生の現場 EP.06Snowflake EP.11 で扱った差分更新と同じで、取りこぼしを定期的に回収します。

3. 削除をどう扱うか

削除の反映は、方式を決めておく必要があります。

  1. 1元のシステムで削除の記録を残す — 最も確実だが、上流の協力が要る
  2. 2全件の一覧と突き合わせる — 索引にあって元に無いものを消す
  3. 3有効期限を設ける — 一定期間、再取得されなければ消す
  4. 4元の文書の存在を、回答時に確認する — 確実だが遅くなる
索引にあって元に無い文書を検出して消す
Python
def reconcile_index(index_client, source_client, dry_run: bool = True):    """索引と元の文書一覧を突き合わせ、消えたものを索引から除く。"""    indexed_ids = set(index_client.list_all_ids())    source_ids = set(source_client.list_all_ids())
    to_delete = indexed_ids - source_ids       # 索引にあるが元に無い    to_add = source_ids - indexed_ids          # 元にあるが索引に無い
    print(f"  索引 {len(indexed_ids):,} 件 / 元 {len(source_ids):,} 件")    print(f"  削除すべき: {len(to_delete):,} 件")    print(f"  追加すべき: {len(to_add):,} 件")
    # 大量に消える場合は、元の取得が失敗している可能性を疑う    if to_delete and len(to_delete) > len(indexed_ids) * 0.1:        print("  ※ 削除対象が1割を超えています。元の取得を確認してください")        if dry_run is False:            raise RuntimeError("安全のため中断しました")
    if dry_run:        print("  (確認のみ。実行するには dry_run=False)")        return {"to_delete": to_delete, "to_add": to_add}
    for doc_id in to_delete:        index_client.delete(doc_id)    return {"deleted": len(to_delete), "to_add": to_add}

削除対象が多すぎる場合に中断するのが重要な安全装置です。元の一覧の取得が失敗して空が返ってきた場合、索引を全消ししてしまいます。「1割を超えたら止める」という上限を入れておいてください。

これは 前処理の現場 EP.22 で扱った件数の検査と同じ発想です。急激な変化は、正しい変化より、失敗である可能性が高い

4. 遅れを可視化する

完全な即時反映は難しいので、遅れがあることを前提に設計します。要点は、遅れを測れる状態にすることです。

元の更新日時と、索引の更新日時を比べる
Python
from datetime import datetime, timedelta, timezone

def check_freshness(index_client, source_client, threshold_hours=24):    """索引が元より古くなっている文書を見つける。"""    stale = []    now = datetime.now(timezone.utc)
    for doc in source_client.list_documents():        indexed = index_client.get_metadata(doc["id"])        if indexed is None:            stale.append((doc["id"], "索引に存在しない", None))            continue
        lag = doc["updated_at"] - indexed["indexed_at"]        if lag > timedelta(hours=threshold_hours):            stale.append((doc["id"], "索引が古い", lag))
    print(f"  確認 {source_client.count():,} 件 / 古い {len(stale):,} 件")    for doc_id, reason, lag in stale[:10]:        lag_str = f"{lag.total_seconds() / 3600:.1f}時間" if lag else "-"        print(f"    {doc_id}: {reason}(遅れ {lag_str})")
    return stale

この確認を定期的に回すと、更新の仕組みが正しく動いているかが分かります。特定の文書だけ更新されない、といった偏りも見つかります。

利用者にも伝える

「この情報は◯月◯日時点です」と回答に添えるだけで、利用者が判断できます。GEO/LLMO EP.08 で扱った鮮度の明示と同じで、時点が分かれば、使う側が古さを考慮できます

5. 即時性が要る場合

価格、在庫、空き状況。古い情報を返すと実害が出る種類のデータは、索引に載せる設計自体を見直します。

データの性質と扱い
データの性質扱い
めったに変わらない(規程、マニュアル)索引に載せる。日次更新で十分
日単位で変わる(案件の状況)索引に載せる。頻度を上げる
刻々と変わる(価格、在庫)索引に載せず、回答時に取得
個人ごとに違う(契約内容)回答時に取得

下2行が要点です。索引に載せるのは、変わりにくい情報だけにする。刻々と変わる値は、回答を作る時点で元のシステムに問い合わせる

この設計にすると、「規程の説明は索引から、現在の価格は問い合わせて」という組み合わせになります。実装は複雑になりますが、古い価格を答えるという事故が構造的に起きなくなります。仕組みで頑張るのではなく、設計で問題を消すほうが確実です。

6. 運用の整理

最後に、運用として決めておくことをまとめます。

  1. 1更新の頻度を、文書の性質ごとに決める — 一律にしない
  2. 2差分を基本に、定期的な全件再構築を組み合わせる — 取りこぼしを回収
  3. 3削除の反映方式を決める — 突き合わせか、有効期限か
  4. 4遅れを測って監視する — 古くなっている文書を検出
  5. 5時点を回答に添える — 利用者が判断できるように
  6. 6即時性が要るものは索引に載せない — 設計で防ぐ

6番目が最も確実な対処です。仕組みで頑張るより、そもそも古くなりうるものを載せない。判断の順序としては、まずここを検討してください。

ここまでのまとめ

古い索引 はエラーにならず、それらしい結果が返るので気づけない。特に削除の反映漏れが危険(廃止された規程が出典として出る)。差分を基本に、定期的な全件再構築で取りこぼしを回収する。突き合わせでの削除には上限の安全装置を入れる。遅れを測って監視し、時点を回答に添える。そして即時性が要るものは、そもそも索引に載せないのが最も確実です。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp