マガジン

古い版のマニュアルが回答に混ざる:版・有効期間・廃止資料の扱い方

AIを使いこなす

Claudeで執筆し、GPTで自動校閲・修正しています。

机に並んだ旧版と新版の紙の手順書を担当者が確認している。
記事の内容をもとに制作したAI生成イメージです。実際の製品画面や検証結果の写真ではありません。

結論から言うと、通常の質問では「検索で絞る」を先に実施し、そのうえで「回答に版を書かせる」を併用する方法が有効です。役割が違うためです。絞り込みはAIに渡す資料を決め、版の表示は回答の根拠を利用者が確かめられるようにします。回答での版表示だけに頼ると、新旧が同じ文脈に入った時点でモデルが取り違える余地が残ります。 ただし、絞り込みの前提として、資料側に版・有効期間・状態のメタデータが必要です。本文のファイル名や見出しに「v3.2」と書いてあるだけでは、検索の条件には使えません。この記事では、そのメタデータの定義例と、検索時の条件の書き方、回答の指示文までを示します。

この記事の目次
  1. なぜ新旧が混ざるのか
  2. メタデータの定義例
  3. 検索時の絞り込み条件の書き方
  4. 絞らずに版を示すべき場面
  5. 回答側の指示文と、人が確認すること

なぜ新旧が混ざるのか

旧版が回答に混ざる原因は、おおむね次の三つです。

  • 新旧の条文や手順がほぼ同じ文面で、意味的な近さでは区別がつかない
  • 取り込み時に版の情報を保存しておらず、検索時に除外する手段がない
  • 改訂時に旧版を差し替えずに追加したため、両方が索引に残っている

特に二つ目と三つ目は、プロンプトの工夫だけでは解消できません。「最新版だけ参照して」と頼んでも、モデルには渡された断片のどれが最新かを判断する材料がない場合があります。先に資料へ材料を持たせます。

メタデータの定義例

以下は説明用の例で、実際のシステムで動作を確認したものではありません。項目名は自社の台帳に合わせて変えてください。

  • doc_id、型:文字列、例:HR-LEAVE、役割:版が変わっても不変の資料ID
  • version、型:文字列、例:3.10、役割:人に見せる版表記
  • version_seq、型:整数、例:12、役割:版の新旧比較用の連番
  • status、型:文字列、例:current / superseded / withdrawn / draft、役割:運用上の状態
  • effective_from、型:整数、例:20250401、役割:有効開始日(yyyymmdd)
  • effective_to、型:整数、例:99991231、役割:有効終了日。未定なら最大値
  • superseded_by、型:文字列、例:HR-LEAVE@4.0、役割:後継版。なければ空
  • title / source_url、型:文字列、例:資料名、原本の場所、役割:回答での出典表示

設計上のポイントは三つあります。

日付の型はストアに合わせる。 たとえばLlamaIndexのフィルタ演算子の仕様では、GTやLTEといった大小比較は数値(int、float)向けとされています。OpenAIのベクトルストアのファイル属性の例では、Unix時間の数値を使っています。日付をどの型・形式で保存するかは、利用するストアの仕様を確認して決めてください。

版表記と比較用の連番を分ける。 3.10と3.9を文字列で比べると順序が逆転します。「最新」を判定したい場面では連番を使います。

終了日を空にしない。 未定を空欄にすると、ストアごとに空値の扱いが違い、条件がすり抜けます。99991231のような最大値を入れておけば、条件式が一本で済みます。

なお、メタデータは分割後の各チャンクにも検索時に参照できる形で保存してください。資料単位でしか持たない構成では、検索方法によっては絞り込みに使えません。OpenAIのベクトルストアのファイル属性はキー数が最大16個、キー長が最大256文字と公式ドキュメントに記載されています。

検索時の絞り込み条件の書き方

基準日をD(今日なら20261028)とすると、通常の質問に使う条件は次のとおりです。

status が "current"
かつ effective_from <= D
かつ effective_to   >= D

statusと日付の両方を見るのは、改訂予定の資料を先にcurrentで登録してしまった事故や、日付の入力漏れを互いに補うためです。

OpenAIのファイル検索(ベクトルストア)の場合

比較フィルタ(eq、lte、gteなど)をandでまとめる形式が公式ドキュメントにあります。上の条件を当てはめると、次のようになります(説明用の例)。

json
{
  "type": "and",
  "filters": [
    {"type": "eq",  "key": "status",         "value": "current"},
    {"type": "lte", "key": "effective_from", "value": 20261028},
    {"type": "gte", "key": "effective_to",   "value": 20261028}
  ]
}

LlamaIndexの場合

