本番環境でAIエージェントをデバッグする方法(実践ガイド)
本番のAIエージェントのデバッグは、どのレイヤーが故障したか — プロンプト、ツール、モデル、オーケストレーション — を切り分けることがほとんどだ。私はすべてのステップをトレースIDで記録し、まったく同じ入力をリプレイし、二分探索する。私のエージェントでは、'AIのバグ'の約70%はモデルのバグではなく配管のバグだと判明する。
毎週水曜。28,400人以上の読者。無駄なし。
✓ メールをご確認ください — 確認リンクをクリックして登録を完了してください。
✓ 登録が完了しました!
✓ すでに登録済みです。
目次
2026年6月更新。
TL;DR: 本番のAIエージェントのデバッグは、どのレイヤーが故障したか — プロンプト、ツール呼び出し、モデル出力、オーケストレーション — を切り分けることがほとんどだ。私はすべてのステップをトレースIDで記録し、まったく同じ入力をリプレイし、そこから二分探索する。私のエージェントでは、「AIのバグ」に見えるもののおよそ70%は配管だと判明する — 不正な形式のツール結果、切り詰められた入力、黙って飲み込まれた例外だ。
オペレーターの視点: 私は100以上の本番エージェントを運用している — Picklelandの予約フロー、コンテンツパイプライン、受信トレイの仕分け係だ。それらはあらゆるソフトウェアが壊れるように壊れ、加えていくつか新しい壊れ方をする。これは私が持っていればよかったと思う実践ガイドだ。トークンの壁を凝視せずに、故障しているレイヤーを見つける方法である。
エージェントが本番で誤動作すると、本能的にモデルを責めたくなる。「Claudeが幻覚を起こした」。時には本当だ。たいていは違う。モデルは5層か6層のスタックの中の一つのレイヤーであり、バグはAnthropicが出荷したレイヤーよりも、あなたが書いたレイヤーにあることのほうがはるかに多い。この記事は、私がそれを見つける体系的な方法である。
何かをデバッグする前に、すべての実行をトレース可能にする
見えないものはデバッグできない。最も効果の高いこと — 特定のバグが現れる前に — は、すべてのエージェント実行にトレースIDを付与し、それが踏むすべてのステップを記録することだ。
「ステップ」とは境界を越えるあらゆるものだ。受信トリガー、各モデル呼び出し(完全なメッセージ配列付き)、各ツール呼び出し(引数付き)、各ツール結果、そして最終出力。それらをトレースIDをキーとする構造化JSONとして記録する。
function logStep(traceId: string, step: string, payload: unknown) {
console.log(JSON.stringify({
traceId,
step, // "trigger" | "model_call" | "tool_call" | "tool_result" | "output"
ts: Date.now(),
payload,
}));
}Cloudflare Workersではこれらをキューとテーブルに送り、ローカルではstdoutに出す。ルールは絶対だ。ステップが記録されていなければ、デバッグに関する限りそれは起きなかったことになる。これは私が使うエージェントスタックで述べた計装を反映している — トレースIDは他のすべてがぶら下がる背骨だ。
レイヤーを切り分ける:プロンプト、ツール、モデル、オーケストレーション
トレースさえあれば、デバッグは二分探索になる。レイヤーは4つあり、バグはたいていそのうちのちょうど一つに潜んでいる。
1. 入力レイヤー(最もよくある原因)
故障したモデル呼び出しに入った、まったく同じ messages 配列を取り出す。再構築ではなく — ログからの文字どおりのペイロードだ。それを、見知らぬ人が読むように読む。「モデルが指示を無視した」という私のバグの半分は、実際には次のものだ。
- 何かが誤って文字列化されたために
"[object Object]"として返ってきたツール結果。 - コンテキストウィンドウを超過し、素朴なスライスが切り取ったために文の途中で切り詰められた入力。
undefinedとして補間され、こっそりプロンプトを汚染した変数。
入力が間違っていれば、モデルはゴミに対して完璧に仕事をしたのだ。配管を直そう。
2. ツールレイヤー
入力がきれいに見えるなら、エージェントが成功として扱ったエラーをツールが返していないか確認する。古典的な例:APIが { "error": "rate limited" } という本文付きで 200 を返し、あなたのツールラッパーが本文を確認せず、エージェントが自信満々にエラーメッセージに基づいて動く。ツール結果を生のまま記録し、その形を検証しよう。
3. モデルレイヤー
1と2を除外して初めて、私はモデルを疑う。それでも、「モデルのバグ」はたいてい「私のプロンプトが曖昧だ」を意味する。故障したまったく同じ入力を取り、同じモデルと温度に対して使い捨てのスクリプトに投入し、再現するか見る。再現するなら、修正はプロンプト作業かより厳密なevalであって、慌ててモデルを差し替えることではない。
4. オーケストレーションレイヤー
単一のステップが単独では問題ないのに複数ステップの実行が失敗するなら、バグは引き継ぎにある — ステップ間で失われた状態、競合状態、冪等でないアクションを再実行したリトライだ。これらが最も厄介で、私はマルチエージェント・オーケストレーションのパターンでそのパターンを扱っている。
非決定性と戦うのではなく再現する
エージェントをデバッグ不能に感じさせるのは非決定性だ。同じ入力が実行ごとに異なる出力を生む。これは飼いならせる。
第一に、固定できるものを固定する。 デバッグ中は temperature: 0 に設定する。Claudeを完全に決定論的にはしないが、分散を大幅に狭めるので、本物のバグをサンプリングノイズと見分けられる。
第二に、N回実行する。 失敗が20回に1回再現するなら、まったく同じ入力を50回ループさせ、すべての出力を捕捉する。これで逸話ではなくサンプルが手に入る。5%の確率で発火するバグは本物のバグだ — それを見るには量が必要なだけだ。
for i in $(seq 1 50); do
node replay.mjs --trace=abc123 >> runs.jsonl
done
# それから失敗を数える
grep -c '"status":"fail"' runs.jsonl第三に、成功した実行と失敗した実行を差分する。 温度を固定し同じ入力なら、出力の違いは、あなたがまだ見つけていない入力の違いを意味する — プロンプト内のタイムスタンプ、変動するツール結果、変わってしまった取得済みドキュメントだ。
リプレイハーネスを作り、本番でのデバッグをやめる
稼働中のエージェントを再トリガーしてデバッグするのは遅くて危険だ — 実際のメールを送り、実際のコートを予約してしまう。代わりに、トレースを捕捉してオフラインでリプレイする。
リプレイハーネスは記録済みトレースを読み込み、任意のステップへのまったく同じ入力を再構築し、そのステップだけをモデルに対して再実行する。完全な messages 配列を記録してあるので、上流システムはまったく必要ない。これは本番での10分の往復を2秒のローカルループに変え、私のデバッグワークフローにおける最大の高速化だ。
良いリプレイハーネスは変異させて再実行することもできる。システムプロンプトの1行を変え、同じ50の失敗トレースをリプレイし、いま何件が通るか見る。これがデバッグからevalへの架け橋だ — 失敗トレースのコーパスができれば、回帰スイートの始まりが手に入る。
故障を実際に予測するメトリクスを監視する
一部の失敗は決して例外を投げない。エージェントは実行され、もっともらしいものを返し、こっそり間違ったことをする。それらを捕まえるには、エラー率だけでなく挙動メトリクスを監視する。
- ツール呼び出し成功率(ツールごと)。ここでの低下はしばしば目に見える失敗に先行する。
- 出力スキーマの妥当性 — 出力の何%が期待される構造に対してパースできるか。私はすべての出力をZodで検証し、妥当性が下がったらアラートを出す。
- ループ長 — 実行あたりの平均ステップ数。急なスパイクはたいてい、エージェントがリトライで詰まっていることを意味する。
- 実行あたりのコスト — 暴走ループは、苦情として現れる前にコストのスパイクとして現れる。(コストが重要なときは、HaikuとSonnetの計算を知っておく価値がある。)
私はこれらを他のすべてと同じように追跡する — AIエージェントが実際に機能しているかをどう測るかを参照。静かな失敗を捕まえるメトリクスは、騒がしい失敗を捕まえるもの10個分の価値がある。
5分のトリアージ・チェックリスト
エージェントが壊れて時間に追われているとき、私はこれを順番に実行する。
- 失敗した実行のトレースIDを取得する。
- 失敗したステップへのまったく同じ入力を読む。 整形式か?(ここで約50%のケースが解決する。)
- そのトレース内の**ツール結果を確認し、**成功を装ったエラーがないか調べる。
temperature: 0でステップをオフラインでリプレイする。 再現するか?- **再現するなら、**プロンプト/モデルの問題だ — 修正してトレースコーパスを再実行する。**しないなら、**非決定性か状態/オーケストレーションのバグだ — 50回ループさせて特徴づける。
規律ある切り分けは、巧妙なプロンプティングに毎回勝る。モデルが問題であることはまれだ。たいてい問題はその周りのシステムにある。
よくある質問
たまにしか失敗しないAIエージェントはどうデバッグしますか?
記録済みトレースからまったく同じ入力を捕捉し、温度0で50回以上リプレイする。断続的な失敗は発火率の低い本物のバグだ — 量が逸話を、差分して修正できる再現可能なサンプルに変える。
バグはたいていモデルにありますか、それとも私のコードにありますか?
私の本番エージェントでは、見かけ上の「AIのバグ」のおよそ70%は配管だ。不正な形式のツール結果、切り詰められた入力、飲み込まれた例外、ステップ間で失われた状態。モデルを疑う前に、入力レイヤーとツールレイヤーを除外しよう。
エージェントをデバッグするのに必要な最小限のロギングは?
すべての実行のトレースID、加えてトリガー、各モデル呼び出し(完全なメッセージ配列)、各ツール呼び出しとその生の結果、そして最終出力の構造化ログ。ステップが記録されていなければ、それはデバッグできない。
稼働中の本番に対するデバッグをやめるには?
記録済みトレースを読み込み、捕捉した入力を使って任意の単一ステップをオフラインで再実行するリプレイハーネスを作る。それは遅くて危険な本番の往復を速いローカルループに変え、回帰スイートの種になる。
毎週水曜。28,400人以上の読者。無駄なし。
✓ メールをご確認ください — 確認リンクをクリックして登録を完了してください。
✓ 登録が完了しました!
✓ すでに登録済みです。
AIプレイブックをメールでお届け
毎週水曜。28,400人以上の読者。無駄なし。
メールをご確認ください。
確認メールをお送りしました — リンクをクリックして登録を完了してください。1分以内に届かない場合は迷惑メールをご確認ください。
登録が完了しました。
ようこそ — 次号がまもなくお手元に届きます。
すでに登録済みです — 毎週水曜日にお届けします。