Microsoft Foundry のモデルカタログでデプロイ可能な Claude モデルにおいて、Structured Outputs が使えるようになりました。この Notebook では、Structured Outputs の二つの柱を段階的に検証します。
-
JSON outputs:
output_config.formatにより最終テキストを JSON Schema に適合させる -
Strict tool use: ツール定義の
strict: trueによりツール入力を JSON Schema に適合させる
MEMO
Microsoft Foundry の Structured Outputs は、Claude Opus 5 の Hosted on Azure と Hosted on Anthropic infrastructure の両方で利用できます。
Structured Outputs が保証するのは、通常完了時の JSON 構文とスキーマ適合です。抽出値の事実性、業務上の正しさ、安全上の拒否、出力上限による打ち切りまでは保証しません。
1. 基本的な JSON Schema 出力
通常の「JSON で答えて」という指示は、JSON の構文や必須項目を生成時には保証しません。JSON outputs はスキーマを文法へコンパイルし、制約付きデコードで生成可能な次トークンを絞ります。
この実験では、生の JSON Schema を output_config.format に渡します。返される JSON は text content block 内の文字列です。処理順は次のとおりです。
-
stop_reasonを検査する - JSON を解析する
- 独立した
jsonschema検証を行う - 値の業務バリデーションを行う
初めて使うスキーマでは文法コンパイルの追加遅延が発生し、コンパイル結果は最終利用から最大24時間キャッシュされます。
ARTICLE_SCHEMA = {
"type": "object",
"properties": {
"name": {"type": "string", "description": "記事の短い名称"},
"summary": {"type": "string", "description": "入力に忠実な要約"},
"keywords": {
"type": "array",
"items": {"type": "string"},
"minItems": 1,
},
},
"required": ["name", "summary", "keywords"],
"additionalProperties": False,
}
source_text = (
"Contoso は製造設備の予知保全システムを試験導入した。"
"振動センサーの時系列データを分析し、異常の兆候を保全担当者へ通知する。"
"初期評価では計画外停止時間が18%減少した。"
)
article_message = client.messages.create(
model=MODEL,
max_tokens=2048,
messages=[
{
"role": "user",
"content": f"次の文章だけを根拠に、名称、要約、キーワードを抽出してください。\n\n{source_text}",
}
],
output_config={
"effort": "low",
"format": {"type": "json_schema", "schema": ARTICLE_SCHEMA},
},
)
# 型は Python の str で、その文字列の内容が JSON
print(article_message.content[0].text)
# json.loads を堅牢にした Util Func を用意
article_payload = parse_and_validate_message(article_message, ARTICLE_SCHEMA)
print(json.dumps(article_payload, ensure_ascii=False, indent=2))
出力結果
{
"name": "Contoso予知保全システム試験導入",
"summary": "Contosoは製造設備向けの予知保全システムを試験導入した。振動センサーの時系列データを分析し、異常の兆候を保全担当者に通知する仕組みで、初期評価では計画外停止時間が18%減少した。",
"keywords": [
"Contoso",
"予知保全",
"振動センサー",
"時系列データ",
"異常検知",
"計画外停止時間",
"製造設備"
]
}
2. Pydantic による型安全な解析
client.messages.parse(..., output_format=Model) は、Pydantic モデルから JSON Schema を生成し、API 応答を同じモデルへ解析します。成功時は message.parsed_output から型付きオブジェクトを取得できます。
Python SDK は、Pydantic が出力したスキーマを API が扱えるサブセットへ変換します。例えば、未対応の minimum や maxLength を送信スキーマから除き、制約を説明文へ移し、受信後に元の Pydantic 制約で再検証します。このため、API が生成時に強制した制約と SDK が受信後に検証した制約を区別することが重要です。
parse() の output_format は Python SDK の便宜的な引数であり、ネットワーク経由で API に実際に送信される HTTP 要求の JSON上では現行の output_config.format に変換されます。
class ArticleSummary(BaseModel):
model_config = ConfigDict(extra="forbid")
name: str = Field(description="記事の短い名称")
summary: str = Field(description="入力に忠実な要約")
keywords: list[str] = Field(min_length=1)
typed_message = client.messages.parse(
model=MODEL,
max_tokens=2048,
messages=[
{
"role": "user",
"content": f"次の文章だけを根拠に構造化してください。\n\n{source_text}",
}
],
output_format=ArticleSummary,
)
if typed_message.stop_reason in {"refusal", "max_tokens", "model_context_window_exceeded"}:
raise StructuredResponseError(f"型付き解析を確定できません: {typed_message.stop_reason}")
if typed_message.parsed_output is None:
raise StructuredResponseError("SDK が parsed_output を生成できませんでした。")
typed_article: ArticleSummary = typed_message.parsed_output
print(type(typed_article).__name__)
print(typed_article.model_dump_json(indent=2))
出力結果
{
"name": "Contoso 予知保全システム試験導入",
"summary": "Contoso は製造設備向けの予知保全システムを試験導入した。振動センサーから得られる時系列データを分析し、異常の兆候を保全担当者へ通知する仕組みである。初期評価では計画外停止時間が18%減少した。",
"keywords": [
"Contoso",
"予知保全",
"製造設備",
"振動センサー",
"時系列データ",
"異常検知",
"計画外停止時間",
"試験導入"
]
}
未対応のスキーマ
JSON スキーマを変換し、APIの要件に準拠するようにします。
class QualityScore(BaseModel):
model_config = ConfigDict(extra="forbid")
label: str = Field(min_length=3, max_length=40)
score: int = Field(ge=0, le=100)
original_schema = TypeAdapter(QualityScore).json_schema()
api_compatible_schema = transform_schema(original_schema)
print("Pydantic が生成した元スキーマ:")
print(json.dumps(original_schema, ensure_ascii=False, indent=2))
print("\nSDK が API 送信用に変換したスキーマ:")
print(json.dumps(api_compatible_schema, ensure_ascii=False, indent=2))
Pydantic が生成した元スキーマ:
{
"additionalProperties": false,
"properties": {
"label": {
"maxLength": 40,
"minLength": 3,
"title": "Label",
"type": "string"
},
"score": {
"maximum": 100,
"minimum": 0,
"title": "Score",
"type": "integer"
}
},
"required": [
"label",
"score"
],
"title": "QualityScore",
"type": "object"
}
SDK が API 送信用に変換したスキーマ:
{
"type": "object",
"title": "QualityScore",
"properties": {
"label": {
"type": "string",
"title": "Label",
"description": "{maxLength: 40, minLength: 3}"
},
"score": {
"type": "integer",
"title": "Score",
"description": "{maximum: 100, minimum: 0}"
}
},
"additionalProperties": false,
"required": [
"label",
"score"
]
}
観察ポイント: minimum / maximum / minLength / maxLength が送信スキーマから除かれ、説明文と受信後の Pydantic 検証へ移されているかを確認します。
3. 列挙型・配列・ネスト構造
実務では、単純な object よりも、列挙型、配列、ネスト、union を組み合わせたスキーマが中心になります。Anthropic は enum、anyOf、ローカル $defs / $ref をサポートしますが、次の上限があります。
- 厳格ツールは1要求あたり最大20個
- 全 Structured Outputs スキーマを合算して、任意パラメーターは最大24個
-
union型を持つパラメーターは最大16個 - 再帰スキーマと外部
$refは非対応
MEMO
Anthropic は、文字列の enum / const について、大文字・小文字がスキーマどおりにならない場合がある既知の例外を文書化しています。この例外は JSON outputs と Strict tool use の両方に適用され、Pydantic やローカル JSON Schema の再検証で拒否される可能性があります。大文字・小文字だけが異なる値を定義せず、短い ASCII の snake_case 値を使用してください。大文字・小文字を無視して受け入れる場合は、正規化後の衝突を事前に検査し、受信値を正規値へ明示的に写像します。
このセルでは、入力テキストの感情、信頼度、根拠、キーワード、補足を型付きで抽出します。スキーマ適合は事実性を保証しないため、根拠文が入力中に実在するかなどは別の業務検証が必要です。
class Sentiment(str, Enum):
POSITIVE = "positive"
NEUTRAL = "neutral"
NEGATIVE = "negative"
MIXED = "mixed"
class Evidence(BaseModel):
model_config = ConfigDict(extra="forbid")
quote: str = Field(description="入力に実在する短い根拠文")
interpretation: str
class AnalysisResult(BaseModel):
model_config = ConfigDict(extra="forbid")
category: Literal["product", "support", "delivery", "other"]
sentiment: Sentiment
confidence: float = Field(ge=0.0, le=1.0)
keywords: list[str] = Field(min_length=1)
evidence: list[Evidence] = Field(min_length=1)
numeric_reference: int | str | None
optional_note: str | None = None
feedback = (
"新しい分析画面は見やすく、日次集計が約40分から8分に短縮された。"
"ただし、CSV エクスポートは二度失敗し、サポートからの返信には2日かかった。"
)
analysis_message = client.messages.parse(
model=MODEL,
max_tokens=4096,
messages=[
{
"role": "user",
"content": (
"次のフィードバックを分類してください。根拠の quote は入力からそのまま引用し、"
"明示されていない optional_note は省略してください。\n\n"
f"{feedback}"
),
}
],
output_format=AnalysisResult,
)
if analysis_message.stop_reason != "end_turn" or analysis_message.parsed_output is None:
raise StructuredResponseError(
f"分析結果を確定できません: {analysis_message.stop_reason}"
)
analysis = analysis_message.parsed_output
comparison = pd.DataFrame(
[
{
"観点": "Python 型",
"値": type(analysis).__name__,
},
{
"観点": "列挙型",
"値": f"{type(analysis.sentiment).__name__}.{analysis.sentiment.name}",
},
{
"観点": "根拠件数",
"値": len(analysis.evidence),
},
{
"観点": "optional_note が生成 JSON に存在",
"値": "optional_note" in analysis.model_fields_set,
},
]
)
display(comparison)
print(analysis.model_dump_json(indent=2, exclude_unset=True))
出力結果
{
"category": "product",
"sentiment": "mixed",
"confidence": 0.86,
"keywords": [
"分析画面",
"日次集計",
"時間短縮",
"CSVエクスポート",
"サポート対応",
"返信遅延"
],
"evidence": [
{
"quote": "新しい分析画面は見やすく、日次集計が約40分から8分に短縮された",
"interpretation": "UIの視認性と処理時間の大幅短縮という製品面の明確な改善が評価されている。"
},
{
"quote": "CSV エクスポートは二度失敗し",
"interpretation": "エクスポート機能に再現性のある不具合があり、製品面の否定的要素となっている。"
},
{
"quote": "サポートからの返信には2日かかった",
"interpretation": "サポート対応の遅さに対する不満が示されている。"
}
],
"numeric_reference": 8
}
4. 制約付きスキーマと厳格モード
Anthropic の JSON outputs と Strict tool use は同じ JSON Schema サブセットを使います。
| 制約 | API の生成時制約 | 実務上の扱い |
|---|---|---|
type、required、enum、const
|
対応 | API と受信側の両方で検証 |
additionalProperties |
false のみ対応 |
すべての object に設定 |
anyOf、非再帰の $ref
|
対応 | 複雑度上限に注意 |
format |
一部対応 | 日付などの意味妥当性も別途確認 |
pattern |
単純な正規表現のみ対応 | lookaround、後方参照などは避ける |
minItems |
0 または1のみ対応 | 最大件数などは受信後に検証 |
minimum / maximum / multipleOf
|
非対応 | SDK 変換またはアプリ側で検証 |
minLength / maxLength
|
非対応 | SDK 変換またはアプリ側で検証 |
再帰スキーマ、外部 $ref
|
非対応 | フラット化または深さを固定 |
ここでの strict はツール入力に付ける指定です。最終 JSON では output_config.format 自体がスキーマ制約を有効にします。
invalid_business_value = {"label": "NG", "score": 120}
try:
QualityScore.model_validate(invalid_business_value)
except ValidationError as exc:
print("Pydantic の受信後検証が、API 非対応の値制約違反を検出しました。")
print(json.dumps(exc.errors(include_url=False), ensure_ascii=False, indent=2))
出力結果
Pydantic の受信後検証が、API 非対応の値制約違反を検出しました。
[
{
"type": "string_too_short",
"loc": [
"label"
],
"msg": "String should have at least 3 characters",
"input": "NG",
"ctx": {
"min_length": 3
}
},
{
"type": "less_than_equal",
"loc": [
"score"
],
"msg": "Input should be less than or equal to 100",
"input": 120,
"ctx": {
"le": 100
}
}
]
未対応制約
UNSUPPORTED_RAW_SCHEMA = {
"type": "object",
"properties": {
"score": {"type": "integer", "minimum": 0, "maximum": 100}
},
"required": ["score"],
"additionalProperties": False,
}
try:
negative_message = client.messages.create(
model=MODEL,
max_tokens=1024,
messages=[{"role": "user", "content": "Return score=50."}],
output_config={
"format": {
"type": "json_schema",
"schema": UNSUPPORTED_RAW_SCHEMA,
}
},
)
print("要求が成功しました。対象環境の制約対応が文書記載から変化した可能性があります。")
print(response_text(negative_message))
except anthropic.APIStatusError as exc:
print("想定どおり API が未対応制約を拒否しました。")
print(json.dumps(describe_api_error(exc), ensure_ascii=False, indent=2))
出力結果
想定どおり API が未対応制約を拒否しました。
{
"error_type": "BadRequestError",
"status_code": 400,
"request_id": "req_011CeUnHamCYT37MtJjBZumU",
"service_error_code": null,
"service_error_message": "output_config.format.schema: For 'integer' type, properties maximum, minimum are not supported"
}
5. 任意項目と null の扱い
「値がない」には少なくとも三つの状態があります。
| 状態 | JSON | 意味の例 |
|---|---|---|
| 省略 | キー自体がない | 情報が入力に存在しない |
明示的な null
|
"note": null |
項目は提示されたが値は未設定 |
| 空文字列 | "note": "" |
文字列として明示的に空 |
Anthropic は required に含めない真の任意プロパティを扱えます。Pydantic では model_fields_set を確認すると、解析後の値がどちらも None である「省略」と「明示的な null」を区別できます。
次のセルは3回の API 要求を送ります。コストを抑えたい場合は、必要なケースだけを optional_cases に残してください。
class OptionalValueResult(BaseModel):
model_config = ConfigDict(extra="forbid")
state: Literal["missing", "null", "empty", "value"]
note: str | None = None
optional_cases = [
("省略", "note という項目への言及はない。"),
("null", "note は明示されているが、値は null である。"),
("空文字列", 'note は明示的に空文字列 "" である。'),
]
optional_rows: list[dict[str, Any]] = []
for case_name, case_text in optional_cases:
optional_message = client.messages.parse(
model=MODEL,
max_tokens=1024,
messages=[
{
"role": "user",
"content": (
"入力の状態を保持してください。言及がない場合は state=missing として note を省略し、"
"null は state=null と note=null、空文字列は state=empty と note=\"\" にしてください。\n\n"
f"{case_text}"
),
}
],
output_format=OptionalValueResult,
)
if optional_message.stop_reason != "end_turn" or optional_message.parsed_output is None:
raise StructuredResponseError(
f"{case_name} ケースを確定できません: {optional_message.stop_reason}"
)
parsed_optional = optional_message.parsed_output
optional_rows.append(
{
"入力ケース": case_name,
"state": parsed_optional.state,
"note の値": repr(parsed_optional.note),
"note キーが存在": "note" in parsed_optional.model_fields_set,
"出力 JSON": parsed_optional.model_dump_json(exclude_unset=True),
}
)
display(pd.DataFrame(optional_rows))
出力結果
| 入力ケース | state | note の値 | note キーが存在 | 出力 JSON | |
|---|---|---|---|---|---|
| 0 | 省略 | missing | None | False | {"state":"missing"} |
| 1 | null | null | None | True | {"state":"null","note":null} |
| 2 | 空文字列 | empty | '' | True | {"state":"empty","note":""} |
6. Function Calling の構造化引数
Structured Outputs には異なる境界を守る二つの仕組みがあります。
- JSON outputs: モデルから利用者へ返す最終データを制約する
- Strict tool use: モデルからツール実装へ渡す引数を制約する
strict: true を付けたツールでは、ツール入力が input_schema に従うように制約付きデコードされます。ただし、ツールを実行するのはモデルではなくアプリケーションです。実行前には、許可リスト、認可、冪等性、値の業務検証を別途適用してください。
最初のセルは検索ツールの呼び出しだけを確認し、外部サービスの代わりにローカルのモック関数を使います。
SEARCH_INPUT_SCHEMA = {
"type": "object",
"properties": {
"query": {"type": "string", "description": "検索語"},
"max_results": {"type": "integer", "enum": [1, 2, 3]},
},
"required": ["query", "max_results"],
"additionalProperties": False,
}
SEARCH_TOOL = {
"name": "search_knowledge",
"description": "ローカルの製品ナレッジを検索する",
"strict": True,
"input_schema": SEARCH_INPUT_SCHEMA,
}
def mock_search_knowledge(query: str, max_results: int) -> dict[str, Any]:
documents = [
{
"title": "Structured outputs",
"url": "https://platform.claude.com/docs/en/build-with-claude/structured-outputs",
"snippet": "JSON outputs and strict tool use constrain different output boundaries.",
},
{
"title": "Claude in Microsoft Foundry",
"url": "https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry",
"snippet": "Microsoft Foundry deployment and authentication guidance.",
},
{
"title": "Use Claude models in Microsoft Foundry",
"url": "https://learn.microsoft.com/azure/foundry/foundry-models/how-to/use-foundry-models-claude",
"snippet": "Foundry deployment names, endpoints, and authentication examples.",
},
]
return {"query": query, "results": documents[:max_results]}
strict_tool_message = client.messages.create(
model=MODEL,
max_tokens=2048,
messages=[
{
"role": "user",
"content": "Microsoft Foundry の Claude Structured Outputs を2件検索してください。",
}
],
tools=[SEARCH_TOOL],
tool_choice={"type": "tool", "name": "search_knowledge"},
)
tool_use_blocks = [
block for block in strict_tool_message.content if block.type == "tool_use"
]
if not tool_use_blocks:
raise StructuredResponseError("tool_use content block が返りませんでした。")
出力結果
validated tool call: search_knowledge
{
"query": "Microsoft Foundry Claude Structured Outputs",
"max_results": 2
}
{
"query": "Microsoft Foundry Claude Structured Outputs",
"results": [
{
"title": "Structured outputs",
"url": "https://platform.claude.com/docs/en/build-with-claude/structured-outputs",
"snippet": "JSON outputs and strict tool use constrain different output boundaries."
},
{
"title": "Claude in Microsoft Foundry",
"url": "https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry",
"snippet": "Microsoft Foundry deployment and authentication guidance."
}
]
}
7. ストリーミング応答の解析
Structured Outputs はストリーミングと併用できます。ただし、途中の断片は完全な JSON ではありません。中途半端な文字列へ json.loads() を繰り返すのではなく、UI にはテキスト断片として表示し、最終メッセージを蓄積してから停止理由、JSON、スキーマの順で検証します。
Claude Opus 5 の thinking が有効な場合、ストリームには thinking、signature、text の各イベントが含まれ得ます。Python SDK の text_stream はテキスト断片だけを取り出し、get_final_message() は content blocks と署名を含む完全なメッセージを再構成します。
streamed_fragments: list[str] = []
with client.messages.stream(
model=MODEL,
max_tokens=2048,
messages=[
{
"role": "user",
"content": f"次の文章を構造化してください。\n\n{source_text}",
}
],
output_config={
"effort": "low",
"format": {"type": "json_schema", "schema": ARTICLE_SCHEMA},
},
) as stream:
for text_delta in stream.text_stream:
streamed_fragments.append(text_delta)
print(text_delta, end="", flush=True)
streamed_message = stream.get_final_message()
print("\n\n--- 完了後の検証 ---")
streamed_payload = parse_and_validate_message(streamed_message, ARTICLE_SCHEMA)
print(f"受信した text delta: {len(streamed_fragments)} 個")
print(json.dumps(streamed_payload, ensure_ascii=False, indent=2))
print(json.dumps(response_metadata(streamed_message), ensure_ascii=False, indent=2))
出力結果
--- 完了後の検証 ---
受信した text delta: 4 個
{
"name": "Contoso予知保全システム試験導入",
"summary": "Contosoは製造設備向けの予知保全システムを試験導入した。振動センサーから得られる時系列データを分析し、異常の兆候を保全担当者に通知する仕組みで、初期評価では計画外停止時間が18%減少した。",
"keywords": [
"Contoso",
"予知保全",
"製造設備",
"振動センサー",
"時系列データ",
"異常検知",
"計画外停止時間",
"18%減少",
"試験導入"
]
}
7.1 Token Count API
Token Count API はメッセージ、ツール、画像などの入力トークン数を生成前に見積もります。Structured Outputs と互換ですが、文法コンパイル自体は行わないため、初回コンパイル遅延は測れません。カウントは推定値であり、実際の入力トークンとわずかに異なる場合があります。
Claude 4.7 以降は新しいトークナイザーを使うため、別モデルで測った値を Opus 5 に流用せず、実際のデプロイ名で数えてください。
token_count = client.messages.count_tokens(
model=MODEL,
messages=[
{
"role": "user",
"content": f"次の文章を構造化してください。\n\n{source_text}",
}
],
output_config={
"format": {"type": "json_schema", "schema": ARTICLE_SCHEMA}
},
)
print(json.dumps({"estimated_input_tokens": token_count.input_tokens}, indent=2))
出力結果
{
"estimated_input_tokens": 437
}
8. 拒否・打ち切り・不正出力の処理
正常な Structured Output を確定する前に、必ず stop_reason を検査します。
| Claude の状態 | HTTP | 処理 |
|---|---|---|
end_turn |
200 | JSON 解析とスキーマ検証へ進む |
refusal |
200 | 拒否を独立状態として扱う。本文のスキーマ適合を期待しない |
max_tokens |
200 | JSON が途中で切れ得る。上限と要求設計を見直して再試行 |
model_context_window_exceeded |
200 | 入力または出力上限を縮小 |
tool_use |
200 | ツールを検証・実行し、結果を返して同じ assistant turn を継続 |
pause_turn |
200 | サーバーツールの長時間実行など。公式手順に従って継続 |
| HTTP 4xx / 5xx | エラー | 状態コード、例外型、request ID を記録して分類 |
Claude Messages API のフィールド名は stop_reason であり、OpenAI の finish_reason ではありません。また、公開された Claude Structured Outputs 仕様には content_filter という停止理由は記載されていません。安全上の拒否は refusal、ゲートウェイやポリシーによる HTTP エラーは APIStatusError として扱います。
9. 画像からの Structured Output
JSON outputs は画像入力にも利用できます。画像を content blocks の先頭、その後にテキスト指示を置きます。この実験は公開サンプル画像へアクセスし、追加の推論コストが発生するため、既定では無効です。
画像理解の結果もスキーマには適合しますが、画像内文字、物体数、空間関係などの認識が正しいとは限りません。高リスク用途では人手確認と個別評価を組み合わせてください。
IMAGE_ANALYSIS_SCHEMA = {
"type": "object",
"properties": {
"description": {"type": "string"},
"visible_objects": {"type": "array", "items": {"type": "string"}},
"dominant_colors": {"type": "array", "items": {"type": "string"}},
"contains_readable_text": {"type": "boolean"},
"uncertainties": {"type": "array", "items": {"type": "string"}},
},
"required": [
"description",
"visible_objects",
"dominant_colors",
"contains_readable_text",
"uncertainties",
],
"additionalProperties": False,
}
vision_message = client.messages.create(
model=MODEL,
max_tokens=4096,
messages=[
{
"role": "user",
"content": [
{
"type": "image",
"source": {
"type": "url",
"url": "https://platform.claude.com/docs/images/vision-example.jpg",
},
},
{
"type": "text",
"text": "画像を観察事実と不確実性に分けて構造化してください。",
},
],
}
],
output_config={
"effort": "low",
"format": {
"type": "json_schema",
"schema": IMAGE_ANALYSIS_SCHEMA,
},
},
)
vision_payload = parse_and_validate_message(
vision_message,
IMAGE_ANALYSIS_SCHEMA,
)
print(json.dumps(vision_payload, ensure_ascii=False, indent=2))
出力結果
{
"description": "フラットデザイン(ベクター風)のミニマルな夕景イラスト。上部はオレンジから淡い黄色へのグラデーションの空で、右上寄りにクリーム色の大きな円(太陽)が描かれている。左上には小さな鳥のシルエットが4羽ほど飛んでいる。中景には紫がかった赤紫の山並みが二層(明るい層と暗い層)重なり、下部には濃い紫の水面が広がる。水面には太陽の反射を表す水平のクリーム色の線が5本、幅を変えて並んでいる。",
"visible_objects": [
"太陽(円)",
"山(複数の三角形の稜線)",
"鳥のシルエット",
"水面・湖または海",
"太陽の反射を示す水平線",
"グラデーションの空"
],
"dominant_colors": [
"オレンジ",
"淡い黄色(クリーム)",
"くすんだ赤紫",
"濃い紫",
"茶紫"
],
"contains_readable_text": false,
"uncertainties": [
"下部の暗い帯が水面か平原かは断定できない",
"太陽が日の出か日の入りかは判別できない",
"小さな黒い印が鳥であるという解釈は推測"
]
}
GitHub
参考