Prior Work Deep-Dive · 소스 코드 근거
이 페이지의 모든 시그니처와 인용은 arc-3-agents-baseline1/secure_baseline1
소스에서 그대로 가져온 것이다. 추측으로 채운 부분은 없으며, 불확실한 곳은 원문을 그대로 인용했다.
샌드박스 없는 Codex CLI를 파일 편집 에이전트로 쓰고, 세계 모델을 네 개의 영속 파일로 강제하는 시스템이다.
외부 컨트롤러(agent.py)가 상황에 맞는 프롬프트를 Codex에 밀어 넣으면, Codex는 게임을
직접 플레이하는 게 아니라 작업 디렉토리 안의 world_model_engine.py(동역학),
world_model_state_io.py(상태 복원 + 렌더링), world_model_main_planner.py(플래너),
world_model.md(텍스트 모델)를 계속 편집한다. 모델이 맞는지는 사람이 아니라
verify_world_model.py가 판정한다 — 기록된 모든 attempt를 처음부터 시뮬레이션해서
렌더링된 ASCII 프레임이 실제 관측과 픽셀 단위로 완전히 일치해야 통과다.
AGENT.md의 자기 설명(원문): “Our agent is based on unsandboxed Codex. …
agent.py is the external controller. It prepares the run directory, starts Codex, sends prompts to Codex,
runs the client, inspects progress, and decides which prompt to send next.”
격리는 Docker 컨테이너 2개(에이전트/서버)와 client–server 경계로 처리해서, 에이전트가
arc_agi 라이브러리(게임 구현)를 훔쳐볼 수 없게 한다.
바깥 루프는 컨트롤러의 프롬프트 상태머신이고, 안쪽 루프는 Codex가 수행하는 “예측 → 실행 → 대조 → 수리” 사이클이다. main_prompt.md의 Action discipline 절이 안쪽 루프를 문장으로 강제한다: “predict the settled result of the next action … if mismatch exists, stop and repair the model. Never continue blindly after a mismatch.”
inspect_sessions)해서 game-over / 새 레벨 / 정지(stuck) / 정상 진행을 판별하고, 그에 맞는 프롬프트 묶음을 선택world_model_engine.py · world_model_state_io.py · world_model_main_planner.py · world_model.md (+ 부분관측 레벨이면 initial_full_frames/level_N.txt)np.array_equal로 대조critique 루프: main_prompt.md는 critique_prompt.md를 그대로 넣은
“Generalization-critique subagent”를 요구한다 — 모델이 복잡해질 때마다, 스페셜 케이스를 추가하고 싶을 때마다,
레벨을 풀었다고 선언하기 전마다 적대적 리뷰를 돌리라는 지시다. 크리틱의 출력 형식도 고정되어 있다:
Findings / What Seems Sound / Bottom Line.
src/agent/workspace_init/world_model_engine.py — 제공되는 스텁 전문 (8줄)
from game_status import RUNNING def world_model_engine(state: dict, action: dict) -> tuple[dict, str]: """Placeholder world-model dynamics.""" return dict(state), RUNNING
단일 attempt 안의 게임 동역학. 에이전트가 이 스텁을 실제 규칙으로 채운다.
state는 "level" 필드가 의무인 dict, action은
name(+ ACTION6일 때 x,y) dict.
반환 status는 RUNNING | LEVEL_COMPLETED | GAME_OVER 셋 중 하나.
입력 → 출력 예시
world_model_engine(
{"level": 1, "avatar": (12, 40), "lives": 3, ...}, # 에이전트가 정의한 내부 상태
{"name": "ACTION6", "x": 15, "y": 47},
)
# -> ({"level": 1, "avatar": (15, 47), "lives": 3, ...}, "RUNNING")
# 목표 도달 스텝이면 -> (terminal_state, "LEVEL_COMPLETED")
src/agent/workspace_init/world_model_state_io.py
def initial_state_reconstruction(level_index: int, initial_frame: np.ndarray) -> dict: """Placeholder initial-state reconstruction.""" return {"level": int(level_index)}
레벨의 초기 관측 프레임에서 내부 상태를 복원하는 유일한 진입점.
파일 로딩은 부분관측 레벨의 load_initial_full_frame(level_index) 단 하나만 허용
(main_prompt 원문: “This rule is strict and non-negotiable.”).
이후 임의 시점 상태는 별도 함수가 아니라 “초기 상태 + 엔진으로 액션 재생”으로만 만든다.
입력 → 출력 예시
initial_state_reconstruction(2, frame) # frame: (64,64) int16, 값 0-15 # -> {"level": 2, "walls": {...}, "avatar": (7, 9), "baseline_lives": 3, ...} # (죽음-리셋이 있는 게임이면 fresh-attempt 재구축용 baseline 값도 여기서 심는다)
src/agent/workspace_init/world_model_state_io.py
def state_renderer(state: dict) -> np.ndarray: """Placeholder renderer.""" return np.zeros((64, 64), dtype=np.int16) def apply_render_overrides(frame, state, level_index, attempt_index, step_count) -> np.ndarray: """Verification-only escape hatch for unresolved frame-specific mismatches.""" return frame
내부 상태 → 기대 ASCII 프레임(64×64 int16). 검증이 이 함수를 통해서만 가능하므로
모델의 “수용 기준”이 곧 렌더러 일치다. apply_render_overrides는 설명 못 한
시각 디테일을 임시로 덮는 최후의 패치 훅인데, 검증기가 이 훅이 프레임을 바꾸면
경고를 출력한다: “treat this as temporary and as a clue to a missing puzzle mechanic”.
입력 → 출력 예시
state_renderer({"level": 1, "avatar": (15, 47), ...})
# -> array([[0,0,...],[...]], dtype=int16) # shape (64,64), 실측 프레임과 완전 일치해야 통과
src/agent/workspace_init/verify_world_model.py
class VerificationMismatchError(AssertionError): """Expected verification mismatch; distinct from world-model implementation failures.""" # 검증 본체: 초기 프레임 렌더 대조 -> 매 스텝 (status, frame) 대조 state, game_status, rendered = _predict_step(state, step, attempt_index, index + 1) if game_status != step["observed_status"]: _raise_status_mismatch(...) if not np.array_equal(rendered, step["final_frame"]): _raise_frame_mismatch(...)
레벨 1..N 전 attempt를 재생 검증한다. 프레임이 하나라도 다르면
아티팩트 5종을 저장하고 그 경로를 예외 메시지에 담아 던진다. 전체 검증에
TIMEOUT_SECONDS = 180 제한이 있고 초과하면
“Your world model is too slow, please consider refactoring it.”이 뜬다.
실패 시 출력 예시 (예외 메시지 형식, 소스의 문자열 그대로)
VerificationMismatchError: Model level 3, replay level 2, attempt 1, replay, step 14: rendered frame mismatch. real ascii: .../client/session/level_2_attempt_1/step_0014_final.txt real png: .../step_0014_final.png simulated ascii: .../mismatch_frames/7/simulated_frame.txt (predicted frame as text) simulated png: .../mismatch_frames/7/simulated_frame.png (predicted frame rendered) mismatch region png: .../mismatch_frames/7/mismatch_region.png (localized mismatch neighborhoods) mismatch as magneta png: .../mismatch_frames/7/mismatch_as_magneta.png (full frame with magenta mismatch pixels)
“magneta”는 소스의 오타를 그대로 옮긴 것이다 — 파일명, 함수명, 메시지 모두
magneta로 일관되게 쓰여 있다.
src/agent/workspace_init/frame_plot_lib.py — MISMATCH_RADIUS = 7, BROWN_RGB = (139, 69, 19), MAGENTA_RGB = (255, 0, 255)
def save_mismatch_region_png_v1(real_frame, predicted_frame, output_png_path, scale=DEFAULT_PNG_SCALE) -> Path: # Shows only neighborhoods around mismatch pixels; everything else is plotted in brown, # a color outside the game palette. (소스 주석 원문) def save_mismatch_as_magneta_png_v2(real_frame, predicted_frame, output_png_path, scale=DEFAULT_PNG_SCALE) -> Path: # Paints exact mismatch pixels as magenta on the full real frame.
불일치 시각화 두 방식. v1은 불일치 픽셀 반경 7 원 안쪽만 실제 색으로 남기고 나머지를 전부 게임 팔레트 밖의 갈색으로 칠해 “어디가 다른가”에 시선을 고정시킨다. v2는 실제 프레임 위에 불일치 픽셀만 마젠타로 찍는다 — 불일치가 흩어져 있을 때 유용.
입력 → 출력 예시
save_mismatch_region_png_v1(real, predicted, Path("mismatch_frames/7/mismatch_region.png")) # -> PosixPath('mismatch_frames/7/mismatch_region.png') # (불일치 셀 주변 반경 7만 원색, 배경은 갈색(139,69,19)인 PNG)
src/agent/workspace_init/plan_executor.py — main() 핵심 루프 (원문 발췌)
for action in actions: predicted_state, predicted_status = world_model_engine(current_state, action) predicted_frame = state_renderer(predicted_state) if predicted_status == RUNNING else None subprocess.run(_client_command(action), cwd=CLIENT_DIR, check=True) # 실제 게임에 실행 ... if actual_status != predicted_status: raise AssertionError(f"plan_executor.py: status mismatch after {format_action(action)}: ...") ... if predicted_frame is None or not np.array_equal(predicted_frame, observed_frame): _raise_frame_mismatch(...) # artifacts 저장 후 AssertionError
계획된 액션 열을 모델과 실제 게임 양쪽에서 동시에 실행하는 유일한 통로.
매 스텝 예측을 먼저 확정한 뒤에야 클라이언트를 호출하므로, “예측 없는 실 액션 금지”
규칙이 코드 수준에서 강제된다. RESET은 지원하지 않는다(명시적 거부).
입력 → 출력 예시
$ python3 plan_executor.py ACTION1 ACTION1 '{"name": "ACTION6", "x": 15, "y": 47}'
plan_executor.py: running ACTION1
plan_executor.py: running ACTION1
plan_executor.py: running ACTION6 x=15 y=47
plan_executor.py: sequence executed without mismatch # 또는 mismatch에서 즉시 중단 + artifact 경로 출력
src/agent/workspace_init/session_tools.py — Attempt / AttemptStep TypedDict (원문)
class Attempt(TypedDict): level_index: int; attempt_index: int; path: str initial_frame: np.ndarray; initial_metadata: dict[str, Any] initial_frame_filename: str; initial_frame_png_filename: str steps: list[AttemptStep]; status: str class AttemptStep(TypedDict): action: dict[str, Any]; final_frame: np.ndarray; metadata: dict[str, Any] final_frame_filename: str; final_frame_png_filename: str intermediate_frame_filenames: list[str]; intermediate_frame_png_filenames: list[str] observed_status: str
클라이언트가 client/session/level_<N>_attempt_<A>/에 남긴
기록(스텝별 metadata JSON + ASCII/PNG 프레임)을 읽어 구조화한다. ASCII 프레임은
한 글자 = 16진수 한 자리로 파싱된다(int(char, 16) → int16 배열).
이것이 검증기와 상태 복원의 유일한 데이터 소스다.
입력 → 출력 예시
read_all_attempts_for_level(2) # -> [ {"level_index": 2, "attempt_index": 1, "status": "GAME_OVER", # "initial_frame": array(64x64), "steps": [ {"action": {"name": "ACTION1"}, # "final_frame": array(64x64), "observed_status": "RUNNING", ...}, ...]}, ... ]
src/agent/workspace_init/world_model_main_planner.py — 스텁 전문
def planner(state: dict) -> list[dict] | None: """Placeholder planner.""" return None
내부 세계 모델 상태에서 레벨 완료까지의 액션 열을 찾는다. 못 찾으면 None.
run_main_planner.py는 반환된 계획을 반드시 엔진으로 재시뮬레이션해서
LEVEL_COMPLETED에 도달하는지 자체 검증한다(도달 못 하면 AssertionError) —
“모델 밖 플래닝 금지”가 여기서도 코드로 강제된다. 보조 플래너는
planner(state, goal=None) 형태로 goal dict를 받을 수 있다.
입력 → 출력 예시
planner({"level": 1, "avatar": (12, 40), ...})
# -> [{"name": "ACTION1"}, {"name": "ACTION1"}, {"name": "ACTION6", "x": 15, "y": 47}]
# 도달 불가면 -> None
컨트롤러는 매 iteration마다 세션 디렉토리만 보고 상태를 판별한다. LLM의 자기 보고를 믿지 않고
파일시스템이 곧 진실이다. 분기는 agent.py :: iteration_loop() 네 갈래:
① game-over → reset_protocol, ② 레벨이 바뀜(레벨 1 제외) → new_level_protocol,
③ 지난 iteration 이후 스텝 수가 그대로 → stuck_protocol, ④ 그 외 → normal_continuation_protocol.
거기에 reset 시 스텝 누적량으로 trouble 1/2 에스컬레이션이 얹힌다
(레벨당 +100스텝마다 trouble1, +200스텝마다 trouble2 — trouble2는 runner.new_session()으로
Codex 세션까지 갈아엎고 main_prompt부터 다시 준다).
| 프롬프트 파일 | 컨트롤러가 보내는 시점 |
|---|---|
main_prompt.md | 시작 시(게임 초기 화면 출력과 함께), recovery 시, trouble2 시(새 세션의 서두로) |
continuation_string.txt | 거의 모든 프롬프트의 꼬리표: “LEVEL_COMPLETED나 GAME_OVER까지 멈추지 말고 계속하라” |
continuation_level1.txt / continuation_l2.txt | 정상 진행 시 계속 지시. 레벨 1이면 “몇 수만 탐색하면 답이 보이는 단순 퍼즐” 버전, 레벨 2+면 “세계 모델 관점에서 생각하고 플래너를 갱신하라” 버전 |
light_simplification_level1.txt | 레벨 1에서 매 continuation/reset/stuck 직전에 — 가벼운 단순화 리뷰 1발 |
world_engine_simplification_step1–3.txtworld_model_state_io_simplification_step1–3.txtworld_model_planner.txt | 레벨 2+에서 매 continuation/reset/stuck 직전에 7연발 pgroup(“hard refactoring”): 엔진 3단계 + state_io 3단계 + 플래너 1단계 압축·일반화 지시 |
on_new_level_v1.txt | 레벨 인덱스가 바뀐 첫 iteration: 새 레벨 첫 프레임의 전 요소 인벤토리(기지/신규/불확실 분류) 지시 |
death_prompt.txt | GAME_OVER로 reset_protocol에 들어갈 때 최우선 전송: “왜 죽었는지 모델에 편입하라, 애니메이션 PNG 검사는 의무” |
trouble1_prompt.txt / trouble1_prompt_level1.txt | 같은 레벨에서 지난 trouble1 이후 100스텝 넘게 소모 후 reset될 때 |
trouble2_prompt.txt / trouble2_prompt_level1.txt | 200스텝 넘게 소모 후 reset될 때 — Codex 세션을 새로 만들고 main_prompt + trouble2로 재시동 (“터널 비전에 빠졌을 수 있다”) |
stuck_reminder_prompt.txt | 한 iteration 동안 게임 스텝이 0일 때: “움직이지 않으면 진다”. 그 후에도 무진행이면 stop_condition이 런 종료 |
recovery_prompt.txt | --recovery 모드 재기동 시: 모델·로그·게임 상태를 리뷰하고 상황 보고 지시 |
reset_prompt.txt, light_simplification.txt | 파일은 존재하지만 agent.py의 load_prompt 호출 목록에는 없음 — 현 컨트롤러 경로에서는 미사용으로 보인다 (정직 고지) |
종료 조건(stop_condition): 현재 레벨 스텝 ≥ 1500,
stuck 프로토콜 이후에도 무진행, 게임 클리어, 또는 현재 레벨을 찾을 수 없음.
save_mismatch_region_png_v1/_raise_frame_mismatch가
우리 엔진의 mismatch 국소화 프리미티브의 원형이다.critique_prompt.md의
적대적 일반화 리뷰(“레벨 특이 암기를 숨기고 있지 않은가”)를 우리는 sleep 단계의 크리틱으로 옮겼다.