1. 「端末出力を丸ごとコピペ」という最大の悪手

CIがコケた。ローカルの単体テストが落ちた。あるいは統合テストの実行中に不穏な例外が吐き出された——。

そんな瞬間、あなたはターミナルに流れた出力ログをマウスでドラッグし、数千トークンに及ぶ生ログ(Raw Logs)をそのままAIエージェント(Claude Code、Cursor、Antigravity、Devinなど)のプロンプト欄に放り投げていないだろうか。

「コンテキストウィンドウが100k、200k、果ては1Mトークンに拡大した現代のLLMなら、これくらいのログは全部読んでよしなに根本原因を突き止めてくれるはずだ」

そう信じたくなる気持ちは痛いほどわかる。しかし、断言しよう。「エラーログの全量をAIエージェントに無加工で食わせる行為」は、自律開発ループにおいて最も致命的な迷走を引き起こすアンチパターンである。

人間がエラーログを見るとき、私たちは無意識のうちに高度な「認知的フィルタリング」を行っている。外部ライブラリの内部スタックトレースを読み飛ばし、環境依存のDeprecation Warningを無視し、自分たちが書いたビジネスロジックのファイル名と行番号、そして「直前に何を実行したか」という因果関係だけを脳内で瞬時に切り出している。

しかし、LLMにその「人間の暗黙の視線誘導」は存在しない。与えられた生ログのすべての単語に対し、等価なベクトル空間から自己注意機構(Self-Attention)を計算し始める。そして、ログの中に紛れ込んだ「派手なエラー文字列」や「フレームワークの内部トレース」に注意を完全にハイジャックされ、本来修正すべきバグから180度離れた幻影と戦い始めるのだ。

2. アテンションハイジャック(Attention Hijacking)の3大病理

生ログを直接入力されたLLMが、なぜこれほどまでに的外れな修正を出力してしまうのか。その背景には、トランスフォーマーモデルの構造的特性に起因する3つの病理が存在する。

病理1: ノイズへの過剰適応(Salience Distortion)

ログの中には、開発ツールやランタイム、ライブラリが吐き出す多種多様な警告文が混ざり込んでいる。[CRITICAL]、DEPRECATION WARNING、あるいはフレームワークの非推奨メソッド警告などだ。

これらの警告は、通常アプリケーションの動作には直接影響を与えない無害なノイズであることが多い。しかし、LLMのトークン重要度スコアリングにおいて、CRITICAL や Deprecated といった強い意味論(Semantics)を持つトークンは極めて高いアテンション重みを獲得してしまう。

AIは「真の原因」ではなく、「プロンプトの中で最も大声で叫んでいるノイズ」に吸い寄せられる。これがSalience Distortion(顕著性歪曲)の罠である。

結果としてエージェントは、テストが落ちた真因(例: トランザクション競合によるデータ不整合)を完全に放置し、「package.jsonの非推奨ライブラリをアップデートするPR」を唐突に作り始める。

病理2: 最上段スタックフレームの盲信(Top-Frame Bias)

スタックトレースの最上行(フレーム0)に表示されるのは何だろうか? 大半のケースにおいて、それは言語ランタイムのアサーション例外(AssertionError)や、ORM・HTTPクライアントの内部タイムアウト処理(node_modules/pg/lib/client.js:521 など)である。

AIエージェントに生のスタックトレースを渡すと、エージェントはスタックフレームの「最上段」を最重要の手がかりとして認識する。

このように、「なぜその手前でnullが発生したのか」「なぜクエリがハングしたのか」という上位のドメインロジックやアーキテクチャの因果関係を一切追わず、エラーが発生した末端の現場に絆創膏を貼るだけの対症療法パッチを量産するのだ。

病理3: コンテキストウィンドウの希釈(Lost in the Middle)

スタックトレースやフレームワークのデバッグログは、たった1回のエラーで数百〜数千行(数千〜1万トークン以上)に達する。

これをプロンプトや会話セッションにそのまま注入すると、システムプロンプトやプロジェクト固有のコーディング規約(アーキテクチャ境界、セキュリティルール、設計原則)が、コンテキストの巨大なトークンの海の中に埋没してしまう(Lost in the Middle現象)。

結果として、エージェントは過去の指示やプロジェクト規約を急速に忘れ、型安全性を無視したキャストを乱用したり、無関係な外部パッケージを勝手にインストールし始めたりする。

