이번 주 개발자 커뮤니티에서 “에이전트가 툴 한 번 부르고 나서도 왜 또 오래 기다려야 하냐”는 말이 계속 나온다. Codex가 버그를 고치려면 코드베이스를 훑고, 파일을 읽어 컨텍스트를 만들고, 수정한 뒤 테스트까지 돌리는데, 그 과정이 Responses API 요청을 수십 번 왕복하는 구조라서다. 특히 GPT-5.3-Codex-Spark처럼 GPU 추론이 빨라질수록, API 서비스 쪽 오버헤드가 더 도드라진다는 불만이 같이 붙는다.

GPT-5.3-Codex-Spark 목표는 1,000 TPS, 기존은 65 TPS

OpenAI는 Responses API에서 이전 플래그십 모델인 GPT‑5와 GPT‑5.2가 대략 65 tokens per second(TPS)로 동작했다고 밝혔다. GPT‑5.3‑Codex‑Spark(빠른 코딩 모델) 출시 목표는 “한 자릿수 배”가 아니라 10배 수준으로, 1,000 TPS를 넘기는 것이었다. 이 모델은 LLM 추론에 최적화된 Cerebras 하드웨어로 구동된다고 설명했다.

또한 2025년 11월 무렵 Responses API에 성능 스프린트를 시작해, 단일 요청의 critical-path latency(핵심 경로 지연)를 줄이는 최적화를 다수 반영했다고 했다. 그 결과 time to first token(TTFT, API가 얼마나 빨리 반응하는지 체감되는 지표)에서 약 45% 개선을 확인했다고 전했다. 다만 GPT‑5.3‑Codex‑Spark에서는 여전히 Responses API 오버헤드가 커서, 사용자가 GPU가 아니라 API를 돌리는 CPU 쪽을 기다려야 하는 상황이 남았다고 했다.

“요청마다 독립 처리”에서 “연결 생명주기 캐시”로 바꿨다

예전에는 Codex 요청을 각각 독립으로 취급하면서, 후속 요청마다 대화 상태와 재사용 가능한 컨텍스트를 매번 다시 처리했다고 한다. 대화가 길어질수록 “대부분 바뀌지 않았는데도 전체 히스토리와 연관된 작업 비용을 계속 낸다”는 구조적 문제가 커졌고, 그 반복 처리 비용이 누적됐다는 설명이다.

이걸 줄이기 위해 전송 프로토콜을 다시 봤고, HTTP로 매번 새 연결을 만들면서 전체 대화 히스토리를 보내는 대신 “지속 연결 + 상태 캐시”를 유지할 수 있는지 검토했다고 했다. 목표는 검증과 처리에 필요한 ‘새 정보’만 보내고, 재사용 가능한 상태는 연결 생명주기 동안 메모리에 캐시해 중복 오버헤드를 줄이는 것이었다.

WebSockets를 선택했고, response.create는 유지했다

여러 접근을 봤는데 WebSockets와 gRPC 양방향 스트리밍(bidirectional streaming)을 포함했다고 한다. 최종 선택은 WebSockets였고, 이유로는 단순 메시지 전송 프로토콜이라 Responses API의 입력/출력 형태를 바꿀 필요가 없다는 점, 개발자 친화적이며 기존 아키텍처에 큰 충돌 없이 들어갈 수 있다는 점을 들었다.

다만 첫 WebSocket 프로토타입은 “가능하다고 생각했던 것”을 바꿀 정도로 지연을 줄였지만, API 모양이 덜 익숙하고 복잡해졌다고 했다. 프로토타입에서는 에이전트 롤아웃을 단일 장시간 Response로 모델링했고, asyncio 기능으로 툴 호출이 샘플링 루프에서 선택된 뒤 비동기적으로 block(대기)한 다음 response.done 이벤트를 클라이언트로 보냈다. 클라이언트가 툴을 실행하고 나서 response.append 이벤트로 툴 결과를 다시 보내면 sampling loop가 풀리며 모델이 계속 진행하는 방식이었다.

이 설계는 “로컬 툴 호출을 hosted tool call처럼 취급”하는 비유로 설명됐다. 모델이 웹 검색을 호출하면 추론 루프가 멈추고 웹 검색 서비스 응답을 컨텍스트에 넣고 계속하는데, 새 설계에서는 원격 서비스 호출 대신 WebSocket으로 툴 호출을 클라이언트에 보내고, 클라이언트 응답을 컨텍스트에 넣어 이어서 샘플링을 진행했다. 그 결과 에이전트 롤아웃 동안 반복되는 API 작업을 제거할 수 있어, preinference 작업을 한 번만 하고 툴 실행 구간에서 멈춘 뒤 postinference도 끝에서 한 번만 수행하는 형태가 됐다고 했다.

하지만 개발자가 WebSocket 지원을 “끼워 넣듯” 붙이려면, 상호작용 모드를 새로 갈아엎지 않아야 한다는 요구 때문에, 실제 출시 버전에서는 다시 익숙한 형태로 돌아갔다. 즉 response.create를 같은 body로 계속 쓰되, 이전 응답의 상태를 이어받기 위해 previous_response_id를 사용하도록 전환했다.

WebSocket 연결에서는 서버가 연결 스코프 기준으로 in-memory 캐시를 유지한다. follow-up response.create에 previous_response_id가 들어오면, 서버가 전체 대화를 처음부터 재구성하지 않고 캐시에서 이전 응답 상태를 가져온다. 이 cached state에는(원문에 이어지는 항목이 더 있으나) “이전 응답 상태를 메모리에서 재사용”하는 방식 자체가 핵심 최적화 근거가 됐다고 설명했다.

개발자 커뮤니티 반응은 여기서 갈린다. “프로토타입의 done/append 이벤트 방식이 더 빠를 텐데, 왜 다시 response.create로 돌아갔냐”는 질문이 나오고, 반대로 “기존 통합을 크게 안 바꾸고도 오버헤드를 줄인 게 실전에서 중요하다”는 쪽이 맞받아친다. 결국 이번 글이 말하는 포인트는 ‘더 빠른 GPU’만으로는 부족하고, 에이전트 루프의 왕복 비용을 구조적으로 잘라야 한다는 쪽으로 모인다.

연결 생명주기 캐시로 TTFT와 전체 에이전트 대기 시간을 줄였다

이번 변경의 목표는 최소 오버헤드 프로토타입에 최대한 가까이 가되, 개발자가 이미 이해하고 구축해 둔 API 모양을 유지하는 것이었다. 결과적으로 Responses API의 에이전트 루프는 end-to-end로 40% 더 빨라졌고, 추론 속도 체감도 65에서 거의 1,000 TPS로 점프하는 흐름을 만들었다고 했다.

마무리로, 에이전트가 느리게 느껴지는 지점은 모델이 아니라 “요청을 쪼개서 반복 처리하는 방식”에서 생긴다는 점이 이번 업데이트의 핵심이다.