메인 콘텐츠로 건너뛰기
모델이 구조화된 출력 시나리오에서 반환하는 JSON은 때때로 파싱에 실패합니다: 출력이 max_tokens에 의해 잘리거나, 후행 쉼표가 붙어 있거나, 내용이 Markdown 코드 블록으로 감싸져 있는 경우입니다. 애플리케이션 측에서는 이를 위해 재시도나 수동 복구 로직을 작성해야 하는 것이 일반적입니다. 구조화된 출력 복구(Structured Output Repair)는 Key 단위의 기능으로, 기본적으로 비활성화되어 있습니다. 활성화하면, 구조화된 JSON 출력을 선언한 비스트리밍 요청에 대해 모델이 반환한 JSON에 형식 오류가 있을 때 AIHubMix 게이트웨이가 반환 전에 이를 파싱 가능한 유효한 JSON으로 자동 복구합니다. 클라이언트 코드는 변경할 필요가 없습니다.
이 스위치는 기본적으로 비활성화되어 있으며, 활성화하지 않은 경우 모든 응답이 원본 그대로 반환됩니다. 스위치는 Key별로 독립적으로 설정되며, 활성화하면 즉시 적용됩니다.

Key 관리 페이지에서 설정하기

Key를 생성하거나 편집하여 「구조화된 출력 복구」(Structured Output Repair) 스위치를 켜세요.
AIHubMix Key 편집 패널에서 구조화된 출력 복구 스위치 켜기

1. 적용 조건

복구는 아래 조건이 모두 충족될 때에만 수행됩니다:
  1. 요청에 사용된 Key가 「구조화된 출력 복구」를 활성화한 상태입니다.
  2. 요청이 비스트리밍입니다(stream: true를 설정하지 않음).
  3. 요청이 구조화된 JSON 출력을 선언했습니다(각 프로토콜의 선언 필드는 아래 표 참조).
  4. 모델이 반환한 텍스트 내용을 JSON으로 파싱할 수 없습니다.
프로토콜엔드포인트구조화된 출력 선언 필드
Chat Completions/v1/chat/completionsresponse_format.typejson_object 또는 json_schema
Responses/v1/responsestext.format.typejson_schema 또는 json_object
Claude Messages/v1/messagesoutput_config.format.typejson_schema
Gemini/gemini/v1beta/models/{model}:generateContentgenerationConfig.responseMimeTypeapplication/json이거나, responseSchema가 설정됨
내용 자체가 유효한 JSON인 경우 아무런 변경 없이 원본 그대로 반환합니다.

2. 복구 가능한 형식 오류

출력 잘림으로 인해 괄호 / 따옴표가 닫히지 않음(예: finish_reasonlength):
복구 전
{"items": [{"name": "Widget", "price": 9.99}, {"name": "Gad
복구 후
{"items": [{"name": "Widget", "price": 9.99}, {"name": "Gad"}]}
Markdown 코드 블록 래핑:
복구 전
```json
{"status": "ok", "count": 3}
```
복구 후
{"status": "ok", "count": 3}
후행 쉼표:
복구 전
{"a": 1, "b": 2,}
복구 후
{"a": 1, "b": 2}
작은따옴표:
복구 전
{'name': 'Widget'}
복구 후
{"name": "Widget"}
키 이름에 따옴표 없음:
복구 전
{name: "Widget", price: 9.99}
복구 후
{"name": "Widget", "price": 9.99}
텍스트와 JSON 혼재(JSON 앞뒤에 설명 문구가 있음):
복구 전
Here is the JSON you requested: {"name": "Widget", "price": 9.99}
복구 후
{"name": "Widget", "price": 9.99}
복구는 보수적인 전략을 취합니다:
  • 복구는 JSON 문법만 보완하거나 정규화합니다. 수치와 문자열 내용은 원문을 유지하며, 정밀도 손실이나 재작성이 발생하지 않습니다.
  • 복구 결과물은 반드시 유효한 JSON 객체 또는 배열이어야 하며, 원문과의 차이가 합리적인 범위 내에 있어야 합니다. 어느 한 조건이라도 충족되지 않으면 복구를 포기하고 원본 내용을 그대로 반환합니다.

3. 응답이 복구되었는지 판단하는 방법

복구가 발생하면 프로그래밍 방식으로 읽거나 눈으로 확인할 수 있는 두 가지 신호가 있습니다:
  1. 응답 헤더 X-JSON-Repaired: true(복구가 발생하지 않은 경우 이 응답 헤더는 없습니다).
  2. 호출 로그 비고: 호출 기록 페이지의 해당 요청 비고 열에 This request gateway automatically corrected the JSON format issue in the model output.가 표시됩니다.
Token 사용량과 과금은 모델의 원본 출력 기준으로 계산되며, 복구는 과금 결과를 변경하지 않습니다.

4. 전체 예시

상품 정보 생성을 예로 들면, 요청은 json_schema로 구조화된 출력을 선언합니다. 스위치를 활성화하면 모델 출력에 위와 같은 형식 오류가 있더라도 받은 content를 바로 파싱할 수 있습니다:
import json
import os
import requests

response = requests.post(
    "https://aihubmix.com/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {os.environ['AIHUBMIX_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "gpt-5.2",
        "messages": [
            {"role": "user", "content": "Generate a product listing for a wireless keyboard."}
        ],
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "product",
                "schema": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "price": {"type": "number"},
                        "description": {"type": "string"},
                    },
                    "required": ["name", "price"],
                    "additionalProperties": False,
                },
            },
        },
    },
)

