Agent Mentor Learn
可観測性とデバッグ: エージェントの一歩一歩を見る · 第 1 回 / 全 6 回

レッスン1: なぜ「どこで間違ったのか」を説明できないのか

学習目標:

  • ユーザーに見える1つの症状の下に、外からは区別のつかない複数の根本原因が隠れうる理由を理解する
  • 非決定性が「再現してブレークポイントを置く」という従来のデバッグ直感をどう無力にするかを説明する
  • エージェントシステムでエラーが取る複数の形態を認識する: 連鎖的な失敗、軌跡の分岐、ターンをまたぐ複利、マルチエージェントの創発

前提: このシリーズの最初の10コースを修了し、stop_reason で駆動するハーネスループを手書きでき、評価トラックが pass/fail しか教えてくれないことを知っていること | 次: レッスン2 >>

説明のつかない、ある火曜日の午後

社内のリサーチエージェントが本番稼働して2週間。火曜の午後、運用チームからユーザーの報告が転送されてきます:

去年の料金プランを探してほしいと頼んだんですが、何も見つかりませんでした、と返ってきました。でもそのドキュメント、ナレッジベースにちゃんとあります——自分で開くのに2秒でしたよ。

セッションを開きます。画面にあるのは2つだけ: ユーザーの質問と、エージェントの最終応答「関連する資料は見つかりませんでした」。その間に何が起きたのか。手がかりは何もありません。

そこで、推測が始まります。

検索クエリの組み立てが下手だったのでしょうか——キーワードを抽出せず、ユーザーの自然言語の質問をそのまま丸ごと検索に投げてしまったとか。あるいは結果は見つかったのに、ソースの選び方を間違えたのでしょうか。8件のヒットのうち最も関連の薄い2件だけを読んで「ここには何もない」と結論づけたとか。それとも検索ツールがエラーを投げ、エージェントがその失敗を「この方向には情報がない」と解釈して先へ進んでしまったのでしょうか。

3つの推測はどれも、ユーザー側からはまったく同じに見えます: エージェントが、明らかにそこにある情報を見つけられない。

これはあなただけの問題ではありません。Anthropicのチームがマルチエージェントリサーチシステムを振り返ったとき、彼らはまったく同じ問題を書き残しています: ユーザーはエージェントが "not finding obvious information"(明らかにある情報を見つけられない)と報告してくるが、なぜかは見えなかった。エージェントは筋の悪い検索クエリを使っていたのか。ソースの選び方が悪かったのか。ツールの失敗を踏んだのか1。同じ3つの問い、そして答えはなし。

評価トラックが答えるのは「壊れたかどうか」だけ

最初の反射は、おそらく前のコース(このシリーズの第10コース)で作ったシステムを開くことでしょう: 終端状態のスコアリング、バリデータ、評価セット。それは正しい第一歩です。この実際のユーザーの質問を評価セットに追加し、「回答は料金ドキュメントを引用していること」という基準を書き、走らせます。

結果: 赤い行が1本。fail

この赤い行には価値があります——主観的な苦情を、再現可能で回帰テストにかけられる判定に変えてくれます。しかし、いま本当に答えてほしい問いには答えてくれません: なぜか、です。スコアラーが見ているのは終端状態です。終端状態は「ドキュメントを引用しなかった」。その「引用しなかった」が、まずいクエリから来たのか、ソース選択から来たのか、握りつぶされたツールエラーから来たのか——スコアラーは気にしませんし、気にしようもありません。ゴールラインに立って採点表を掲げているだけです。

このコースが埋めるのは、その中間の区間です。このコースが独自に立てた枠組みを使います:

検証は「壊れたかどうか」を教えます。可観測性は「なぜか」を教えます。

これは公式ドキュメントのどこかにある一文ではありません——このコースが以降の5レッスンを束ねるために置いた枠組みで、裏づけは2つの実体験です。1つはあのマルチエージェントシステムの振り返りから: 本番環境に完全なトレーシングを追加したことで、エージェントがなぜ失敗したのかを診断し、問題を体系的に修正できるようになりました1。もう1つはツール設計の側から: 評価用エージェントが残した生のトランスクリプトを分析すれば、エージェントが特定のツールをなぜ呼ぶのか、なぜ呼ばないのかを掘り下げられます2。どちらの文も同じところを指しています——プロセスが痕跡を残して初めて、「なぜ」を問えるのです。

