OpenAI Agents API 이후 AI Agent가 JSON을 더 필요로 하는 이유: Harness, Tool Calling, JSON Schema

2026년 9월 18일 기준: Agents API 공개 베타는 Codex harness를 호스팅한다. 루프를 직접 쓰지 않게 되면, 손에 남는 것은 거의 JSON뿐이다 — 도구 Schema, arguments, tool_result, MCP, 세션 이벤트. 계약이 느슨하면 호스팅된 루프는 잘못된 파라미터를 더 빨리 돌릴 뿐이다.

결론부터: Agents API는 루프를 호스팅한다. 계약을 호스팅하지는 않는다. 2026년 9월 10일, OpenAI는 Codex를 돌리는 Agent Harness를 public beta로 개발자에게 넘겼다. 모델 스케줄링, 컨텍스트 compaction, subagent, 샌드박스 수명은 당신 프로세스에서 나와 beta.agents.sessions로 갔다. 당신이 아직 쥐고 있는 것은 거의 전부 JSON이다: function 도구의 JSON Schema, arguments, tool_result의 문자열, MCP의 inputSchema, session 이벤트 스트림. Harness가 루프를 대신 돈다고 해서 필드를 대신 검증하지는 않는다. 계약이 느슨하면, 호스팅된 루프는 나쁜 파라미터를 더 자주 돌릴 뿐이다.

2026년 9월 18일 기준. 본 사이트는 이미 Agent가 JSON 없이는 안 되는 이유, Tool Calling은 왜 JSON Schema에 의존하는가, JSON Schema가 표준 Contract가 될까, MCP / Skills / Tools / Subagents, MCP란 무엇인가를 다뤘다. 이 글은 Agents API 이후 JSON이 왜 덜 중요해지지 않고 더 중요해지는지만 답한다.

9월 10일에 실제로 나온 것

OpenAI의 원문은: Codex를 돌리는 같은 harness와 인프라를, 개발자용 호스팅 클라우드 Agent로 준다. 공개 문서는 beta.agents 네임스페이스에 두고, 요청에는 OpenAI-Beta: agents=v1를 붙인다. Harness 자체는 별도 요금이 없다. 내는 것은 모델 토큰, 도구, 샌드박스 시간이다.

session 생성 한 번에 제출하는 것은 JSON 한 장이다: 모델, 지시, 도구 목록, 환경, 입력. 공식 예시는 gpt-6-astra를 쓴다. 도구는 MCP, 커스텀 function, 내장 검색일 수 있다. 환경은 none, openai_hosted, 또는 Blaxel, Cloudflare, Daytona, E2B, Modal, Vercel 같은 자체 / 협력 샌드박스다. 멀티 Agent는 multi_agent.enabled와 max_concurrent_subagents로 켠다.

이것은 또 하나의 「JSON을 출력하라」는 채팅 인터페이스가 아니다. Responses API는 그대로 있다. Agents SDK도 그대로 있다. Agents API가 가져가는 것은 루프 자체다: 다음 홉을 누가 고르는지, 컨텍스트를 언제 압축하는지, 언제 subagent를 띄우는지. public beta 기간에 필드 이름은 아직 바뀔 수 있다. 계층은 이미 분명하다: OpenAI가 harness를 돌리고, 당신은 도구 계약과 비즈니스 결과를 제공한다.

세 입구: Responses, Agents SDK, Agents API

2026년 9월, OpenAI는 Agent를 만드는 길을 세 갈래로 나란히 둔다. 섞기 전에 「루프가 어디서 도는지」를 나눠라:

입구루프가 도는 곳상태가 있는 곳당신이 아직 쓰는 것
Responses API당신 앱당신이 조립하는 history / Conversations모델 호출, 도구 회수, 루프 전체
Agents SDK당신 프로세스SDK session + 당신 저장소승인, 배포, 루프는 아직 고칠 수 있다
Agents APIOpenAI가 호스팅하는 Codex harness서버 측 session / turn / item도구 정의, function 결과, 환경 선택; 루프는 고칠 수 없다

단발 완성은 계속 Responses를 써라. 승인과 저장을 직접 쥐려면 SDK. 「며칠짜리 작업, 압축, subagent, 샌드박스」를 넘기려면 Agents API. 세 길 모두 도구 파라미터는 JSON Schema다. 차이는: 앞 둘은 루프에 패치를 넣을 수 있다; 셋째는 패치를 계약과 회수에만 넣을 수 있다.

Agent Harness가 무엇인가, 무엇을 서명하지 않는가

