OpenAI Codex · Code Mode

exec와 wait: 다 못 돈 프로그램은 어떻게 끝나나

도구 호출을 스무 번 보내는 대신, 모델에게 JavaScript 한 편을 쓰게 하세요. 어디서 도는지, 무엇을 할 수 있는지, 열 초 안에 못 끝내면 어떻게 되는지 — 이번 레슨은 그 세 질문 뒤의 아이디어 두 가지입니다.

강의 목표읽고 나면 두 가지를 말할 수 있어요. 모델용 런타임을 왜 Node도, 파일 시스템도, 네트워크도, console조차 없게 깎는지, 그리고 예산 안에 다 못 돈 프로그램을 Codex가 왜 실패가 아니라 “아직 도는 중”으로 적는지요.
먼저 해보기 · 예산이 닿으면, 죽일까 양보할까
같은 프로그램: 하위 작업 여섯, 각 약 2초, 합 12초
예산
왼쪽은 벽시계 상한으로, 오른쪽은 양보 간격으로 봐요. 20초로 올리면 양쪽 차이가 사라집니다.
기한에 종료0/6 유효0건 전달
시작을 기다리는 중.
기한에 양보0/6 완료0회 왕복
시작을 기다리는 중.
논리 궤적 · 애니메이션 각 단계가 소스의 어느 구간에 대응하는지
  1. 모델은 첫 줄에 pragma 한 줄을 써서, 이번 양보 시간을 선언할 수 있어요description.rs L22
  2. 실행 입구는 그 밀리초를 “닿으면 한 번 관찰” 모드로 바꿉니다service.rs L77
  3. 10초를 넘는 예산에는 유예 1초를 더 주고, 서버 상한에도 막혀요service.rs L198
  4. 타이머가 닿으면 모아 둔 출력을 한 묶음으로 넘기고, 버퍼는 동시에 비워요cell_actor/mod.rs L242
  5. 양보는 모델이 읽을 수 있는 문장으로 번역되고, cell 번호가 붙어요code_mode/mod.rs L283
  6. 모델은 번호를 들고 돌아와 이어 돌릴 수 있고, 예산을 고치거나 길이를 제한하거나 바로 멈출 수도 있어요wait_handler.rs L24
  7. 스크립트가 끝나면 결과를 돌려주고 이 cell을 닫아요runtime.rs L24
재생을 눌러, 같은 프로그램이 두 타임아웃 전략에서 어떻게 끝나는지 보세요.
성과의 행방기한 종료 쪽에서는 끝난 하위 작업 성과가 프로세스와 함께 사라져요. 모델은 타임아웃 메시지 하나와 쓸 수 있는 중간 결과 제로를 받습니다.
이어 돌리는 대가기한 양보 쪽에서는 모델이 왕복을 몇 번 더 쓰고, 하위 작업 여섯을 모두 끝내며, 양보마다 새 진전을 봐요.
예산이 충분할 때예산을 20초로 올리면 양쪽 모두 한 번에 끝나요. 두 전략의 차이는 예산이 모자랄 때만 드러납니다.
수업용 스케치: 하위 작업 수와 소요 시간은 두 타임아웃 전략의 구조 차이를 보여 주기 위한 수업 설정입니다. 논리 궤적 오른쪽 줄 번호는 openai/codex 저장소 commit 4f39251a01에 대응합니다.
아이디어 1 · 모델에게 런타임을 주고, 최소로 깎아 내세요
어떤 문제를 푸는가

파일 다섯을 읽고 모으면, 일반 도구 호출은 왕복 다섯이 필요해요. 왕복마다 파일 전문이 컨텍스트에 들어가고, 다음 턴에 모델은 커진 이력을 다시 읽어요. 진짜 비싼 것은 왕복의 리듬이지, 도구 자체가 아닙니다.

그럼 모델에게 프로그램을 쓰게 하면 되지요. 문제는 그 프로그램을 한 줄도 심사한 사람이 없고, 모델이 지금 써서 바로 낸다는 점이에요. 파일을 읽고 네트워크를 칠 수 있는 런타임을 주면, 호스트의 능력을 통째로 내주는 셈입니다.

아이디어는 무엇인가

Codex는 모델에게 도구 둘, execwait를 줘요. exec는 JavaScript 소스를 받아 새 V8 isolate에서 async module로 평가해요. 도구는 모두 전역 tools에 달리고, 이름은 적법한 JS 식별자로 정규화되어 await tools.exec_command(...)처럼 씁니다. 루프도, 분기도 되고, 중간 값은 변수에 남으며, 직접 넘긴 부분만 모델로 돌아와요.

핵심은 이 런타임이 아주 얇게 깎였다는 점이에요. 도구 설명서는 모델에게, 무엇이 없는지를 바로 말합니다.

