> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arouter.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 構造化出力

> モデルが正確なスキーマに一致する有効な JSON を出力するよう強制します。スキーマ準拠を保証するには json_schema モードを使用してください。

構造化出力は、モデルが定義したスキーマに一致する JSON オブジェクトを返すよう強制します。これにより、非構造化のモデル出力を解析、検証、または再試行する必要がなくなります。

ARouter は 2 つの構造化出力モードをサポートしています：

| モード           | 説明                          |
| ------------- | --------------------------- |
| `json_object` | 有効な JSON を保証します；スキーマは強制しません |
| `json_schema` | 正確なスキーマに一致する JSON を保証します    |

## 構造化出力の使用

リクエストボディに `response_format` を渡します：

### JSON Object モード

```json theme={null}
{
  "model": "openai/gpt-5.4",
  "messages": [
    {
      "role": "user",
      "content": "Return information about the planet Mars as JSON"
    }
  ],
  "response_format": { "type": "json_object" }
}
```

モデルは有効な JSON を返しますが、スキーマは強制されません：

```json theme={null}
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"Mars\",\"diameter_km\":6779,\"moons\":2,\"habitable\":false}"
      }
    }
  ]
}
```

### JSON Schema モード

厳格なスキーマを強制するには `json_schema` を使用します：

```json theme={null}
{
  "model": "openai/gpt-5.4",
  "messages": [
    {
      "role": "system",
      "content": "You are a data extraction assistant."
    },
    {
      "role": "user",
      "content": "Extract the planet information: Mars is a terrestrial planet with 2 moons and a diameter of 6779 km."
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "planet_info",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "diameter_km": { "type": "number" },
          "moons": { "type": "integer" },
          "habitable": { "type": "boolean" }
        },
        "required": ["name", "diameter_km", "moons", "habitable"],
        "additionalProperties": false
      }
    }
  }
}
```

レスポンス：

```json theme={null}
{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"Mars\",\"diameter_km\":6779,\"moons\":2,\"habitable\":false}"
      },
      "finish_reason": "stop"
    }
  ]
}
```

## 完全な例

<Tabs>
  <Tab title="Python (OpenAI)">
    ```python theme={null}
    import json
    from openai import OpenAI
    from pydantic import BaseModel

    client = OpenAI(
        base_url="https://api.arouter.ai/v1",
        api_key="lr_live_xxxx",
    )

    # Pydantic を使ってスキーマを定義
    class PlanetInfo(BaseModel):
        name: str
        diameter_km: float
        moons: int
        habitable: bool

    # OpenAI の parse() メソッドを使用（推奨）
    response = client.beta.chat.completions.parse(
        model="openai/gpt-5.4",
        messages=[
            {"role": "system", "content": "You are a data extraction assistant."},
            {"role": "user", "content": "Tell me about Mars."},
        ],
        response_format=PlanetInfo,
    )

    planet = response.choices[0].message.parsed
    print(planet.name)        # "Mars"
    print(planet.moons)       # 2
    print(planet.diameter_km) # 6779.0

    # または response_format dict を手動で使用
    response = client.chat.completions.create(
        model="openai/gpt-5.4",
        messages=[
            {"role": "user", "content": "Tell me about Mars as JSON."},
        ],
        response_format={
            "type": "json_schema",
            "json_schema": {
                "name": "planet_info",
                "strict": True,
                "schema": PlanetInfo.model_json_schema(),
            },
        },
    )
    data = json.loads(response.choices[0].message.content)
    planet = PlanetInfo(**data)
    ```
  </Tab>

  <Tab title="Node.js (OpenAI)">
    ```typescript theme={null}
    import OpenAI from "openai";
    import { zodResponseFormat } from "openai/helpers/zod";
    import { z } from "zod";

    const client = new OpenAI({
      baseURL: "https://api.arouter.ai/v1",
      apiKey: "lr_live_xxxx",
    });

    // Zod でスキーマを定義
    const PlanetInfo = z.object({
      name: z.string(),
      diameter_km: z.number(),
      moons: z.number().int(),
      habitable: z.boolean(),
    });

    // Zod と parse() を使用（推奨）
    const response = await client.beta.chat.completions.parse({
      model: "openai/gpt-5.4",
      messages: [
        { role: "system", content: "You are a data extraction assistant." },
        { role: "user", content: "Tell me about Mars." },
      ],
      response_format: zodResponseFormat(PlanetInfo, "planet_info"),
    });

    const planet = response.choices[0].message.parsed;
    console.log(planet?.name);        // "Mars"
    console.log(planet?.moons);       // 2
    console.log(planet?.diameter_km); // 6779

    // または response_format を直接使用
    const rawResponse = await client.chat.completions.create({
      model: "openai/gpt-5.4",
      messages: [{ role: "user", content: "Tell me about Mars." }],
      response_format: {
        type: "json_schema",
        json_schema: {
          name: "planet_info",
          strict: true,
          schema: {
            type: "object",
            properties: {
              name: { type: "string" },
              diameter_km: { type: "number" },
              moons: { type: "integer" },
              habitable: { type: "boolean" },
            },
            required: ["name", "diameter_km", "moons", "habitable"],
            additionalProperties: false,
          },
        },
      },
    });

    const data = JSON.parse(rawResponse.choices[0].message.content ?? "{}");
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl https://api.arouter.ai/v1/chat/completions \
      -H "Authorization: Bearer lr_live_xxxx" \
      -H "Content-Type: application/json" \
      -d '{
        "model": "openai/gpt-5.4",
        "messages": [
          {"role": "user", "content": "Tell me about Mars."}
        ],
        "response_format": {
          "type": "json_schema",
          "json_schema": {
            "name": "planet_info",
            "strict": true,
            "schema": {
              "type": "object",
              "properties": {
                "name": {"type": "string"},
                "diameter_km": {"type": "number"},
                "moons": {"type": "integer"},
                "habitable": {"type": "boolean"}
              },
              "required": ["name", "diameter_km", "moons", "habitable"],
              "additionalProperties": false
            }
          }
        }
      }'
    ```
  </Tab>
