AIエージェントの「ツール呼び出しが時々失敗する」を、1,894回の実測で切り分けた

2026.08.28 ・ 約9分で読めます

社内で提案資料を生成するAIエージェントを運用していたところ、ツール呼び出しが不定期に失敗する事象が続きました。エラーメッセージは Your tool call was malformed and could not be parsed. というものです。再現条件が掴めず、会話が長いせいだ、日本語のせいだ、といった説が現場で飛び交っている状態でした。

そこで全セッションのログを集計し、1,894回のツール呼び出しを横断で分析しました。結果として通説はすべて否定され、原因は入力の内容ではなく特定モデルバージョンの挙動にあることが分かりました。AIエージェントを業務で運用する際のデバッグ手順として、そのまま使える形で記録します。

測定時点について: 本記事の実測は2026年6月時点のものです。その後モデルは更新されており、記事中で挙げる特定バージョンの挙動が現在も同じとは限りません。値ではなく切り分けの手順を参照してください。観測結果はAnthropic社にも報告済みです。

症状: 再現条件が掴めない失敗

AIエージェントにファイル操作やコマンド実行を任せていると、ツール呼び出しが失敗して処理が止まることがあります。厄介なのは、同じ作業を繰り返しても毎回落ちるわけではない点です。数十回に一度、脈絡なく発生します。

この手の事象は、原因を推測した順に対策を試すと時間を消費します。「長い会話が悪いのだろう」と会話をリセットし、「日本語がまずいのだろう」と英語で書き直し、それでも再発する。推測ベースの対策は、効いたかどうかの判定もできません。発生が確率的なので、しばらく起きなければ直った気になってしまうためです。

やるべきことは1つで、全数を数えることです。

まず全数を数える

使っていたエージェント基盤(Claude Code)は、セッションのやり取りをJSONL形式でローカルに保存します。ここにはモデル名・ツール呼び出し・停止理由がすべて残るため、後から集計できます。

