Claude Agent SDK のエージェントは、ふつうは自由形式のテキストで結果を返します。この記事では公式ドキュメント「Get structured output from agents」に沿って、outputFormat(Python では output_format)に JSON Schema を渡し、ツールを使った複数ターンの作業の最後に、スキーマどおりに検証された JSON を受け取る方法を整理します。単発の API 呼び出しでの構造化出力は「Claude API の構造化出力」を参照してください。
構造化出力が役立つ場面
チャットとして読むだけなら自由形式のテキストで十分ですが、結果をプログラムで使うとなると話が変わります。公式ドキュメントはレシピアプリの例を挙げています。エージェントに Web でレシピを探させると、テキストのままでは「調理時間 15分」を数値に変換したり、材料と手順を切り分けたり、回答ごとに揺れる書式に対応したりと、アプリ側で解析する手間がかかります。
構造化出力を使うと、欲しいデータの形(名前・準備時間の分数・材料の配列・手順の配列など)を先に定義でき、その形の型付きデータがそのまま返ってきます。アプリのロジック、データベース、UI コンポーネントに直接渡せるのが利点です。
ポイントは、エージェントが途中で必要なツールを自由に使えることです。検索やファイル読み取り、コマンド実行を何ターン重ねても、最後に受け取るのはスキーマに合った JSON です。SDK は出力をスキーマで検証し、合わなければ再度出力を求めます。リトライの上限までに検証が通らなければ、構造化データではなくエラーの結果になります(後述)。
outputFormat の基本と設定項目
使い方は、データの形を JSON Schema で書き、query() のオプション outputFormat(TypeScript)または output_format(Python)に渡すだけです。エージェントが作業を終えると、結果メッセージ(result メッセージ)の structured_output フィールドに検証済みのデータが入ります。次は Anthropic について調べ、社名・設立年・本社所在地を返させる例です。
import { query } from "@anthropic-ai/claude-agent-sdk";
const schema = {
type: "object",
properties: {
company_name: { type: "string" },
founded_year: { type: "number" },
headquarters: { type: "string" }
},
required: ["company_name"]
};
for await (const message of query({
prompt: "Research Anthropic and provide key company information",
options: {
outputFormat: { type: "json_schema", schema: schema }
}
})) {
if (message.type === "result" && message.subtype === "success" && message.structured_output) {
console.log(message.structured_output);
}
}
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async for message in query(
prompt="Research Anthropic and provide key company information",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": schema}
),
):
if isinstance(message, ResultMessage) and message.structured_output:
print(message.structured_output)
オプションに渡すオブジェクトの項目は2つです。
| 項目 | 内容 |
|---|---|
type | 構造化出力では "json_schema" を指定する |
schema | 出力の構造を定義する JSON Schema オブジェクト。Zod の z.toJSONSchema(schema, { target: "draft-7" }) や Pydantic の .model_json_schema() から生成してもよい |
スキーマでは基本型(object・array・string・number・boolean・null)、enum、const、required、入れ子のオブジェクト、$ref 定義といった標準的な機能が使えます。細かな制限は Claude API 側の「JSON Schema limitations」が参照先として案内されています。
バージョンによる違いにも注意が必要です。公式ドキュメントによれば、JSON Schema として不正なスキーマは開始時に問題点を示すエラーで実行が失敗します。v2.1.205 より前は不正なスキーマが黙って無視され、構造化されていないテキストが返っていました。また "format": "email" のような format キーワードは注釈として受け付けられるだけで、SDK の検証では強制されません(v2.1.205 より前は format を含むスキーマ全体が不正扱いでした)。Python SDK のリファレンスでも、ClaudeAgentOptions の output_format は {"type": "json_schema", "schema": {...}} の形の dict で渡すと説明されています。
Zod と Pydantic で型安全にする
JSON Schema を手書きする代わりに、TypeScript なら Zod、Python なら Pydantic でスキーマを定義できます。どちらも JSON Schema を生成してくれるうえ、返ってきた値を型付きオブジェクトとして解析できるので、補完や型チェックが効きます。
注意点として、SDK はスキーマを JSON Schema draft-07 で検証するため、より新しいバージョンを宣言したスキーマは拒否されます。Zod は既定で draft 2020-12 を出力するので、変換時に target: "draft-7" を指定します。
import { z } from "zod";
import { query } from "@anthropic-ai/claude-agent-sdk";
const FeaturePlan = z.object({
feature_name: z.string(),
summary: z.string(),
steps: z.array(
z.object({
step_number: z.number(),
description: z.string(),
estimated_complexity: z.enum(["low", "medium", "high"])
})
),
risks: z.array(z.string())
});
// SDK が期待する draft-07 で変換する
const schema = z.toJSONSchema(FeaturePlan, { target: "draft-7" });
for await (const message of query({
prompt: "Plan how to add dark mode support to a React app. Break it into implementation steps.",
options: { outputFormat: { type: "json_schema", schema } }
})) {
if (message.type === "result" && message.subtype === "success" && message.structured_output) {
const parsed = FeaturePlan.safeParse(message.structured_output);
if (parsed.success) {
console.log(parsed.data.summary);
}
}
}
Python では Pydantic のモデルから model_json_schema() でスキーマを作り、結果を model_validate() で型付きオブジェクトに戻します。
from pydantic import BaseModel
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
class Step(BaseModel):
step_number: int
description: str
estimated_complexity: str # 'low', 'medium', 'high'
class FeaturePlan(BaseModel):
feature_name: str
summary: str
steps: list[Step]
risks: list[str]
async for message in query(
prompt="Plan how to add dark mode support to a React app. Break it into implementation steps.",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": FeaturePlan.model_json_schema()}
),
):
if isinstance(message, ResultMessage) and message.structured_output:
plan = FeaturePlan.model_validate(message.structured_output)
print(plan.summary)
公式ドキュメントには、ツールを複数使う例として「コードベースの TODO コメントを Grep で探し、Bash で git blame を引いて追加者を調べる」エージェントも載っています。このとき author と date は required に入れていません。git blame の情報が取れないファイルもあるため、取れた分だけ埋めて残りは省略させる設計です。
StructuredOutput ツール呼び出しの見分け方
outputFormat を設定すると、メッセージのストリームに、自分で定義していない StructuredOutput という名前のツール呼び出しが現れます。これは Claude Code がセッションに追加するツールで、エージェントはこれを呼んで答えを返します。システムの init メッセージの tools 配列にも載り、1回の呼び出しは次の2つのブロックで届きます。
StructuredOutputという名前のtool_useブロック。入力はエージェントがスキーマに合わせて作ったオブジェクト- 「Structured output provided successfully」と書かれた
tool_resultブロック
オブジェクトがスキーマに合わない場合は tool_result が is_error: true になり、エージェントは StructuredOutput をもう一度呼びます。そのため1回のクエリで複数の呼び出しが流れることがあります。検証済みのデータは tool_use ブロックからではなく、結果メッセージの structured_output から読みます。
アプリでツール呼び出しを画面表示やログに出している場合は、StructuredOutput の呼び出しを除外すると、この仕組みがエージェントの選んだ作業手順のように見えずに済みます。tool_result ブロックにはツール名がなく、対応する呼び出しの tool_use_id しか入っていないので、除外した呼び出しの id と突き合わせて一緒に外します。ストリームの扱い自体は「Agent SDK のストリーミング出力」を参照してください。
エラー処理と失敗を減らすコツ
スキーマに合う JSON をエージェントが作れないと、構造化出力は失敗します。スキーマが作業に対して複雑すぎる、作業自体があいまい、検証エラーを直そうとしてリトライ上限に達した、といった場合です。検証の失敗がなくても、モデルの自動フォールバックで完成済みの出力がストリームの途中で取り消され、再試行で置き換わらなければ同じエラーで終わることがあります。スキーマを疑う前に、エラー結果の errors リストでどちらの原因かを確認するよう公式は勧めています。
| subtype | 意味 |
|---|---|
success | 出力が生成され、検証も通った |
error_max_structured_output_retries | 複数回試しても有効な出力が残らなかった(検証の失敗、または再試行が成功しなかったフォールバックによる取り消し) |
Python SDK のリファレンスでは、ResultMessage の subtype は success・error_during_execution・error_max_turns・error_max_budget_usd・error_max_structured_output_retries のいずれかとされています。さらに、subtype が success なのに structured_output が空のまま終わることもあります(エージェントが構造化出力を出さずに完了した場合など)。これも失敗として扱い、「success かつ structured_output がある」ときだけ成功とみなすのが公式の例の書き方です。
async for message in query(
prompt="Extract contact info from the document",
options=ClaudeAgentOptions(
output_format={"type": "json_schema", "schema": contact_schema}
),
):
if isinstance(message, ResultMessage):
if message.subtype == "success" and message.structured_output:
print(message.structured_output)
elif message.subtype == "error_max_structured_output_retries":
print("Could not produce valid output")
else:
print("Run ended without a structured output")
なお単発の query() は、エラー結果を出したあとに例外を投げます。公式の例はループ全体を try で囲み、接続やプロセスの失敗(結果メッセージが出ない場合)も含めて受け止めています。
失敗を減らすコツとして、公式は次の3点を挙げています。
- スキーマを絞る: 深い入れ子で必須項目が多いスキーマほど満たしにくい。単純な形から始めて必要に応じて足す
- スキーマを作業に合わせる: 作業で必要な情報が手に入らない可能性があるなら、その項目は任意にする
- プロンプトを明確にする: あいまいな指示では、エージェントがどんな出力を作ればよいか判断しにくい
本ページは Get structured output from agents を中心に、Agent SDK reference - Python の記述を踏まえた非公式の日本語解説です(確認日 2026-10-26)。コード例は一部を簡略化しています。最新の仕様は公式ドキュメントを確認してください。