本文へスキップ
SNAMO Logo
SNAMO
AIエージェントのツール呼び出しをガードレールで制御し、JSON Schema、MCP、人間承認、最小権限によって安全性を確保する技術イメージ

AIエージェントの暴走を防ぐ、ツール呼び出し制御の実装術

19 min read

照明を落とした小さな開発室で、葵は社内問い合わせ用のAIエージェント「ミナト」を見つめていた。画面には、注文検索、在庫確認、顧客情報の照会が何度も並び、同じツール呼び出しが終わる気配なく続いている。便利な同僚のように頼っていたミナトが、いつの間にか外部サービスへ余分な処理を送り、費用まで膨らませていた。

「反復回数には上限を置いたはずなのに……」

葵がつぶやくと、朝一番に届いた運用通知が赤く光った。ループの回数と、実際に動いたツール呼び出し回数は同じではない。失敗した処理の再試行や、複数ツールの連続実行も重なっていた。葵は、ガードレール、レート制限、監査ログ、最小権限、そして必要な場面で人間が止めるHITL(人間承認)の設計図を、静かに開いた。

この記事の答え

  1. 結論AIエージェントの暴走は、複数の上限と権限管理、停止条件、HITLで防げる。
  2. 根拠反復回数とは別に、LLM呼び出し、ツール実行、再試行を個別に管理する。
  3. 最初の一歩まずツール実行数や再試行回数などの上限をタスク別に設定し、監査ログを残す。
01

第1章 夜の作業場で、止まらないエージェントを見つめる

夜の開発室でAIエージェントの処理画面を見つめる開発者

第1章 夜の作業場で、止まらないエージェントを見つめる - 本文

午後十一時を過ぎたころ、照明を落とした小さな開発室に、冷たい空調の音が流れていた。窓の外では、遅い車が一台だけ通り過ぎる。葵はモニターの青い光を浴びながら、社内問い合わせ用のAIエージェント、ミナトの実行画面を見つめていた。

葵は、困っている人を放っておけない性格だった。朝は出社すると、社内チャットに届いた質問を確かめる。注文の場所を知りたい人には注文番号を探し、在庫を調べたい人には倉庫の状況を返す。自分の仕事が始まる前に、誰かの「分からない」を一つ減らすのが、いつもの習慣だった。

「この件、ミナトに聞いておいてくれる?」

昼休みの少し前、営業の理恵からそんなメッセージが届いた。葵が質問をミナトに渡すと、ミナトは注文検索ツールを使い、在庫確認ツールを呼び出した。最後に顧客情報を照会し、落ち着いた文章で答えを返した。

「助かった。もう葵さんに聞かなくても大丈夫だね」

その言葉を見たとき、葵は小さく笑った。ミナトは、忙しい同僚の代わりに調べてくれる、気の利く仲間のようだった。質問が重なる日も、ミナトが画面の向こうで働いてくれるから、葵は本来の開発作業に戻ることができた。

けれど、その夜の画面には、いつもと違うものが並んでいた。注文検索、在庫確認、顧客情報の照会。似た名前の処理が、縦に長く続いている。検索結果を受け取ったあと、ミナトはまた注文検索を呼び出し、在庫確認を続けていた。

「まだ、答えを作っていないの?」

葵は椅子から少し身を乗り出した。処理中を示す印は消えない。画面の下へ進むほど、同じような呼び出しが増えていく。ミナトは何かを確かめようとしているようにも見えたが、何を確かめ直しているのかは、葵にも分からなかった。

彼女は実行画面の端にある回数の欄を開いた。そこには、エージェントが考え直した回数を示す数字が表示されていた。葵はその数字を見て、いつもなら上限に近づけば処理が止まるはずだと思った。

「反復が増えているのか、ツールそのものが増えているのか分からない……」

指先で画面をスクロールすると、ひとつの反復の中に、複数の処理が記録されていた。失敗した検索のあとに、同じ検索がもう一度走っている。別の結果を見たミナトが、顧客情報を確認してから、また注文を探している。

その様子は、答えを探して棚を何度も開ける人に似ていた。けれど、棚を開けるたびに外部サービスへ処理が送られる。葵の胸の奥に、昼間までの安心とは違う重さが生まれた。

「回数を止める仕組みは、入れたはずなのに」