Harness는 모델과 부작용 사이의 런타임이다: 이벤트를 읽고, 도구를 고르고, 결과를 먹이고, 컨텍스트를 압축하고, 긴 작업에서 진행을 지킨다. Codex 쪽은 지금 오픈소스로 볼 수 있다. Agents API는 OpenAI가 같은 로직을 운영하고 모델 버전과 함께 올린다. 발표가 짚은 능력은 자동 compaction, Tool search, Programmatic Tool Calling, 병렬 subagents다.

서명하지 않는 것들:

  • 어떤 customer_id가 있어야 하는지, UUID여야 하는지;
  • 함수가 여분 키를 받아도 되는지;
  • MCP Server의 inputSchema가 느슨한지 빡빡한지;
  • 모델에 되돌리는 output이 객체인지, 문자열인지, 채팅 문단인지.

이들은 여전히 JSON Schema와 당신 자신의 이차 검증이다. 호스팅 harness가 올리는 것은 「루프가 얼마나 오래, 얼마나 병렬로 도느냐」다. 「이번 홉의 파라미터가 적법한가」는 올리지 않는다. 둘을 같은 일로 보는 것이, 이 글이 쪼개는 첫 번째 오해다.

호스팅 뒤 JSON 홉이 오히려 늘어나는 이유

직접 루프를 쓸 때, 나쁜 JSON은 대개 당신 쪽에서 죽는다: parse 실패, 필드 불일치, 그러면 멈춘다. 루프가 호스팅되면 실패는 미뤄지고, 복제되고, 더 많은 채널로 간다:

홉페이로드누가 만드나누가 검증해야 하나
session 생성agent / tools / environment JSON당신 앱당신: 제출 전
function 정의JSON Schema (parameters)당신 앱당신: required / additionalProperties를 조여라
모델이 호출arguments 객체호스팅 harness + 모델당신: 실행 전 한 번 더 검증
결과 회수tool_result.output 문자열당신 앱당신: 적법한 값을 먼저 stringify
MCPJSON-RPC + inputSchemaServer / harnessServer와 당신 허용 목록
이벤트 스트림agent.session.* JSON 이벤트호스팅 서비스당신: type으로 분기하고, 채팅 본문으로 parse하지 마라

여기에 Tool search가 정의를 필요 시 로드하고, Programmatic Tool Calling이 코드에서 호출을 직병렬로 잇고, subagent가 각자 컨텍스트를 가져가면 — 사용자 작업 한 번의 JSON 왕복은 「단발 Function Calling」보다 한 토막 길다. 호스팅은 이 홉들을 보이지 않게 한다. 보이지 않는다고 검증을 생략해도 된다는 뜻은 아니다. 홉별 분해는 Tool Calling에서 MCP까지를 보라.

Tool Calling: function 도구는 여전히 JSON Schema

Agents API의 function 도구는 Responses API와 같은 정의다. agent.tools에 넣는 것은 자연어 문단이 아니라 이름, 설명, JSON Schema 한 장이다:

{
  "type": "function",
  "name": "get_customer",
  "description": "Look up a customer by ID.",
  "parameters": {
    "type": "object",
    "properties": { "customer_id": { "type": "string" } },
    "required": ["customer_id"],
    "additionalProperties": false
  }
}

공식 예시는 required를 채우고 additionalProperties를 false로 둔다. 조판 습관이 아니다. Agent가 키를 하나 더 써도 되면, 그 키는 경로, SQL 조각, 또는 삭제가 될 수 있다. Schema는 모델이 디코드할 때 보는 계약이고, 당신이 실행 전에 한 번 더 돌려야 하는 계약이다. strict 모드, ajv, 이차 검증 파이프라인은 Tool Calling은 왜 JSON Schema에 의존하는가를 보라.

description 필드는 여전히 쓸모 있다. 모델이 도구를 고르는 데 돕는다. 타입, enum, required를 대체하지는 않는다. Harness가 똑똑할수록, 도구 더미에서 「대충 맞는」 하나를 고른다 — 대충 맞는 호출은 Schema만이 막는다.

requires_action과 tool_result: 회수도 JSON

모델이 당신 함수를 돌릴 때, session은 agent.session.requires_action에서 멈춘다. 대기 항목은 required_actions에 있다. 「history에 function_call 한 줄」만으로는 충분하지 않다. 전형적인 pending 호출은 이렇다:

{
  "type": "function_call",
  "turn_id": "turn_123",
  "call_id": "call_123",
  "name": "get_customer",
  "arguments": { "customer_id": "123" }
}