repaired = response.headers.get("X-JSON-Repaired") == "true"
product = json.loads(response.json()["choices"][0]["message"]["content"])
print(product, "repaired:", repaired)
const response = await fetch("https://aihubmix.com/v1/chat/completions", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AIHUBMIX_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    model: "gpt-5.2",
    messages: [
      { role: "user", content: "Generate a product listing for a wireless keyboard." },
    ],
    response_format: {
      type: "json_schema",
      json_schema: {
        name: "product",
        schema: {
          type: "object",
          properties: {
            name: { type: "string" },
            price: { type: "number" },
            description: { type: "string" },
          },
          required: ["name", "price"],
          additionalProperties: false,
        },
      },
    },
  }),
});

const repaired = response.headers.get("X-JSON-Repaired") === "true";
const data = await response.json();
const product = JSON.parse(data.choices[0].message.content);
console.log(product, "repaired:", repaired);
curl -sD - https://aihubmix.com/v1/chat/completions \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5.2",
    "messages": [
      {"role": "user", "content": "Generate a product listing for a wireless keyboard."}
    ],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "product",
        "schema": {
          "type": "object",
          "properties": {
            "name": {"type": "string"},
            "price": {"type": "number"},
            "description": {"type": "string"}
          },
          "required": ["name", "price"],
          "additionalProperties": false
        }
      }
    }
  }'
복구를 직접 관찰해 보고 싶다면 max_tokens를 작은 값(예: 80)으로 설정하여 출력이 잘리도록 할 수 있습니다: 응답의 finish_reasonlength가 되고, content는 복구된 유효한 JSON이며, 응답 헤더에는 X-JSON-Repaired: true가 붙습니다.

5. 경계와 유의 사항

스트리밍 요청(stream: true)은 복구하지 않으며, 원본 그대로 전달합니다. 복구 기능이 필요한 경우 비스트리밍 요청을 사용하세요.
복구는 내용이 JSON으로 파싱될 수 있음을 보장하지만, 잘려서 누락된 데이터는 복원할 수 없습니다. finish_reason / stop_reason을 함께 확인하고, 출력이 잘린 경우 필요에 따라 max_tokens를 늘려 재시도할 것을 권장합니다.
  • 문법 복구: 필드명과 필드 타입이 사용자의 schema에 부합하는지는 여전히 애플리케이션 측에서 검증해야 합니다.
  • 도구 호출 파라미터는 복구 범위에 포함되지 않음: tool_calls / function_call의 파라미터 JSON은 복구하지 않습니다. 도구 파라미터는 실행 전에 항상 애플리케이션 측에서 검증해야 합니다.
  • 다중 텍스트 블록 출력은 복구하지 않음: 모델 출력에 여러 텍스트 블록이 포함된 경우(예: Gemini가 여러 parts를 반환) 원본 그대로 반환합니다.
  • 추론 내용은 영향을 받지 않음: reasoning_content, Gemini의 thought 부분 등 추론 텍스트는 복구에 관여하지 않습니다.

자주 묻는 질문 FAQ

복구가 반환 내용의 수치나 텍스트를 변경하나요? 아니요. 복구는 JSON 문법만 보완하거나 정규화하며(닫는 괄호 보완, 후행 쉼표 제거, 따옴표 정규화, 코드 블록 표기 제거), 수치와 문자열 내용은 모델 출력 원문을 유지합니다. 스위치를 활성화하면 구조화된 출력을 선언하지 않은 일반 요청에도 영향이 있나요? 아니요. response_format 등 구조화된 출력 필드를 선언하지 않은 요청과 모든 스트리밍 요청은 응답이 원본 그대로 반환됩니다. 복구로 인해 추가 과금이 발생하거나 token 사용량이 변경되나요? 아니요. Token 사용량과 과금은 모델의 원본 출력 기준으로 계산되며, 복구 과정에서 추가 비용이 발생하지 않습니다. 복구된 JSON은 반드시 내가 정의한 schema에 부합하나요? 복구는 내용이 JSON으로 파싱될 수 있음을 보장합니다. 필드명과 필드 타입이 schema에 부합하는지는 모델 출력 자체에 달려 있으므로, 애플리케이션 측에서 schema 검증을 계속 유지할 것을 권장합니다.