codex-rs/code-mode-protocol/src/description.rs20–25행
- Runs raw JavaScript -- no Node, no file system, no network access, no console.
- Accepts raw JavaScript source text, not JSON, quoted strings, or markdown code fences.
- You may optionally start the tool input with a first-line pragma like `// @exec: {"yield_time_ms": 10000, "max_output_tokens": 1000}`.
- `yield_time_ms` asks `exec` to yield early if the script is still running. Defaults to 10000 ms.
- `max_output_tokens` sets the token budget for direct `exec` results. Defaults to 10000 tokens.
- When the JS code is fully evaluated, the isolate's lifetime ends and unawaited promises are silently discarded.
소스 스냅샷 안내: 로컬 저장소 openai/codex 기준, 확인 파일 codex-rs/code-mode-protocol/src/description.rs, commit 4f39251a01, 확인일 2026-08-22. 코드 블록은 소스 원문을 유지합니다. 이 텍스트 자체가 모델에게 보내는 도구 설명입니다.

Node도, 파일 시스템도, 네트워크도, console조차 없어요. 잊은 게 아니라 일부러 안 준 거예요. 부작용을 내려면 길 하나, tools뿐이에요. 그 길에는 승인과 샌드박스가 그대로 있고, 띄울 창은 띄우고 막을 것은 막아요.

덤으로 심사할 면적이 줄어요. isolate가 파일을 직접 읽으면 이 층이 파일 권한을 또 만들어야 해요. 지금은 아무것도 못 하니, 권한은 아래 층에 두면 되고 코드를 두 번 쓰지 않아도 됩니다.

하나씩 호출 모델 도구 왕복마다 샘플링 한 라운드, 중간 결과는 통째 컨텍스트로 프로그램 한 편 모델 샘플링 한 라운드 V8 isolate Node·파일시스템·네트워크·console 없음, 부작용은 tools만 JavaScript 한 편 직접 넘긴 부분만
수업용 구조도: 같은 일, 위는 왕복 여러 번, 아래는 한 번.
왜 오래가는가

왕복은 비싸고 배치는 싸다 — 수십 년의 옛 장부예요. 데이터베이스에는 일괄 쓰기가 있고, RPC 프레임워크는 batch를 모아요. 모델 샘플링 한 라운드는 네트워크 왕복보다 훨씬 비싸니, N번을 한 번으로 합치면 이득은 더 커집니다.

빼기의 절반은 더 보편적이에요. 믿지 못할 코드에 가능한 한 작은 환경을 주어, 나쁜 짓을 하고 싶어도 인터페이스가 없게 하는 것 — 그게 보안 설계의 보편 모양이고, V8이라는 구체 기술과는 상관없어요. 언어를 바꾸든 샌드박스를 바꾸든 물을 질문은 같아요. 이 코드에 정말 필요한 능력은 몇 가지이며, 나머지는 하나도 안 줄 수 있는가.

아이디어 2 · 다 못 돈 것은 실패가 아니라, 아직 도는 중
어떤 문제를 푸는가

프로그램이 3분 걸려요. 타임아웃은 얼마로 둘까요.

3분으로 두면 사용자는 3분 동안 움직임도 못 보고 멈출 곳도 없어요. 10초로 두면 긴 일은 끝나지 않고, 더 괴로운 건 앞 9초 성과가 함께 사라진다는 점이에요. 모델은 타임아웃 하나만 받고 처음부터 다시 해야 합니다. 양쪽 다 틀렸고, 문제는 “아직 안 끝남”을 실패로 본 데 있어요.

아이디어는 무엇인가

Codex는 도는 중인 스크립트를 정체 있는 것으로 만들어요. 이름이 cell. yield_time_ms가 닿아도 cell은 죽지 않고, 그동안 모은 출력을 한 묶음으로 넘긴 뒤 버퍼를 비우고 계속 돌아요. exec는 그때 한 문장을 돌려, 스크립트가 아직 돌고 번호가 얼마인지 말합니다.

번호를 받은 모델에게는 선택 셋이 있어요. wait으로 시간을 더 사거나, terminate: true로 멈추거나, 아니면 다른 일부터. wait는 지난 양보 이후의 새 출력만 돌려줘요. 넘길 때 버퍼를 비웠으니, 같은 내용이 컨텍스트를 두 번 차지하지 않습니다.

기한에 종료 스크립트 실행 중, 출력은 버퍼에 벽시계가 닿아 프로세스 종료 모은 출력이 함께 사라짐 예산 도달 기한에 양보 스크립트 실행 중, 출력은 버퍼에 한 묶음 전달, 버퍼 비움 스크립트는 계속 돔 wait으로 이어 돌림 새로 생긴 구간만 예산 도달
수업용 시퀀스: 같은 순간, 한쪽은 프로세스를 죽이고 한쪽은 성과를 넘기며 계속 돌아요.

