AI 개발

OpenAI Agents API + JSON Schema 실전: 안정적이고 검증 가능하며 실행 가능한 JSON을 어떻게 내나 (2026)

약 16분

일반 Schema 글은 왜 계약이 필요한지를 이미 썼다. 이 글은 Agents API에만 둔다: 모델이 채운 JSON은 문을 통과한 뒤에만 실행한다. 도구 인자, MCP 입력, 당신 장부는 세 문이지 「JSON을 출력하라」는 문장이 아니다.

읽기 전 한눈

문 무엇을 잠그나 누가 실행하나
Function parameters order_id, 불리언, 열거 additionalProperties: false
호스트 검증 required_actions의 arguments 통과하지 않으면 함수를 돌리지 않음
최종 카드 사용자에게 보여줄 객체 별 Schema. text.format이 아님
MCP 다른 도구 표의 inputSchema subagent가 이어받을 수 있음

Schema는 문이지 예의가 아니다. 넓은 object는 문을 하나 더 열어 둔 것이다.

안정, 검증, 실행은 세 절차이지 스위치 하나가 아니다

Agents API에서도 모델은 JSON만 채운다. 공식 function에는 parameters Schema가 필요하다. required, additionalProperties: false, 필요할 때 pattern / enum. 이것이 채움을 안정시킨다. 검증은 실행 전에 같은 Schema로 arguments를 돌리는 것이다. 실행은 문을 통과한 뒤 호스트가 부작용을 내는 것이다.

  • 안정: 도구 인자를 엄한 Schema로 쓴다. 넓은 object는 키 발명을 부른다.
  • 검증: required_actions의 호출을 로컬에서 검증한다. 이력의 function_call로는 부족하다.
  • 실행: 문을 못 넘으면 lookup_order를 돌리지 않는다. success: false와 이유를 되돌린다. 조용히 저장소를 건드리지 마라.

도구 호출은 OpenAI Agents API 완전 해설. 네 대상을 ID로 잇는 이야기는 Agents API JSON 데이터 구조. Schema / Function Calling / Structured Output의 일반 구분은 AI에 JSON Schema가 필요한 이유. 이 글은 그 단어를 다시 정의하지 않는다. Agents session에 얹는 법만 쓴다.

첫 문: agent.tools의 parameters

공식 function은 name, description, JSON Schema다. session 생성 때 agent.tools에 쓴다. 가상 주문. 실제 비밀은 붙이지 마라:

{
  "type": "function",
  "name": "lookup_order",
  "description": "Look up an order by ID. Confirm before any refund.",
  "parameters": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" },
      "include_lines": { "type": "boolean" }
    },
    "required": ["order_id", "include_lines"],
    "additionalProperties": false
  }
}

요점은 적다. 모든 칸을 required에 넣는다. 객체는 여분 키를 닫는다. 번호는 pattern이지 맨 string이 아니다. 함수는 기본으로 미리 로드된다. 미루려면 defer_loading: true와 tool_search. 문 자체는 바뀌지 않는다.

문을 못 넘는 인자는 실행하지 않는다

session이 agent.session.requires_action에서 멈추면 required_actions에 function_call이 나온다. 아래 예는 조회처럼 보이지만 이미 울타리를 넘었다. 번호가 pattern에 맞지 않고 refund가 생겼다:

{
  "type": "function_call",
  "turn_id": "turn_2",
  "call_id": "call_3c91",
  "name": "lookup_order",
  "arguments": {
    "order_id": "1042",
    "include_lines": true,
    "refund": true
  }
}

같은 turn_id / call_id. 통과하는 모양은 이렇다. 여분 키, 빠진 required, 흐른 pattern. 어느 것도 실행하면 안 된다:

{
  "type": "function_call",
  "turn_id": "turn_2",
  "call_id": "call_3c91",
  "name": "lookup_order",
  "arguments": {
    "order_id": "A-1042",
    "include_lines": true
  }
}

둘째 문: 호스트가 같은 Schema를 다시 돈다

공식 Schema는 채움 품질을 올린다. 부작용은 대신지지 않는다. 실행 전에 로컬에서 검증한다. 실패는 되짚을 수 있는 객체로 남긴다:

{
  "check": "lookup_order",
  "schema": "parameters",
  "pass": ["order_id", "include_lines"],
  "fail": [
    { "path": "/order_id", "reason": "pattern" },
    { "path": "/refund", "reason": "additionalProperties" }
  ],
  "execute": false
}

실패면 events 엔드포인트에 agent.session.input.tool_result를 보내고 success: false와 읽을 수 있는 error를 쓴다. 성공이면 output을 문자열로 직렬화한다. 새 session으로 「재시도」하지 마라.

{
  "type": "agent.session.input.tool_result",
  "turn_id": "turn_2",
  "call_id": "call_3c91",
  "success": false,
  "error": "order_id failed schema: expected ^[A-Z]-\\d{4}$"
}

최종 카드는 셋째 문이지 Responses의 text.format이 아니다

