エージェントループ
エージェントループは、モデル呼び出し、ツール実行、結果の返却をつなぎます。while ループでモデルを呼び、要求されたツールを実行し、結果を会話へ追加して再びモデルを呼びます。ツール要求ではなくテキストが返ると、通常のループは終了します。実行基盤は無限実行を防ぐため、ターン数にも上限を設けます。
モデルは、要求するツール、引数、回答するタイミングを選びます。コードは返答を読んで分岐を実行します。ループ自体が問題の内容を理解する必要はありませんが、会話を保持し、失敗や上限到達を扱う必要があります。
ツール呼び出しでは要求と結果の返却を、ReAct ループではモデルの推論を見ます。このレッスンは call を含む制御フローを扱います。複数ステップの例は ReAct レッスンで記録した claude-opus-5-5 の実行、単発回答は同じモデルの別の記録を再生します。いずれも選定した実際の実行記録です。
エージェントループの仕組み
各ターンで四つの処理を行います。現在までの messages でモデルを呼び出し、返答の stop_reason を確認します。tool_use なら、その返答が要求したツールをすべて実行し(現在のモデルは複数をまとめて要求することがよくあります)、すべての結果を一つのメッセージとして messages に追加して次のターンへ進みます。この簡略化コードは、tool_use 以外の返答をテキストとして返します。
messages は、この実行でモデルが見られる履歴です。このレッスンが模すステートレスな API では、モデル呼び出し間で会話の状態が自動的に残るわけではありません。質問、助手の返答、ツールの結果を再送します。結果を省く、順序を変える、モデル自身のツール要求を保存しない、といった変更は次の呼び出しの文脈を変えます。
ここでは二つの通常の停止理由を示します。stop_reason: tool_use はツール要求、stop_reason: end_turn は回答を返す場合です。コードはこのフラグを読み、テキストが完了したように見えるかを推測して先にループを切りません。
モデルがツールを要求し続けると、while は終了しません。そのため、本番の実行基盤は上限を設けるべきです。このレッスンでは for turn in range(max_turns) を使います。エージェント SDK も、最大ターン数や最大反復回数として同じつまみを提供しています。回答前に上限へ達すると、強制終了して None を返します。上限到達は実行基盤の制御であり、タスクを完了したという意味ではありません。
エージェントループのステップ
初期設定は multi-step タスク、max_turns = 10 です。質問は「Aurora Run の監督は何年生まれか」です。映画を調べて監督を特定し、その出生年を調べます。
- 開始:
messagesにはユーザーの質問だけがあり、turn = 0です。 - ターン 0、呼び出し:モデルは
stop_reason: tool_useとsearch["Aurora Run"]を返します。助手のメッセージをmessagesに追加します。 - 確認:
stop_reasonがtool_useなので、ツール実行へ進みます。 - 実行:コードが検索を行います。最初のページは、Aurora Run が Henrik Dahl による 1998 年の小説で、作者は 1951 年生まれ、2011 年の映画の監督は Mara Voss だと説明します。
1951は小説家の出生年であり、質問が求める監督の年ではありません。 - 追加:結果を
messagesに入れ、turnを 1 に進めて再びモデルを呼びます。 - ターン 1、呼び出し:モデルは再び
stop_reason: tool_useを返し、search["Mara Voss"]を要求します。無関係な1951を答えにしません。 - 確認、実行、追加:二番目のページは、Mara Voss が 1974 年生まれのノルウェーの映画監督だと説明します。結果を
messagesに入れ、turnを 2 に進めます。 - ターン 2、呼び出し:二つの結果を見たモデルは、テキスト
1974とstop_reason: end_turnを返します。 - 確認:
end_turnを読み、回答を返す分岐へ進みます。 - 終了:
"1974"を返します。ツール呼び出し二回に対し、モデル呼び出しは三回です。
one-shot に切り替えると、初期質問はフランスの首都です。モデルは最初の呼び出しで stop_reason: end_turn · "Paris" を返します。ツール要求も二回目のターンもなく、同じループが一回で終了します。
複数ステップのタスクで max_turns を 2 にすると、ターン 0 と 1 の後に for の範囲を使い切ります。三回目のモデル呼び出しは行えません。会話に必要な資料がそろっていても、結果は None です。赤いメーターは上限到達を示し、回答が得られたことは示しません。
モデル呼び出しの回数
この実行では、どの返答もツールを一つだけ要求します。そのため、N 回のツール呼び出しを終えてから回答するには、N + 1 回のモデル呼び出しが必要です。複数ステップの例は二ツール・三呼び出し、単発回答は零ツール・一呼び出しです。モデルが一つの返答で互いに独立した複数のツールを要求すると、それらは一回の呼び出しを共有します。一般には、N はツールの個数ではなくツール要求のラウンド数です。ここでの二回の検索は一つのラウンドにまとめられません。二回目の検索語が一回目の結果に依存するからです。合計は max_turns を超えません。
各呼び出しで messages 全体を再送します。ツールの結果と助手のメッセージが増えるため、後のリクエストは通常、前より長いコンテキストを含みます。長い実行の費用は、呼び出し回数だけでなく、各回で送る内容の量にも依存します。会話をサーバー側に保存し、ID で呼び出しをつなげる API もありますが、各呼び出しが読むのは増え続ける文脈全体です。
境界条件
- ツールが結果を見つけない:“No matching page found.” なども、
messagesへ追加できる観察結果です。モデルはそれをもとに検索を変えられます。命中も未発見も、同じ結果返却の経路を使います。 - 最初に回答する:
stop_reasonがすぐend_turnになり、一回で終了します。ツールは使いません。 - 上限へ達する:
max_turnsまでツール要求が続くとNoneを返します。上限内で回答を得られなかったと示し、答えを作りません。 - 不正なツール要求:存在しないツールや解析できない引数は、実行層が捕捉してエラー結果を返します。モデルは次のターンで修正できます。
- ほかの停止理由:返答は、出力トークンの上限に達した場合(
max_tokens。含まれるツール要求が途中で切れていることがあります)、モデルが断った場合(refusal)、サーバー側ツールの長いターンが一時停止した場合(pause_turn。会話を送り返すと再開します)にも止まります。このレッスンの!= "tool_use"の判定は、三つとも最終回答として返してしまいます。本番のループはそれぞれを扱います。
よくある間違い
- テキストの印象で先に終了する:通常の分岐は
stop_reasonに従います。完了したように見えるだけで中断すると、モデルが求めた後続処理を省いてしまいます。 - タスクの推論をループに固定する:問題ごとにツール選択の
if/elseを書くと、汎用の実行基盤が固定フローになります。モデルが要求を選び、実行基盤が実行と制約を担当します。 - 呼び出し間が無状態であることを忘れる:ツール結果の削除、メッセージの並べ替え、助手要求の保存漏れは、次の会話を不完全にします。
- ツール実行後に
stop_reasonを調べる:まずツール要求か確認します。順序を逆にすると、最終回答をツール呼び出しとして扱う場合があります。 - 並列の結果を一つずつ返す:一つの返答が複数のツールを要求したら、すべて実行し、次の呼び出しの前にすべての結果を一つのメッセージで返します。ツール呼び出しの結果が欠けた要求は、多くの API で拒否されます。
max_turnsを設けない:モデルがいつか停止する保証はありません。繰り返し要求を上限で制御します。
このレッスンの簡略化
ここで扱うのは基本の制御フローです。実用の実行基盤には、ストリーミング応答、独立したツール呼び出しの並行実行、モデル要求のエラー処理と再試行、ほかの停止理由の処理、そして messages の増加に合わせた文脈管理が必要です。文脈ウィンドウが埋まる前に、古いツール結果を消したり、以前のターンを要約に圧縮したりします。このループを自分で書くとは限りません。SDK のツールランナーやエージェントフレームワークが代わりに回し、ホスト型のエージェントサービスは提供側でループを実行します。
記録に使った検索ツールは四ページの資料だけを扱います。「呼び出し、確認、実行、追加、再呼び出し」は理解の出発点であり、本番の実装仕様全体ではありません。
続けて学ぶ
同じ手法につながるレッスンや、次の概念への入口です。
鍵マークのあるレッスンの操作には有料アクセスが必要です。解説はすべて無料で読めます。
誤りや気になる点を見つけましたか? フィードバックを送る。