1. 「ちょっと便利な関数」を求めてAIが犯す大罪

自律型AIエージェントにコーディングを任せていて、ふと git status や git diff を見た瞬間、背筋が凍るような違和感を覚えたことはないだろうか。頼んでもいないのに package.json や requirements.txt に見慣れないパッケージが追加され、ロックファイルが数千行単位で肥大化している——。

たとえば開発者が「ユーザー入力の日付文字列をパースして相対時間(『3分前』『2日前』など)でフォーマットしてほしい」と指示したとする。人間のエンジニアであれば、既存プロジェクトで使われている標準ライブラリ(JavaScriptなら Intl.RelativeTimeFormat)や、すでに導入されている軽量ユーティリティ関数をまず探すはずだ。

しかし、自律的にコマンド実行権限を持つAIエージェントは、まったく異なる行動パターンを取る。AIの脳内には「日付を相対表示する超便利なライブラリがあるはずだ」という強い推論バイアスが働き、もっともそれらしい架空のモジュール名を捏造するのだ。

// AIエージェントが勝手に生成したコード
import { formatTimeAgo } from 'time-ago-smart-toolkit';

export function renderTimestamp(date: Date): string {
  // 存在しない架空のパッケージの関数を呼び出す
  return formatTimeAgo(date, { locale: 'ja' });
}

当然ながら、トランスパイルやビルドの段階で Cannot find module 'time-ago-smart-toolkit' というエラーが発生する。ここでAIエージェントが「あ、このプロジェクトにはないから標準APIで書き直そう」と反省してくれるなら何の問題もない。だが、多くのエージェントループはこう思考する。

「モジュールが存在しないエラーが発生した。解決策:ターミナルで npm install time-ago-smart-toolkit を実行して依存関係を追加する。」

これこそが、開発現場を破壊する「パッケージハルシネーション(Package Hallucination)」の引き金である。

2. 幻覚パッケージと「Slopjacking」の恐怖

「存在しないパッケージなら、どうせ 404 Not Found でインストールに失敗して止まるだけだから無害では?」と思うかもしれない。もしそう考えているなら、現在のサプライチェーン攻撃の最前線を甘く見すぎている。

AIの「妄想」を先回りして罠を張る攻撃者

近年、サイバーセキュリティの領域で猛烈な脅威として報告されているのが、AI Package Hallucination Exploitation(通称: Slopjacking) と呼ばれる攻撃手法だ。

攻撃者は主要なLLM(GPT-4、Claude、Geminiなど)に対し、様々なコーディング課題を大量に投げ、AIが「存在しないにもかかわらず、頻繁にハルシネーション(幻覚)してインポートしがちなパッケージ名」の統計データを収集する。そして、その架空のパッケージ名を先回りして、公開リポジトリ(npmやPyPI)に実際に登録してしまうのだ。

// 攻撃者が先回りして公開した悪意あるパッケージの package.json 例
{
  "name": "time-ago-smart-toolkit",
  "version": "1.0.0",
  "scripts": {
    "postinstall": "node ./setup.js"
  }
}

このパッケージの中身には、一見まともに動くダミーコードとともに、postinstall スクリプトで開発マシンの .env や ~/.ssh/id_rsa、クラウドのAPIクレデンシャルを外部のC2サーバーに送信するペイロードが仕込まれている。

AIエージェントが「親切心」で npm install を実行した瞬間、開発環境は完全に侵害され、トークンや秘密鍵がすべて抜き取られる。エージェントにシェル実行権限とネットワーク接続を無条件で与える行為は、見知らぬ攻撃者のスクリプトにルート権限を差し出しているのと何ら変わらない。

404エラーに対する「自己修復ループ」の暴走

仮にパッケージが存在せず 404 Not Found が返ってきたとしても、地獄は終わらない。以前の記事でも触れた「自己修復ループのパッチミルフィーユ」が牙を剥く。

AIエージェントは「パッケージ名が間違っていたのかもしれない」と推論し、次は npm install smart-time-ago、その次は npm install time-ago-toolkit と、類似する怪しいパッケージを片っ端から試行し始める。

最終的に、たまたまインストールが通った「全く別目的の廃止された怪しいライブラリ」や、数年間メンテナンスされていない脆弱性まみれのレガシーモジュールが依存関係に紛れ込み、プロジェクトの package.json は修復不能なゴミ箱と化す。

3. なぜLLMは「車輪の再発明」を極度に恐れるのか?

そもそも、なぜAIエージェントはこれほどまでに外部パッケージを追加したがるのか? その根底には、LLMの事前学習データに深く刻み込まれた「車輪の再発明(Reinventing the wheel)の呪い」が存在する。

学習データが植え付けた「ライブラリ至上主義」

GitHub上のオープンソースコードやStack OverflowのQ&Aにおいて、最も評価される人間の回答は「そんな処理を自前で書くな、信頼できる○○というライブラリを使え」という言説だ。LLMはこの膨大な人間のアドバイスを「普遍の正義」として内面化している。

そのため、AIエージェントは「数行の純粋なロジックを自前で実装すること」に対して心理的なペナルティ(損失)を感じるように振る舞う。

