OpenAI Agents API의 JSON 데이터 구조: Agent, Tool Call, Tool Result, Handoff는 어떻게 이어지나 (2026)
공개 베타 이후 흐려지는 것은 「도구를 부를 수 있는가」가 아니다. 네 대상이 한 사슬에 어떻게 고정되는가다. 공식에는 handoff라는 item이 없다. 고정하는 것은 ID다: session_id, turn_id, call_id, agent_id. 이 글은 그 JSON 사슬만 푼다.
읽기 전 한눈
| 대상 | 공식 모양 | 고정핀 |
|---|---|---|
| Agent | 모델, 도구 표, multi_agent | session 생성 때 기록 |
| Tool Call | required_actions 안의 function_call | turn_id + call_id |
| Tool Result | agent.session.input.tool_result | 같은 쌍을 되돌림 |
| Handoff | create / send / wait / interrupt items | agent_id / subagent_id |
이력의 function_call은 결과 대기를 뜻하지 않는다. 대기는 required_actions를 본다.
네 대상은 ID로 이어진다. 자연문이 아니다
Agents API에서 Agent는 session 생성 때 쓴 설정이다. Tool Call은 멈출 때 required_actions 안의 function_call이다. Tool Result는 이벤트 스트림으로 되돌리는 agent.session.input.tool_result다. 여기서 Handoff는 편성 item의 묶음이지 SDK handoff 객체가 아니다.
- 같은 turn_id / call_id 쌍: 호출과 결과가 맞아야 harness가 이어진다.
- 이력만으로는 부족하다: items의 function_call은 「불렀다」는 뜻뿐이다. 대기 여부는
required_actions를 읽는다. - Handoff는 type이 아니다:
create_subagent_call과subagent_id를 본다. handoff라는 칸을 찾지 마라.
도구 호출은 OpenAI Agents API 완전 해설. 긴 일의 이음은 오래 도는 Agents API. 루프를 맡길지는 Agents API vs Responses API. Agent와 Function Calling이 아직 한 단어라면 AI Agent 완전 가이드.
Agent: 설정 객체이지 답변이 아니다
생성 때 모델, 지시, 도구, multi_agent를 한 번에 쓴다. 이후 item의 주어가 된다. 공식은 POST /v1/agents/sessions, 헤더는 OpenAI-Beta: agents=v1:
{
"agent": {
"model": "gpt-6-astra",
"instructions": "Look up orders. Confirm before any refund.",
"tools": [
{
"type": "function",
"name": "lookup_order",
"parameters": {
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
},
"required": ["order_id"],
"additionalProperties": false
}
}
],
"multi_agent": { "enabled": true, "max_concurrent_subagents": 3 }
},
"environment": { "type": "openai_hosted" },
"input": [{ "role": "user", "content": [{ "type": "input_text", "text": "Look up A-1042." }] }]
}
Agent 설정은 session에 남는다. 모델이나 추론 강도를 바꾸면 이 session 설정을 갱신한다. 저장된 템플릿은 새 session에만 적용된다. 환경은 다른 자원이다: openai_hosted 또는 자체 샌드박스. session을 지워도 sandbox는 남는다.
Tool Call: required_actions만 믿는다
함수 결과가 필요하면 session이 agent.session.requires_action을 낸다. 대기는 required_actions에 있고, 모양은 turn_id / call_id가 있는 function_call이다:
{
"type": "function_call",
"turn_id": "turn_2",
"call_id": "call_3c91",
"name": "lookup_order",
"arguments": { "order_id": "A-1042" }
}
인자는 Schema를 통과해야 한다. 넓은 object는 문이 아니다. 계약은 AI에 JSON Schema가 필요한 이유. 이력에도 function_call이 나올 수 있다. 공식은 이력만으로 대기를 단정할 수 없다고 쓴다.
Tool Result: 같은 쌍을 이벤트 스트림에 되돌린다
함수를 실행한 뒤 session events 엔드포인트에 agent.session.input.tool_result를 보낸다. turn_id와 call_id는 대기 동작에서 그대로 베낀다. 성공은 success: true와 문자열 output. 실패는 success: false와 error:
{
"type": "agent.session.input.tool_result",
"turn_id": "turn_2",
"call_id": "call_3c91",
"success": true,
"output": "{\"order_id\":\"A-1042\",\"status\":\"paid\"}"
}
객체는 먼저 문자열로 직렬화한다. 끊기기 전에 이미 실행했다면 같은 쌍으로 저장 결과를 다시 낸다. 새 session으로 「재시도」하지 마라. harness는 필요한 결과가 모인 뒤에 turn을 이어간다.
Handoff: 편성 item이지 칸 이름이 아니다
multi_agent.enabled를 켜면 harness가 생성, 전달, 대기, 중단을 준다. 이 도구를 선언하지 않는다. items에 편성 동작이 나타난다:
{
"data": [
{
"type": "create_subagent_call",
"agent_id": "agent_main",
"task": "Compare A-1042 packing notes with the invoice."
},
{
"type": "send_subagent_input_call",
"subagent_id": "sub_7a12",
"input": "Focus on line items only."
},
{
"type": "agent_message",
"from": "sub_7a12",
"text": "Invoice lines match packing notes for A-1042."
}
]
}
- create_subagent_call: 파견.
agent_id는 누가 요청했는지. 완료는 하위 일 종료가 아니다. - send / wait / interrupt: 전달, 대기, 중단.
agent_message는 보이는 내부 텍스트를 담을 수 있다. 스트림은 전체 대본이 아니다. - SDK handoff 객체: 프로세스 안의 runner다. 이 items와는 다른 자원이다.
다음 주어에 넘기는 것은 감사 가능한 JSON이어야 한다. 주문 번호, 의도, 근거. Schema로 담는 법은 JSON Schema와 Agent 워크플로.
고정핀이 맞아야 사슬이 선다
네 대상을 로컬에서 다시 세울 수 있는 사슬로 접는다. 가상 주문. 실제 비밀은 붙이지 마라:
{
"session_id": "sess_8f21",
"links": [
{ "object": "agent", "id": "agent_main" },
{ "object": "turn", "id": "turn_2" },
{ "object": "function_call", "call_id": "call_3c91" },
{ "object": "tool_result", "call_id": "call_3c91" },
{ "object": "subagent", "id": "sub_7a12", "via": "create_subagent_call" }
]
}
| 대상 | 보이는 type | 어디에 고정하나 | 흔한 어긋남 |
|---|---|---|---|
| Agent | session.agent | session_id | 새 session을 재시도로 씀 |
| Tool Call | function_call | turn_id + call_id | items 이력만 봄 |
| Tool Result | input.tool_result | 같은 쌍 | output이 객체로 남음 |
| Handoff | create_subagent_call | agent_id / subagent_id | handoff라는 칸을 찾음 |
| Turn | turn.completed / failed | turn_id; 루트의 subagent_id는 비어 있음 | 유휴를 성공으로 읽음 |
MCP 행은 다른 도구 표다. 두 번째 ID 체계를 만들지 않는다. 경계는 MCP Tool Schema vs Function Calling.
이 사슬을 되짚으려면 JSON 두 줄을 펼친다
어긋남의 대부분은 고정핀이 흘렀기 때문이다. 모델이 둔해진 것이 아니다.
- required_actions와 되돌림 이벤트를 포맷하고
call_id가 한 글자도 다르지 않은지 본다. - JSONPath 추출로
turn_id,call_id,subagent_id를 뽑는다. - 같은 주문의 호출과 결과를 JSON Diff. 흘렀으면 먼저 ID를 본다.
- 주 Agent items와 어떤 subagent items는 따로 나열한다. 한 페이지에 섞이면 편성이 다른 주체에 붙는다.
- 주문, session, 사내 MCP를 공개 포맷터에 붙이지 마라. JSONSort는 브라우저에서 로컬로 돈다.
두 궤적이 ID로 다시 세워져야 구조를 읽는 것이다. 아니면 구호를 읽는 것이다.
흔한 함정
- items의 function_call을 대기로 본다. 대기는 required_actions다.
- 되돌릴 때 쌍을 바꾼다. harness가 맞추지 못하고 turn이 멈춘다.
- output에 객체를 그대로 넣는다. 공식은 문자열 또는 지원되는 content 배열을 요구한다.
- payload에서 type: handoff를 찾는다. Agents API는 create / send / wait / interrupt다.
- subagent에게 function을 돌리게 한다. 하위 에이전트는 function 도구를 쓰지 못한다. 조회는 주 Agent에 남긴다.
- SDK handoff와 session items를 한 자원으로 본다. 하나를 지워도 다른 것은 남는다.
FAQ
Tool Call과 Tool Result는 반드시 쌍인가?
대기 중인 호출은 되돌려야 한다. 이미 끝난 이력 function_call에는 다시 낼 필요가 없다. 기준은 required_actions이지 items 길이가 아니다.
Agents API에 handoff라는 JSON 칸이 있나?
일급 item type으로는 없다. 편성은 create_subagent_call, send_subagent_input_call, wait_for_subagents_call, interrupt_subagent_call. SDK handoff는 다른 객체다.
call_id와 item_id는 무엇이 다른가?
call_id는 함수 호출과 그 결과를 고정한다. item_id는 저장된 item의 이름이다. 스트림은 item_id로 증분을 같은 부분에 붙인다. 핀이 둘이다. 섞지 마라.
turn이 완료인데 도구가 실패할 수 있나?
공식은 completed가 모든 도구 성공을 뜻하지 않는다고 쓴다. tool_result의 success와 그 turn의 items도 본다.
앱 안에서 자체 handoff JSON을 가져도 되나?
된다. 가져야 한다. 그것은 업무 객체다: 주문 번호, 의도, 근거. 공식 item이 금액을 기억하길 바라지 마라. Schema로 쓰고 session items와 따로 둔다.
결론
Agent, Tool Call, Tool Result, Handoff는 네 편의 산문이 아니다. 한 JSON 사슬 위의 네 대상이고 고정핀은 ID다. 공식 되돌림은 이벤트이고 공식 인계는 편성 item이다.
call_id만으로 호출과 결과를 맞출 수 있는지를 먼저 쓴다. 되면 구조가 이어진 것이다. 모델은 바뀐다. 고정핀을 먼저 안정시킨다.
더 읽기
- OpenAI Agents API 완전 해설 Session, Tool Calling, Schema의 이음
- 오래 도는 Agents API Session, compact, Subagents
- Agents API vs Responses API 누가 루프를 도나
- AI에 JSON Schema가 필요한 이유 Schema / Function Calling / Structured Output
- JSON Schema와 Agent 워크플로 도구와 상태를 Schema로 담기
- JSONSort 로컬 도구상자 포맷, 검증, Diff
- 모든 글 JSON과 AI 개발 메모
변경 기록: 최초 게시