3. 実例: 泥沼に沈むAIエージェントの迷走劇

具体的な例を見てみよう。Eコマースのチェックアウト処理における「同時実行制御(並行決済の冪等性)」のテストが失敗したシナリオだ。

人間が無邪気に放り込んだ生のエラーログ

ターミナルに出力された以下のログを、開発者がそのままAIに「これ直して」と投げたとする。

(node:91204) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
(Use `node --trace-deprecation ...` to show where the warning was created)
2026-10-03T08:00:12.401Z [WARN] [db-pool] Connection pool nearing saturation: active=9, idle=1, max=10
2026-10-03T08:00:17.405Z [ERROR] Query read timeout (5000ms) exceeded
    at Timeout._onTimeout (/Users/dev/project/node_modules/pg/lib/client.js:521:19)
    at listOnTimeout (node:internal/timers:573:17)
    at process.processTimers (node:internal/timers:514:7)
    at async Timeout.run (/Users/dev/project/node_modules/pg-pool/index.js:412:11)
    at async DatabaseClient.query (/Users/dev/project/src/infra/database.ts:45:12)
    at async OrderRepository.updateBalance (/Users/dev/project/src/infra/order-repository.ts:78:9)
    at async CheckoutService.processOrder (/Users/dev/project/src/domain/checkout-service.ts:112:5)
    at async /Users/dev/project/tests/checkout.test.ts:42:7

FAIL tests/checkout.test.ts > CheckoutService > should prevent double spending on concurrent checkout
AssertionError: expected 'ORDER_SUCCESS' to equal 'CONCURRENT_CONFLICT'
  + expected
  - actual

  - ORDER_SUCCESS
  + CONCURRENT_CONFLICT

アテンションを奪われたAIエージェントの迷走回答

この生ログを受け取ったAIエージェントは、瞬時にアテンションハイジャックを起こし、以下のような頓珍漢な「解決策」を提案し始める。

### エラー原因の分析
1. Node.jsの `punycode` モジュールが非推奨(Deprecated)になっています。
2. データベースのコネクションプールが枯渇し、5000msのクエリタイムアウトが発生しています。

### 修正方針
1. `package.json` に `punycode-esm` を追加し、非推奨警告を解消します。
2. データベース接続のタイムアウトを 5000ms から 30000ms に延長し、コネクションプールの上限(max)を 10 から 50 に引き上げます。

……完全な徒労である。

このテストが落ちた真の原因は、src/domain/checkout-service.ts において、残高更新時に SELECT ... FOR UPDATE(悲観的排他ロック)または楽観的バージョニング(Optimistic Locking)を行っていないため、並行トランザクションがデッドロックを起こしてクエリが詰まり、結果として並行制御の期待値(CONCURRENT_CONFLICT)が返らなかったことにある。

しかしAIは、ログの先頭にあった [DEP0040] と、最上段スタックトレースにあった pg/lib/client.js のタイムアウト例外に視界を奪われ、ビジネスロジックの欠陥には指一本触れないまま、無関係な設定変更とパッケージ追加で茶を濁そうとしたのだ。

4. なぜ「生ログ直食い」はアーキテクチャ的に破綻するのか

自律エージェントの精度を決定づけるのは、プロンプトの工夫以上に「フィードバックループ(Feedback Loop)の入力純度」である。

生ログは、人間がターミナルでスクロールしながら「視覚的に眺める」ために設計された極めて低密度(Low-Signal, High-Noise)なデータ形式だ。ノイズ比率は95%を超え、真のシグナルはわずか数行にすぎない。

LLMは超一流の論理推論エンジンだが、砂の中から砂金を見分ける比重選別機ではない。不純物だらけの原油をそのまま精密ジェットエンジンに流し込めば、エンジンが煤(すす)だらけになって失速するのは自明である。

エージェントに自律的な自己修復やバグ修正を行わせる際、生ログをそのまま読ませるアーキテクチャは根本から破綻している。エージェントに与えるべきは「ログ全量」ではなく、「不純物を完全に除去した蒸留済みエラーコンテキスト」なのだ。

5. 現場の処方箋: エラー境界抽出とサニタイズパイプライン

では、本番運用の現場において、AIエージェントにエラーを正しく理解させるにはどのようなパイプラインを組むべきか? 私たちが実践している3つの防壁設計を紹介する。