문서는 arguments를 객체로 적는다. 채팅 답에 다시 싸서 JSON.parse로 긁지 마라 — 그건 이전 글 JSON.parse가 실패하는 이유의 채널 오류다. 할 일은: 같은 Schema로 이 객체를 검증하고, 함수를 실행하고, session 이벤트 인터페이스에 agent.session.input.tool_result를 되돌리되 원래 turn_id / call_id를 붙이는 것이다.

성공이면 success: true, output은 문자열 또는 지원되는 콘텐츠 배열이다. 객체는 먼저 JSON.stringify. 실패면 success: false, 모델이 읽을 수 있는 error를 줘라. 스택, 비밀, DB 행 전체를 되돌리지 마라. 프로세스가 실행 뒤, 회수 전에 죽으면 session / turn / call로 멱등하게: 재시작 후 먼저 pending을 읽고, 다시 돌릴지 결정하라.

Function은 항상 당신 앱에서 돈다. session에 샌드박스가 있어도. Harness는 get_customer를 대신 실행하지 않는다. 당신이 온라인이 아니면 이 홉은 걸린다. 호스팅 루프에서, 아직 완전히 당신 것인 몇 안 되는 동기 지점이다 — JSON을 맞춰야 하는 그 홉이기도 하다.

Tool search와 Programmatic Tool Calling

도구가 많아지면 Schema 전부를 컨텍스트에 넣으면 토큰을 태우고 캐시를 깨뜨린다. Agents API는 기본으로 function을 즉시 로드한다. 드물게 쓰는 것은 defer_loading: true로 두고, agent.tools에 {"type": "tool_search"}를 넣는다. 모델은 먼저 관련 정의를 찾고, 그다음 호출한다. 그래서 「정의 자체도 JSON」인 홉이 하나 더 생긴다: 검색된 Schema는 당신이 실제로 구현한 함수와 같아야 한다. 넓은 계약을 검색시키고 좁은 구현을 실행하지 마라.

Programmatic Tool Calling은 지원 모델이 짧은 코드를 써서 적격 도구를 병렬 또는 직렬로 돌리고, 필터된 결과만 컨텍스트로 가져온다. 「매 홉마다 창을 채우는」 비용은 낮춘다. 「중간 JSON이 적법해야 한다」는 요구는 높인다. 중간 결과의 타입이 표류하면, 뒤의 필터와 병합은 당신이 못 보는 harness 안에서 조용히 틀린다. SDK 쪽에는 이미 「구조화 오류를 JSON으로 인코딩」하는 패치가 있다. 이 경로는 Schema를 먹지, 산문을 먹지 않는다.

MCP와 Subagents: Schema가 늘고 JSON이 는다

MCP Server를 agent.tools에 쓰면, harness가 도구를 발견하고, 호출하고, 결과를 모델에 먹인다. function과 다르다: 이 호출은 당신 앱을 거치지 않는다. HTTP는 기본으로 OpenAI가 연결한다; 환경에서 연결하거나, 샌드박스에서 stdio로 프로세스를 띄울 수도 있다. 이 층에서 당신이 할 수 있는 것은 allowed_tools, 초기화 실패가 turn을 실패시킬지(required: true), Server 자신의 inputSchema가 얼마나 빡빡한지다.

MCP 메시지는 여전히 JSON-RPC다. Schema가 느슨하면, 호스팅 harness는 당신이 못 보는 요청을 더 많이 친다. 「프로토콜이 안전하게 해 줬다」가 아니다. 「루프가 더 멀어졌다」다. 프로토콜 계층은 MCP란 무엇인가; Skills, Subagents와의 경계는 2026 Agent 스택을 보라.

Subagents는 각자 컨텍스트를 갖고, 메인 Agent가 모은다. 병렬은 지연을 낮추고, arguments도 병렬로 여러 장 친다. 메인 Agent가 받는 요약이 여전히 Schema 없는 긴 텍스트면, 「채팅을 파싱」하는 일을 마지막 홉으로 미룬 것뿐이다. 프로그램에 들어갈 결론은 최종 답도 Structured Output 또는 당신이 정의한 결과 Schema로 가라. 산문에서 다시 긁지 마라. Structured Output이란 무엇인가를 보라.

로컬에서 여전히 검증할 네 가지