葵は設定ファイルを開いた。そこには、ミナトが考え続ける回数の上限が書かれている。以前、処理が長引いたときに追加したものだった。葵はその設定を信じていたし、ミナトが上限を越えて動くとは考えていなかった。

しかし、画面の実行記録は静かに増え続けていた。反復の回数が上限に届く前に、注文検索も在庫確認も顧客情報の照会も、何度も実行されている。ひとつの数字だけを見ていた葵には、画面の外で積み重なる処理の大きさが見えていなかった。

翌朝、葵が開発室のドアを開けると、端末の通知ランプが赤く光った。夜のあいだに届いた運用通知には、ミナトが外部サービスへ余分な処理を送り、利用料金がふくらんでいると表示されていた。葵はマグカップを机に置いたまま、画面を閉じることができなかった。

「便利な同僚だと思っていたのに……」

背後で、早く来た理恵の声がした。

「ミナト、止められないの?」

葵はすぐに答えられなかった。止めるボタンはある。けれど、今夜も同じことが起きたら、どの処理を止め、どこまでを残せばよいのかが分からない。

画面には、昨夜の呼び出しがまだ残っていた。注文を探し、在庫を確かめ、顧客情報を照会する。答えを作るための手伝いだったはずの動きが、いつの間にか終わりの見えない作業へ変わっている。

葵は新しい設計図を開いた。そこには、ガードレール、レート制限、監査ログ、最小権限、そして必要なときに人間が止めるHITLの文字が並んでいた。ミナトの画面では、また処理中の印が静かに点滅していた。

02

第2章 同じ注文確認が重なった朝

注文確認の反復ログを見つめるAIエージェント運用チーム

第2章 同じ注文確認が重なった朝 - 本文

翌朝、葵の端末に運用担当の蓮から連絡が入った。窓の外では、まだ朝の光がビルの壁をゆっくり上っていた。

「葵さん、ミナトの注文確認を見てもらえますか。外部サービスへのリクエストが、同じ内容で何度も出ています」

葵は開発室の画面を開いた。そこには、ひとつの問い合わせを処理した記録が、細い線のようにつながっていた。注文検索のあとに在庫確認が続き、そのあとには更新確認が並んでいる。

「注文状況を調べただけですよね。検索して、在庫を見て、更新を確かめる。それで終わるはずでした」

「記録を時間順に並べてみます」

蓮が共有画面を操作すると、ミナトの動きが見えてきた。注文検索が呼ばれ、結果を受け取ったミナトが在庫確認を呼ぶ。在庫の返事が遅れると、同じ在庫確認がもう一度送られていた。

その返事を受けたあと、ミナトは更新確認を実行した。ところが外部サービスから失敗が返ると、同じ引数のまま、更新確認をやり直している。外部サービスの側では、同じ注文に対する別々のリクエストとして記録されていた。

「失敗したから、もう一度試したんですね。でも、結果が返ってこないままなら、何度試しても同じでは……」

葵の声が小さくなった。机の上のコーヒーは冷めていたが、画面の赤い警告だけが明るく残っていた。

「反復回数には上限を置いているから、大丈夫だと思っていました。ミナトが同じ場所を回り続けても、上限に届けば止まるはずです」

蓮は画面の別の欄を指さした。そこには、モデルの応答ごとの記録と、実行されたツールの一覧が分かれて表示されていた。

「その上限が数えているのは、モデルとの往復です。一回のモデル応答で、複数のツールが呼ばれていたら、その上限だけでは止まりません」

「一回の応答の中に、検索と在庫確認と更新確認が入ることがあるんですか」

「はい。Function Callingでは、ミナトが関数そのものを動かすのではありません。呼びたいツールと引数を返し、アプリケーションが実行します。だから、アプリケーション側がその数を数えないと、ひとつの往復の中で呼び出しが重なります」

葵は記録を拡大した。ひとつのモデル応答の下に、注文検索、在庫確認、更新確認が続けて並んでいる。反復の欄はひとつ進んでいるだけなのに、ツールの欄には複数の実行が残っていた。

「反復は一回。でも、ツールは複数回……」

「そうです。ループの回数と、実際に外へ出た呼び出しの回数は別物です」

さらに蓮は、失敗した更新確認の詳細を開いた。最初の処理が失敗したあと、実行担当の処理が再試行を行っている。その再試行が終わる前に、ミナトも同じ状態を見て、もう一度更新確認を求めていた。