なぜここでは「再現してブレークポイント」が効かないのか

まず定義から。決定的(deterministic) なシステムとは、同じ入力を与えれば毎回同じ出力を返すもののことです。非決定的(non-deterministic) なシステムはその逆で——エージェントはこちら側です。コンピューティングにおいて、決定的なシステムは同一の入力に対して毎回同じ出力を生成しますが、エージェントのような非決定的なシステムは、開始条件が同じでも異なる応答を生成しえます2

これが従来のデバッグの足場を崩します。エージェントは動的に判断を下し、プロンプトが同一でも実行ごとに非決定的です。これがデバッグを難しくします1。従来の評価はしばしば、AIが毎回同じ手順を踏むことを前提にします: 入力Xを与えれば、システムは経路Yをたどって出力Zを生むはずだ、と。しかしマルチエージェントシステムはそう動きません。出発点が同一でも、エージェントはゴールに至るまでにまったく異なる、しかしどれも妥当な経路を取りえます1

実際にはこう見えます:

text
同じ質問、同じプロンプトのバージョン、2回の実行
実行A                              実行B1 search("2024年の料金プラン")     1 search("料金プラン 2024 社内ドキュメント")2 read_doc(doc_17)                 2 search("料金 アーカイブ")3 respond (doc_17 を引用)          3 read_doc(doc_09)                                   4 read_doc(doc_17)                                   5 respond (doc_17 と doc_09 を引用)

どちらの経路も間違いではなく、終端状態はどちらも合格します。しかし失敗が実行Aのステップ2で起きたのに、10回走らせて9回が実行Bだったとしたら、その9回は何の役にも立ちません。

ブレークポイントも同じく役に立ちません。ブレークポイントはコードの行に置くもので、「2回目の実行も同じコンテキストでこの行を通る」ことが前提です。しかしエージェントがつまずくのは、多くの場合あなたのコードの中ではなく、モデルの判断です。そもそも止めたくても、どのターンで止めればいいのかがわかりません: 今回はターン3で壊れ、次はターン11かもしれず、まったく壊れないかもしれません。

サンプリングの temperature を下げる、ツールのレスポンスを記録して再生する、といった手が思い浮かぶかもしれません。こうしたエンジニアリング上の工夫は確かにゆらぎをいくらか減らしますし、このコースはそれを使うなとは言いません。ただしそれらが変えるのは、いまあなたの手元で走らせる実行であって、すでに本番で失敗した実行ではありません——そちらはもう消えていて、残したのは記録だけです。

Anthropicのチームは別のやり方を取りました。彼らはそれを "think like your agents"(エージェントのように考える)と呼びます: システムとまったく同じプロンプトとツールを使ってシミュレーションを組み、エージェントの作業を一歩ずつ観察するのです。これによって失敗モードが即座に明らかになりました: 十分な結果がすでにあるのに続行してしまう、過度に冗長な検索クエリを使う、間違ったツールを選ぶ1

鍵は「同じ実行を再現する」ことではなく、「一歩ずつを見る」ことです。ここが可観測性と従来のデバッグの分かれ道です。

エージェントが投げるエラーには、いくつかの形がある

「記録が要る」を受け入れたとしても、もう一段理解しておくことがあります: エージェントシステムのエラーの形は、従来のバグと同じではありません。

従来のソフトウェアでは、バグは機能を壊し、性能を落とし、あるいは障害を起こします。エージェント的なシステムでは、小さな変更が大きな挙動の変化へと連鎖し、長時間動き続けるプロセスの中で状態を保たなければならない複雑なエージェントのコードを書くことを、著しく難しくします1

1回の実行の中で最も典型的な形が軌跡の分岐です。エージェントはステートフルで、エラーは複利で積み上がります。エージェント的なシステムにおけるエラーの複利的な性質は、従来のソフトウェアなら些細な問題が、エージェントを丸ごと脱線させうることを意味します。1つのステップの失敗が、エージェントにまったく別の軌跡を探索させ、予測不能な結果につながります1