작은 디테일이 설계자가 무엇을 생각했는지 잘 보여 줘요. 양보 시간이 10초를 넘으면 Codex는 유예 1초를 더 준 뒤에야 정말 관찰합니다.출처:codex-rs/code-mode-runtime/src/service.rs 198–210행경계에 딱 맞춰 끝난 스크립트는, 몇 밀리초 차이로 왕복을 한 번 더 낭비하지 않아요.

비대칭 한 곳도 적어 둘 만해요. 모델에게 보내는 설명서에서 wait는 인자 넷이에요. cell 번호, 양보 시간, 반환 길이 상한, 종료 여부. 그런데 프로토콜 층 요청 구조체는 앞의 둘만 들고, 뒤의 둘은 핸들러 층에 머물러요. 종료는 다른 길을 타고, 길이 상한은 결과를 받은 뒤에야 자릅니다. 소스를 읽을 때 이 두 층은 한 층으로 섞이기 쉬워요.

예산이 닿으면, 죽이지 말고 숙제부터 내세요.
왜 오래가는가

“아직 안 끝남”을 일등 상태로 만드는 것은 긴 작업 인터페이스의 보편 모양이에요. HTTP는 202와 폴링, 작업 큐는 job id와 poll, 큰 파일 내보내기도 번호를 먼저 줘요. 공통점은 호출자에게 “계속 기다리기”와 “실패로 보기” 중 하나를 강요하지 않고, 다시 물어볼 손잡이를 준다는 점입니다.

Agent 위에 두면 이 손잡이는 한 층 더 가치가 있어요. 중간 출력을 본 모델은 마음을 바꿀 수 있고, 앞 네 스텝이 틀렸으면 바로 terminate해서 남은 8분을 함께 달리지 않아도 돼요. 제어권은 생각하는 쪽으로 돌아갑니다.

가로 비교 · 같은 문제에 대한 다른 답

타임아웃: DeepSeek Harness는 죽이기를 고름

DSH의 run_code는 장부 두 권이에요. 한 권은 바쁜 시간으로, worker 이벤트 루프 이용률을 폴링해 핫 루프를 숨기지 못하게 하고, 느린 도구를 기다리는 시간은 억울하게 과금하지 않아요. 다른 한 권은 벽시계로, 닿으면 worker를 바로 종료해요. 기본값은 6만 밀리초와 60만 밀리초입니다.

대가는 분명해요. run_code 한 번은 예산 안에 끝나야 하고, 타임아웃은 실패이며, “같은 프로그램이 이어 돈다”는 일등 상태는 없어요. 대신 구현이 단순하고, 호스트가 살아있는 cell 더미를 유지하지 않아도 됩니다. Codex는 반대예요. 모델은 wait 프로토콜을 하나 더 배워야 하고, cell은 끝나거나 멈추거나 세션이 끝날 때까지 자원을 차지합니다.

양쪽 모두 소스 대조 완료 · 2026-08-22 · DSH · Code Mode

상태: 일회용 세계인가, 남겨 두는 서랍인가

DSH 설계 노트는 아주 직설적이에요. 프로그램이 사는 세계는 worker와 함께 끝나고, 풀링도 실행 간 상태도 없어요. 다음 실행에 넘길 것은 도구 결과에 쓰거나 작업 공간 파일에 떨어뜨리세요. 좋은 점은 실행마다 깨끗한 새 세계이고, 문제가 나면 재현하기 쉽다는 것입니다.

Codex는 storeload를 줘요. 같은 세션의 exec 여러 번은 데이터를 공유하고, 세션을 넘기면 서로 못 봐요. 오케스트레이션은 편하고, 대가는 정리 책임이 다시 자기에게 온다는 점이에요. 이 서랍은 항목별 크기 상한이 없고, JSON에 못 들어가는 값만 거절하며, TTL도 없어서 세션 전체가 끝나야 런타임과 함께 풀립니다.

양쪽 모두 소스 대조 완료 · 2026-08-22
수업 실습
01

양보 예산을 어떻게 잡아야 손해 보지 않나

프로그램이 40초 걸리고, 양보 예산 기본값은 10초예요. 추론해 보세요. 모델은 wait을 몇 번 보내야 하고, 매번 받는 것은 전체 출력인지 새로 생긴 구간인지, 기본값을 30초로 바꾸는 일이 왜 언제나 더 이득은 아닌지.

심화 한 질문: 35초에 프로그램이 아주 큰 객체를 store에 넣었고, 이어서 모델이 terminate를 결정하면, 그 객체는 언제 지워지고 크기는 누가 관리하나요.

Takeaway:모델에게 프로그램을 쓰게 해 N번 왕복을 한 번으로 누르세요. 그 프로그램의 런타임은 빼기: Node·파일시스템·네트워크·console 없고, 부작용은 이미 승인이 있는 도구 길로만. 예산이 닿으면 숙제부터 내고 이어 달리며, “아직 안 끝남”을 일등 상태로 만들어 성과를 잃지 않고 제어권을 모델에게 돌려주세요.