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 を手作業で書き換える手口に対しても防壁を敷く。
- パーミッションの制限: エージェントが読み書き可能なファイルパスのホワイトリストから、
package.json、*-lock.*、*.lock、go.sum等の依存定義ファイルを完全に除外する。 - Pre-commit フックによる監視: エージェントが作成したコミットや差分にマニフェストファイルの変更が含まれていた場合、CIやGitフックで自動的にリジェクトする。
3. プロンプトでの「Zero Dependency 原則」の注入
システムプロンプトやコーディング規約ルールファイル(CLAUDE.md や .cursorrules)には、以下の文言を例外なき鉄則として刻み込む。
## 依存関係管理ルール(Zero Dependency Protocol)
1. 既存のプロジェクトに含まれていない外部パッケージの新規追加は**完全禁止**とする。
2. ユーティリティ機能は、言語の標準ライブラリ(Standard Library)を最大限活用し、
必要な関数はすべてプロジェクト内のユーティリティモジュールへ自前実装(クリーンルーム設計)せよ。
3. 代替不可能な外部ライブラリがどうしても必要な場合、コードを修正せず、
「提案理由・ライセンス・バンドルサイズ・選定根拠」を人間に提示して停止せよ。
5. 結論:AIエージェントの真価は「ゼロからの自前実装」にある
我々エンジニアは、数十年にわたって「車輪を再発明するな」「ライブラリを賢く使え」と教え込まれてきた。それは人間の脳のメモリが限られており、コードを書く速度が遅く、自前実装がバグの温床になりやすかった時代の合理的な知恵だ。
しかし、AI駆動開発という新たなパラダイムにおいて、その常識は逆転する。
AIエージェントは、適切なプロンプトと型制約、テストケースさえ与えられれば、任意のアルゴリズムやデータパーサーを数秒で、しかも外部依存ゼロの綺麗なクリーンルーム実装として書き上げることができる。
怪しげな外部パッケージを呼び込んでサプライチェーンリスクに怯え、ブラックボックスな依存関係に縛られる必要はどこにもない。「依存を増やすな、お前の頭で書け」とAIに命じることこそが、最も安全で、最もフットワークの軽いプロダクトを生み出すための最強のプラクティスなのだ。