</Tabs>

## モデルサポート

`strict: true` の `json_schema` モードをサポートするモデル：

* `openai/gpt-5.4`, `openai/gpt-5.4-pro`, `openai/o3`, `openai/o4-mini`
* `anthropic/claude-sonnet-4.6`, `anthropic/claude-opus-4.5`
* `google/gemini-2.5-flash`, `google/gemini-2.5-pro`

`json_object` モード（スキーマ強制なし）はより広くサポートされています。最新の機能情報は `GET /v1/models` を確認してください。

## 構造化出力のストリーミング

構造化出力はストリーミングに対応しています。JSON コンテンツは段階的に配信され、クライアント側で組み立てます：

```typescript theme={null}
const stream = await client.chat.completions.create({
  model: "openai/gpt-5.4",
  messages: [{ role: "user", content: "Tell me about Mars." }],
  response_format: {
    type: "json_schema",
    json_schema: {
      name: "planet_info",
      strict: true,
      schema: { /* ... */ },
    },
  },
  stream: true,
});

let jsonBuffer = "";
for await (const chunk of stream) {
  const content = chunk.choices[0]?.delta?.content;
  if (content) jsonBuffer += content;
}

const data = JSON.parse(jsonBuffer);
```

## ベストプラクティス

1. **`strict: true` を使用** — これによりスキーマ準拠が保証されます。指定しない場合、モデルはスキーマに完全に一致しない有効な JSON を返す可能性があります。

2. **`additionalProperties: false` を設定** — 厳格モードに必須です。モデルが余分なキーを追加するのを防ぎます。

3. **すべての必須フィールドを明示的にリスト化** — 厳格モードでは、`properties` のすべてのフィールドを `required` にも含めてください。

4. **システムプロンプトを含める** — データ抽出や構造化出力アシスタントとしての役割をモデルに伝えることで信頼性が向上します。

## エラー処理

モデルがスキーマに一致する有効な JSON を生成できない場合（例：プロンプトがスキーマと根本的に矛盾する場合）、レスポンスの `finish_reason` は `"length"` または `"content_filter"` になります。コンテンツを解析する前に必ず `finish_reason` を確認してください。

```python theme={null}
response = client.chat.completions.create(...)

choice = response.choices[0]
if choice.finish_reason != "stop":
    raise ValueError(f"Unexpected finish reason: {choice.finish_reason}")

data = json.loads(choice.message.content)
```
