エージェントループの仕組み
メッセージライフサイクル、ツール実行、コンテキストウィンドウ、および SDK エージェントを支えるアーキテクチャを理解します。
Agent SDK を使用すると、Claude Code の自律型エージェントループを独自のアプリケーションに組み込むことができます。SDK はスタンドアロンパッケージで、ツール、権限、コスト制限、および出力をプログラムで制御できます。
TypeScript と Python の両方の SDK には、ネイティブな Claude Code バイナリがバンドルされているため、ほとんどのインストールでは別途 Claude Code をインストールする必要がありません。インストールが必要な場合については、クイックスタートのインストール注記を参照してください。
エージェントを開始すると、SDK は Claude Code を支える実行ループと同じものを実行します。Claude はプロンプトを評価し、ツールを呼び出してアクションを実行し、結果を受け取り、タスクが完了するまで繰り返します。このページでは、そのループ内で何が起こるかを説明し、エージェントを効果的に構築、デバッグ、最適化できるようにします。
ループの概要
すべてのエージェントセッションは同じサイクルに従います。
- プロンプトを受け取る。 Claude はプロンプト、システムプロンプト、ツール定義、および会話履歴とともにプロンプトを受け取ります。SDK はセッションメタデータを含むサブタイプ
"init"のSystemMessageを生成します。 - 評価して応答する。 Claude は現在の状態を評価し、どのように進めるかを決定します。テキストで応答したり、1 つ以上のツール呼び出しをリクエストしたり、その両方を行ったりできます。SDK はテキストブロックやツール呼び出しリクエストなどのコンテンツブロックごとに 1 つずつ、1 つ以上の
AssistantMessageオブジェクトを生成します。 - ツールを実行する。 SDK は要求された各ツールを実行し、結果を収集します。ツール結果の各セットは次の決定のために Claude にフィードバックされます。hooksを使用して、ツール呼び出しを実行前に傍受、変更、またはブロックできます。
- 繰り返す。 ステップ 2 と 3 がサイクルとして繰り返されます。Claude はツール呼び出しと結果の処理を続け、ツール呼び出しのない応答を生成するまで続きます。
- 結果を返す。 SDK は最終的な
AssistantMessage(テキスト応答、ツール呼び出しなし)を生成し、その後に最終テキスト、トークン使用量、コスト、およびセッション ID を含むResultMessageを生成します。
「ここにはどのようなファイルがありますか?」のような簡単な質問は、2 ~ 3 ターンで済む場合があります。Glob を呼び出すのに 1 ~ 2 ターン、結果を返して応答するのに 1 ターンです。「認証モジュールをリファクタリングしてテストを更新する」のような複雑なタスクでは、Claude が各結果を受けて調整しながら、多くのターンにわたって数十のツール呼び出しを連鎖させることがあります。
ターンとメッセージ
ターンはループ内の 1 往復です。Claude が出力を生成し、その出力にツール呼び出しが含まれている場合、SDK はそれらを実行し、結果は自動的に Claude にフィードバックされます。これはコードに制御を戻さずに発生します。Claude がツール呼び出しのない出力を生成するまでターンが続き、その時点でループが終了し、最終結果が配信されます。
プロンプト「Fix the failing tests in auth.ts」の完全なセッションがどのようなものかを考えてみましょう。
まず、SDK はプロンプトを Claude に送信し、セッションメタデータを含むSystemMessageを生成します。その後、ループが開始されます。
- ターン 1: Claude は
Bashを呼び出してnpm testを実行します。SDK はAssistantMessageとツール呼び出しを生成し、コマンドを実行し、出力(3 つの失敗)を含むUserMessageを生成します。 - ターン 2: Claude は
Readを呼び出してauth.tsとauth.test.tsを読み取ります。SDK は各呼び出しに対してAssistantMessageを生成し、ファイルの内容を返します。 - ターン 3: Claude は
Editを呼び出してauth.tsを修正し、Bashを呼び出してnpm testを再実行します。3 つのテストすべてが成功します。SDK は各呼び出しに対してAssistantMessageを生成します。 - 最終ターン: Claude はツール呼び出しのないテキストのみの応答を生成します。「Fixed the auth bug, all three tests pass now.」SDK はこのテキストを含む最終
AssistantMessageを生成し、その後、同じテキストとコストおよび使用量を含むResultMessageを生成します。
これは 4 ターンでした。3 つはツール呼び出し、1 つは最終テキストのみの応答です。
max_turns / maxTurns でループをキャップできます。ツール呼び出しを含む各ターンをカウントし、さらに最終的なテキストのみの応答のために 1 を加えてください。この追加のターンがない場合、実行はサブタイプ error_max_turns の ResultMessage で終了し、最終応答は得られません。たとえば、上記のループで max_turns=2 は編集ステップの前に停止していたでしょう。max_budget_usd / maxBudgetUsd を使用して、支出しきい値に基づいてターンをキャップすることもできます。
制限がない場合、ループは Claude が独自に終了するまで実行されます。これは適切にスコープされたタスクには問題ありませんが、オープンエンドのプロンプト(「improve this codebase」)では長時間実行される可能性があります。予算を設定することは、本番エージェントの良いデフォルトです。以下のターンと予算でオプションリファレンスを参照してください。
メッセージタイプ
ループが実行されると、SDK はメッセージのストリームを生成します。各メッセージは、ループのどのステージから来たかを示すタイプを持ちます。5 つのコアタイプは次のとおりです。
-
SystemMessage: セッションライフサイクルイベント。subtypeフィールドはそれらを区別します。"init":実行のセッションメタデータ。セッション起動中にSessionStartまたはSetupフックが実行される場合、そのフックライフサイクルメッセージはinitメッセージの前に到着します"compact_boundary":圧縮後に発火します"informational":ループからのプレーンテキストステータスバナー"worker_shutting_down":ホストが終了しているか Remote Control が切断されました
TypeScript では、
"init"以外の各サブタイプはSDKSystemMessageのサブタイプではなく、SDKMessageユニオン内の独自のタイプです。 -
AssistantMessage: 最終テキストのみの応答を含む、Claude の各応答のコンテンツブロックごとに生成されます。各メッセージは、テキストやツール呼び出しなどの単一のコンテンツブロックを持ち、1 つの応答からのメッセージは同じメッセージ ID を共有します。 -
UserMessage: 各ツール実行後、Claude に送り返されるツール結果コンテンツとともに生成されます。ループ中盤でストリーミングするユーザー入力に対しても生成されます。 -
StreamEvent: 部分メッセージが有効な場合のみ生成されます。生の API ストリーミングイベント(テキストデルタ、ツール入力チャンク)を含みます。ストリーム応答を参照してください。 -
ResultMessage: エージェントループの終了をマークします。最終テキスト結果、トークン使用量、コスト、およびセッション ID を含みます。subtypeフィールドをチェックして、タスクが成功したか制限に達したかを判断します。prompt_suggestionなどの少数の末尾システムイベントはその後に到着する可能性があるため、結果で中断するのではなく、ストリームを完了まで反復処理します。結果を処理するを参照してください。
これら 5 つのタイプは、エージェントループライフサイクル全体をカバーしています。両方の SDK は、レート制限ステータスやタスク通知などの観測可能性イベントも生成し、ループを駆動するために必須ではありません。完全なリストについては、Python メッセージタイプリファレンスと TypeScript メッセージタイプリファレンスを参照してください。
メッセージを処理する
処理するメッセージは、構築しているものによって異なります。
- 最終結果のみ:
ResultMessageを処理して、出力、コスト、およびタスクが成功したか制限に達したかを取得します。 - 進捗更新:
AssistantMessageを処理して、Claude が各ターンで何をしているか、どのツールを呼び出したかを確認します。 - ライブストリーミング: 部分メッセージを有効にする(Python では
include_partial_messages、TypeScript ではincludePartialMessages)して、リアルタイムでStreamEventメッセージを取得します。リアルタイムでストリーム応答を参照してください。
メッセージタイプをチェックする方法は SDK によって異なります。
- Python:
claude_agent_sdkからインポートされたクラスに対してisinstance()でメッセージタイプをチェックします(たとえば、isinstance(message, ResultMessage))。 - TypeScript:
type文字列フィールドをチェックします(たとえば、message.type === "result")。AssistantMessageとUserMessageは生の API メッセージを.messageフィールドでラップするため、コンテンツブロックはmessage.contentではなくmessage.message.contentにあります。
[accordion] 例:メッセージタイプをチェックして結果を処理する
import asyncio from claude_agent_sdk import query, AssistantMessage, ResultMessage, TextBlock, ToolUseBlock async def main(): try: async for message in query(prompt="Summarize this project"): if isinstance(message, AssistantMessage): # Each AssistantMessage carries one content block for block in message.content: if isinstance(block, TextBlock): print(f"Claude: {block.text}") elif isinstance(block, ToolUseBlock): print(f"Tool call: {block.name}") if isinstance(message, ResultMessage): if message.subtype == "success": print(message.result) else: print(f"Stopped: {message.subtype}") except Exception as error: # A single-shot query() raises after yielding an error result. If the # failure was an error result, the error subtype branches above have # already run; connection or process failures yield no result message. print(f"Session ended with an error: {error}") asyncio.run(main())import { query } from "@anthropic-ai/claude-agent-sdk"; try { for await (const message of query({ prompt: "Summarize this project" })) { if (message.type === "assistant") { // Each assistant message carries one content block for (const block of message.message.content) { if (block.type === "text") { console.log(`Claude: ${block.text}`); } else if (block.type === "tool_use") { console.log(`Tool call: ${block.name}`); } } } if (message.type === "result") { if (message.subtype === "success") { console.log(message.result); } else { console.log(`Stopped: ${message.subtype}`); } } } } catch (error) { // A single-shot query() throws after yielding an error result. If the // failure was an error result, the error subtype branches above have // already run; connection or process failures yield no result message. console.log(`Session ended with an error: ${error}`); }
ツール実行
ツールはエージェントにアクションを実行する機能を提供します。ツールがなければ、Claude はテキストでのみ応答できます。ツールを使用すると、Claude はファイルを読み取り、コマンドを実行し、コードを検索し、外部サービスと相互作用できます。
組み込みツール
SDK には Claude Code を支えるのと同じツールが含まれています。
| カテゴリ | ツール | 機能 |
|---|---|---|
| ファイル操作 | Read、Edit、Write |
ファイルを読み取り、変更、作成 |
| 検索 | Glob、Grep |
パターンでファイルを検索、正規表現でコンテンツを検索 |
| 実行 | Bash |
シェルコマンド、スクリプト、git 操作を実行 |
| Web | WebSearch、WebFetch |
Web を検索、ページを取得して解析 |
| 検出 | ToolSearch |
すべてをプリロードする代わりに、オンデマンドでツールを動的に検索してロード |
| オーケストレーション | Agent、Skill、AskUserQuestion、TaskCreate、TaskUpdate |
サブエージェントを生成、スキルを呼び出し、ユーザーに質問、タスクを追跡 |
モデルがタスク追跡ツールを取得しない場合、Claude Code は TaskCreate と TaskUpdate をオプトインした場合にのみ提供します。
組み込みツール以外に、以下を実行できます。
- 外部サービスを接続する MCP サーバー(データベース、ブラウザ、API)
- カスタムツールを定義する カスタムツールハンドラー
- プロジェクトスキルをロードする 設定ソース経由で再利用可能なワークフロー
ツール権限
Claude はタスクに基づいてどのツールを呼び出すかを決定しますが、それらの呼び出しの実行を許可するかどうかはユーザーが制御します。以下のオプションを使用して、ツールの自動承認、ツールのブロック、権限モードの設定を行います。
allowed_tools/allowedToolsリストされたツールを自動承認します。許可されたツールリストに["Read", "Glob", "Grep"]がある読み取り専用エージェントは、ネットワークパスからの読み取りを除き、プロンプトなしでそれらのツールを実行します。リストされていないツールは引き続き利用可能ですが、それらへの呼び出しで承認が必要な場合は、権限モードとcanUseToolにフォールスルーします。disallowed_tools/disallowedToolsリストされたツールをブロックします。他の設定に関係なく。ツールが実行される前にルールがチェックされる順序については、権限を参照してください。permission_mode/permissionMode必要な人間の監視の量を制御します。SDK は有効なモードと許可および拒否ルールを固定順序で評価します。詳細については、権限がどのように評価されるかを参照してください。利用可能なモードについては、権限モードを参照してください。
"Bash(npm *)" のようなルールで個別のツールをスコープすることもできます。これにより、特定のコマンドのみを許可できます。完全なルール構文については、権限を参照してください。
ツールが拒否されると、Claude はツール結果として拒否メッセージを受け取り、通常は別のアプローチを試みるか、進めなかったことを報告します。
並列ツール実行
Claude が単一のターンで複数のツール呼び出しをリクエストすると、両方の SDK はツールに応じて同時または順序に実行できます。読み取り専用ツール(Read、Glob、Grep、読み取り専用としてマークされた MCP ツール)は同時に実行できます。状態を変更するツール(Edit、Write、Bash)は競合を避けるために順序に実行されます。
カスタムツールはデフォルトで順序実行されます。カスタムツールの並列実行を有効にするには、その注釈で readOnlyHint を設定します。TypeScriptとPythonSDK の両方は MCP SDK からこのフィールド名を使用します。
ループの実行を制御する
ループのターン数、コスト、Claude の推論の深さ、ツールの実行前に承認を必要とするかどうかを制限できます。これらはすべて ClaudeAgentOptions(Python)/ Options(TypeScript)のフィールドです。
ターン数と予算
| オプション | 制御内容 | デフォルト |
|---|---|---|
最大ターン数(max_turns / maxTurns) |
ターンの最大数 | 制限なし |
最大予算(max_budget_usd / maxBudgetUsd) |
ループが停止する推定支出額 | 制限なし |
いずれかの制限に達すると、SDK は対応するエラーサブタイプ(error_max_turns または error_max_budget_usd)を持つ ResultMessage を返します。これらのサブタイプの確認方法については結果を処理するを、構文については ClaudeAgentOptions / Options を参照してください。
予算の上限はサブエージェントにも適用され、サブエージェントの支出も合計に含まれます。支出が上限に達すると、新たなサブエージェントの起動は Budget limit reached で失敗し、Claude Code はまだ実行中のバックグラウンドのサブエージェントをすべて停止します。この上限適用の動作には Claude Code v2.1.217 以降が必要です。
ストリーミング入力では、最大ターン数の制限によってターンが終了した時点でまだキューに入っているメッセージは、キューに残ったままになります。Claude Code はそのメッセージをそのターンの最後のモデル呼び出しには追加しません。代わりにそのメッセージのために新しいターンを開始し、そのターンでは最大ターン数のカウントが最初からやり直しになります。予算の合計はメッセージをまたいで累積し続け、支出が maxBudgetUsd に達すると、同じ会話内の以降のメッセージは error_max_budget_usd の結果で終了します。/clear を実行すると予算はリセットされます。
予算の余裕
各応答のコストは API が応答とともに返すトークン使用量から算出されるため、Claude Code はモデルの応答が届いた後に支出を max_budget_usd / maxBudgetUsd の上限と比較します。上限に達した応答自体は最後まで完了し、total_cost_usd に計上されます。そのため、支出は最大でその 1 つの応答のコストに加え、その時点でまだ実行中のサブエージェントが停止するまでに費やした分だけ上限を超える可能性があります。上限を設定する際は、この分の余裕を見込んでください。
effort レベル
effort オプションは、Claude がどの程度推論を行うかを制御します。effort レベルを低くすると、ターンあたりのトークン使用量が減り、コストが削減されます。すべてのモデルが effort パラメータをサポートしているわけではありません。サポートしているモデルについては Effort を参照してください。
| レベル | 動作 | 適した用途 |
|---|---|---|
"low" |
最小限の推論、高速な応答 | ファイルの検索、ディレクトリの一覧表示 |
"medium" |
バランスの取れた推論 | 日常的な編集、標準的なタスク |
"high" |
徹底的な分析 | リファクタリング、デバッグ |
"xhigh" |
より深い推論 | サポートされているモデルでのコーディングおよびエージェント型タスク |
"max" |
最大限の推論の深さ | 深い分析を必要とする複数ステップの問題 |
effort を設定しない場合、Claude Code は effort レベルを調整するで説明されている順序で effort レベルを自ら決定します。
[!NOTE]
effortは、各応答内でレイテンシとトークンコストを推論の深さと引き換えにするものです。拡張思考は出力にthinkingブロックを生成し、Python または TypeScript のThinkingConfigにあるdisplayフィールドで、そのテキストを受け取るかどうかを制御します。
ファイルの一覧表示や単一の grep の実行など、シンプルで範囲が明確なタスクを行うエージェントには、低い effort を使用してコストとレイテンシを削減してください。effort は、セッションに対してトップレベルの query() オプションで設定するか、サブエージェントごとに AgentDefinition の effort フィールドで設定してセッションレベルを上書きします。
権限モード
権限モードオプション(Python では permission_mode、TypeScript では permissionMode)は、エージェントがツールを使用する前に承認を求めるかどうかを制御します。
| モード | 動作 | ユースケース |
|---|---|---|
"default" |
承認が必要で、かつ許可ルールでカバーされていないツール呼び出しは、canUseTool コールバックをトリガーします。コールバックがない場合は拒否されます |
カスタムの承認コールバックを持つインタラクティブなアプリケーション |
"acceptEdits" |
作業ディレクトリ内のファイル編集と一般的なファイルシステムコマンド(mkdir、touch、mv、cp など)を自動承認します。その他の Bash コマンドはデフォルトのルールに従います |
Claude の編集を信頼し、プロトタイピング中や分離されたディレクトリでの作業時など、より速く反復したい場合 |
"plan" |
Claude は調査と計画を行います。Claude のファイルツールで行われる編集は、許可ルールに一致する場合でも canUseTool コールバックに送られます |
コードレビュー中や大きな変更の前など、Claude にまず計画を作成させたい場合 |
"dontAsk" |
確認を一切行いません。権限ルールで事前承認されたツールは実行され、作業ディレクトリ内のファイル読み取りなど、default モードで承認を必要としない呼び出しも実行されます。それ以外で確認が必要になる呼び出しはすべて拒否されますが、インストールした mod がそれらの呼び出しの一部を承認することは引き続き可能です。ネットワークパスからの読み取りなど、一部の呼び出しは許可していても拒否されます。その一覧については Don't ask モードを参照してください |
呼び出しを承認する人がいないヘッドレスエージェント |
"auto" |
モデル分類器を使用して、シェルコマンドやネットワークリクエストなどのアクションをレビューし、レビューした各アクションを許可またはブロックします。利用可能性と判定の順序については auto モードを参照してください | ツール使用に対する安全のためのガードレールを維持したい自律型エージェント |
"bypassPermissions" |
承認を求めずにツール呼び出しを実行します。これには allowedTools に記載していないツールの呼び出しも含まれます。一部の呼び出しは引き続き canUseTool コールバックに送られます。それらは Bypass permissions モードに記載されています。TypeScript SDK では、options に allowDangerouslySkipPermissions: true も必要です。Unix で root として実行している場合は使用できません。エージェントのアクションが重要なシステムに影響を与えない分離された環境でのみ使用してください |
CI、コンテナ、またはその他の分離された環境 |
インタラクティブなアプリケーションでは、"default" とツール承認コールバックを組み合わせて使用し、承認プロンプトを表示してください。開発マシン上でファイルを編集するエージェントには、"acceptEdits" を使用してください。"bypassPermissions" は CI、コンテナ、またはその他の分離された環境のために取っておいてください。詳細については権限を参照してください。
モデル
model オプションを設定して、セッションを実行するモデルを選択します。詳細についてはモデルを選択するを参照してください。
コンテキストウィンドウ
コンテキストウィンドウは、セッション中に Claude が利用できる情報の総量です。セッション内のターン間でリセットされません。すべてが蓄積されます。システムプロンプト、ツール定義、会話履歴、ツール入力、およびツール出力です。ターン間で同じままのコンテンツ(システムプロンプト、ツール定義、CLAUDE.md)は自動的にプロンプトキャッシュされ、繰り返されるプリフィックスのコストとレイテンシが削減されます。カスタムシステムプロンプトまたは append テキストがセッション間でキャッシュ再利用にどのように影響するかについては、システムプロンプトの変更を参照してください。
コンテキストを消費するもの
SDK でのコンテキストへの各コンポーネントの影響は次のとおりです。
| ソース | ロード時期 | 影響 |
|---|---|---|
| システムプロンプト | すべてのリクエスト | 小さい固定コスト、常に存在 |
| CLAUDE.md ファイル | セッション開始時、settingSources経由 |
すべてのリクエストで完全なコンテンツ(ただしプロンプトキャッシュされるため、最初のリクエストのみが完全なコストを支払う) |
| ツール定義 | Bash、Read、Edit などのコア組み込みツールはすべてのリクエスト。MCP ツールとその他の組み込みツールはオンデマンド(ツール検索がデフォルトで遅延させるため) | ツール検索を構成に記載されたケースでは、すべてのスキーマが事前にロードされます |
| 会話履歴 | ターン間で蓄積 | 各ターンで増加。プロンプト、応答、ツール入力、ツール出力 |
| スキル説明 | セッション開始時、設定ソース経由 | 短い要約。完全なコンテンツは呼び出し時のみロード |
大きなツール出力は大量のコンテキストを消費します。大きなファイルを読み取るか、詳細な出力を含むコマンドを実行すると、単一のターンで数千のトークンを使用できます。コンテキストはターン間で蓄積されるため、多くのツール呼び出しを含む長いセッションは、短いセッションよりもはるかに多くのコンテキストを構築します。
自動圧縮
コンテキストウィンドウが制限に近づくと、SDK は会話を自動的に圧縮します。古い履歴を要約してスペースを解放し、最新の交換と重要な決定を保持します。SDK はこれが発生したときにストリームで type: "system" と subtype: "compact_boundary" を含むメッセージを生成します(Python では SystemMessage。TypeScript では別の SDKCompactBoundaryMessage タイプです)。
圧縮は古いメッセージを要約に置き換えるため、会話の早い段階からの特定の指示は保持されない可能性があります。永続的なルールは初期プロンプトではなく CLAUDE.md に属します(settingSources経由でロード)。CLAUDE.md コンテンツはすべてのリクエストで再注入されるためです。
圧縮動作をいくつかの方法でカスタマイズできます。
- CLAUDE.md の要約指示: 圧縮機は他のコンテキストと同様に CLAUDE.md を読むため、要約時に保持する内容を指示するセクションを含めることができます。圧縮機は意図に基づいて一致するため、セクションヘッダーは自由形式です。
PreCompactフック: 圧縮が発生する前にカスタムロジックを実行します。たとえば、完全なトランスクリプトをアーカイブします。フックはtriggerフィールド(manualまたはauto)を受け取ります。hooksを参照してください。- 手動圧縮:
/compactをプロンプト文字列として送信して、オンデマンドで圧縮をトリガーします。この方法で送信されるコマンドは SDK 入力です。名前でコマンドをディスパッチを参照してください。
[accordion] 例:CLAUDE.md の要約指示
プロジェクトの CLAUDE.md にセクションを追加して、圧縮機に保持する内容を指示します。ヘッダー名は特別ではありません。明確なラベルを使用してください。
# Summary instructions When summarizing this conversation, always preserve: - The current task objective and acceptance criteria - File paths that have been read or modified - Test results and error messages - Decisions made and the reasoning behind them
コンテキストを効率的に保つ
長時間実行されるエージェントのいくつかの戦略。
- サブタスク用にサブエージェントを使用します。 サブエージェントは最終応答のみを返すため、メインエージェントのコンテキストはサブタスクのトランスクリプトではなく、要約の分だけ増加します。
- ツールを選別します。 すべてのツール定義はコンテキストスペースを取ります。
AgentDefinitionのtoolsフィールドを使用してサブエージェントを必要な最小セットにスコープします。 - MCP サーバーコストを監視します。 MCP ツール検索はデフォルトで MCP ツールスキーマを遅延させ、オンデマンドでロードします。ツール検索がオフの場合またはサポートされていないモデルと特定のプラットフォームでアップフロントロードにフォールバックした場合、各 MCP サーバーはすべてのツールスキーマをすべてのリクエストに追加するため、多くのツールを持つ少数のサーバーは、エージェントが何か作業を行う前に大量のコンテキストを消費できます。完全な構成については、ツール検索を構成を参照してください。
- ルーチンタスクに低い努力を使用します。 ファイルを読み取るか、ディレクトリをリストするだけで済むエージェント用に努力を
"low"に設定します。これはトークン使用量とコストを削減します。
機能ごとのコンテキストコストの詳細な内訳については、コンテキストコストを理解するを参照してください。
セッションと継続性
SDK との各インタラクションはセッションを作成または継続します。ResultMessage.session_id からセッション ID をキャプチャして(両方の SDK で利用可能)、後で再開します。TypeScript SDK は init SystemMessage の直接フィールドとしても公開します。Python では、SystemMessage.data にネストされています。
再開すると、以前のターンからの完全なコンテキストが復元されます。読み取られたファイル、実行された分析、および実行されたアクション。セッションをフォークして、元のセッションを変更せずに別のアプローチに分岐することもできます。
セッション再開、継続、フォークパターンの完全なガイドについては、セッション管理を参照してください。ステートレスコンテナまたはサーバーレスホスト全体でセッションを再開するには、session_store / sessionStore アダプターを渡して、SDK がトランスクリプトを独自のバックエンドにミラーリングし、別のホストがそれらを再開できるようにします。Claude Code サブプロセスは引き続きローカルディスクに最初に書き込みます。デュアルライトアーキテクチャを参照して、新しいセッションと比較してストアから再開された実行のどちらのコピーが存続するか、およびローカルコピーを一時的に保つ方法を確認してください。
[!NOTE]
Python では、
ClaudeSDKClientは複数の呼び出し間でセッション ID を自動的に処理します。詳細については、Python SDK リファレンスを参照してください。
結果を処理する
ループが終了すると、ResultMessage は何が起こったかを示し、出力を提供します。subtype フィールド(両方の SDK で利用可能)は、終了状態をチェックする主な方法です。
| 結果サブタイプ | 何が起こったか | result フィールドは利用可能か? |
|---|---|---|
success |
Claude は通常、タスクを完了しました | はい |
error_max_turns |
完了前に maxTurns 制限に達しました |
いいえ |
error_max_budget_usd |
完了前に maxBudgetUsd 制限に達しました |
いいえ |
error_during_execution |
エラーがループを中断しました(たとえば、キャンセルされたリクエスト) | いいえ |
error_max_structured_output_retries |
設定された再試行制限内で有効な構造化出力が生成されませんでした。すべての試行が検証に失敗したか、モデルフォールバックが成功した再試行なしで完了した出力を取り消しました | いいえ |
result フィールドは最終テキスト出力を保持し、success バリアントにのみ存在するため、読み取る前に常にサブタイプをチェックしてください。
すべての結果サブタイプは total_cost_usd、usage、num_turns、および session_id を持つため、コストを追跡し、エラー後でも再開できます。守るべき 2 つのことがあります:
- セッションクラッシュ後、最終結果は
error_during_executionであり、そのコストフィールドはゼロになる可能性があり、そのstop_reasonはnullです。プロセスはそれを発行した後に終了します。セッションクラッシュ後に合計を復旧するを参照してください。 - Python では、
total_cost_usd、usage、およびmodel_usageはオプションとして型付けされているため、読み取る前にNoneでないことを確認してください。
usage フィールドはメインエージェントループのみをカバーします。ツリー全体のトークンとコスト会計には、Python で modelUsage または model_usage を使用してください。usage フィールドの解釈の詳細については、コストと使用量の追跡を参照してください。
[!NOTE]
クエリがエラー結果で終了する場合:
- 単一ショットの
query()呼び出しは最終結果メッセージを生成し、その後Reached maximum number of turnsなどの失敗テキストを含むエラーを発生させます。発生は意図的です。コードがそれを超えて続行する必要がある場合は、ループを try ブロックでラップしてください。基盤となる Claude Code プロセスも 0 以外のコードで終了します。- ストリーミング入力セッションは生きたままで、メッセージを送信し続けることができます。ただし、セッションクラッシュ後は除きます。セッションクラッシュは最終
error_during_execution結果を発行し、プロセスを終了します。
結果には、モデルが最終ターンで生成を停止した理由を示す stop_reason フィールド(TypeScript では string | null、Python では str | None)も含まれます。一般的な値は end_turn(モデルが通常終了)、max_tokens(出力トークン制限に達した)、および refusal(モデルがリクエストを拒否)です。ループが生成したエラー結果では、stop_reason はループが終了する前の最後のアシスタント応答からの値を持ちます。セッションクラッシュ後に Claude Code が合成する結果は null を持ちます。
拒否を検出するには、stop_reason === "refusal"(TypeScript)または stop_reason == "refusal"(Python)をチェックしてください。完全なタイプについては、SDKResultMessage(TypeScript)または ResultMessage(Python)を参照してください。
Hooks
Hooksは、ループの特定のポイントで発火するコールバックです。ツールが実行される前、戻った後、エージェントが終了したときなど。一般的に使用されるフックは次のとおりです。
| フック | 発火時期 | 一般的な用途 |
|---|---|---|
PreToolUse |
ツール実行前 | 入力を検証、危険なコマンドをブロック |
PostToolUse |
ツール戻り後 | 出力を監査、副作用をトリガー |
UserPromptSubmit |
プロンプト送信時 | プロンプトに追加コンテキストを注入 |
Stop |
エージェント終了時 | 結果を検証、セッション状態を保存 |
SubagentStart / SubagentStop |
サブエージェント生成/完了時 | 並列タスク結果を追跡して集約 |
PreCompact |
コンテキスト圧縮前 | 要約前に完全なトランスクリプトをアーカイブ |
フックはエージェントのコンテキストウィンドウ内ではなく、アプリケーションプロセスで実行されるため、コンテキストを消費しません。フックはループをショートサーキットすることもできます。ツール呼び出しを拒否する PreToolUse フックはそれが実行されるのを防ぎ、Claude は代わりに拒否メッセージを受け取ります。
両方の SDK はすべての上記のイベントをサポートしています。TypeScript SDK には、Python がまだサポートしていない追加のイベントが含まれています。完全なイベントリスト、SDK ごとの利用可能性、および完全なコールバック API については、フックで実行を制御するを参照してください。
すべてをまとめる
この例は、このページの主要な概念を、失敗するテストを修正する単一のエージェントに組み合わせています。許可されたツール(自動承認されるため、エージェントが自律的に実行される)、プロジェクト設定、およびターンと推論努力の安全制限でエージェントを構成します。ループが実行されると、潜在的な再開のためにセッション ID をキャプチャし、最終結果を処理し、総コストを出力します。
単一の query() 呼び出しはエラー結果を生成した後に発生するため、ループは try ブロックでラップされており、制限に達したときにスクリプトがクリーンに終了します。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def run_agent():
session_id = None
try:
async for message in query(
prompt="Find and fix the bug causing test failures in the auth module",
options=ClaudeAgentOptions(
allowed_tools=[
"Read",
"Edit",
"Bash",
"Glob",
"Grep",
], # Listing tools here auto-approves them (no prompting)
setting_sources=[
"project"
], # Load CLAUDE.md, skills, hooks from current directory
max_turns=30, # Prevent runaway sessions
effort="high", # Thorough reasoning for complex debugging
),
):
# Handle the final result
if isinstance(message, ResultMessage):
session_id = message.session_id # Save for potential resumption
if message.subtype == "success":
print(f"Done: {message.result}")
elif message.subtype == "error_max_turns":
# Agent ran out of turns. Resume with a higher limit.
print(f"Hit turn limit. Resume session {session_id} to continue.")
elif message.subtype == "error_max_budget_usd":
print("Hit budget limit.")
else:
print(f"Stopped: {message.subtype}")
if message.total_cost_usd is not None:
print(f"Cost: ${message.total_cost_usd:.4f}")
except Exception as error:
# A single-shot query() raises after yielding an error result. If the
# failure was an error result, the error subtype branches above have
# already run; connection or process failures yield no result message.
print(f"Session ended with an error: {error}")
asyncio.run(run_agent())
import { query } from "@anthropic-ai/claude-agent-sdk";
let sessionId: string | undefined;
try {
for await (const message of query({
prompt: "Find and fix the bug causing test failures in the auth module",
options: {
allowedTools: ["Read", "Edit", "Bash", "Glob", "Grep"], // Listing tools here auto-approves them (no prompting)
settingSources: ["project"], // Load CLAUDE.md, skills, hooks from current directory
maxTurns: 30, // Prevent runaway sessions
effort: "high" // Thorough reasoning for complex debugging
}
})) {
// Save the session ID to resume later if needed
if (message.type === "system" && message.subtype === "init") {
sessionId = message.session_id;
}
// Handle the final result
if (message.type === "result") {
if (message.subtype === "success") {
console.log(`Done: ${message.result}`);
} else if (message.subtype === "error_max_turns") {
// Agent ran out of turns. Resume with a higher limit.
console.log(`Hit turn limit. Resume session ${sessionId} to continue.`);
} else if (message.subtype === "error_max_budget_usd") {
console.log("Hit budget limit.");
} else {
console.log(`Stopped: ${message.subtype}`);
}
console.log(`Cost: $${message.total_cost_usd.toFixed(4)}`);
}
}
} catch (error) {
// A single-shot query() throws after yielding an error result. If the
// failure was an error result, the error subtype branches above have
// already run; connection or process failures yield no result message.
console.log(`Session ended with an error: ${error}`);
}
エージェントが正常に完了すると、この例は Done: 行にエージェントの修正の概要を出力し、その後 Cost: $0.0312 のような行を出力します。
次のステップ
ループを理解したので、構築しているものに応じて、ここに行くべき場所があります。
- まだエージェントを実行していませんか? クイックスタートから始めて、SDK をインストールし、完全な例をエンドツーエンドで実行してください。
- プロジェクトにフックする準備ができていますか? CLAUDE.md、スキル、ファイルシステムフックをロードして、エージェントがプロジェクト規約に自動的に従うようにします。
- インタラクティブ UI を構築していますか? ストリーミングを有効にして、ループが実行されるときにライブテキストとツール呼び出しを表示します。
- エージェントが何をできるかについてより厳密な制御が必要ですか? 権限でツールアクセスをロックダウンし、フックを使用して、実行前にツール呼び出しを監査、ブロック、または変換します。
- 長時間または高コストのタスクを実行していますか? 隔離された作業をサブエージェントにオフロードして、メインコンテキストをリーンに保ちます。
- サービスとしてデプロイしていますか? コンテナおよびサーバーレスガイダンスについてはAgent SDK のホスティングを参照し、セッションを独自のバックエンドに永続化するにはセッションストレージを参照してください。
agentic ループのより広い概念的な図(SDK 固有ではない)については、Claude Code の仕組みを参照してください。Claude Code でループを設計するための実践的なガイド(ターンベースループからゴールベースループおよびプロアクティブループまで)については、ブログのループエンジニアリング:ループの開始を参照してください。