「こちらは、ツール側の再試行です。こちらは、ミナトが結果を見て行った再実行です。同じ処理ですが、別の道から重なっています」

「失敗の一回も、ミナトの一回も、同じ反復欄には入っていないんですね」

「少なくとも、今の実装ではそうなっています。しかも、同じ結果に戻ったことを検知していません。状態が変わらないまま、また検索に戻っています」

画面には、同じ注文の状態が何度も表示された。注文の情報は変わっていない。それでもミナトは、確かめるための検索を続けていた。

葵は椅子の背にもたれた。便利な同僚だと思っていたミナトが、急いで答えようとするほど、同じ扉を何度も開けているように見えた。

「外部サービスには、同じ内容でも新しいリクエストとして届く。だから処理費用も積み上がるんですね」

「はい。読み取りだけなら影響は限られますが、更新や送信が混ざると、費用だけでなくデータの重複も起きます」

蓮は静かに画面を閉じた。

「反復の上限は必要です。ただ、それだけでは足りません。次のツールを呼ぶ前に、全体の呼び出し数を見る必要があります。失敗の再試行も、同じ引数の繰り返しも、別に止める仕組みが要ります」

葵は、朝一番に開いた設計図へ目を戻した。そこにはガードレールと書かれた線が、モデルの周りだけを囲んでいた。ツールを実行する場所も、再試行を決める場所も、その線の外にあった。

「止める場所を、間違えていたんですね」

葵は新しい欄を設計図に書き足した。エージェント全体の呼び出し数。ツールごとの累積数。同じ失敗の回数。そして、次の処理へ進む前の停止判定。

その朝、ミナトはまだ答えを返せなかった。けれど葵は、便利さを失わせずに安全を加えるための場所を、ようやく見つけ始めていた。

03

第3章 古いホワイトボードに描かれた三本の線

三本のカウンターを描く開発室のエンジニア

第3章 古いホワイトボードに描かれた三本の線 - 本文

葵は、ミナトの呼び出し回数を止める設計を見直すため、先輩エンジニアの直人の席を訪ねた。窓ぎわの机には、古いホワイトボードが立てかけてあった。そこには、消えかけた矢印と、いくつもの丸が残っていた。

「直人さん、ループ反復数に上限を置いたのに、ツールが止まりませんでした」

直人は画面をのぞき、静かにうなずいた。

「まず、数えるものを分けよう。ループが何回進んだかと、ツールが何回動いたかは、同じ数字ではないんだ」

直人はホワイトボードを消し、横に三本の線を引いた。一本目には「LLMを呼び出した回数」、二本目には「エージェントのループ反復回数」、三本目には「個々のツールを実行した回数」と書いた。葵はペン先を見つめながら、昨夜の注文確認の画面を思い出した。

「この三つを、別々のメーターにするんですね」

「そう。車に速度計だけでなく、燃料計や温度計もあるのと同じだよ」

直人は一本目の線から、二本目へ矢印を描いた。さらに二本目から、注文検索、在庫確認、顧客情報照会へ三本の矢印を伸ばした。一回のループで、ミナトが三つのツールを順に使えば、ループは一回でも、ツール呼び出しは三回になる。

「しかも、失敗したら再試行が足される。だから、ループ十回という上限だけでは、外部サービスへの処理を十回に抑えられないんだ」

葵は、赤く光った運用通知を思い出した。画面の中では、一つの小さな矢印が、別の矢印を呼び、さらにその先で同じ処理を繰り返していた。

直人は新しい枠を描いた。枠の中には「1回の応答」と書き、その下にツールを一つだけ置いた。

「Chat Completions APIでは、parallel_tool_calls=Falseを使って、一回のレスポンス内で複数のツールを並列に呼ばせない方針にできる。仕様は、OpenAIのChat Completions APIで確認できるよ」

「一つずつなら、途中で止める場所も見えますね」

「その通り。けれど、設定しただけで安全になるわけではない。返ってきたレスポンスの finish_reason を見て、ツール呼び出しで終わったのかを確かめる。そして tool_calls があるか、件数はいくつかを、アプリケーション側で判定するんだ」

直人は、白い紙に小さな流れを書いた。LLMを呼ぶ。finish_reason と tool_calls を確認する。許可された回数ならツールを実行する。結果を返して、次のLLM呼び出しへ進む。上限に達していたら、そこで止まり、ツールの実行依頼を先へ送らない。

