AIエージェントに連載記事や大量のタスクを一括生成(Ahead-of-Time / Batch 生成)させようとしたとき、過去に公開したコンテンツや未公開の兄弟タスクとの重複が発生し、後工程で大きな手戻りに直面することがあります。

AIエージェントにおける一括生成とは、複数のタスク仕様やコンテンツを事前にまとめて出力する手法であり、直前の実行結果や台帳の最新状態を動的に反映する直前動的生成(Just-in-Time Generation、以下JIT)と対比されます。「まとめて一度に作った方が効率的である」という直感に従ってパイプラインを組むと、コンテキストの固定化によるネタ被りや仕様の衝突が構造的に発生します。

実際に私たちの開発現場でも、全12話からなる技術連載シリーズのタスクファイルを一括生成しようとした際、オーナーからの「タスクファイルは今作るのと後で作るのとで結果が変わると言えるか?」という問いかけを契機に入力依存関係を調査したところ、12話中11話が過去の連載シリーズと同一技術で重複していると判明し、大事故を未然に回避した経験があります。

本記事では、なぜ一括生成で重複事故が起きるのかというコンテキスト管理の構造的要因を解き明かし、一括生成前に自問すべき「3つの確認規律」と、自シリーズ内・他シリーズ間の衝突を防ぐ「二段構えの既出照合スクリーニング」の実践設計を解説します。

なぜ一括生成で重複が起きるのか——コンテキスト鮮度と入力依存の罠

AIエージェントに複数話の構成やタスク定義を一括生成させる場合、プロンプトに注入されるコンテキスト(過去の公開記事台帳やメモリ)は「生成を開始した時点のスナップショット」に固定されます。

このとき、直前の話でどのような技術や具体例が採用されたのか、あるいは並行して存在する未公開タスクがどのような課題を扱っているのかという最新状態は反映されません。その結果、エージェントは話ごとに独立して最適と思われる定番パターン(リトライ、サーキットブレーカー、レート制限など)を順に選択してしまい、シリーズ全体を見渡すと過去記事や隣接話と全く同じ題材が並ぶ現象が起きます。

LangChainが提唱する「コンテキストエンジニアリング」では、静的なプロンプト設計ではなくランタイムにおいてそのステップに必要な情報だけを動的に注入する設計思想が重視されており、古い情報や過剰な文脈が蓄積することによるコンテキスト劣化(Context Rot)や文脈衝突(Context Clash)を防ぐ重要性が指摘されています1

一括生成と直前動的生成にはそれぞれ明確な向き不向きがあり、固定すべき要素と動的に決めるべき要素で使い分ける必要があります。

比較軸一括生成(Ahead-of-Time)直前動的生成(Just-in-Time)
生成タイミングパイプライン開始時に全件まとめて作成各タスクの実行直前に1件ずつ作成
コンテキスト鮮度開始時点のスナップショットに固定直前タスクの実行結果や最新台帳を反映
得意な対象連番、slug、共通設定などの共有名前空間個別仕様、詳細構成、直前依存の題材選定
主なリスク既出重複、コンテキスト衝突(Context Clash)全体の整合性維持や名前空間衝突の見落とし

したがって、すべてを一括で生成するのではなく、共有名前空間のみを一括固定し、個別仕様は直前動的生成に委ねる境界設計が求められます。

一括生成前に自問すべき「3つの確認規律」

バッチ処理や雛形展開によってまとめて生成を行う前に、人間およびエージェント自身が確認すべき3つの規律があります。

1. この成果物は何を入力にしているか

生成対象の成果物が、どの定義ファイル、どの台帳、どのプロンプトを入力に依存しているかを正確に洗い出します。

入力に「過去の公開済み一覧」や「他話の進捗状況」が含まれている場合、その入力が静的な固定値なのか、それとも時間の経過や他タスクの完了によって変化する動的なデータなのかを明確にします。

2. いつ作ると何が変わるか

「今作る」のと「後で作る」のとで、入力コンテキストにどのような差が生じるかを検証します。

例えば、1話目を執筆した後に得られる読者の反応や詳細なコード検証結果は、4話目以降の概要仕様に影響を与えます。後から作った方が解像度が高くなる部分があるならば、その部分を今無理に固定化してはいけません。

3. 変わると困る部分と変わってよい部分を切り分ける

成果物に含まれる要素を「変わると困る部分(事前固定すべきもの)」と「変わってよい部分(動的生成すべきもの)」に明確に分離します。

  • 変わると困る部分(今作るべきもの): 識別子(slug)、公開日時の連番、全体の章立て構成、共有リソースの名前空間。これらは後からバラバラに決めると衝突や順序の狂いが生じるため、最初に一括で整合性を取って固定します。
  • 変わってよい部分(後で作るべきもの): 個別タスクの詳細仕様、本文の下書き、選択する要素技術の割り当て。これらは直前の実行結果や台帳の最新状態を見てから、直前動的生成(JIT)で決定します。