施策1: ユーザーランド・スライシング(User-Land Slicing)

スタックトレースから node_modules、vendor/、言語標準ライブラリ(node:internal や runtime/)のフレームを機械的にすべて削ぎ落とす。

エージェントが必要としているのは、「自プロジェクトのリポジトリ管理下にあるファイル(User-Land Code)」の呼び出し階層だけである。

// エージェント入力前のスタックトレース・サニタイザー
export function sanitizeStackTrace(rawTrace: string): string {
  const IGNORE_PATTERNS = [
    /node_modules\//,
    /node:internal\//,
    /\(internal\//,
    /vendor\//,
  ];

  return rawTrace
    .split("\n")
    .filter((line) => !IGNORE_PATTERNS.some((pattern) => pattern.test(line)))
    .filter((line) => !line.trim().startsWith("at async Timeout.run"))
    .slice(0, 5) // ユーザーコードの上位最大5フレームに限定
    .join("\n");
}

施策2: 構造化エラー封筒(Structured Error Envelope)の導入

ターミナルの生文字列をそのままプロンプトに貼り付けることを禁止し、エージェント専用の「構造化封筒(Envelope)」に詰め替えて入力する。

非推奨警告やコネクションプールのWARNは破棄し、「何が」「どう期待され」「実際はどうだったのか」の3点に絞り込む。

# エージェントに渡す構造化エラーコンテキスト
error_envelope:
  test_name: "CheckoutService > should prevent double spending on concurrent checkout"
  failure_type: "AssertionError"
  assertion:
    expected: "CONCURRENT_CONFLICT"
    actual: "ORDER_SUCCESS"
  userland_stack:
    - "src/domain/checkout-service.ts:112:5 (CheckoutService.processOrder)"
    - "src/infra/order-repository.ts:78:9 (OrderRepository.updateBalance)"
  last_query_executed: "UPDATE accounts SET balance = balance - 100 WHERE id = 'acc_123'"
  underlying_exception: "Query read timeout (5000ms)"

この構造化封筒を渡された瞬間、AIエージェントのアテンションは punycode や内部タイマーから解放され、checkout-service.ts の並行処理制御と UPDATE クエリのロック機構という「真の急所」に100%集中するようになる。

施策3: RCA(根本原因分析)プロトコルの強制

エージェントに「すぐに修正コードを書け」と命じてはならない。エラーを受け取ったエージェントには、必ず以下の4段階思考(RCAプロトコル)を踏むことを義務付ける。

以下のRCA手順を厳格に遵守せよ。手順を飛ばしてコードを提示することは禁ずる。

1. 現象(Phenomenon):
   - 期待値と実測値のギャップを1行で定義せよ。
2. 仮説(Hypothesis):
   - なぜこのギャップが生じたのか、アーキテクチャ・競合・状態遷移の観点から2つの仮説を立てよ。
   - ※注意: スタックトレースの最上行で発生した例外は「結果」であり「原因」ではない可能性を前提とせよ。
3. 検証(Verification):
   - どちらの仮説が正しいかをコードのAST・ロジックから証明せよ。
4. 修正(Fix):
   - 症状を隠蔽する try-catch やタイムアウト延長ではなく、根本原因を排除する最小限の差分を提示せよ。

このプロトコルをプロンプトまたはエージェントの思考ループ(Chain-of-Thought)に埋め込むことで、局所的な対症療法パッチを完全に撲滅できる。

まとめ: エージェントの知性を殺すのは「過剰な情報」である

コンテキストウィンドウの巨大化は、現代のAIエンジニアリングに「すべての情報をそのまま放り込めばよい」という危険な怠惰をもたらした。

しかし、現場で自律エージェントを本気で運用しているエンジニアなら誰もが知っている。エージェントの知性を最も残酷に奪うのは、情報の不足ではなく「無秩序な情報の過剰」であることを。

生ログの垂れ流しをやめよ。ノイズを削ぎ落とし、スタックトレースをユーザーコードに限定し、構造化された真実のシグナルだけをエージェントに手渡すのだ。

「AIに何を読ませるか」ではなく、「AIに何を読ませないか」。 その引き算のアーキテクチャを設計できるエンジニアこそが、AI時代において真の開発生産性を手に入れるのである。