MetadataFilterにkey、value、operatorを指定し、MetadataFiltersでFilterCondition.ANDによって束ねます。これらの型は前掲の仕様に載っています。以下は未実行の例です。

python
from llama_index.core.vector_stores import (
    MetadataFilter, MetadataFilters, FilterOperator, FilterCondition,
)

D = 20261028  # 実運用では実行日から生成する
filters = MetadataFilters(
    filters=[
        MetadataFilter(key="status", value="current", operator=FilterOperator.EQ),
        MetadataFilter(key="effective_from", value=D, operator=FilterOperator.LTE),
        MetadataFilter(key="effective_to", value=D, operator=FilterOperator.GTE),
    ],
    condition=FilterCondition.AND,
)
retriever = index.as_retriever(filters=filters, similarity_top_k=8)

使うベクトルストアによって、対応する演算子や型が異なる可能性があります。導入前に、自分の環境で小さなデータを使って確認してください。

Azure AI Searchの場合

フィルタはOData式で書き、対象はfilterable属性のフィールドです。文字列の比較は完全一致で大文字小文字も区別されます(Azure AI Searchのフィルタ)。注意点が二つあります。

  • 既存のフィールドをfilterableに変更することはできず、フィールド追加か索引の再構築が必要です。メタデータの設計は、本格的に取り込む前に決めてください。
  • ベクトル検索では、フィルタを検索の前に適用するpreFilterと、後に適用するpostFilterがあります。公式ドキュメントでは、postFilterは絞り込みが厳しい場合に該当文書を取りこぼすことがあると説明されています。「現行版だけ」という条件は旧版が多いほど厳しくなりやすいため、preFilterを使うのが無難です。ただし古い索引ではpostFilterが既定の場合があるので、設定値を確認してください。

絞らずに版を示すべき場面

常に現行版だけで足りるわけではありません。質問のタイプで方針を分けます。

  • 「育休の申請手順は?」、検索の条件:現行のみ(上の条件)、回答での扱い:版と有効開始日を明記
  • 「2025年3月時点の規程は?」、検索の条件:基準日Dをその日にして同じ条件で検索。statusはcurrentかsuperseded、回答での扱い:「その時点の版」と明記
  • 「3.2版では何と書いてある?」、検索の条件:doc_idとversionを指定、回答での扱い:指定版である旨を明記
  • 「4.0で何が変わった?」、検索の条件:新旧2つの版を明示的に取得、回答での扱い:版ごとに分けて比較
  • 廃止された手順を尋ねられた、検索の条件:現行条件では該当なし、回答での扱い:廃止されたことと後継を案内

一番下の行は見落とされがちです。現行条件で何も出ないときに、近い別資料からそれらしく答えてしまうのが典型的な事故です。withdrawnの資料は通常は検索の対象外にしつつ、廃止の事実だけは案内できるよう、台帳側にsuperseded_byを残しておくと対応しやすくなります。誤って公開した資料など、そもそも誰にも見せたくないものは、フィルタで隠すのではなく索引から削除してください。

質問が過去時点を求めているかどうかの判定は、検索の前に別の処理で行うか、アプリ側の入力欄(基準日)として用意するのが確実です。モデルに自由に条件を作らせると、条件の誤りが検索結果の誤りに直結します。

回答側の指示文と、人が確認すること

絞り込みの後で、回答の根拠を明示させる指示文の例です。

あなたは社内規程の回答担当です。以下の【資料】だけを根拠に答えてください。
- 回答の末尾に、根拠にした資料名・版(version)・有効開始日(effective_from)を書く
- 【資料】に複数の版が含まれる場合は、混ぜずに版ごとに分けて書く
- 【資料】に答えがない場合は、推測せず「該当する現行資料が見つかりません」と答える
- status が withdrawn または superseded の資料を根拠にするときは、その旨と後継版(superseded_by)を明記する

この指示文は、メタデータを各チャンクと一緒にモデルへ渡して初めて機能します。版の情報を本文の前に付けて渡す実装にしてください。

人が確認するのは次の三点です。

  1. 台帳の更新:改訂のたびに、新版の登録と旧版のeffective_to・statusの更新を同時に行う手順になっているか。ここが漏れると、検索の条件がすべて空回りします。
  2. 確認用の質問セット:改訂前後で答えが変わる質問を5問ほど用意し、改訂のたびに現行版の内容で答えるか、旧版の内容が出ないかを見ます。
  3. 条件で0件になる場合:絞り込みの結果が空のときに、上の指示文どおり「見つからない」と答えているか。

旧版を索引に残すか削除するかは、過去時点の質問に答える必要があるかで決めます。必要なら残してフィルタで制御し、不要なら削除する方が、設定ミスの余地が減ります。

出典・参考資料