結論
AIエージェントの暴走や無限ループは、モデルの性能だけで防ぐものではありません。
実装側で「何回まで動いてよいか」「どのツールを使ってよいか」「何をもって完了とするか」「危険な操作を誰が承認するか」を決めておく必要があります。
最初に入れるべき対策は次の5つです。
| 対策 | 目的 |
|---|---|
| 最大ステップ数 | 終了できないループを強制的に止める |
| 同一操作の検知 | 同じツール呼び出しの繰り返しを止める |
| ツール権限の分離 | 読み取り、作成、削除などの危険度を分ける |
| 人間の承認 | 破壊的操作や外部送信を自動実行させない |
| 実行ログ | 何が起きたかを後から調査できるようにする |
![]()
この記事の対象読者
この記事は、次のような人向けです。
- AIエージェントにツール実行をさせたい開発者
- 自動実行の安全設計に不安がある人
- 同じ処理を繰り返すエージェントの止め方を知りたい人
- 社内ツール、GitHub操作、ファイル操作などをエージェントに任せる前に設計を整理したい人
なぜAIエージェントは暴走しやすいのか
AIエージェントは、目的を受け取り、必要なツールを選び、実行結果を見て次の行動を決めます。
この流れ自体は便利ですが、終了条件が曖昧だと、モデルは「まだ何かできる」と判断して作業を続けることがあります。
よくある原因は次の通りです。
| 原因 | 起きること |
|---|---|
| 終了条件が曖昧 | いつ完了してよいか判断できない |
| ツール結果が曖昧 | 成功したのか失敗したのか分からず再実行する |
| ステップ制限がない | 失敗しても延々と別案を試す |
| 同一操作の検知がない | 同じ検索、同じAPI呼び出しを繰り返す |
| 権限が広すぎる | 不要な書き込みや削除まで実行できてしまう |
重要なのは、AIエージェントに「賢く止まってほしい」と期待するだけでは不十分だという点です。
止まる条件は、アプリ側で設計します。
基本構成
AIエージェントの実行ループは、最低限次のように分けて考えます。
| 要素 | 役割 |
|---|---|
| 目的 | エージェントが達成すべきゴール |
| 状態 | これまでの入力、ツール実行、結果 |
| ツール | 検索、DB参照、ファイル操作、外部APIなど |
| 判断 | 次に何をするか、終了するか |
| 制限 | 最大回数、権限、承認条件 |
| ログ | 実行履歴、失敗理由、停止理由 |
このうち、暴走対策で特に重要なのは「制限」と「ログ」です。
最大ステップ数を決める
最初に入れるべき対策は、最大ステップ数です。
たとえば、1回の依頼でツール実行は最大10回まで、外部API呼び出しは最大3回まで、というように上限を決めます。
const maxSteps = 10;
for (let step = 0; step < maxSteps; step += 1) {
const action = await decideNextAction(state);
if (action.type === "finish") {
return action.result;
}
const result = await runTool(action);
state.history.push({ action, result });
}
return {
status: "stopped",
reason: "max_steps_reached",
};
最大ステップ数に到達したら、無理に続けず、途中結果と停止理由を人間に返します。
同じ操作の繰り返しを検知する
最大ステップ数だけでは、無駄な操作を減らせません。
たとえば、同じキーワードで検索を繰り返す、同じAPIを同じ引数で呼ぶ、といった動きは早めに止めるべきです。
| 検知するもの | 例 |
|---|---|
| 同じツール名 | searchDocs を何度も呼ぶ |
| 同じ引数 | 同じクエリで検索する |
| 同じ失敗 | 404や権限エラーを繰り返す |
| 変化のない結果 | 前回と同じ結果しか返っていない |
実装では、ツール名と引数をキーにして履歴を持つと扱いやすくなります。
function actionKey(action: ToolAction) {
return `${action.tool}:${JSON.stringify(action.args)}`;
}
const repeatedCount = history.filter(
(item) => actionKey(item.action) === actionKey(nextAction),
).length;
if (repeatedCount >= 2) {
return {
status: "stopped",
reason: "repeated_action_detected",
};
}
ツール権限を分ける
すべてのツールを同じ扱いにすると危険です。
読み取り専用ツールと、書き込み・削除・外部送信を行うツールは分けて設計します。
| ツール種別 | 例 | 推奨制御 |
|---|---|---|
| 読み取り | 検索、ファイル参照、DB参照 | 自動実行可。ただし回数制限を入れる |
| 作成 | Issue作成、下書き作成 | 内容をログに残す。必要に応じて承認 |
| 更新 | ファイル編集、DB更新 | 原則、人間の承認を挟む |
| 削除 | ファイル削除、データ削除 | 自動実行させない |
| 外部送信 | メール、Webhook、SNS投稿 | 宛先と内容を人間が確認する |
最初は読み取り中心にし、書き込み系は承認制にするのが安全です。
人間の承認を挟むポイント
AIエージェントにすべてを自動実行させる必要はありません。
特に次の操作は、承認を挟む設計にします。
- ファイルを作成・編集・削除する
- GitHub IssueやPRを作成する
- 外部APIへ大量にリクエストを送る
- ユーザーに通知を送る
- 本番環境の設定を変更する
- 課金や公開に関係する操作をする
承認画面やログには、少なくとも次の情報を出します。
| 表示する情報 | 理由 |
|---|---|
| 実行したい操作 | 人間が判断しやすくする |
| 対象ファイルや対象URL | 影響範囲を明確にする |
| 変更前後の要約 | 実行結果を予測しやすくする |
| 失敗時の戻し方 | 安全に試せるようにする |
ログに残す項目
暴走対策では、ログがないと原因を調べられません。
最低限、次の項目を残します。
| ログ項目 | 目的 |
|---|---|
| requestId | 1回の依頼を追跡する |
| step | 何回目の判断かを見る |
| selectedTool | どのツールを選んだかを見る |
| toolArgsSummary | 引数の概要を確認する |
| resultStatus | 成功、失敗、空結果を分ける |
| stopReason | なぜ止まったかを見る |
| durationMs | 遅い処理を見つける |
APIキー、個人情報、長い入力全文をそのままログに出してはいけません。
実装前チェックリスト
AIエージェントを動かす前に、次を確認します。
- 最大ステップ数を決めている
- ツールごとの実行回数制限を決めている
- 同一操作の繰り返しを検知できる
- 書き込み、削除、外部送信には承認を挟む
- 停止理由をユーザーに返せる
- 実行ログに秘密情報を出していない
- 失敗時に途中結果を確認できる
関連記事
- Next.jsでAIアプリを作る基本構成:画面・API・AI API・ログの役割
- Next.jsでAIアプリを作る基本構成:画面・API・AI API・ログの役割
- AI APIの料金を見積もる方法:トークン・実行回数・月間コストの考え方
まとめ
AIエージェントの暴走対策は、モデルの出力を信じることではなく、実行環境を制御することです。
最大ステップ数、同一操作検知、権限分離、承認フロー、ログを最初から設計に入れることで、自動化の便利さを保ちながら危険な動きを抑えられます。