AIの業務活用

AI要約を原文と文ごとに照合する

AI要約を個別の主張に分け、それぞれを具体的な原文の文に結び付け、再利用する前に根拠のない表現や言い過ぎた表現を確認します。小さな合成文書を使って、自然に読める要約でも根拠確認が必要な理由を示します。

目次を表示

この翻訳はAIで作成しました。コード、単位、数値は原文と併せて確認してください。各言語のネイティブ話者による校閲は、まだ完了していません。 English

対象読者AIを使ってレポート、メモ、研究資料を要約し、文ごとに追跡可能なレビューを行いたい人向けです。

準備するもの
  • Python 3.12と、そのバージョンを起動できるターミナルコマンドが必要です。
  • UTF-8のテキストファイルとCSVファイルを保存できるテキストエディタが必要です。
  • スクリプトがoutputs配下に新しいフォルダを作成できる作業フォルダが必要です。
  • 必要なのはPython標準ライブラリのcsvとpathlibだけです。

01要約の各文を1つの主張として扱う

AI要約は文法的に自然でも、原因を変えたり、制約条件を落としたり、原文にない結論を追加したりすることがあります。そのため、実用的なレビューでは各要約文について、どの原文部分がこの文を裏付けているのかを確認します。

このチュートリアルでは3つの判定を使います。SUPPORTEDは原文がその文を直接裏付けている場合です。PARTIALは文の一部だけが裏付けられているか、重要な条件が欠けている場合です。UNSUPPORTEDは原文がその主張を裏付けていないか、内容と矛盾する場合です。

02合成した原文とAI要約を作成する

以下の原文と要約は合成データで、このページ専用に作成したものです。最初のブロックをsource.txt、2つ目をai_summary.txtとして保存してください。

text
S1|The pilot processed 20 test cases.
S2|Eighteen cases completed successfully.
S3|Two cases stopped during input validation before the solver started.
S4|Among the 18 completed cases, the median runtime was 42 seconds.
S5|The test was performed on one workstation under the same software configuration.
S6|No production deployment or long-term reliability test was performed.
text
A1|The pilot processed 20 cases, and 18 completed successfully.
A2|Two cases failed because the solver crashed.
A3|The median runtime for completed cases was 42 seconds.
A4|The results show that the automation is reliable enough for production use.

原文は6文、AI要約は4文です。ソフトウェアを使わなくても確認できるよう、数値は意図的に小さくしています。

03要約の各文を原文と比較する

A1はS1とS2で裏付けられます。A2はS3と矛盾します。原文では、2件はsolverが開始する前のinput validationで停止したとあり、solver crashが原因とは書かれていません。A3はS4で直接裏付けられます。A4は根拠の範囲を超えています。S6にはproduction deploymentもlong-term reliability testも実施していないと明記されています。

要約ID判定根拠理由
A1SUPPORTEDS1;S2ケース数と成功完了数が原文と一致しています。
A2UNSUPPORTEDS3原文では、停止原因をsolver開始前のinput validationとしています。
A3SUPPORTEDS4完了した18件のmedian runtimeが42秒と記載されています。
A4UNSUPPORTEDS6production readinessはテストされていません。

したがって、期待されるレビュー結果はSUPPORTEDが2文、UNSUPPORTEDが2文です。この4つの合成文ではPARTIAL判定は必要ありません。

04追跡可能なCSVにレビューを記録する

以下をsummary_review.csvとして保存してください。evidence列には段落全文ではなく原文の文IDを記録します。これにより、レビューを簡潔に保ちながら追跡可能性を維持できます。

csv
summary_id,verdict,evidence,reason
A1,SUPPORTED,S1;S2,The case count and successful completions match the source.
A2,UNSUPPORTED,S3,The source attributes the stops to input validation before solver start.
A3,SUPPORTED,S4,The 42-second median is stated for the 18 completed cases.
A4,UNSUPPORTED,S6,Production readiness was not tested.

このレビュー自体は人による判断です。次のスクリプトは主張が正しいかどうかを判断しません。各AI要約文に有効な判定が付いているか、引用した原文IDが実際に存在するかを確認します。

05すべての文がレビューされているか確認する

以下のスクリプトをcheck_summary_review.pyとして保存してください。原文、要約、完成したレビューCSVを読み込み、IDと判定を確認し、入力ファイルを変更せずに新しい検証済みレポートを書き出します。

python
import csv
from pathlib import Path

SOURCE = Path("source.txt")
SUMMARY = Path("ai_summary.txt")
REVIEW = Path("summary_review.csv")
OUTPUT_DIR = Path("outputs") / "summary_source_check"
OUTPUT = OUTPUT_DIR / "validated_review.csv"
VALID_VERDICTS = {"SUPPORTED", "PARTIAL", "UNSUPPORTED"}


def read_id_text(path: Path) -> dict[str, str]:
    result = {}
    with path.open("r", encoding="utf-8") as stream:
        for line_number, line in enumerate(stream, start=1):
            line = line.rstrip("\n")
            if not line:
                continue
            item_id, separator, text = line.partition("|")
            if not separator or not item_id or not text:
                raise ValueError(f"Invalid line {line_number} in {path}.")
            if item_id in result:
                raise ValueError(f"Duplicate ID {item_id} in {path}.")
            result[item_id] = text
    return result


