Responses API와 Chat Completions의 선택은 어떤 엔드포인트가 더 최신인지의 문제가 아니라, 대화 상태를 누가 관리하느냐의 문제입니다. Chat Completions는 매 턴마다 오케스트레이터가 전체 messages 배열을 다시 전송해야 합니다. 반면, Responses는 저장된 응답 ID(response ID)에서 이어서 진행할 수 있습니다. 2026년 10월 3일 기준으로 TokenLab의 공개 API 문서와 라이브 모델 API를 확인한 결과, 선택은 모델이 허용하는 형식에 따라 달라집니다. 따라서 결정 테이블부터 살펴보겠습니다.
| 에이전트 요구사항 | 선택 | 이유 |
|---|---|---|
| 기존 OpenAI 호환 채팅 클라이언트, 여러 모델 간 이식성 | Chat Completions (/v1/chat/completions) |
API 형식 가이드(2026-10-03 관찰)에 따른 가장 폭넓은 모델 호환성 |
| 서버 측에 저장된 응답에서 대화 이어가기 | Responses (/v1/responses) |
previous_response_id 및 store는 Responses 필드임 |
| 턴 사이의 임의적인 기록 편집, 가지치기 또는 메시지 마스킹 | Chat Completions | 전체 messages 배열을 직접 관리함 |
| 백그라운드 응답 또는 Responses 이벤트 | Responses | 이를 지원하는 모델에 대해 문서화됨 |
| 연속성을 가진 지속적인 WebSocket 연결 | Responses | wss://api.tokenlab.sh/v1/responses |
| 이식 가능한 JSON 객체 출력 | Chat Completions | response_format: {"type": "json_object"} |
모델이 openai_responses를 나열하지 않음 |
Chat Completions 또는 모델의 네이티브 형식 | Responses는 이를 나열하는 모델 전용임 |
| Claude thinking 블록, Claude 도구 사용, 프롬프트 캐시 필드 | Anthropic Messages (/v1/messages) |
Chat Completions는 해당 필드를 보장하지 않음 |
Gemini contents, 파일, 캐시된 콘텐츠, Gemini 도구 |
Gemini (/v1beta/models/:model:generateContent) |
Gemini 네이티브 필드는 Gemini 경로에서 유지 |
핵심 요약
- Chat Completions는 클라이언트 관리 상태입니다. Responses는
previous_response_id를 통해 서버 참조가 가능합니다. - 도구 호출은 중첩된
tool_calls와role: "tool"메시지에서 타입이 지정된function_call및function_call_output항목으로 변경됩니다. - 스트리밍은
choices[0].delta청크에서 명명된 이벤트로 변경되며, Responses 클라이언트는response.failed및response.incomplete도 감시해야 합니다. - 형식 지원은 모델마다 다릅니다. 2026년 10월 3일 기준으로 예시 모델 3개 중
gpt-5.6-terra만openai_responses를 허용했습니다. - 한 대화 내에서 형식을 혼용하지 마십시오. 다음 요청 전에 저장된 기록을 변환하고 검증하십시오.
Responses API vs Chat Completions: 동일한 작업, 두 가지 요청
단일 턴 작업을 예로 들어보겠습니다: 하늘이 파란 이유를 두 문장으로 설명하세요. 다음은 API 형식 가이드(2026-10-03 관찰)의 클라이언트 설정을 사용한 Chat Completions 요청입니다:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
)
response = client.chat.completions.create(
model="gpt-5.6-terra",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain why the sky is blue in two sentences."},
],
max_tokens=1000,
)
print(response.choices[0].message.content)
다음은 동일한 작업을 Responses 요청으로 수행한 것입니다:
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
)
response = client.responses.create(
model="gpt-5.6-terra",
instructions="You are a helpful assistant.",
input="Explain why the sky is blue in two sentences.",
max_output_tokens=1000,
)
print(response.output_text)
이 간단한 사례에서 필드는 일대일로 매핑됩니다. 시스템 메시지는 최상위 instructions가 됩니다. messages는 문자열이나 항목 배열을 허용하는 input이 됩니다. max_tokens는 max_output_tokens가 됩니다.
응답 본문은 더 큰 차이가 있습니다. Chat Completions는 choices[0].message와 finish_reason을 반환합니다. Responses는 타입이 지정된 항목들의 output 배열과 status 필드를 반환합니다. Create Response 참조(2026-10-03 관찰)에는 queued, in_progress, completed, incomplete, failed, cancelled가 나열되어 있으며, HTTP 요청이 성공하더라도 status를 확인하라고 안내합니다. 사용량 키도 다릅니다: 한쪽은 prompt_tokens와 completion_tokens, 다른 쪽은 input_tokens와 output_tokens입니다. 참조 예시에는 각각 20/9/29와 10/12/22가 표시되어 있습니다. 이는 측정값이 아닌 샘플 페이로드입니다.
대화 이어가기
지원 에이전트가 질문 하나에 답한 후 "주문 #4472는 어떻게 됐나요?"라는 질문을 받았다고 가정해 봅시다. Chat Completions의 경우, 새로운 사용자 메시지를 저장된 배열에 추가하고 모든 것을 다시 전송합니다. Responses의 경우, 이전 응답의 id를 전달하고 새로운 턴만 보냅니다:
{
"model": "gpt-5.6-terra",
"previous_response_id": "resp_abc123",
"input": "What about order #4472?"
}
store는 기본값이 true이므로, 끄지 않는 한 이전 응답은 나중에 검색할 수 있도록 보관됩니다. 애플리케이션은 반환된 id를 홉(hop) 사이에서 지속적으로 유지해야 합니다. 사용자 지정 가지치기나 메시지 마스킹이 필요하다면 상태 비저장(stateless) 형식이 더 적합합니다.
대략적인 추정치를 보면 에이전트 규모에서 상태 비저장 재전송이 왜 중요한지 알 수 있습니다. 20번의 호출 각각에 10,000 토큰의 기록을 재전송한다고 가정하면 총 200,000 입력 토큰이 됩니다. gpt-5.6-terra 입력 가격인 100만 토큰당 $0.60를 적용하면 200,000 × 0.60 / 1,000,000 = $0.12가 됩니다(추정치; 토큰 수 가정, 캐싱 없음). 이 가격은 2026년 10월 2일 16:53:30.068Z에 업데이트된 가격으로, 2026년 10월 3일 관찰된 라이브 모델 API에서 가져왔습니다.
두 가지 주의사항이 있습니다. 우리가 읽은 문서에는 서버가 보유한 컨텍스트가 입력 토큰으로 청구되는지 명시되어 있지 않으므로, 자신의 트래픽에서 usage를 직접 비교하십시오. 또한 272K 입력 토큰을 초과하는 요청은 전체 요청에 대해 입력 2배, 출력 1.5배의 가격이 적용됩니다: $0.60 및 $3.60 대신 100만 토큰당 $1.20 및 $5.40입니다. 긴 상태 비저장 기록은 이 기준을 넘을 수 있습니다.
도구 호출: 요청 및 응답 형태
두 형식 모두 Structured Outputs & Tool Calling 가이드(2026-10-03 관찰)의 루프를 따릅니다. 도구를 보내고, 호출을 읽고, 백엔드에서 실행하고, 결과를 같은 형식으로 반환한 뒤 최종 답변이 올 때까지 반복합니다. 형태는 다릅니다.
Chat Completions. 도구는 {"type": "function", "function": {...}}로 선언됩니다. 모델은 choices[0].message의 tool_calls로 응답하며, finish_reason은 tool_calls입니다. 계속하려면 tool_calls가 포함된 어시스턴트 메시지와 tool_call_id를 포함한 role: "tool" 메시지를 포함하여 전체 배열을 다시 보냅니다. 아래 값은 예시이며, lookup_customer는 가이드에서 사용된 도구 이름입니다:
{
"model": "gpt-5.6-terra",
"messages": [
{"role": "user", "content": "Look up ana@example.com"},
{
"role": "assistant",
"content": null,
"tool_calls": [
{
"id": "call_xyz789",
"type": "function",
"function": {"name": "lookup_customer", "arguments": "{\"email\": \"ana@example.com\"}"}
}
]
},
{"role": "tool", "tool_call_id": "call_xyz789", "content": "{\"status\": \"active\"}"}
]
}
Responses. 모델은 output 내의 function_call 항목으로 응답하며, 각 항목은 call_id, name, arguments를 포함합니다. 결과를 function_call_output 입력 항목으로 반환하고 이전 응답을 가리킵니다:
{
"model": "gpt-5.6-terra",
"previous_response_id": "resp_abc123",
"input": [
{
"type": "function_call_output",
"call_id": "call_xyz789",
"output": "{\"status\": \"shipped\", \"tracking_number\": \"1Z999\"}"
}
]
}
중간 어시스턴트 도구 메시지를 다시 작성할 필요가 없습니다. parallel_tool_calls는 두 형식 모두에 존재합니다. Create Response 참조에서는 기본값이 true입니다. Chat 참조에서는 기본값 없이 "선택된 모델이 지원할 때" 적용된다고 명시되어 있습니다.
한 가지 간극을 발견했습니다. 우리가 읽은 페이지에서는 Responses의 tools와 tool_choice를 일반적인 용어로만 설명합니다. 도구 스키마를 포팅하기 전에 Create Response 참조에서 도구 정의 형태를 읽고 대상 모델에서 테스트하십시오. 또한 가이드의 다음 규칙을 준수하십시오:
- TokenLab은 내장 도구를 형식 간에 변환하지 않습니다.
- 부작용이 발생하기 전에 도구 인수를 검증하십시오.
- 클라이언트 재시도가 동일한 호출을 반복할 수 있으므로 도구 실행을 멱등성(idempotent) 있게 만드십시오.
- 자체 권한 확인을 적용하십시오.
스트리밍: 클라이언트가 처리해야 할 이벤트
Chat Completions는 SSE 청크를 스트리밍합니다. 클라이언트는 choices[0].delta.content를 읽고, finish_reason을 기록하며, choices가 없는 청크는 건너뜁니다. stream_options={"include_usage": True}를 사용하면 사용량 정보가 해당 청크에 포함되어 도착합니다. 스트리밍 가이드(2026-10-03 관찰)는 stop을 완료로, length를 토큰 제한으로, tool_calls를 도구 실행 요청으로 처리합니다.
Responses는 명명된 이벤트를 스트리밍합니다. 가이드의 소비자는 다음을 처리합니다:
- 추가할 텍스트에 대한
response.output_text.delta - 성공 마커인
response.completed - 종료 문제인
response.failed,response.incomplete및error
import os
from openai import OpenAI
with OpenAI(
api_key=os.environ["TOKENLAB_API_KEY"],
base_url="https://api.tokenlab.sh/v1",
timeout=30.0,
max_retries=0,
) as client:
completed = False
with client.responses.create(
model="gpt-5.6-terra",
input="Write a short poem.",
stream=True,
) as stream:
for event in stream:
if event.type == "response.output_text.delta":
print(event.delta, end="", flush=True)
elif event.type == "response.completed":
completed = True
elif event.type in {"response.failed", "response.incomplete", "error"}:
raise RuntimeError(f"Stream ended with {event.type}")
if not completed:
raise RuntimeError("Stream closed before response.completed")
가이드에 따라 클라이언트는 다음 세 가지 규칙을 따라야 합니다:
- 첫 번째 이벤트가 도착한 후 중단된 스트림은 불완전하며 자동으로 다시 시작되지 않습니다. 도움이 된다면 부분 출력을 유지하되 완료된 것으로 표시하지 마십시오. 반복된 요청은 다를 수 있으며 비용이 다시 발생할 수 있습니다.
- 네트워크 청크 하나가 반드시 완전한 이벤트 하나는 아니므로 SDK를 사용하여 SSE를 파싱하십시오.
- 사용자가 중지하거나 연결을 끊으면 업스트림 스트림을 취소하고 서버의 키를 유지하십시오.
Responses는 wss://api.tokenlab.sh/v1/responses에서 WebSocket도 제공합니다. 이곳의 응답은 항상 스트리밍되며, background와 response.cancel은 지원되지 않고, 각 연결은 한 번에 하나의 활성 응답을 실행하며, 연결은 최대 60분 동안 지속됩니다. 이벤트는 응답 범위의 sequence_number를 가집니다. error라는 이름의 스트림 이벤트는 평면 객체인 반면, 연결 및 프로토콜 오류는 중첩된 error 객체를 사용하므로 두 형태를 모두 처리하십시오.
전송 전 모델이 허용하는 형식 확인
Get a Model 참조(2026-10-03 관찰)에 설명된 대로 모델 페이지 또는 GET /v1/models/{model}에서 tokenlab.accepted_request_formats를 읽으십시오:
curl "https://api.tokenlab.sh/v1/models/gpt-5.6-terra"
응답에는 accepted_request_formats, pricing, 제한 사항 및 lifecycle이 포함된 tokenlab 객체가 있습니다. 값은 오류 가이드에 설명된 대로 엔드포인트에 매핑됩니다: openai_chat_completions는 /v1/chat/completions로, openai_responses는 /v1/responses로, anthropic_messages는 /v1/messages로, gemini_generate_content는 /v1beta/models/{model}:generateContent로 매핑됩니다. 허용된 형식은 엔드포인트만 확인합니다. 개별 도구와 필드는 여전히 모델마다 다를 수 있습니다.
2026년 10월 3일 기준으로 이 기사에서 참조하는 세 가지 모델을 라이브 모델 API와 대조했습니다:
| 모델 | 허용된 형식 | 최대 입력 / 출력 토큰 | 100만 토큰당 가격 (USD) | 출처 |
|---|---|---|---|---|
gpt-5.6-terra |
openai_chat_completions, openai_responses |
1,050,000 / 128,000 | 0.60 / 3.60 (입력 272K까지) | 라이브 API |
claude-sonnet-5 |
anthropic_messages, openai_chat_completions |
1,000,000 / 128,000 | 0.90 / 4.50 | 라이브 API |
gemini-3.5-flash |
gemini_generate_content, openai_chat_completions |
1,048,576 / 65,536 | 0.75 / 4.50 | 라이브 API |
세 모델 모두 대체 모델 없이 활성 수명 주기 단계에 있었으며, 가격은 2026년 10월 2일 16:53:30.068Z에 업데이트되었습니다. 따라서 오늘날 Responses 요청을 받는 것은 gpt-5.6-terra뿐입니다. 나머지 두 모델은 Chat Completions 또는 네이티브 경로를 통해 작동합니다. 수명 주기와 가격이 변경될 수 있으므로 배포 전에 다시 확인하십시오.
오류 처리도 중요합니다. 생성 호출에서 모델 이름을 잘못 입력하면 400 model_not_found가 반환되며 did_you_mean이 포함될 수 있습니다. 알 수 없는 ID에 대해 GET /v1/models/{model}을 요청하면 404 model_not_found가 반환됩니다. retryable: false인 503 all_channels_failed는 요청을 반복해도 소용없음을 의미합니다. 제품에 권한이 없는 한 자동으로 모델을 교체하지 마십시오.
마이그레이션 체크리스트: Responses API vs Chat Completions
에이전트를 Chat Completions에서 Responses로 이동할 때 다음 순서를 따르십시오.
- 모델이
openai_responses를 나열하는지 확인하십시오. 그렇지 않다면 여기서 멈추십시오. Chat Completions를 유지하는 것이 지원되는 경로입니다. - 전환 경계를 선택하십시오. 새로운 대화는 Responses에서 시작하십시오. 형식마다 상태와 도구 호출을 다르게 표현하므로 라이브 스레드를 중간에 전환하지 마십시오.
- 저장된 기록을 신중하게 변환하십시오. 이전 스레드를 마이그레이션해야 한다면 다음 요청 전에 애플리케이션에서 변환하고 검증하십시오.
- 핵심 필드 이름을 변경하십시오.
messages는input이 되고, 시스템 또는 개발자 메시지는instructions가 되며,max_tokens또는max_completion_tokens는max_output_tokens가 됩니다. - 도구 루프를 다시 작성하십시오.
choices[0].message.tool_calls파싱을function_call항목으로 교체하고,role: "tool"메시지를call_id로 키가 지정된function_call_output항목으로 교체하십시오. - 응답 ID를 유지하십시오.
previous_response_id를 전달할 수 있도록 각id를 저장하십시오.store와 보존 기간을 미리 결정하고, TokenLab이 저장된 응답을 얼마나 오래 보관하는지 확인하십시오(읽은 페이지에는 명시되지 않음). - 스트림 소비자를 다시 작성하십시오.
response.output_text.delta,response.completed,response.failed,response.incomplete및error를 처리하십시오.response.completed없이 닫힌 스트림은 실패로 처리하십시오. status를 확인하십시오. HTTP 성공 응답에도incomplete또는failed가 포함될 수 있습니다.- 전달되지 않을 수 있는 매개변수를 감사하십시오. Create Response 페이지에는
temperature,top_p,tools,parallel_tool_calls,reasoning,text가 나열되어 있습니다.n,logprobs,logit_bias,frequency_penalty,presence_penalty,seed,top_k,stop,user,stream_options는 나열되어 있지 않습니다. 의존하기 전에 모델에서 각각 확인하십시오. TokenLab이 알 수 없는 필드를 통과시킬 수는 있지만, 모델이 이를 허용한다는 의미는 아닙니다. - JSON 출력을 매핑하십시오. Chat은
response_format을 사용합니다. Responses는text객체를 사용하며,text.format지원은 모델에 따라 다릅니다. - 누락된 엔드포인트를 고려하여 계획하십시오. 현재 Cancel, input-items, input-tokens 엔드포인트를 사용할 수 없습니다. 저장된 응답을 삭제해도 생성은 취소되지 않습니다.
- 재시도 없이 리플레이 테스트를 수행하십시오.
max_retries=0을 유지하고 재시도가 안전한지 여부를 코드에서 직접 결정하십시오. 시간 초과된 생성은 자동으로 재실행되어서는 안 됩니다.
FAQ
Claude Sonnet 5나 Gemini 3.5 Flash에 Responses 요청을 보낼 수 있나요?
2026년 10월 3일 기준 라이브 모델 API에 따르면 불가능합니다. claude-sonnet-5는 anthropic_messages와 openai_chat_completions를 나열합니다. gemini-3.5-flash는 gemini_generate_content와 openai_chat_completions를 나열합니다. Chat Completions 또는 네이티브 경로를 사용하고, 배포 전에 accepted_request_formats를 다시 확인하십시오.
previous_response_id를 사용하면 이전 턴에 대한 비용 지불이 중단되나요?
우리가 읽은 페이지에는 명시되어 있지 않습니다. 서버가 저장된 응답에서 계속할 수 있다는 점은 문서화되어 있지만, 복원된 컨텍스트가 어떻게 청구되는지는 나와 있지 않습니다. 절감을 계획하기 전에 다중 턴 호출을 몇 번 실행하고 usage 필드를 상태 비저장 기준선과 비교하십시오.
대화 중간에 형식을 전환할 수 있나요?
아니요. TokenLab의 가이드는 각 형식이 상태와 도구 호출을 다르게 표현하므로 대화당 하나의 형식을 유지할 것을 권장합니다. 저장된 기록을 마이그레이션해야 한다면 다음 요청을 보내기 전에 애플리케이션에서 변환하고 검증하십시오.
실행 중인 Responses 요청을 어떻게 취소하나요?
현재 취소 엔드포인트를 사용할 수 없습니다. 저장된 응답을 삭제해도 생성은 취소되지 않으며, WebSocket 응답은 response.cancel을 지원하지 않습니다. 스트림의 경우, 사용자가 중지하거나 연결을 끊으면 서버에서 업스트림 스트림을 닫으십시오.
엔드포인트를 변경하기 전에 TokenLab 모델 카탈로그를 열고 에이전트가 호출하는 정확한 모델에 대한 accepted_request_formats를 확인하십시오.
출처
2026-10-03 기준 가격
- TokenLab Docs: API formats2026-10-03 기준 확인
- TokenLab Docs: Create Response2026-10-03 기준 확인
- TokenLab Docs: Create Chat Completion2026-10-03 기준 확인
- TokenLab Docs: Structured Outputs & Tool Calling2026-10-03 기준 확인
- TokenLab Docs: Streaming2026-10-03 기준 확인
- TokenLab Docs: Errors agents can act on2026-10-03 기준 확인
- TokenLab Docs: Migration Guides2026-10-03 기준 확인
- TokenLab Docs: Get a Model2026-10-03 기준 확인
