포맷

strict 모드 JSON Schema oneOf 미지원: 위반 항목 고치기

Sume는 OpenAI strict 모드 부분집합을 벗어난 output_schema를 400 output_schema_invalid로 거부합니다. 위반마다 rule을 읽고 oneOf와 nullable을 옮기세요.

읽는 시간 5분Sume
전체 글

Sume가 output_schema에 강제하는 strict 모드 부분집합은 JSON Schema oneOf를 지원하지 않으므로, 스키마에 oneOf를 쓴 Format 실행은 제출 시점에 400 output_schema_invalid로 거부됩니다. oneOf를 anyOf로 바꾼 뒤 details.violations[]를 하나씩 해결하세요. 각 항목은 path와 안정적인 rule을 알려 주고, 400 한 번에 모든 문제가 나열되며, 스키마가 통과할 때까지 아무것도 실행되거나 청구되지 않습니다.

아래의 rule 토큰과 이식 방법은 2026-09-26에 확인한 Sume 문서 구조화 출력 (영문) 페이지의 지원 스키마 섹션에서 가져왔습니다. 허용 키워드 목록 자체는 Sume Format 구조화 출력에 있습니다.

details.violations[]는 어떻게 읽나요?

각 항목은 { path, rule, message }입니다. 첫 번째 문제만이 아니라 모든 문제가 보고되므로, 400 한 번이면 스키마를 고치기에 충분합니다.

  • path는 #/properties/scenes/items/properties/clip 같은 JSON Pointer 형식의 위치입니다.
  • rule은 switch로 분기해도 안전한 안정적인 소문자 토큰입니다.
  • message는 사람이 읽도록 쓰였으며 바뀔 수 있습니다.

위반 rule은 각각 무슨 뜻인가요?

rule 토큰은 각각 부분집합의 한 부분을 가리킵니다. 표 다음의 예시는 최상위 배열에서 생기는 root_must_be_object를 문서가 고친 방법입니다.

위반 rule, 구조화 출력 (영문) 기준, 2026-09-26 확인.
`rule`의미
root_must_be_object루트가 없거나, 객체가 아니거나, type이 정확히 "object"가 아님. ["object", "null"]도 거부됨
not_an_object스키마 노드가 JSON 객체가 아님
missing_type노드에 type, $ref, anyOf가 모두 없음. 다른 키 없이 { "description": "…" }만 있는 노드도 해당됨
unsupported_typestring, number, integer, boolean, object, array, null 밖의 type
unsupported_keyword허용 목록에 없는 키워드
additional_properties_falseadditionalProperties: false가 없는 객체 노드. items와 $defs 안도 포함
required_completeness선언한 속성이 required에 없거나, required 항목에 대응하는 속성이 없음
missing_itemsitems가 없는 array 노드
unsupported_ref#/$defs/<name>도 SumeMediaFile#도 아닌 $ref, 또는 존재하지 않는 정의를 가리키는 $ref
invalid_defs$defs가 있지만 이름 붙은 스키마들의 객체가 아님
max_depth중첩이 10단계를 넘음
max_properties문서 전체에서 센 속성이 5000개를 넘음
max_enum_valuesenum 하나의 값이 1000개를 넘음
max_string_length모든 속성 이름, 키, 문자열 값을 합쳐 120,000자를 넘음
// rejected
{ "type": "array", "items": { "type": "string" } }

// accepted
{
  "type": "object",
  "additionalProperties": false,
  "required": ["captions"],
  "properties": { "captions": { "type": "array", "items": { "type": "string" } } }
}

oneOf, allOf, nullable을 쓰는 스키마는 어떻게 옮기나요?

이 키워드들은 모두 unsupported_keyword입니다. OpenAPI에서 옮겨 온 스키마는 반사적으로 oneOf를 쓰는데, 문서는 흔한 경우마다 대체 방법을 알려 줍니다.

  • oneOf: anyOf를 쓰세요. 허용 목록에 있는 것은 anyOf뿐입니다.
  • allOf: 분기들을 하나의 객체로 평평하게 펴세요.
  • not, if / then / else, dependentRequired, dependentSchemas: 표현할 수 없습니다. 대안을 anyOf로 모델링하거나, output을 읽은 뒤 여러분 쪽에서 검증하세요.
  • nullable: true: nullable union인 "type": ["string", "null"]을 쓰세요. 선언한 모든 속성은 required에 있어야 하므로, 선택 속성도 같은 union으로 대체합니다.
  • patternProperties, propertyNames, unevaluatedProperties, additionalItems: 원하는 속성을 선언하세요. 나머지는 additionalProperties: false가 처리합니다.

$ref나 anyOf 옆의 제약은 왜 아무 효과가 없나요?

$ref와 anyOf는 각각 자기가 놓인 노드를 단락(short-circuit)시킵니다. 형제 키워드는 허용 목록 검사만 받을 뿐 그 밖에는 아무 의미가 없으므로, $ref 옆에 쓴 제약은 효과가 없습니다. 제약은 anyOf 분기 안이나, $ref가 가리키는 $defs 항목 안에 넣으세요.

$defs는 어디에 있어야 하고, 스키마는 재귀할 수 있나요?

해석되는 $ref 대상은 두 가지뿐입니다. 스키마 문서의 루트에 선언한 #/$defs/*와 SumeMediaFile#입니다. 그래서 다른 도구에서 익숙했던 몇 가지 습관은 쓸 수 없습니다.

  • 외부 참조는 실패합니다. URL, 형제 문서, OpenAPI 스타일의 #/components/... 경로가 해당됩니다.
  • 하위 스키마 안에 중첩한 $defs 블록은 실패합니다. 그 #/$defs/* 대상에 대응하는 루트 항목이 없기 때문입니다.
  • $ref: "#"는 거부됩니다. OpenAI의 strict 모드는 이 방식의 루트 재귀를 허용하지만, Sume는 허용하지 않습니다.
  • 이름 붙은 정의를 통한 재귀는 괜찮습니다. $defs 항목은 자기 자신을 $ref할 수 있으며, 이는 depth 한도를 쓰지 않습니다. depth 한도는 문서에 실제로 적힌 중첩만 세기 때문입니다.

큰 스키마는 왜 max_string_length에 걸리나요?

120,000자 한도는 필드별 상한이 아니라 문서 전체 예산입니다. 큰 스키마에 긴 description 주석을 달면 눈에 띄게 긴 문자열이 하나도 없어도 예산을 다 쓸 수 있으므로, 필드를 줄이기 전에 주석부터 줄이세요.

strict: false로 위반을 피해 갈 수 있나요?

아닙니다. strict: false는 수락되어 저장되지만 부분집합에 대해서는 아무것도 바꾸지 않습니다. 대신 쓸 JSON 모드도 없습니다. 부분집합에 맞는 스키마를 바인딩하거나 내장 스키마를 쓰세요.

제출을 통과해도 그것이 마지막 검사는 아닙니다. 나중에 실행 결과가 유효한 스키마와 맞지 않으면 실행은 대신 output_schema_unsatisfied로 실패합니다. Sume Format 실행 실패 코드를 참고하세요.

출처

관련 글

작성자 Sume