def main() -> None:
    for path in (SOURCE, SUMMARY, REVIEW):
        if not path.is_file():
            raise FileNotFoundError(f"Missing input: {path}")
    if OUTPUT_DIR.exists():
        raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")

    source = read_id_text(SOURCE)
    summary = read_id_text(SUMMARY)

    reviewed = {}
    with REVIEW.open("r", encoding="utf-8-sig", newline="") as stream:
        reader = csv.DictReader(stream)
        required = {"summary_id", "verdict", "evidence", "reason"}
        if reader.fieldnames is None or not required.issubset(reader.fieldnames):
            raise ValueError("Review CSV is missing a required column.")

        for row in reader:
            summary_id = row["summary_id"].strip()
            verdict = row["verdict"].strip()
            evidence_ids = [x.strip() for x in row["evidence"].split(";") if x.strip()]

            if summary_id not in summary:
                raise ValueError(f"Unknown summary ID: {summary_id}")
            if summary_id in reviewed:
                raise ValueError(f"Duplicate review: {summary_id}")
            if verdict not in VALID_VERDICTS:
                raise ValueError(f"Invalid verdict for {summary_id}: {verdict}")
            if not evidence_ids:
                raise ValueError(f"No evidence listed for {summary_id}.")
            unknown = [item for item in evidence_ids if item not in source]
            if unknown:
                raise ValueError(f"Unknown source IDs for {summary_id}: {unknown}")
            if not row["reason"].strip():
                raise ValueError(f"Missing reason for {summary_id}.")

            reviewed[summary_id] = {
                "summary_id": summary_id,
                "summary_text": summary[summary_id],
                "verdict": verdict,
                "evidence": ";".join(evidence_ids),
                "reason": row["reason"].strip(),
            }

    missing = [item for item in summary if item not in reviewed]
    if missing:
        raise ValueError(f"Summary sentences not reviewed: {missing}")

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()
    fields = ["summary_id", "summary_text", "verdict", "evidence", "reason"]
    with OUTPUT.open("x", encoding="utf-8", newline="") as stream:
        writer = csv.DictWriter(stream, fieldnames=fields)
        writer.writeheader()
        for summary_id in summary:
            writer.writerow(reviewed[summary_id])

    counts = {verdict: 0 for verdict in VALID_VERDICTS}
    for row in reviewed.values():
        counts[row["verdict"]] += 1

    print(f"Summary sentences checked: {len(summary)}.")
    print(
        f"Supported: {counts['SUPPORTED']}; partial: {counts['PARTIAL']}; "
        f"unsupported: {counts['UNSUPPORTED']}."
    )
    print(f"Output: {OUTPUT.as_posix()}")


if __name__ == "__main__":
    main()

06期待される結果を確認する

上記の合成レビューでは、4つの要約文すべてに有効な判定と有効な原文参照があります。以下に期待されるコンソール出力を示します。これは手作業で導いたもので、実際の実行ログではありません。

text
Summary sentences checked: 4.
Supported: 2; partial: 0; unsupported: 2.
Output: outputs/summary_source_check/validated_review.csv
  • 各A-IDがレビュー内に正確に1回だけ出てくることを確認します。
  • 引用されたすべてのS-IDがsource.txtに存在することを確認します。
  • UNSUPPORTEDの主張が、根拠なくSUPPORTEDに書き換えられていないことを確認します。
  • レビューを追跡可能に保つため、元のAI要約は変更せずに保存します。
  • OUTPUT_DIRを変更せずにスクリプトを再実行します。以前の検証結果を上書きせず、FileExistsErrorで停止するはずです。

07よくある誤りと限界を理解する

問題対応方法
1つの要約文に2つの別々の主張がある文を主張単位に分けるか、それぞれの主張を個別にレビューします。
根拠が文の一部しか裏付けていないPARTIALを使い、不足している条件をreason列に記録します。
原文に結論を裏付ける記述がないもっともらしい推測を出典付きの事実として扱わず、UNSUPPORTEDとします。
原文の文を引用しているが実際には主張を裏付けていない人によるレビューを修正します。有効なIDだけでは意味的な裏付けを証明できません。
異なる出典が食い違っている根拠のない1つの結論に無理にまとめず、相違点を明示的に記録します。

Pythonスクリプトが検証するのは、レビューがすべて揃っているかと参照IDが有効かどうかであり、真偽そのものではありません。意味的な裏付けには、依然として人による読解または別の根拠評価プロセスが必要です。人の判定が間違っていても、構造チェックは通る可能性があります。

長いレポートでは、曖昧な参照ではなくページ番号、段落ID、表番号、引用位置を使ってください。また、原文の直接的な記述、計算結果、解釈、外部知識を区別してください。要約の中で、これらの区分を暗黙に入れ替えないようにします。

実行・検証の記録

2026-09-20 · 手作業で確認した例 · 対象: Python 3.12 · 標準ライブラリ: csv, pathlib · 未実行

  • 合成したAI要約4文を、合成した原文6文と手作業で照合しました。
  • A1とA3をSUPPORTED、A2とA4をUNSUPPORTEDに分類しました。
  • 原文ではsolver開始前にinput validationで停止したと書かれているため、A2がS3と矛盾することを確認しました。
  • S6ではproduction deploymentとlong-term reliabilityがテストされていないため、A4が原文の範囲を超えていることを確認しました。
  • 期待される判定数をSUPPORTED 2、PARTIAL 0、UNSUPPORTED 2として手作業で計算しました。
  • 未レビュー、重複ID、不正な判定、存在しない原文ID、出力衝突の防止、レポート生成をスクリプトで確認しました。
検証範囲の限界
  • この回答の作成者はコードを実行しておらず、検証CSVは作成していません。
  • 構造チェッカーは、引用された原文が意味的に主張を裏付けているかどうかまでは判断しません。
  • ここに示した合成原文と要約だけをレビューしており、長文、表、図、相反する出典はテストしていません。
  • 公式ドキュメントのURLは既知の文書位置を使用していますが、リアルタイムでは確認していません。

サイト全体の執筆・検証方針

参考資料

説明と例は独自に作成しました。関連する動作や概念は、以下の公式資料で確認できます。