text
ターン3: 検索がタイムアウトし、ツールが "Error: upstream timeout" を返すエージェントはそれを「この方向には情報がない」と読むまったく別の検索の切り口に切り替える続く12ターンはすべてこの誤った枝の上で育つ終端状態: 一見完成したレポート、ただし出典はすべて周辺的な資料

この連鎖の最初の環がどれほど小さいかに注目してください: タイムアウト1回。従来のサービスなら、リトライのログが1行増えるだけかもしれません。ここではそれが続く12ターンを書き換え、しかも終端状態のレポートはエラーも出さず、クラッシュもせず、読み心地はまったく滑らかです。

2つ目の形はターンをまたぐ複利です。エージェントはステートフルで、エラーは複利で積み上がります。エージェントは長時間動き続け、多数のツール呼び出しにまたがって状態を保ちます。つまり、コードを永続的に実行し、その道中でエラーを処理する必要があるということです1。エラーが起きたときに最初からやり直せない理由もここにあります: やり直しは高くつき、ユーザーにとっては苛立たしい。代わりに彼らは、エラーが起きた地点のエージェントの状態から再開できるシステムを作りました1。あなたにとっての含意は直接的です: 失敗時点の状態が記録されていなければ、「どこから再開するか」という問いには答えがありません。

3つ目の形はマルチエージェントシステムでしか現れません: 創発的な振る舞いです。マルチエージェントシステムには、特定のプログラミングなしに生じる創発的な振る舞いがあります。たとえば、リードエージェントへの小さな変更が、サブエージェントの挙動を予測不能に変えることがあります。成功に必要なのは、個々のエージェントの振る舞いだけでなく、相互作用のパターンを理解することです1

これらの形がすべて重なるとどうなるか、そのチームの初期バージョンが非常に直接的な答えを出しています: 初期のエージェントは、単純なクエリに50のサブエージェントを生成し、存在しないソースを求めてウェブを果てしなく漁り、過剰な更新を送り合って互いの気を散らす、といった誤りを犯しました1

この3種類のエラーには共通点があります: 外から見ると、終端状態は「回答の質がいまひとつ」としか見えないかもしれない。どれなのかは判別できません。

抽象化のレイヤーが証拠を隠す

もう一段の厄介があり、それはモデル由来ではありません——あなたのツールチェーン由来です。

エージェントフレームワークは、LLMの呼び出し、ツールの定義とパース、呼び出しの連結といった標準的な低レベルの作業を単純化して、着手を容易にします。しかしその一方で、余分な抽象化のレイヤーを作り出し、下層のプロンプトとレスポンスを見えにくくして、デバッグを難しくすることがよくあります3。だから開発者への助言はこうです: まずLLMのAPIを直接使うところから始める——多くのパターンは数行のコードで実装できます。フレームワークを使うなら、下層のコードを理解しておくこと。中身についての誤った思い込みは、顧客側のエラーのよくある発生源です3。(この記事にはツールのエコシステムに関する記述が古くなっているという編集注記が付いています。ここで引いているのは原則であって、現時点の選定ガイドとして扱っているわけではありません。)

あなたにとって、これはむしろ良い知らせです。このシリーズの第7コースで、ハーネスループを手書きしました。その抽象化のレイヤーは、あなたにはありません:

証拠は毎回、必ずこのループを流れていきます。問題は、流れたあとに消えてしまうことです。遮るものはないが、留めるものもない——事後に証拠を求める立場からすれば、この2つは同じくらい痛い。

そこでこのコースの診断はこうです: エージェントのデバッグの難しさの半分は非決定性という客観的な事実から来ており、もう半分は「記録できたはずのものが記録されていない」ことから来ています。前半は変えられません。後半はあなたの手の中にあります。

この先の道筋: 以降の5レッスンが与えるもの

あのマルチエージェントシステムのチームは、これをプロンプトエンジニアリングやツール設計と同じ階層に置いています: これを正しくやるには、丁寧なプロンプトとツールの設計、確かなヒューリスティクス、可観測性、そして密なフィードバックループが要る1。振り返りにはもっと直接的な一文もあります——彼らは可観測性とテストケースを備えた、速いイテレーションループに注力しました1