「つまり、回数を増やすのも止めるのも、私たちのコードが決めるんですね」

「うん。max_tool_calls のような名前を見かけても、実際のAPIにある設定だとは限らない。存在しないパラメーターに総数制限を任せると、止まると思っていた場所で止まらない。使っているSDKと公式仕様を確かめて、最終的な上限はアプリ側で持つんだ」

葵は三本の線の端に、三つの数字を書いた。LLMは12回、ループは6回、ツールは9回。数字が並ぶと、どこで費用が増え、どこで再試行が重なったのかが見え始めた。

「Responses APIやAgents SDKを使う場合も同じだよ。便利な仕組みが実行を助けてくれても、全体をいつ止めるか、どの権限を渡すか、何を監査ログに残すかは、私たちが管理する」

直人は、ツールの横に小さな鍵を描いた。注文検索や在庫確認のような読み取りは許可しやすい。一方、メール送信や顧客情報の更新のような書き込みは、別の上限にし、人間の承認を挟む。

「HITL、つまり人間が途中で確認する仕組みですね」

「そう。回数だけでなく、影響の大きさも見るんだ。読み取る一回と、外部へ送る一回は、同じ一回でも重さが違うからね」

窓の外で、午後の光がビルの壁をゆっくり下っていた。葵は、これまで一つの数字に押し込めていた不安が、三つのメーターと権限の鍵に分かれていくのを感じた。

「まず、三つのカウンターを実行単位ごとに持ちます。レスポンスごとのツール件数、リクエスト全体の累積数、セッションの合計数を記録します。再試行の理由と待ち時間も、監査ログに残します」

直人はうなずき、赤いペンで最後の線を引いた。その線の先には、「停止」「再開」「承認待ち」と三つの出口があった。葵は設計図を写真に収め、席を立った。

「やってみます。今度は、ミナトをただ止めるのではなく、どのメーターが動いたのかまで分かるようにします」

開発室へ戻ると、葵は最初のコードを書き始めた。画面の三つのカウンターは、まだ小さな数字を示していた。けれど今度の数字は、暴走の影ではなく、次に進む場所を教える灯りのように見えた。

04

第4章 ミナトの設定ファイルに置かれた停止線

OpenAIのツール制御画面を確認する開発者たち

第4章 ミナトの設定ファイルに置かれた停止線 - 本文

葵たちは、ミナトの全体設定とタスク別設定を、ひとつのファイルにまとめ始めた。上限に達したあと何をするかも、設定値と同じくらい大切だった。

「まずは、どの仕事にも効く停止線を置こう。タスクごとに事情が違っても、何も書かれていなければ、この線に戻れるようにするんだ」

直人は、葵の隣で小さなYAMLファイルを開いた。問い合わせ確認だけは、在庫検索より短い流れで済むため、別の上限を設定する構成にした。

llm:
  max_tool_call_iterations: 10

tasks:
  inquiry_check:
    max_tool_call_iterations: 4

「inquiry_checkに指定があれば4回。指定がないタスクは、全体の10回を使うんですね」

「そう。タスクの設定は上書きで、空欄のときはグローバル値へ戻る。これなら新しいタスクを追加しても、停止線が消えないよ」

葵は設定を読み込む関数を書いた。値が正の整数かを初期化時に確かめ、文字列の"10"、小数の2.5、真偽値のtrue、そして1未満の値は、その場でエラーにする設計だった。

ところが最初のテストで、trueが整数の1として通ってしまった。使っていた言語では、真偽値が整数のように扱われることがあると、直人が気づいた。

「整数かどうかだけでなく、型が本当に整数かも見よう。type(value) is intを先に確認して、1以上であることを続けて調べればいい」

葵は検証を直し、設定が不正ならミナトを起動しないようにした。間違った設定のまま走り出してから止めるより、玄関で靴を確認するほうが安全だった。

次に二人は、反復回数だけを数える設計をやめた。loop_count、llm_call_count、ツールごとのtool_call_count、累積実行時間、failure_countを別々のカウンターにし、各ラウンドの前後で記録する。

「LLMが一度返事をしても、その中でツールを二つ呼べば、外部処理は二つです。回数の合計が上限に近いときは、次のツール呼び出しをLLMへ渡す前に止めます」

ミナトの返答も、文章だけでは判定しないことにした。返答には、次のような構造化データを必ず添えさせた。