Harness가 호스팅된 뒤, 목록은 짧아지지 않는다. 좁아진다:

  1. 도구 Schema. required를 채우고, additionalProperties: false, enum을 조여라. 설명 문구로 부작용을 막지 마라.
  2. 실행 전 arguments. 벤더가 Schema를 이미 적용했어도, 당신 프로세스에서 같은 문서로 다시 검증하라. 타입 오류, 빠진 필드, 여분 키는 이 층에서 막는다.
  3. 회수하는 output. 먼저 적법한 JSON을 만든 뒤 stringify. 오류는 success: false로 보내라. 내부 예외 원문을 모델에 주지 마라.
  4. 이벤트와 채팅 본문을 채널로 나눠라. event.type을 읽고, SSE 전체를 JSON 값으로 취급하지 마라. 사용자에게 줄 구조화 답은 Structured Output으로 보내고, assistant 문장을 JSON.parse하지 마라.

보안 쪽도 기억하라: arguments 안의 문자열은 주입일 수 있다. 「타입이 맞으니 실행」이 아니다. 악성 JSON과 Prompt Injection을 보라. JSON Schema가 벤더를 가로지르는 계약이 될지는 표준 Contract를 보라 — Agents API는 이 판단을 약화하지 않는다. 당신이 아직 고칠 수 있는 유일한 층으로 밀어 올렸을 뿐이다.

로컬 JSON 도구로 계약을 보라

호스팅 session에 넘기기 전에, 브라우저에서 텍스트 세 장을 보라: 도구 Schema, 샘플 arguments, 회수할 output.

  • JSON 검증기 — 문법이 적법한가; Schema가 있으면 필드, 필수, 여분 키를 같이 검사하라.
  • JSON 포맷터 — 한 줄로 눌린 tool_result를 펼쳐, DB 행 전체를 직렬화했는지 보라.
  • JSON Diff — 「모델이 보낸 arguments」와 「Schema가 허용하는 최소 객체」를 비교하라.

데이터는 브라우저를 떠나지 않는다. 실패한 required_actions, parameters 한 장, stringify한 결과를 나란히 보기 좋다. 계약이 안정되면, 그때 hosted harness에 며칠을 맡겨라.

FAQ

Agents API면 JSON Schema를 안 써도 되나?

반대다. 루프가 호스팅된 뒤, Schema는 당신이 아직 쥐고 있는 주 계약이다. function의 parameters, MCP의 inputSchema, 회수하는 output은 여전히 JSON이다.

Agents API, Agents SDK, Responses API는 어떻게 고르나?

단발 호출은 Responses. 루프, 승인, 저장을 직접 쥐려면 SDK. 긴 작업, 압축, subagent, 샌드박스를 OpenAI에 넘기려면 Agents API. 세 길 모두 도구 파라미터는 JSON Schema다.

arguments가 이미 객체인데 JSON.parse를 해야 하나?

주변 채팅을 다시 parse하지 마라. 문서대로 객체로 보고, 같은 JSON Schema로 검증하라. 산문에서 arguments를 긁는 것은 채널을 잘못 쓴 것이다.

tool_result는 왜 stringify해야 하나?

문서는 output을 문자열 또는 지원되는 콘텐츠 배열로 요구한다. 먼저 적법한 JSON을 만든 뒤 stringify하라. 이중 인코딩과 「객체처럼 보이지만 실제는 문자열」을 섞지 않기 위해서다.

MCP 도구는 내 앱을 거치나?

기본은 아니다. harness가 Server에 직접 붙는다. 조여야 하는 것은 Server 자신의 inputSchema, allowed_tools, 그리고 Server 안 비가역 작업의 승인이다.

public beta 기간에 필드가 바뀌나?

바뀐다. 이 글은 2026년 9월 18일 공개 문서를 따른다. 계층은 바뀌지 않는다: harness가 루프를 돌리고, 당신은 JSON 계약을 제공한다. 필드 이름이 바뀌어도 검증 책임은 당신 쪽이다.

요약

Agents API가 낮추는 것은 「Agent를 끝까지 어떻게 돌릴까」의 공수다. 높이는 것은 「매 홉 JSON이 맞아야 한다」의 가중치다. 9월 10일에 넘긴 것은 Codex harness다: session, 압축, 도구 검색, 프로그램적 호출, subagent, 샌드박스. customer_id가 어떻게 생겨야 하는지는 검사하지 않고, tool_result를 적법한 문자열로 만들어 주지도 않는다.

2026년에 Agent를 프로그램에 붙이는 순서는 그대로다: 도구는 JSON Schema, 결과는 Structured Output, 채팅 본문은 API가 아니다. 바뀐 것은, 루프가 호스팅되면 패치를 넣을 곳이 계약뿐이라는 점이다. 먼저 로컬 검증 도구에서 Schema, arguments, 회수 결과를 보고, 그다음 hosted session에 맡겨라. 모델은 바뀐다. harness는 버전을 바꾼다. 당신의 필드 계약은 같이 느슨해지면 안 된다.