現場の実装——既出照合スクリーニングの「二段構え」設計

3つの確認規律のうち、特に②「いつ作ると何が変わるか」と③「変わると困る部分の切り分け」を機械的に支援する仕組みとして、私たちが実際に開発パイプラインに導入した再発防止策が、機械的な照合と人間・エージェントによる文脈読解を分離した「二段構えの既出照合スクリーニング」です。Anthropicが提唱するエージェントオーケストレーションのベストプラクティスにおいても、単一プロンプトでの判断に頼るのではなく、段階的な検証ステップをワークフローとして明示することが推奨されています2

司令塔による機械照合(台帳再生成+未公開兄弟タスク直読)

第1段階は、個別タスクの企画に入る前の軽量な機械スクリーニングです。

  1. 公開済み記事から自動再生成される「消費済み技術台帳」を読み込みます。直前の記事公開によって台帳が更新されている可能性があるため、公開フェーズと読み込みフェーズの両方で再生成を実行します。
  2. まだ台帳に掲載されていない未公開の「兄弟タスクファイル」を直接読み込み、同一シリーズ内でのネタ被りを検知します。
  3. 題材やキーワードが近接する候補を最大3件機械的に抽出します。この段階では機械的な抽出にとどめ、可否の判定は行いません。

以下は、公開済み台帳と兄弟タスクの定義から隣接候補を抽出するスクリーニングスクリプトの最小実装例です。

# ファイル: scripts/screening_duplicates.py
import os
import re
import sys
from pathlib import Path

def extract_topics(file_path: Path) -> dict:
    content = file_path.read_text(encoding="utf-8")
    # フロントマターや見出しからトピックと課題・解決策のペアを抽出
    match = re.search(r"problem:\s*(.+)\n\s*solution:\s*(.+)", content)
    if match:
        return {"problem": match.group(1).strip(), "solution": match.group(2).strip()}
    return {"problem": "", "solution": ""}

def check_adjacent_candidates(current_task: Path, sibling_dir: Path) -> list:
    target = extract_topics(current_task)
    candidates = []
    
    for sibling in sibling_dir.glob("*.md"):
        if sibling == current_task:
            continue
        sibling_topic = extract_topics(sibling)
        # problem文字列同士の部分一致による簡易判定(最大3件抽出)
        if sibling_topic["problem"] and sibling_topic["problem"] in target["problem"]:
            candidates.append((sibling.name, sibling_topic))
            if len(candidates) >= 3:
                break
    return candidates

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Usage: python screening_duplicates.py <current_task.md> <tasks_dir>")
        sys.exit(1)
    
    results = check_adjacent_candidates(Path(sys.argv[1]), Path(sys.argv[2]))
    print(f"隣接候補: {len(results)} 件検出")
    for name, data in results:
        print(f"- {name}: 問題={data['problem']} / 解決={data['solution']}")

このスクリプトを実行すると、以下のように未公開タスク間で類似した課題を扱っている候補が瞬時にリストアップされます。

$ python scripts/screening_duplicates.py tasks/task-04.md tasks/
隣接候補: 2 件検出
- task-01.md: 問題=外部APIの遅延によるタイムアウト / 解決=指数バックオフ
- task-02.md: 問題=接続先の障害伝播 / 解決=サーキットブレーカー

この実装は problem 文字列同士の部分一致という最小限の判定にとどめている点が重要です。表記揺れのある問題文まで拾いたい場合は単語単位のキーワード抽出や埋め込みベースの類似度判定に差し替えられますが、判定アルゴリズムの精緻さそのものは本質ではありません。この段階の役割は候補を機械的に絞り込むことだけであり、「本当に重複か」の可否判定は次のプランニング段階に委ねる設計だからです。

プランニングでの本判定と (問題, 解決) ペアの差別化

第2段階は、抽出された候補の本文や詳細仕様を読み込んで行う本判定です。

ここで重要な編集理念は、 「技術名の一致=重複」と短絡させないこと です。同一の技術名であっても、対象とする問題やコンテキストが異なっていれば、コンテンツとしての価値は十分に成立します。

  • 例: 「多段モデル(安いモデルで前捌きをして上位モデルへ回す)」と「ルーター(役割に応じたエージェントへの振り分け)」は、どちらもモデルの呼び分けを扱いますが、解決する問題(コストとレイテンシの削減 vs 責務の混線防止)が明確に異なります。

本判定では、完全重複(同じ問題を同じ解決策で扱っている)であれば企画を中断し、隣接(技術は共通だが問題が異なる)であればその差別化理由を1〜3行記述して進行します。

中断規定とセッション分離