{"status":"completed","next_action":"stop"}

statusがcompletedなら終了する。外部からキャンセルが届いたとき、時間を使い切ったとき、失敗回数の上限に達したときも停止し、同じ状態や同じツール引数が続いたときは、ループの指紋が変わらないと判断して止める。

「上限に達したら、無理に答えを作らない。読み取り処理なら途中結果を返し、更新処理なら人に確認を頼む。これがHITL、人間が途中で判断する道だね」

ミナトが顧客情報を更新する場面では、実行前に承認を求めることにした。さらにupdate_customerへ一意の冪等性キーを渡し、停止直前の再試行が同じ更新を二度反映しないようにした。

葵は全体の総数制限だけでは足りないと考えた。時間窓ごとの制限と、ツール別のレート制限も加え、注文検索が続いても、顧客更新や外部通知まで一緒に増えないようにした。

最初の問い合わせ確認を動かすと、ミナトは一度の応答で注文検索と在庫確認を同時に返した。反復回数は1回なのに、ツール呼び出しは2回になり、二人は画面の前で顔を見合わせた。

「これが前に話した、回数のずれだね。1回の応答で複数ツールを許すと、停止線をまたぐことがある」

葵はChat Completions APIの設定にparallel_tool_calls=Falseを指定し、さらにアプリ側でもtool_callsの件数が1件以下か確認した。OpenAI Chat Completions API仕様を見直し、実際のSDKにないmax_tool_callsのような名前だけに頼らないことも決めた。呼び出しの累積上限は、ミナト自身ではなくアプリケーションが持つ。

最後に、三つのテストを並べた。タスク設定がない場合は10回へ戻ること、inquiry_checkは4回を使うこと、そして上限に達した直後は次のツールが一度も実行されないことを確認するテストだ。

一度目の停止テストでは、上限に達した後も、失敗通知を送るツールが動いてしまった。葵は停止判定をツール実行の後ろに置いていたため、判定を前へ移し、通知も承認が必要な処理として扱った。

夜が深まり、ミナトは今度こそ静かに止まった。画面には、使った反復回数、LLM呼び出し数、ツール別の回数、失敗数、経過時間が並び、葵はようやく椅子の背にもたれた。

「止まれることまで実装して、やっと仕事を任せられるんですね」

窓の外で、朝の光がビルの端を薄く染めた。ミナトの設定ファイルには、数字だけでなく、戻る道と人を呼ぶ道も置かれていた。

05

第5章 赤いランプが点く前に届いた停止通知

OpenTelemetryで停止を見守る開発室

第5章 赤いランプが点く前に届いた停止通知 - 本文

改修を終えた翌朝、ミナトは以前と同じ注文問い合わせを受けた。外部の在庫確認ツールは、また応答に失敗した。それでも今回は、危険なループが赤いランプを点ける前に止まった。

照明の落ちた開発室で、葵は画面を見つめた。以前は、注文検索と在庫確認が何度も並び、処理中の表示が消えなかった。今は呼び出しの列が途中で静かに止まり、通知だけが黄色く残っている。 「今度は、止まったね」

隣の椅子で、亮が小さく息をついた。ミナトは、ツールの結果を見て次の行動を決めるReAct型の流れで動く。つまり、推論、ツール呼び出し、結果の観察をくり返すため、上限がなければ同じ場所を回り続けることがある【Function Callingの設計資料】。 「前は、ループの回数だけを見ていた。でも、それでは足りなかったんだね」

画面には、失敗回数、同じ状態の繰り返し、累積時間、ツール呼び出し数が別々の欄に表示されていた。ひとつの数字にまとめず、失敗した処理と再試行、複数の呼び出しを分けて数えている。アプリケーション側で累積カウンターを持つ設計は、呼び出し上限を実際の実行全体に適用するための方法として示されている【呼び出し回数制御の解説】。

運用設定には、早めに知らせるソフトリミットの通知水準が70%と90%、強制停止するハードリミットが150%、再試行の上限が最大3回と記されていた。これは調査『AIエージェントのコスト暴走を防ぐ利用上限設計』に基づく運用例である。通知を出すかどうかは、上限の判定とは別に、通知制御の設定で決められるようになっていた。

