Claude Agent SDK でエージェントを起動すると、Claude Code と同じ実行ループが動きます。Claude がプロンプトを評価し、ツールを呼んで結果を受け取り、作業が終わるまでそれを繰り返す仕組みです。ループの中で何が起きているかを知っておくと、エージェントの作り込みやデバッグ、コストの最適化がしやすくなります。
この記事では、公式ドキュメント「How the agent loop works」に沿って、ターンとメッセージの流れ、ツールの実行と権限、ループを制御するオプション、コンテキストの消費と自動コンパクション、ResultMessage による結果の判定を整理します。SDK の導入から始める場合は「Claude Agent SDK でエージェントを構築する」を先に読んでください。
ループの流れとターン
どのセッションも同じサイクルをたどります。
- プロンプトを受け取る:Claude はプロンプトと一緒に、システムプロンプト、ツール定義、会話履歴を受け取ります。SDK はセッションのメタデータを持つ、サブタイプ
"init"のSystemMessageを返します。 - 評価して応答する:Claude は現在の状態を評価し、テキストで答えるか、1つ以上のツール呼び出しを要求するか、その両方を行います。SDK はコンテンツブロックごとに
AssistantMessageを返します。 - ツールを実行する:SDK が要求されたツールを実行して結果を集め、次の判断のために Claude へ戻します。実行前にフックで横取り・変更・ブロックすることもできます。
- 繰り返す:2と3がサイクルとして繰り返されます。1周が1ターンです。ツール呼び出しを含まない応答が出るまで続きます。
- 結果を返す:最後にツール呼び出しを含まないテキストの
AssistantMessageが届き、続いて最終テキスト、トークン使用量、コスト、セッション ID を持つResultMessageが届きます。
ターンの数え方の例
ターンは、Claude がツール呼び出しを含む出力を出し、SDK がそれを実行し、結果が自動で Claude に戻るまでの1往復です。この間、制御はあなたのコードに戻りません。公式の例「auth.ts の失敗しているテストを直して」では、次のように進みます。
| ターン | Claude の動き |
|---|---|
| 1 | Bash で npm test を実行し、3件の失敗を受け取る |
| 2 | Read で auth.ts と auth.test.ts を読む |
| 3 | Edit で auth.ts を直し、Bash でテストを再実行して全件成功 |
| 最終 | ツールを呼ばないテキストだけの応答を返す |
ツール呼び出しのある3ターンと、テキストだけの最終応答で計4ターンです。max_turns / maxTurns で上限を付けられますが、数えるのはツールを使うターンだけです。たとえば max_turns=2 なら、上の例は編集の前で止まります。上限を付けなければ Claude が自分で終えるまで回り続けるため、「このコードベースを改善して」のような範囲の広いプロンプトでは長引くことがあります。公式は、本番のエージェントでは予算を設定するのがよい既定だとしています。
5つのメッセージ型
ループが進むあいだ、SDK はメッセージのストリームを返します。中心になる型は次の5つです。
| 型 | 内容 |
|---|---|
SystemMessage | セッションのライフサイクルイベント。subtype で区別し、"init"(セッションのメタデータ)、"compact_boundary"(コンパクション後)、"informational"(ループからの状態表示)、"worker_shutting_down"(ホストの終了や Remote Control の切断)がある |
AssistantMessage | Claude の応答のコンテンツブロックごとに1つ。テキストやツール呼び出しを1ブロックずつ持ち、同じ応答から出たメッセージはメッセージ ID を共有する |
UserMessage | ツール実行ごとに、Claude へ戻すツール結果を持って届く。ループ途中でストリーミング入力したユーザー入力でも届く |
StreamEvent | 部分メッセージを有効にしたときだけ届く、API の生のストリーミングイベント(テキストの差分、ツール入力の断片) |
ResultMessage | ループの終わり。最終テキスト、トークン使用量、コスト、セッション ID を持ち、subtype で成功か上限到達かを判定する |
TypeScript では、"init" 以外のシステムメッセージは SDKSystemMessage のサブタイプではなく、SDKMessage ユニオンの独立した型です。また ResultMessage のあとにも prompt_suggestion などのシステムイベントが少し届くことがあるため、結果を受け取った時点で break せず、ストリームを最後まで回します。
何を拾うかは作るものによる
- 最終結果だけ欲しい:
ResultMessageで出力・コスト・成否を見る - 進捗を見せたい:
AssistantMessageで各ターンの動きと呼ばれたツールを見る - ライブ表示したい:Python は
include_partial_messages、TypeScript はincludePartialMessagesを有効にしてStreamEventを受け取る
型の判定は、Python では claude_agent_sdk から import したクラスに対する isinstance()、TypeScript では type 文字列(例 message.type === "result")で行います。TypeScript の AssistantMessage と UserMessage は生の API メッセージを .message に包んでいるので、コンテンツブロックは message.content ではなく message.message.content にあります。
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):
# AssistantMessage は1つのコンテンツブロックを持つ
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:
# 単発の query() はエラー結果を返したあとに例外を投げる
print(f"Session ended with an error: {error}")
asyncio.run(main())
ツールの実行と権限
SDK には Claude Code と同じ組み込みツールが入っています。
| 分類 | ツール | できること |
|---|---|---|
| ファイル操作 | Read、Edit、Write | ファイルの読み取り・変更・作成 |
| 検索 | Glob、Grep | パターンでファイルを探す、正規表現で中身を検索する |
| 実行 | Bash | シェルコマンド、スクリプト、git 操作 |
| Web | WebSearch、WebFetch | Web 検索、ページの取得と解析 |
| 探索 | ToolSearch | すべてを先読みせず、必要なツールをその場で探して読み込む |
| 統括 | Agent、Skill、AskUserQuestion、TaskCreate、TaskUpdate | サブエージェントの起動、スキルの呼び出し、ユーザーへの質問、タスクの追跡 |
このほか、MCP サーバーで外部サービスをつなぐ、カスタムツールを定義する、setting sources でプロジェクトのスキルを読み込む、といった拡張ができます。
何を実行させるかは3つのオプションで決める
allowed_tools/allowedTools:列挙したツールを自動承認する。たとえば["Read", "Glob", "Grep"]なら読み取り専用のエージェントになる。列挙していないツールも使える状態のままで、承認が必要な呼び出しは権限モードとcanUseToolに回るdisallowed_tools/disallowedTools:列挙したツールを、ほかの設定に関係なくブロックするpermission_mode/permissionMode:人の監督をどこまで入れるかを決める(次の節を参照)
"Bash(npm *)" のようなルールで、特定のコマンドだけ許可することもできます。拒否されたツール呼び出しは、Claude に拒否メッセージがツール結果として返り、Claude はたいてい別の方法を試すか、続けられなかったと報告します。詳しい評価順は「Agent SDK の権限設定」を参照してください。
並列実行
1ターンで複数のツール呼び出しが要求されたとき、Read、Glob、Grep、読み取り専用と印の付いた MCP ツールは同時に実行できます。状態を変える Edit、Write、Bash は衝突を避けるため順番に実行されます。カスタムツールは既定で順番に実行され、並列にしたい場合はアノテーションで readOnlyHint を設定します。
ループを制御するオプション
ターン数、コスト、推論の深さ、ツールの承認は、Python の ClaudeAgentOptions、TypeScript の Options のフィールドで指定します。
ターンと予算
| オプション | 制御するもの | 既定 |
|---|---|---|
max_turns / maxTurns | ツールを使う往復の最大数 | 上限なし |
max_budget_usd / maxBudgetUsd | ループを止める推定支出額(USD) | 上限なし |
どちらかに達すると、error_max_turns または error_max_budget_usd のサブタイプを持つ ResultMessage が返ります。予算の上限にはサブエージェントの支出も含まれ、上限に達するとそれ以降のサブエージェントの起動は Budget limit reached で失敗し、動いているバックグラウンドのサブエージェントも止められます(この動作は Claude Code v2.1.217 以降)。
支出の比較は、モデルの応答が届いたあとに、API が返すトークン使用量から計算して行われます。そのため上限に達した応答自体は最後まで完了してコストに数えられ、支出はその1応答分(と、その時点で動いていたサブエージェントが止まるまでの分)だけ上限を超えることがあります。上限には余裕を持たせて設定します。
effort(推論の深さ)
| 値 | 動き | 向いている作業 |
|---|---|---|
"low" | 最小限の推論で速く答える | ファイルの検索、ディレクトリの一覧 |
"medium" | バランスのとれた推論 | 定型の編集、標準的な作業 |
"high" | 入念な分析 | リファクタリング、デバッグ |
"xhigh" | より深い推論 | 対応モデルでのコーディング・エージェント作業 |
"max" | 最大の推論深度 | 深い分析が要る複数手順の問題 |
effort に対応しないモデルもあります。effort を指定しない場合は Claude Code が自分で決めます。effort は拡張思考(thinking ブロックを出す機能)とは独立しており、effort: "low" で拡張思考を有効にすることも、effort: "max" で無効にすることもできます。セッション全体は query() のオプションで、サブエージェントごとには AgentDefinition の effort で上書きできます。
permissionMode(権限モード)
| モード | 動き |
|---|---|
"default" | 許可ルールにない承認待ちの呼び出しは canUseTool コールバックに回る。コールバックがなければ拒否 |
"acceptEdits" | ファイル編集と mkdir・touch・mv・cp などの一般的なファイル操作コマンドを自動承認。ほかの Bash コマンドは通常どおり |
"plan" | ソースファイルを編集せず調査と計画だけを行う。ファイル編集は自動承認されず canUseTool に回る |
"dontAsk" | 一切確認しない。権限ルールで事前承認されたものと、承認不要の呼び出しだけが動き、確認が必要になるものはすべて拒否 |
"auto" | モデルの分類器がシェルコマンドやネットワーク要求などを審査し、1件ずつ許可かブロックを決める |
"bypassPermissions" | 明示的な ask ルールなどの例外を除き、許可されたツールを確認なしで実行。TypeScript では allowDangerouslySkipPermissions: true も必要。Unix の root では使えない |
公式の目安は、対話型のアプリなら "default" と承認コールバック、手元の開発機で自律的に動かすなら "acceptEdits"、"bypassPermissions" は CI やコンテナなど、エージェントの操作が大事なシステムに影響しない隔離環境に限る、というものです。使うモデルは model オプションで選びます。
コンテキストと自動コンパクション
コンテキストウィンドウは、セッション中に Claude が参照できる情報の総量で、ターンをまたいでもリセットされません。システムプロンプト、ツール定義、会話履歴、ツールの入出力がすべて積み上がります。ターン間で変わらない部分(システムプロンプト、ツール定義、CLAUDE.md)は自動でプロンプトキャッシュされ、繰り返し部分のコストとレイテンシが下がります。
| 消費元 | 読み込まれる時点 | 影響 |
|---|---|---|
| システムプロンプト | 毎リクエスト | 小さな固定コスト |
| CLAUDE.md | セッション開始時(settingSources 経由) | 毎リクエストに全文が入るが、キャッシュされるので全額がかかるのは最初だけ |
| ツール定義 | Bash・Read・Edit などの中核ツールは毎リクエスト。MCP ツールなどはツール検索で必要時に | ツール検索が無効な構成ではすべてのスキーマが先に読み込まれる |
| 会話履歴 | ターンごとに蓄積 | プロンプト、応答、ツールの入出力で増え続ける |
| スキルの説明 | セッション開始時 | 短い要約だけ。本文は呼び出したときに読み込まれる |
大きなファイルの読み取りや出力の多いコマンドは、1ターンで数千トークンを使うことがあります。
自動コンパクション
コンテキストが上限に近づくと、SDK は古い履歴を要約して空きを作ります。直近のやり取りと重要な決定は残ります。このときストリームに type: "system"、subtype: "compact_boundary" のメッセージが流れます(Python では SystemMessage、TypeScript では独立した SDKCompactBoundaryMessage 型)。
要約で置き換えられるため、会話の初めに出した指示が残らないことがあります。守らせ続けたいルールは最初のプロンプトではなく CLAUDE.md に書きます。CLAUDE.md は毎リクエストに入るからです。コンパクションの調整方法は次の3つです。
- CLAUDE.md に「要約するときに残すもの」を書く。見出し名は自由で、意図で読み取られる
PreCompactフックで、要約の前に全文のトランスクリプトを保存するなどの処理を入れる。フックはtrigger(manualかauto)を受け取る- プロンプトとして
/compactを送り、任意のタイミングで実行する
# 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 サーバーのコストに注意する。ツール検索が無効、または先読みに戻っている構成では、サーバーごとに全ツールのスキーマが毎リクエストに入る
- ファイルを読むだけ、一覧を出すだけの作業は
effortを"low"にする
結果の扱い
ループが終わると、ResultMessage の subtype で何が起きたかを判定します。
| subtype | 意味 | result の有無 |
|---|---|---|
success | 正常に完了した | あり |
error_max_turns | 完了前に maxTurns に達した | なし |
error_max_budget_usd | 完了前に maxBudgetUsd に達した | なし |
error_during_execution | リクエストの取り消しなど、エラーでループが中断した | なし |
error_max_structured_output_retries | 設定した再試行回数内に有効な構造化出力が得られなかった | なし |
result は success のときだけあるので、必ず先に subtype を確認します。一方、total_cost_usd、usage、num_turns、session_id はどのサブタイプにもあり、エラー後でもコストを記録したりセッションを再開したりできます。Python ではこれらのうち total_cost_usd、usage、model_usage が Optional 型なので、None でないことを確かめてから読みます。usage はメインのループ分だけなので、サブエージェントを含めた集計には modelUsage(Python は model_usage)を使います(「Agent SDK でコストと使用量を追う」も参照)。
エラー結果のあとの動き
- 単発の
query()は最終の結果メッセージを返したあと、Reached maximum number of turnsのような失敗理由を含むエラーを投げます。これは意図された動作なので、続けて処理したいならループを try ブロックで囲みます - ストリーミング入力のセッションは生きたままで、メッセージを送り続けられます。ただしセッションがクラッシュした場合は、最後に
error_during_executionを出してプロセスが終了します
stop_reason には最終ターンでモデルが生成を止めた理由が入り、代表的な値は end_turn(正常終了)、max_tokens(出力トークン上限)、refusal(モデルが依頼を断った)です。拒否を検出するには stop_reason == "refusal" を確認します。クラッシュ後に合成された結果では null になります。セッション ID は ResultMessage.session_id から取り、あとで再開できます(「Agent SDK のセッション」を参照)。
まとめの例
ツールを自動承認し、プロジェクト設定を読み込み、ターン上限と effort を付けて失敗しているテストを直させる公式の例です。結果のサブタイプごとに処理を分け、コストを表示します。
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"], // 列挙したツールは確認なしで実行
settingSources: ["project"], // カレントディレクトリの CLAUDE.md・スキル・フックを読み込む
maxTurns: 30, // 暴走を防ぐ
effort: "high" // 複雑なデバッグなので入念に推論させる
}
})) {
// あとで再開できるようにセッション ID を保存
if (message.type === "system" && message.subtype === "init") {
sessionId = message.session_id;
}
if (message.type === "result") {
if (message.subtype === "success") {
console.log(`Done: ${message.result}`);
} else if (message.subtype === "error_max_turns") {
// ターンを使い切った。上限を上げて再開する
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) {
// 単発の query() はエラー結果を返したあとに例外を投げる
console.log(`Session ended with an error: ${error}`);
}
本ページは How the agent loop works の内容に基づく非公式の日本語解説で、オプション名と ResultMessage のサブタイプは Agent SDK reference - Python の ClaudeAgentOptions と ResultMessage の定義と照合しています(確認日 2026-10-23)。ツール名・フィールド名は公式表記のまま記載しています。最新の仕様は公式ドキュメントを確認してください。