「テストケース」の側にも注目してください: 可観測性は評価トラックを置き換えるために来たのではありません。両者は1本の天秤棒の両端です。評価は、この実行が壊れたか、そして変更で良くなったかを教えます。観測は、この実行がなぜ壊れたか、どこを変えるべきかを教えます。

以降の5レッスンはこの順で進みます:

  • レッスン2 は土台を敷きます: 生の記録が一次証拠です。エージェントの自己申告は当てになりません——省いたことのほうが、書いたことより重要なことがよくあります。
  • レッスン3 は各ステップをデータに変えます: 構造化ログとメトリクス。第10コースがスコアリングに使ったのと同じメトリクスを、このコースは診断に使います。
  • レッスン4 は散らばった記録を1本の木に束ねます: 1つのプロンプトが引き起こしたモデルリクエストとツール実行をすべてひとまとまりとして読み、サブエージェントの呼び出しは親の内側に入れ子にします。
  • レッスン5 はループのライフサイクルの要所にプローブを仕掛け、非決定性の下での特定のワークフローを通します: 記録の山から最初の分岐点を見つける。
  • レッスン6 は実習です: 第7コースで書いたハーネスに、可観測性のレイヤーを丸ごと組み込み、「説明できない」症状から、分岐した具体的なステップまでを追跡します。

このレッスンでは名前を挙げるだけで展開はしません: 記録の読み方はレッスン2の仕事、メトリクスの設計はレッスン3、トレースがどんな構造を持つかはレッスン4です。

フルスタックが要らないとき

すべてのエージェントにこれ全部が要るわけではありません。使い捨てのスクリプト——1回走らせて、出力をちらっと見て、削除する——にログとメトリクスとトレースを付けるのは、純粋な無駄です。

判断基準は単純です: 可観測性への投資は「壊れたあと、なぜかを説明するのにどれだけ時間がかかるか」に比例させる。 自分で走らせ、自分で見ていて、失敗しても再実行のコストがゼロなら、記録しなくていい。他人が使う、長時間動く、失敗したらチャットのやり取りを半日掘る羽目になる——そういうものなら、初日から記録します。

規模とは無関係な一線が1つあります: エージェントが行動を取れるなら——ファイルを書く、リクエストを送る、お金を使う——テストを省いてはいけません。エージェントの自律的な性質は、より高いコストと、エラーが複利で積み上がる可能性を意味します。サンドボックス環境での広範なテストと、適切なガードレールを併せて推奨します3

正しくできたかをどう知るかについては: 他のあらゆるLLM機能と同じく、成功の鍵は性能を測定し、実装を反復することです3。そして測定の前提は、測るものがあること——ここでこのコースが解こうとしている問題に戻ってきます。

💻 演習