本判定で完全重複が判明した場合、その場ですぐにタスク仕様を書き換えて無理に進めるのではなく、明確な「中断規定」を適用します。

シリーズ全体のロードマップやマニフェストといった共有定義に手を加える必要がある場合は、執筆セッションを即座に中断し、課題をIssueとして切り出して設計セッションへと差し戻します。設計判断と執筆作業を同一のコンテキストウィンドウ内で混在させないことで、コンテキストの汚染と手戻りの拡大を防ぐことができます。

メリットの裏にあるトレードオフと適用限界

二段構えのスクリーニング設計は高い重複防止効果を持ちますが、運用上のトレードオフや適用限界も存在します。

適用限界とオーバーヘッド

各話の実行前に台帳の再生成や兄弟タスクの読み込みを挟むため、わずかな実行時間とトークン消費のオーバーヘッドが発生します。

そのため、各タスクが完全に独立しており相互の文脈共有が不要なバッチ処理や、全タスクが同一スキーマに従う機械的データ変換などでは過剰設計となります。連載企画や複雑な業務フローのように、「一貫した世界観や技術の積み上げが必要なドメイン」において最大の効果を発揮します。

よくある質問(FAQ)

なぜ最初からLLMに過去記事の全ログを渡して重複判定させないのですか?

コンテキストウィンドウの消費が激しくなるだけでなく、過剰な情報によって重要な重複を見落とす「Lost in the Middle」現象やコンテキスト劣化(Context Rot)が発生するためです。軽量な機械スクリーニングで隣接候補を最大3件程度に絞り込み、絞り込んだ候補に対してのみ詳細な文脈読解を行う方が、精度とコストのバランスに優れます。

重複判定を完全に自動化して人間の確認をなくすことはできますか?

技術名や単語の完全一致レベルであれば自動判定が可能ですが、「同じ技術を使って別の課題を解決しているか」というコンテキストの差異を判定するには文脈の深い読解が必要です。機械にスクリーニング(候補抽出)を任せ、プランニングフェーズでエージェントまたは人間が文脈を評価して判断するという役割分担が最も現実的で安全です。

前提が変われば結論はどう変わるか

LLMのコンテキストウィンドウ拡張やプロンプトキャッシング機能の進化は、この設計にどのような影響を与えるでしょうか。

AnthropicやOpenAIが提供するプロンプトキャッシングでは、プロンプトの先頭部分(静的プレフィックス)と動的に変化する末尾部分(サフィックス)を分離してキャッシュし、コスト削減だけでなくコンテキストの一貫性担保にも寄与します34。これは本記事で述べた「変わると困る部分(共有名前空間や固定ルール)を事前固定し、動的に変化するサフィックスと分離する」という設計原則と同じ発想が別のレイヤーでも現れている例だと言えます。

キャッシュ技術やモデル性能がどれほど向上したとしても、「何を入力にし、いつ作ると結果が変わるか」を把握する設計責任は人間に残ります。AIエージェントに自律的な作業を委譲するからこそ、人間側が入力依存関係と生成タイミングの規律を堅牢に保つ必要があります。

まとめ

AIエージェントによる一括生成の事故を防ぎ、高品質なコンテンツ・タスクパイプラインを運用するための判断基準は以下の通りです。

  1. 一括生成の前に依存関係を問う: まとめて生成する前に、「この成果物は何を入力にしているか」「いつ作ると何が変わるか」を必ず自問する。
  2. 共有名前空間は一括固定、個別仕様は直前動的生成: slugや連番など変わると困る要素は最初に一括固定し、直前の文脈に依存する個別仕様はJITで動的に生成する。
  3. 既出照合は二段構えで設計する: 機械照合(台帳再生成+兄弟タスク直読)で候補を絞り込み、プランニングフェーズで (問題, 解決) のペアに基づく本判定を行う。
  4. AIのせいにせず人間の設計規律で防ぐ: 重複や手戻りはAIの精度の問題ではなく、入力と生成タイミングの設計を見落とした人間の運用の問題として向き合う。

AIエージェントを活用したコンテンツ制作パイプラインの品質管理、AI Operations のプロセス設計、開発ワークフローの標準化についてのご相談は、ぜひ コンタクトフォーム よりお問い合わせください。

Footnotes

  1. Context Engineering LangChain, 2024年10月。ランタイムにおける動的コンテキスト管理、Context Rot(劣化)および Context Clash(文脈衝突)のメカニズム。

  2. Building Effective AI Agents Anthropic, 2024年12月。ワークフローと自律エージェントの分類、段階的なオーケストレーションとゲート設計の有効性。

  3. Prompt Caching Anthropic, 2024年8月。静的プレフィックスと動的サフィックスの分離設計、キャッシュブレークポイントの活用。

  4. Prompt Caching OpenAI, 2024年10月。1,024トークン以上の入力における自動プレフィックスキャッシング仕様。