# セッションログ(JSONL)から tool_use の回数とモデルを集計する
cat ~/.claude/projects/<project>/*.jsonl \
  | jq -r 'select(.message.content != null)
      | .message as $m
      | ($m.content[]? | select(.type=="tool_use")) as $t
      | [$m.model // "unknown", $t.name] | @tsv' \
  | sort | uniq -c | sort -rn
セッションログからモデル別・ツール別の呼び出し回数を集計する。まずは分母を確定させる。

ここで重要なのは、失敗した回数だけを見ないことです。失敗率を出すには成功も含めた全呼び出し数が要ります。「今日は5回落ちた」は情報量がありません。5回/50回なのか、5回/2,000回なのかで、話がまったく変わります。

集計対象は12セッション、ツール呼び出し1,894回。うち失敗47回でした。全体では2.5%程度ですが、この数字自体には意味がありません。分解して初めて構造が見えます。

実測データ

モデル別に分けたところ、分布が偏っていました。

モデルツール呼び出し回数失敗失敗率
Opus 4.81,691472.8%
Opus 4.720300.0%
表1:初回集計(12セッション横断)。失敗は片方のバージョンに集中していた。

失敗が0件だった側について、サンプル数が少ないのではないかという疑いが残ります。ここは確率で確認できます。仮に両者の失敗率が同じ2.8%だとすると、203回連続で失敗しない確率はおよそ0.3%です。偶然でこの偏りが出るとは考えにくい水準と判断しました。

翌日、対象を広げて再集計しました。サブエージェントの実行分も含め、モデルを4種類に分けています。

モデルツール呼び出し回数失敗失敗率
Opus 4.8912475.15%
Sonnet 4.679200.0%
Opus 4.713600.0%
Fable 52500.0%
表2:再集計。失敗は1バージョンでのみ発生し、他の3種は0件だった(Fable 5はサンプル数が少ないため参考値)。

Sonnet 4.6 が792回で0件だったのは実務上ありがたい結果でした。前バージョンの Opus 4.7 も0件です。失敗は特定の1バージョンに閉じていました。

通説を1つずつ否定する

原因が分かった後より、分かる前にどう潰していったかの方が再利用できるので、否定した仮説を残しておきます。

仮説判定否定した根拠
会話が長い・文脈が大きい無関係横断で見ると、文脈863kのセッションが最も失敗率が低く(0.76%)、101kのセッションが最も高かった(13.6%)。長さと失敗率が逆相関していた。
ツールの引数が大きい無関係数十バイトしかない echo ok 相当の呼び出しでも失敗が発生していた。
日本語(マルチバイト)主因ではないASCIIのみの呼び出しでも失敗した。多少の増幅要因である可能性は残るが、単独の原因ではない。
複数ツールの同時呼び出し無関係失敗した47件はすべて1メッセージ1ツールだった。
ローカル環境(拡張機能・パス・CLIバージョン)除外プロジェクト固有の拡張設定・フックはなし。CLIも要件を満たすバージョンだった。
表3:否定された仮説。いずれも「入力の中身が原因」という前提に立っており、その前提自体が誤りだった。

文脈長の項目は特に意外でした。直感に反して、大きな文脈のセッションほど失敗率が低い。もし長さが原因なら逆の傾向が出るはずで、この1点だけで「会話をリセットすれば直る」という対処が無意味だと分かります。

失敗時に何が起きていたか

ツール呼び出しは、モデルが直接プログラムを起動しているわけではありません。モデルはタグ形式のテキストを生成し、それを実行環境(ハーネス)が解析して実際のツール実行に変換します。この解析に失敗すると malformed になります。

失敗したケースのログを見ると、共通の形がありました。

# 落ちたケースの記録 (要約)
{
  "stop_reason": "tool_use",        # ツールを呼ぼうとしている
  "content": ["text"]               # しかし中身はテキストブロックのみ
}
# 期待される正常形
{
  "stop_reason": "tool_use",
  "content": ["text", "tool_use"]   # 構造化されたツール呼び出しが入る
}
失敗時の記録。ツールを呼ぶ意図はあるのに、出力がテキストのまま構造化されていない。

停止理由はツール呼び出しになっているのに、記録された中身はテキストブロックだけでした。つまりモデルはツールを呼ぼうとしていて、その出力が構造として成立しないままこぼれていた、ということです。入力の内容ではなく生成側のほころびなので、決定論的な再現条件が見つからなかったのも筋が通ります。

対処: バージョンを固定する

原因が特定バージョンに閉じている以上、対処は明快で、そのバージョンを経路から外すことです。ただし1点、見落としやすい穴がありました。

サブエージェントの定義でモデルをエイリアス指定していると、そこだけ問題のバージョンに解決されます。主セッションのモデルを切り替えても、エージェント定義に model: opus のような別名で書いてあると、それは「最新のOpus」と解釈され、当時の最新=問題のバージョンに繋がってしまいます。主セッションだけ直して安心していると、サブエージェントの中で再発します。

---
name: conceptor
description: 提案のコンセプトと章立てを作る
model: claude-fable-5      # ← エイリアス(opus)ではなくフルモデル名で固定
---
エージェント定義のモデル指定。エイリアスではなくフルモデル名で固定する。

あわせて、チーム共有の設定ファイルは触らず、個人の設定でモデルを上書きする形にしました。共有設定のモデルは実測で問題が出ていないものだったため、変更する理由がないからです。影響範囲を最小にして、必要な箇所だけ固定するのが安全です。

運用として持ち帰ったこと

(1) 確率的な不具合は、分母を数えるまで議論しない。 「よく落ちる」「最近増えた」といった体感は、発生が確率的な事象では当てになりません。全呼び出し数を分母に置いて初めて、比較できる数字になります。ログが残る基盤を使っているなら、集計は数分で終わります。

(2) 変数を1つずつ分離する。 今回はモデル別に分けた瞬間に答えが出ました。逆に言えば、モデルという軸で分けるまでは、文脈長・言語・引数サイズといった軸をいくら調べても構造は見えませんでした。手当たり次第に条件を変えるより、ログにある属性で機械的に層別する方が早いです。

(3) AIエージェントを業務で使うなら、モデルのバージョンは固定する。 エイリアス指定は「常に最新を使う」という意味になり、モデル更新のたびに挙動が変わる余地を抱えることになります。品質を保証したい処理では、フルモデル名でピン留めし、更新は検証してから行うのが安全です。今回の件は、その運用ルールが無かったために調査が必要になった、とも言えます。

(4) 観測結果は提供元に報告する。 バージョン間の差分が数字で出ている場合、報告する価値があります。自社で回避策を打つのと並行して、デバッグログ付きで報告しました。

私たちはこうした検証をふまえて、AIを業務プロセスに組み込む設計・実装を行っています。AIエージェントの導入で同種の運用課題に当たっている場合は、AI・自動化の導入もご覧ください。

AIを業務に組み込む設計から、運用の詰めまで。
どこにAIを使うかの整理から、品質保証・監視まで含めた運用設計を日本語で支援します。
AI・自動化の導入を見る
相談する →