사용자에게 보여줄 주문 카드는 도구 인자 Schema가 아니다. Agents session은 Responses의 text.format.json_schema를 자동으로 걸지 않는다. 세 일을 나눠 쓴다:

  • 도구 인자: 부작용 입구를 잠근다. 조회와 쓰기는 여기.
  • 최종 카드: items에서 객체를 뽑은 뒤 다른 Schema로 검증하고 보여 준다.
  • 최종 모양을 Structured Outputs로 잠그려면: 그것은 Responses 층이다. 같은 session 자원에 섞지 마라.

루프를 맡길지는 Agents API vs Responses API. MCP 행은 다른 도구 표다. inputSchema에도 additionalProperties: false가 필요하다. 경계는 MCP Tool Schema vs Function Calling. subagent는 MCP Schema를 이어받고 function은 이어받지 않는다.

실행 가능한 JSON으로 가는 다섯 걸음

「JSON을 출력하라」에서 시작하지 마라. 문을 걷는다:

  1. 먼저 도구 Schema를 쓴다. pattern, enum, required, additionalProperties: false를 한 번에 조인다.
  2. 같은 Schema를 호스트 검증기에 넣는다. 공식 parameters와 따로 쓰지 마라.
  3. required_actions에서는 검증한 뒤 실행한다. 못 넘으면 success: false.
  4. 최종 카드는 별 Schema. items에서 뽑은 뒤 검증한다. 산문을 카드로 쓰지 마라.
  5. 장부 칸은 당신이 쥔다. compact는 세부를 버린다. 금액과 승인을 대화에만 두지 마라.

워크플로를 Schema로 담는 이야기는 JSON Schema와 Agent 워크플로. 이 글이 요구하는 것은 Agents session의 문이 모두 객체라는 것이다.

안정 출력을 되짚으려면 JSON 두 줄을 펼친다

흐름의 대부분은 문이 어긋난 것이다. 모델이 둔해진 것이 아니다.

  • parameters와 arguments를 포맷하고 여분 키와 빠진 칸을 본다.
  • JSONPath 추출로 order_id와 $.parameters.required를 뽑는다.
  • 문을 못 넘긴 호출과 넘긴 호출을 JSON Diff. pattern이 흘렀으면 먼저 Schema를 본다.
  • 카드 Schema와 도구 Schema는 따로 본다. 카드만 잠그면 입구는 열린 채로 남는다.
  • 주문, session, 사내 MCP를 공개 포맷터에 붙이지 마라. JSONSort는 브라우저에서 로컬로 돈다.

두 궤적이 Schema로 다시 세워져야 JSON을 모은 것이다. 아니면 산문을 모은 것이다.

흔한 함정

  • instructions에 「JSON을 출력하라」만 쓴다. 그것은 문이 아니다. 문은 parameters와 호스트 검증기다.
  • required에 일부 칸만 나열한다. 빠진 키는 비어 남을 수 있고 다음 홉에 구멍이 난다.
  • additionalProperties를 안 쓰거나 true로 둔다. 공식 예는 여분 키를 닫는다. refund가 하나 늘면 부작용이 기운다.
  • Responses의 text.format을 Agents session에 복사한다. 자원이 둘이다. 최종 카드는 직접 검증한다.
  • 검증에 떨어져도 함수를 실행한다. 실행의 전제는 문 통과다. 먼저 error를 되돌린다.
  • subagent에게 function을 돌리게 한다. 하위 에이전트는 function 도구를 쓰지 못한다. 엄한 Schema는 주 Agent에 남긴다.

FAQ

Agents API에 Structured Outputs가 있나?

도구 인자는 JSON Schema로 조인다. 사용자에게 보여줄 최종 객체에 session이 Responses text.format을 자동으로 걸지 않는다. 최종 모양을 잠그려면 직접 검증하거나 그 홉을 Responses에 둔다.

공식 Schema가 있는데도 다시 검증하나?

한다. 공식 문은 채움 품질을 올린다. 부작용은 당신 프로세스에서 난다. required_actions의 arguments를 같은 Schema에 통과시키고, 실패면 success: false.

pattern과 enum이 필요한가?

필요하다. 맨 string이면 모델이 1042를 넣고 A-1042를 넣지 않는다. 금액과 상태는 enum이나 명시 형식. 넓은 타입이 가장 흔한 월담이다.

MCP 도구도 같은 규율이 필요한가?

필요하다. inputSchema도 여분 키를 닫고 required를 다 적는다. subagent는 MCP를 이어받고 function은 이어받지 않는다. 자사 주문의 문은 주 Agent에 남긴다.

검증 실패가 session을 멈추게 하나?

같은 turn_id / call_id 쌍으로 tool_result를 되돌리면 된다. error를 분명하게 쓰면 모델이 인자를 바꿔 다시 부를 수 있다. 새 session을 재시도로 쓰지 마라.

결론

Agents API의 안정 JSON은 더 긴 지시가 아니다. 세 문이다. 도구 parameters, 호스트 재검증, 최종 카드의 별 Schema. 실행은 문을 통과한 뒤다.

A-1042가 어느 규칙에서 떨어지는지를 먼저 쓴다. 경로를 가리킬 수 있으면 데이터는 검증 가능하다. 모델은 바뀐다. 문을 먼저 안정시킨다.

더 읽기

변경 기록: 최초 게시