まとめ

  • ユーザーに見える1つの症状の下には、外からはまったく区別のつかない複数の原因が隠れていることがよくあります。ユーザーがエージェントは "not finding obvious information"(明らかにある情報を見つけられない)と報告してきたとき、それが筋の悪い検索クエリなのか、ソース選択の失敗なのか、ツールの失敗を踏んだのかは判別できません1
  • 評価トラックが答えるのは「壊れたかどうか」です。このコースは分担を明確にするために独自に立てた枠組みを使います: 検証は壊れたかどうかを、可観測性はなぜかを教える。裏づけは2つの実体験——本番環境に完全なトレーシングを追加したことで失敗を診断し体系的に修正できるようになったこと1、そして生のトランスクリプトを読むことでエージェントが特定のツールをなぜ呼ぶのか、なぜ呼ばないのかを掘り下げられること2
  • 「再現してブレークポイント」が効かないのは、エージェントが動的に判断を下し、プロンプトが同一でも実行ごとに非決定的だからです1。決定的なシステムは同じ入力に同じ出力を返しますが、非決定的なシステムであるエージェントはそれを保証しません2。従来の評価の前提——「入力Xが経路Yをたどって出力Zを生む」——も成り立ちません。出発点が同一でも、まったく異なる、しかしどれも妥当な経路が生まれえます1
  • エージェントのエラーの形は従来のバグとは違います: 従来のソフトウェアではバグは機能を壊しますが、エージェント的なシステムでは小さな変更が大きな挙動の変化へと連鎖し、状態を保たなければならない複雑なエージェントのコードを書くことを著しく難しくします1。1つのステップの失敗が、エージェントにまったく別の軌跡を探索させ、予測不能な結果につながります1。エージェントはステートフルでエラーは複利で積み上がるため、永続的に実行し、その道中でエラーを処理する必要があります1。マルチエージェントシステムには創発的な振る舞いもあります——リードエージェントへの小さな変更がサブエージェントの挙動を予測不能に変えることがあり、理解すべきは個々ではなく相互作用のパターンです1。初期バージョンはかつて、単純なクエリに50のサブエージェントを生成し、存在しないソースを求めてウェブを果てしなく漁り、過剰な更新を送り合って互いの気を散らしました1
  • 抽象化のレイヤーは証拠を隠します: フレームワークは余分な抽象化のレイヤーを作り出し、下層のプロンプトとレスポンスを見えにくくして、デバッグを難しくすることがよくあります3。助言は、まずLLMのAPIを直接使うところから始めること、フレームワークを使うなら下層のコードを理解しておくことです3。手書きのハーネスには遮るものはありませんが、留めるものもありません。
  • この先の道筋は、可観測性と密なフィードバックループを第一級の要件として扱い1、可観測性とテストケースを備えた速いイテレーションループに組み込むことです1。以降の5レッスンの順序: 生の記録、ログとメトリクス、トレース、フックと特定のワークフロー、実習での組み込み。
  • 適用範囲の判断: 使い捨てのスクリプトにフルスタックは要りません。ただしエージェントが行動を取れるなら、サンドボックス環境での広範なテストを、適切なガードレールとともに行ってください3。正しくできたかを知る手がかりは、性能を測定し、実装を反復することにあります3

レッスン2 >>

Footnotes

  1. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

  2. Writing effective tools for agents — with agents — Anthropic Engineering — https://www.anthropic.com/engineering/writing-tools-for-agents 2 3 4

  3. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2 3 4 5 6 7 8

練習

01

コードは不要です。以下は本番で遭遇する3つの症状で、それぞれユーザー側から見える情報だけが与えられています:

レベル1: 1つの症状、区別のつかない3つの原因
  1. エージェントのリサーチレポートが docs/pricing-2024.md というファイルを引用していますが、そのファイルはリポジトリのどこにも存在しません。
  2. 同じアーカイブ作業が、昨日は12ターンで完了しました。今日は47ターンまで走ってから止まり、結果はほぼ同じでした。
  3. ユーザーが「このエラーがコードベースに何回出てくるか調べて」と尋ねたのに、エージェントはそのエラーが何を意味するかの長い説明を返しました。

各症状について:

  • 外からは区別のつかない候補原因を3つ以上挙げてください。
  • 各候補原因について、「これだと確定させ、他の2つを除外するには、どんな証拠を見る必要があるか」をフィールド単位まで具体的に書いてください。「ログを見る」は答えになりません。
完了基準 · ローカルでチェック
02

コードは不要です。以下は、どのバックエンドエンジニアも知っている4ステップのデバッグ手順です:

レベル2: 従来のデバッグ手順を一段ずつ解体する
text
1 再現             同じ入力でローカルにバグを再現する2 ブレークポイント 怪しい行で止める3 ステップ実行     1行ずつ進め、変数がいつおかしくなるかを見る4 修正して検証     直してもう一度走らせ、グリーンなら直った

各ステップについて、2つの問いに答えてください:

  • エージェントではなぜ機能しないのか。理由を具体的なメカニズム(非決定性、軌跡の分岐、証拠が隠れている、あるいはそもそも記録されていない)に接地させてください。
  • エージェントの世界での対応する代替は何か。方向性——「どんな証拠を、どの次元で整理して取るか」——だけ書けば十分です。具体的な実装は以降のレッスンの仕事です。
完了基準 · ローカルでチェック