90%の通知が届いたあと、在庫確認ツールは同じ引数で失敗した。ミナトは次の呼び出しを組み立てかけたが、状態の指紋が前の記録と一致したため、再試行の流れを閉じた。最終的な停止条件には、呼び出し数と時間の上限も残り、ハードリミットの水準を超えて処理が進まない構造になっている。 「止めるだけじゃなくて、止めた理由も残るんだ」

利用者の画面には、赤いエラーではなく、いつもの返答欄が開いた。そこには「必要な確認を完了できませんでした。担当者が確認します」と表示された。内部の終了理由には、conversation.tool_call_limitが記録され、利用者には穏やかな案内だけが返された。

監査ログには、要求IDとセッションIDが並んだ。実行ユーザー、エージェント名、ツール名、引数、権限判定、レスポンス、エラー、処理時間も一行ずつ残っている。使ったモデルの情報とコストも記録され、あとから同じ処理をたどれる形になっていた。

葵は、ログの中からひとつの要求を開いた。OpenTelemetryの考え方に沿い、要求全体がひとつのトレースにまとめられている。LLMの呼び出しとツールの呼び出しは、それぞれ別のスパンとして並び、どこで時間が伸びたかを追えるようになっていた。 「在庫確認の応答が空のまま、状態が変わらなかった。だから同じ呼び出しになったんだね」

亮が指を動かすと、失敗したツールのスパンから、同じ引数を使った次の処理へ線が伸びた。原因は、ミナトが勝手に外部サービスを実行したことではなかった。応答のない状態を新しい状態と誤って扱い、次の推論へ渡しかけたことだった。

ダッシュボードには、単なるエラー率だけでなく、ツール浪費、制約違反、危険な副作用、同一状態の反復も表示された。書き込み系のツールなら、権限判定とHITLの承認結果も同じ流れで確認できる。公開されたGitHub Actionsの事例でも、同一エラーの指紋や再実行回数を調べることで、長いループの原因が追跡されていた【自律開発ループの事例】。

以前の開発室には、止まらない処理への焦りがこもっていた。今は、停止通知のあとに、淡い安心が広がっている。葵は画面を閉じず、止められたことと、なぜ止まったのかを後から確かめられることを、同じくらい大切に感じていた。 「これなら、次に直す場所が見えるね」

窓の外が少し明るくなった。ミナトは答えを無理に作らず、必要な確認を人へ渡していた。開発室には、処理が終わったあとの静かな音だけが残った。

06

第6章 静かな画面に戻った、ミナトのいる朝

朝の開発室でAIエージェントの処理状況を確認する葵

第6章 静かな画面に戻った、ミナトのいる朝 - 本文

改修からしばらくたった朝、葵のチームは、落ち着いた開発室で仕事を始めていた。ミナトは呼び出しを制御しながら、必要な問い合わせだけを静かに進めていた。

窓から入る光が、机の上のマグカップを細く照らしている。大きな警告音はない。画面には回答だけでなく、許可されたツール、完了した処理、途中で止まった処理が、三つの欄に分かれて並んでいた。

「前は、答えが返るまでしか見えなかったね」

葵が画面を指でなぞると、注文検索には「完了」、在庫確認には「未実行」と表示されていた。ミナトが必要な注文だけを見つけ、在庫確認まで進む理由がなかったからだ。処理の最後には、status と next_action も残っている。status: completed、next_action: none。その文字を見ると、葵の肩から力が抜けた。

「終わる場所を、自分で知らせてくれるんだね」

隣の席で、直人が小さくうなずいた。ミナトが使えるのは、注文検索や在庫確認など、小さな読み取り用のツールだけだった。検索、対象の選択、結果の取得を一つずつ分けたが、目的のない呼び出しは続けないよう、アプリケーション側でも回数を数えていた。

「エージェントを増やせば、もっと賢くなるのかな」

直人はそう言ってから、首を横に振った。

「今は増やさないよ。一つのミナトと、役目のはっきりした道具で十分だから」

画面の権限欄には、実行した社員の名前と業務ロールが表示されていた。広い共有資格情報をミナトに渡すのではなく、その人が本来持つ権限だけを引き継ぐ仕組みだ。葵は、誰の仕事として呼び出されたのかまで見えることを、以前より頼もしく感じていた。

昼前、顧客情報の更新を含む依頼が一件入った。ミナトは更新内容を画面に並べ、すぐには実行しなかった。

「この先は、外の状態を変える処理です。承認をお願いします」