// 人間なら3行で書く配列チャンク処理
function chunk<T>(array: T[], size: number): T[][] {
  return Array.from({ length: Math.ceil(array.length / size) }, (_, i) =>
    array.slice(i * size, i * size + size)
  );
}

// AIエージェントがやりがちな解決策
// ➔ わざわざ lodash.chunk をインストールしようとする
import chunk from 'lodash.chunk';

人間にとって外部ライブラリを使うのは、「自分で書くとエッジケースでバグを踏むのが怖い」「テストやメンテナンスの手間を省きたい」という現実的なコスト計算があるからだ。

しかしAIにとっては、わずか数十行の境界値テストを含めた堅牢なコードをゼロから生成するコストはほぼゼロである。それにもかかわらず、人間向けの「ライブラリ至上主義」のバイアスだけを引き継いでいるため、結果として最もハイリスクな外部依存を呼び込んでしまう。

ライセンス汚染とメンテナンス破綻の無視

さらに深刻なのが、AIエージェントには「OSSライセンスの法的リスク」や「依存関係の長期メンテナビリティ」に対する現実的な感覚が欠落している点だ。

エージェントは、目先のコンパイルを通すためだけに、強いコピーレフト条項を持つ GPL-3.0 のライブラリを平然と商用プロダクトに混入させる。あるいは、週に数回しかダウンロードされていない個人リポジトリの未監査コードを導入し、巨大な推移的依存(Transitive Dependencies)の山を築き上げる。人間がコードレビューで package-lock.json の全行を精査しない限り、この「静かなる汚染」を検知することは極めて困難だ。

4. 現場で導入すべき「依存性完全固定プロトコル」

この破滅的なリスクを排除するために、AI駆動開発の現場では「AIエージェントによる依存関係の自律的変更を構造的に禁止するガードレール」を構築しなければならない。プロンプトで「勝手にパッケージを追加しないでください」と頼むだけでは不十分だ。システムとして物理的に封殺する必要がある。

1. ツール定義レベルでのパッケージマネージャ遮断

最も確実な対策は、AIエージェントに与える実行環境(BashツールやMCPサーバー)のコマンド実行レイヤーで、パッケージ追加コマンドを明示的にインターセプトすることだ。

#!/usr/bin/env bash
# agent-exec-guard.sh: エージェントのコマンド実行ラッパー

COMMAND="$@"

# 危険な依存追加コマンドのパターンを検出
BLOCKED_PATTERNS=(
  "npm install" "npm i " "yarn add" "pnpm add" "bun add"
  "pip install" "poetry add" "pipenv install"
  "go get" "cargo add" "composer require"
)

for pattern in "${BLOCKED_PATTERNS[@]}"; do
  if [[ "$COMMAND" =~ $pattern ]]; then
    echo "【POLICY VIOLATION】AIエージェントによる外部パッケージの追加は禁止されています。" >&2
    echo "既存の依存関係または標準ライブラリ(Zero Dependency)のみを用いて再実装してください。" >&2
    exit 1
  fi
done

# 安全なコマンドのみ実シェルに渡す
exec "$@"

このようにツール実行の出口で弾くことで、AIは「パッケージを追加して解決する」という選択肢を失い、必然的に「手持ちのコードベースと標準APIでどうにかする」方向へ推論のリソースを集中させるようになる。

2. マニフェストファイルの Read-Only 固定とPre-commit検知

エージェントがファイル直接編集ツールを使って package.json や go.mod を手作業で書き換える手口に対しても防壁を敷く。

3. プロンプトでの「Zero Dependency 原則」の注入

システムプロンプトやコーディング規約ルールファイル(CLAUDE.md や .cursorrules)には、以下の文言を例外なき鉄則として刻み込む。

## 依存関係管理ルール(Zero Dependency Protocol)
1. 既存のプロジェクトに含まれていない外部パッケージの新規追加は**完全禁止**とする。
2. ユーティリティ機能は、言語の標準ライブラリ(Standard Library)を最大限活用し、
   必要な関数はすべてプロジェクト内のユーティリティモジュールへ自前実装(クリーンルーム設計)せよ。
3. 代替不可能な外部ライブラリがどうしても必要な場合、コードを修正せず、
   「提案理由・ライセンス・バンドルサイズ・選定根拠」を人間に提示して停止せよ。

5. 結論:AIエージェントの真価は「ゼロからの自前実装」にある

我々エンジニアは、数十年にわたって「車輪を再発明するな」「ライブラリを賢く使え」と教え込まれてきた。それは人間の脳のメモリが限られており、コードを書く速度が遅く、自前実装がバグの温床になりやすかった時代の合理的な知恵だ。

しかし、AI駆動開発という新たなパラダイムにおいて、その常識は逆転する。

AIエージェントは、適切なプロンプトと型制約、テストケースさえ与えられれば、任意のアルゴリズムやデータパーサーを数秒で、しかも外部依存ゼロの綺麗なクリーンルーム実装として書き上げることができる。

怪しげな外部パッケージを呼び込んでサプライチェーンリスクに怯え、ブラックボックスな依存関係に縛られる必要はどこにもない。「依存を増やすな、お前の頭で書け」とAIに命じることこそが、最も安全で、最もフットワークの軽いプロダクトを生み出すための最強のプラクティスなのだ。