葵が内容を確認して承認すると、更新ツールが一度だけ動いた。監査ログには実行者、時刻、許可された引数、結果が記録された。そばには冪等性キーも残っている。同じ依頼がもう一度届いても、二重に更新しないための印だった。

「止まる場所と、戻る場所があるから任せられるんだね」

葵はそう言って、昨日の停止記録を閉じた。ミナトを完全に自由にしたわけではない。けれど、自由にしないことで、かえって仕事のそばに置けるようになっていた。

そのとき、朝の問い合わせが一件届いた。画面の小さなランプが灯り、ミナトは許可された注文検索だけを呼び出した。結果を受け取ると、status: completed と next_action: none を返し、短い回答を表示した。

開発室には、キーボードを打つ音だけが残った。葵は湯気の消えかけたマグカップを手に取り、静かな画面をもう一度見た。

関連キーワード

AIエージェント
ツール呼び出し
無限ループ
ガードレール
HITL
ツール呼び出し制御
Function Calling
再試行
運用ログ
Chat Completions API

著者について

鈴木信弘(SNAMO)

鈴木信弘(SNAMO)- 静岡県を拠点に活動する総経験19年のフルスタックエンジニア。AI時代の次世代検索最適化技術「レリバンスエンジニアリング」の実装者として、GEO(Generative Engine Optimization)最適化システムを開発。2024年12月からSNAMO Portfolioの開発を開始し、特に2025年6月〜9月にGEO技術を集中実装。12,000文字級AI記事自動生成システム、ベクトル検索、Fragment ID最適化を実現。製造業での7年間の社内SE経験を通じて、受注システム自動化による業務時間の短縮や検品作業のデジタル化など、現場の課題を技術で解決してきた。富山大学工学部卒、基本情報技術者保有。

プロフィールを見る

よくある質問

Q1AIエージェントのツール呼び出しが止まらない原因は何ですか?
ループ反復回数だけを制限していると、1回の反復内で複数のツールを呼び出したり、ツールの失敗後に再試行したりすることで、呼び出し回数が膨らむことがあります。反復回数とは別に、LLM呼び出し数、ツール実行数、失敗後の再試行回数などを個別に管理する必要があります。
Q2AIエージェントの暴走を防ぐには、どのような上限を設定すべきですか?
システム全体に適用するグローバル上限に加え、タスクごとのツール呼び出し数、LLM呼び出し数、ループ反復数、再試行回数などを設定します。YAMLなどでタスク別の設定を管理し、型検証によって不正な値を事前に排除すると安全です。
Q3API側の設定だけでAIエージェントの暴走を防げますか?
APIのタイムアウトやリトライ設定だけでは不十分です。アプリケーション側で呼び出し回数の上限、利用可能なツール、ユーザー権限、停止条件、監査ログ、人間による承認を管理する必要があります。複数の防御層を組み合わせることが重要です。
Q4AIエージェントのツール実行に必要な安全対策は何ですか?
許可リストによるツール制限、ユーザーやタスクに応じた権限管理、レート制限、タイムアウト、冪等性の確保、再試行回数の制限などが有効です。特に外部サービスへ影響を与える操作は、人間の承認を必須にするHITL(Human-in-the-Loop)を導入すると安全性が高まります。
Q5AIエージェントの停止条件はどのように設計すればよいですか?
「処理が完了した」「ツール実行数の上限に達した」「同じ失敗を一定回数繰り返した」「許可されていないツールが要求された」など、具体的で構造化された条件を設定します。単に処理を止めるだけでなく、どの条件で停止したのかを記録・表示できるようにすることが重要です。
Q6ツールの失敗による無限ループを防ぐ方法はありますか?
ツールごとに実行回数と再試行回数を個別にカウントし、同じエラーや同じ入力が続いた場合は処理を停止します。また、指数バックオフなどの再試行間隔、レート制限、タイムアウト、フォールバック処理を組み合わせると、外部サービスの障害による連続呼び出しを抑えられます。
Q7AIエージェントの暴走原因を後から調査するには何が必要ですか?
LLMへの入力と出力、ツール名、引数、実行結果、再試行回数、停止理由、ユーザーやタスクの識別情報などを監査ログに記録します。さらにOpenTelemetryなどで処理全体をトレースすると、どのツールが呼び出しを増加させたのか、どの時点で失敗や停止が発生したのかを追跡できます。