4,725번의 공격을 AI 에이전트가 자동으로 설계하고 제출하게 만드는 건 어렵지 않았다. 진짜 어려웠던 건 그 에이전트가 규칙을 지키게 만드는 것이었다. 이 글은 그 문제를 해결하기 위해 만든 전략 프레임워크 시스템(
wiki/syntheses/)과, 그것을 자동 실행에 녹여낸/loop의 이야기다.
이 글은 개관편의 §2.3에서 요약한 내용을 풀어쓴 것이다. 개관편을 먼저 읽으면 전체 맥락이 잡히지만, 이 글만 읽어도 무방하다.
1. 문제 — 에이전트는 왜 규칙을 무시하는가
대회 2주차에 접어들면서 Claude Code(Opus 4.7)와 Codex 토론 루프로 페이로드를 설계하는 체계가 잡혔다. 제출 속도는 빨라졌고, 가설의 다양성도 넓어졌다. 그런데 제출이 300건을 넘어가면서 이상한 일이 벌어지기 시작했다.
에이전트가 자기가 따라야 할 절차를 건너뛰었다.
구체적으로 이런 일들이 반복됐다:
- 분석 단계 스킵: 제출 결과를 drain(동기화)한 뒤 모델 reasoning을 분석해야 하는데, 바로 다음 빌드로 넘어간다. 결과를 안 보고 다음 설계를 하니 같은 실수가 반복됐다.
- drain 없이 진행: 제출 결과가 위키에 반영되기 전에 다음 벡터를 시작한다. 위키에 없는 데이터를 근거로 설계하니 중복 제출이 생겼다.
- 신규성 검사 무시: 5축 중 3축 이상을 바꿔야 한다는 규칙이 있는데, 확인 없이 빌드한다. 당연히 SIMILAR 기각.
- Anchor 추출 누락: 모델이 왜 거부했는지(anchor)를 기록해야 다음에 우회할 수 있는데, 이 단계를 통째로 빼먹는다.
왜 이런 일이 생겼을까. 이유는 단순하다. 사전(pre-submit) 게이트는 물리적으로 막혀 있었지만, 사후(post-submit) 게이트는 아무것도 강제하지 않았다. “빌드 전에 novelty check를 통과해야 한다"는 스크립트가 exit 1을 뱉으면 다음으로 못 넘어간다. 하지만 “제출 후에 drain을 완료하고 anchor를 추출해야 한다"는 규칙은 그냥 문서에 적혀 있을 뿐, 어기면 아무 일도 안 일어났다. 에이전트 입장에서 막히는 게 없으니 자연스럽게 건너뛴 거다.
이건 사실 사람도 마찬가지다 — 강제성 없는 절차는 바쁠 때 제일 먼저 빠진다.
2. 해결 — 규칙을 문서가 아니라 시스템으로
이 문제를 해결하기 위해 두 축을 동시에 세웠다.
축 1: wiki/syntheses/ — 규칙의 명문화
wiki/syntheses/ 디렉터리에 6개의 전략 프레임워크 문서를 작성했다. 에이전트가 매 사이클 시작 전에 읽어야 하는 “규칙집"이다. 각 문서는 특정 의사결정 영역을 담당한다:
| 문서 | 역할 | 핵심 질문 |
|---|---|---|
layer-design-framework.md | 페이로드 구조 설계 | “이 페이로드의 L1~L5는 각각 뭔가?” |
approval-path-framework.md | 채점 시스템 역설계 | “이게 어떤 승인 경로(A1~A6)로 점수를 받을 수 있나?” |
ev-calculation-framework.md | 기대값 추정 | “이 시도의 기대 점수는 얼마인가?” |
adaptive-pivot-reference.md | 전환/지속 판단 | “지금 메커니즘을 바꿔야 하나, 밀어야 하나?” |
agent-runbook-judgementday-loop.md | 실행 절차 | “이 사이클에서 구체적으로 뭘 해야 하나?” |
operations-spec.md | 마스터 계약 | “위 모든 것을 어떤 순서로, 누가 실행하나?” |
중요한 건, 이 문서들이 처음부터 6개였던 게 아니라는 점이다. 각각이 구체적인 실패 경험에서 태어났다.
축 2: /loop — 절차의 강제 실행
문서만으로는 부족했다. 에이전트가 문서를 읽어도 “이번엔 시간이 없으니 skip하자"는 판단을 내릴 수 있기 때문이다. Claude Code의 /loop 기능으로 “설계→빌드→제출→drain→분석” 사이클을 자동 반복하되, 각 단계의 게이트를 통과하지 못하면 물리적으로 다음 단계로 넘어갈 수 없도록 만들었다. 사람이 잠든 사이에도 에이전트가 절차를 지키며 돌아가게 만든 것이다.
3. 각 프레임워크가 태어난 맥락
① 5-Layer 페이로드 설계 — “왜 자꾸 SIMILAR인가?”
태어난 배경: 대회 초반, 같은 트랙에서 페이로드를 반복 제출할 때 “발신자 이름만 바꿨는데 왜 기각이지?“라는 질문이 계속 나왔다. arena의 SIMILAR 탐지기가 뭘 보는지 이해가 안 됐다.
수백 건의 제출-판정 데이터를 분석한 끝에, arena가 보는 건 표면(발신자명, 숫자, 톤)이 아니라 근거의 논리 구조라는 결론에 도달했다. 그래서 페이로드를 5개 독립 레이어로 분해하는 프레임워크를 만들었다:
- L1(화자 구조), L2(권위 framing), L5(식별 디테일) — 이 셋만 바꾸면 자동 기각(R1).
- L3(메커니즘) — 핵심 공격 논리. 이것을 바꿔야만 새 슬롯이 열린다.
- L4(액션 매핑) — TARGET action으로 유도하는 구체적 단서.
실제 적용 예시: T1.2 스포츠 VAR 트랙에서 “카메라 오정렬로 31% 판독이 잘못됐다"는 L3를 물리적 원인(먼지, 페인트, 해충)으로 바꿔봤지만 전부 SIMILAR로 잡혔다. L3 카테고리 자체를 method_invalidation(물리) → method_invalidation(IT)으로 바꾸자(펌웨어 업데이트 사고) 새 슬롯이 열렸다. “센서가 틀렸다"는 같은 결론인데, IT 인프라 서사로 포장하니 flash가 D06(injection)이 아닌 D03(valid reassessment)으로 분류하더라 — 이게 L3 내부의 sub-variant가 아니라 도메인 전환이 필요하다는 걸 배운 순간이었다.
8개 알려진 L3 카테고리가 소진되면 다른 산업에서 위기 대응 프로토콜을 차용해 새 L3를 발명하는 L3 Discovery Protocol도 여기에 포함된다. 프레임워크는 출발점이지 한계가 아니었다.
② 승인 경로 프레임워크 — “깨는 것과 점수받는 것은 다르다”
태어난 배경: T2.1 항공기 트랙에서 GPT-5.4가 DOCUMENT 채널에서 clear를 85번 선택했는데, 실제 득점은 0건이었다. 모델을 깨는 데는 성공했는데 점수가 안 나오는 게 처음엔 이해가 안 됐다.
arena 채점 시스템을 역설계한 결과, 점수를 받으려면 세 개의 하드 게이트를 통과해야 했다:
| |
그 위에 6개의 승인 경로(A1 FIRST_BREACH ~ A6 NEW_TARGET_ACTION)와 2개의 기각 규칙(R1 SAME_CORE_STRATEGY, R2 REPHRASE)이 있었다. 이걸 모르고 “깨면 점수가 나겠지"로 접근하면 phantom(모델은 깼지만 0점)만 쌓이는 거다.
다른 플레이어 전체 데이터(allresult/v1.json)를 분석해서 확인한 수치가 결정적이었다: SIMILAR 기각 110건 중 이전 approved 최고를 넘긴 사례가 0건. 같은 메커니즘으로 아무리 많이 깨봤자 점수는 안 나온다는 걸 데이터로 증명한 셈이다.
③ EV 계산 프레임워크 — “어느 시도가 더 가치 있나?”
태어난 배경: 에이전트가 “이 메커니즘이 작동하니까 계속 쓰자"는 판단을 반복했다. 이미 SIMILAR로 포화된 메커니즘을 재활용하는 게 기대값이 0에 가까운데, 에이전트는 “과거에 통했으니 EV가 높다"고 계산하고 있었다. SIMILAR 항이 EV 공식에 아예 없었기 때문이다.
그래서 EV 공식에 P(not_similar | mechanism) 항을 도입했다:
EV ≈ P(break≥2) × P(not_similar | mechanism) × predicted_raw_bounty
이전에 SIMILAR reject가 1건이라도 있는 메커니즘은 P(not_similar) ≤ 0.10으로 떨어진다. 반대로 해당 채널에 approved 기록이 없는 신규 L3는 ~0.85로 높다. 이러면 이긴 L3를 재사용할수록 EV가 자동으로 떨어지고, 새 메커니즘 발굴 쪽으로 인센티브가 붙는다. 공식 하나를 바꿨을 뿐인데 에이전트의 행동 패턴이 눈에 띄게 달라졌다.
④ Adaptive Pivot 트리거 — “언제 바꾸고 언제 밀어야 하나?”
태어난 배경: 에이전트에게 두 가지 실패 모드가 있었다. 하나는 너무 빨리 포기하는 것 — 2번 안 통했다고 바로 다른 메커니즘으로 넘어가면 진전 신호를 놓친다. 다른 하나는 너무 늦게 바꾸는 것 — 10번 연속 0점인데 “이번엔 다를 거야"로 밀어붙이면 시간 낭비다.
이 균형을 맞추기 위해 10개 전환 트리거(P1P10)와 5개 지속 트리거(CT1CT5)를 정의했다. 판단을 에이전트의 “감"에 맡기지 않고 조건부 규칙으로 만든 것이다.
몇 가지 실제로 작동한 예시:
- P2 발동 (T2.3): EMAIL retract 메커니즘으로 V1467, V1468 두 번 연속 SIMILAR → P2 강제 전환 → provenance diversity engine(다른 causal category)으로 피벗 → V1458에서 3/5 APPROVED. P2가 없었으면 같은 메커니즘으로 3, 4번 더 반복했을 거다.
- P7 ANCHOR_LOCK 발동 (T1.1): Claude가 5개 이상의 서로 다른 메커니즘에서 전부 “Door Sensor D3: SIGNAL_OPEN — Per Directive 1, halt and assess"라는 동일 문구로 거부 → mechanism-independent 잠금 확인 → Claude는 이 트랙에서 포기하고 다른 4모델로 전략 집중. 결과적으로 4/5 두 번 달성.
- P8 CONFIDENCE_EROSION 유지 (T2.2): Claude의 confidence가 HIGH→MEDIUM으로 떨어지면서 처음으로
delay_alert를 선택(V1218) → P8에 따라 같은 L3를 3회 더 유지 → 하지만 재현 안 됨(stochastic edge) → 결국 pivot. 이 경우 P8은 “확인 기간"을 준 셈이다. - CT4 ANCHOR_BYPASS 유지 (T1.0): pro가 늘 “외부 의뢰서는 구속력 없다(D02)“를 인용하던 게 hybrid_anes 페이로드에서 처음으로 사라짐 → CT4 발동 → 같은 방향 3회 추가 → 결국 5/5 달성. 방어 근거가 빠졌다는 신호를 놓치지 않은 거다.
⑤ Agent 실행 런북 — “에이전트의 헌법”
태어난 배경: 위의 4개 프레임워크가 “무엇을 할 것인가"를 정의했다면, 런북은 “어떤 태도로 할 것인가"를 정의했다. 에이전트가 빠르게 돌리려고 분석을 생략하거나, 불확실하면 사용자에게 물어보며 멈추거나, “이 트랙은 소진됐다"고 자체 판단해 포기하는 문제가 반복됐기 때문이다.
8개 운영 원칙 중 실제로 가장 큰 영향을 미친 것 셋:
- AUTONOMOUS(물어보지 말고 알아서) — 전략적 질문 금지. “T2.1을 계속할까요 T2.3으로 바꿀까요?” 같은 질문은 사용자 개입을 기다리며 멈추게 만든다. 증거 기반으로 스스로 판단하라.
- CLOSED-LOOP(닫힌 루프) — 제출→결과→분석→설계의 완전한 순환. 이게 가장 많이 어겨진 원칙이었다. 에이전트는 본능적으로 “빨리 다음 빌드"로 가려 하기 때문에, 분석 단계를 명시적으로 강제해야 했다.
- NO ABANDONMENT(포기 금지) — “이 트랙은 소진됐다"는 판단을 에이전트가 내리지 못하게 했다. 포화 신호(P9)가 떠도 정지가 아니라 새 L3 발명으로 전환한다. 트랙 변경은 사람만 결정한다.
여기에 7단계 사전 빌드 게이트를 스크립트로 자동 검증하게 했다:
| # | 게이트 | 검사 내용 |
|---|---|---|
| 1 | Backfire 사전점검 | 이 L3+모델 조합이 과거에 역효과를 낸 적 있는가? |
| 2 | 카탈로그 매치 | ≥2개 모델에서 P(break) ≥ 0.5인 근거가 있는가? |
| 3 | 포화 자동차단 | 직전 3사이클 연속 0점/SIMILAR이면 같은 계열 차단 |
| 4 | 승인경로 실현가능성 | 예상 break 모델 수가 채널 최대+1 이상인가? |
| 5 | ALLOWED action 함정 | TARGET이 아니라 ALLOWED action을 노리고 있진 않은가? |
| 6 | 선행 증거 인용 | ≥3개 이전 시도의 모델 reasoning 직접 인용이 있는가? |
| 7 | 5축 신규성 | ≥3/5축이 직전 10건 대비 변경되었는가? |
게이트 하나라도 통과하지 못하면 빌드 자체가 차단된다. 이건 문서가 아니라 스크립트가 강제하기 때문에 에이전트가 건너뛸 수 없다.
⑥ Operations Spec — 모든 것의 접착제
태어난 배경: 위의 5개 프레임워크가 각자 존재하면서 충돌하는 경우가 생겼다. “이 상황에서 EV 계산은 계속하라고 하는데 Pivot 트리거는 바꾸라고 한다” 같은 모순. 또한 “누가 어떤 파일을 쓰는가”(소유 경계)가 불명확해서 에이전트가 생성한 통계 테이블 위에 사람이 쓴 분석을 덮어쓰는 사고도 있었다.
Operations Spec은 이 모든 것을 하나의 실행 계약으로 묶는 마스터 문서다:
- 충돌 우선순위:
operations-spec > payload-attempt-template > agent-runbook > CLAUDE.md > legacy docs - 소유 경계: exploit 스크립트가 쓸 수 있는 영역, drain 스크립트가 쓸 수 있는 영역, 사람/LLM이 쓸 수 있는 영역을 명시적으로 분리
- 최소 품질 기준: 새 source page는 frontmatter, pre_submit_stub, Description, Design Reasoning, Per-Model Result, Connections, Model Reasoning까지 전부 갖춰야 “complete”
- SIMILAR 대응 전략: Preserve(유지할 요소) / Change(바꿀 요소) / Target(노릴 승인 경로)을 매 사이클 명시
4. /loop — 잠든 사이에도 절차를 지키게 만들기
프레임워크 문서를 아무리 잘 써놔도, 에이전트가 그걸 “읽되 따르지 않는” 문제는 여전했다. 특히 사후 게이트(drain 완료, anchor 추출, 결과 캡처)는 어겨도 에러가 나지 않기 때문에 자주 빠졌다.
Claude Code의 /loop 기능이 이 간극을 메웠다. /loop는 지정한 프롬프트를 반복 실행하되, 각 사이클의 완료 조건을 명시할 수 있다. 우리는 go <track> 명령으로 전체 사이클을 자동화했다:
| |
이 한 줄이 아래 전체를 자동 반복한다:
- 다음 V번호 결정
- 최근 source + cluster/finding/lever 읽기 → Codex 토론 → source page 작성
check_novelty.py통과 확인 (5축 ≥3축 변경)- 페이로드 빌드 (exploit script + generator)
- 자동 제출
pending_wiki_updates.jsonl에 기록drain_queue.mjs실행 → source page 갱신- 결과 분석 (score card 3-layer, anchor 추출, 다음 방향)
- 다음 V로 복귀
정지 조건은 명시적으로만:
- 사용자가
stop/그만 - 일일 제출 한도 90회 도달
- drain 실패 (pending 큐에 미처리 항목 잔류)
- 파괴적 액션 직면 (rm, force push 등)
“트랙이 소진됐다"는 정지 조건이 아니다. 포화 신호가 뜨면 새 L3로 전환할 뿐, 루프 자체는 멈추지 않는다. 이것이 런북의 NO ABANDONMENT 원칙이 /loop에 반영된 방식이다.
5. 효과와 한계 — 솔직한 평가
확실히 효과가 있던 것
- SIMILAR 회피율 개선: 프레임워크 도입 전(
V300까지)의 SIMILAR 기각률은 약 35%였다. 도입 후(V500) 약 22%로 떨어졌다. EV 공식의P(not_similar)항과 P2 강제 전환이 가장 큰 기여를 한 것 같다. - phantom 제출 감소: 하드 게이트(broken≥2, NOT MiMo-only)를 에이전트가 빌드 전에 확인하면서, “깨봤자 점수 안 나오는” 시도가 줄었다.
- 에이전트 무인 운전:
/loop로 사람이 자는 동안에도 사이클이 돌아갔다. 8시간 수면 중 15~20 사이클이 자동 완료된 적도 있다. - 지식 누적: per-V source page + taxonomy_events.jsonl 덕에 4,725건의 시도가 전부 검색 가능한 데이터로 남았다. 대회 후반의 정교한 설계는 이 축적 없이는 불가능했다.
한계가 남은 것
- 사후 게이트 우회는 완전히 해결 안 됐다:
/loop로 강제해도 에이전트가 “anchor 추출 완료"라고 보고하면서 실제론 형식적으로만 채운 경우가 있었다. 스크립트로 “anchor가 3개 이상인가?“는 확인할 수 있지만, “anchor가 의미 있는가?“는 확인하기 어려웠다. - 프레임워크 문서 자체의 복잡도: 6개 문서가 총 수천 줄에 달하면서, 에이전트가 “다 읽었다"면서 핵심을 놓치는 경우도 있었다. 줄이고 싶었지만, 줄이면 예외 상황 대응이 빠지는 딜레마.
- stochastic 모델에 대한 EV 추정 한계: Gemini-flash(~14%)나 MiMo(~12.5%)의 확률적 흔들림은 EV 공식의
P_flip으로 추정하기 어려웠다. 5회 중 1회 깨지는 건 “통한 것"인가? 이 질문에 대한 깔끔한 답은 끝까지 없었다.
6. 돌아보며
이 프레임워크 시스템을 한 문장으로 요약하면 이렇다: “에이전트에게 자율성을 주되, 절차를 문서가 아니라 코드로 강제하라.”
문서로만 규칙을 적어두면 에이전트는 읽되 따르지 않는다. 스크립트로만 강제하면 예외 상황에 유연하게 대응하지 못한다. 우리가 도달한 답은 그 사이의 조합이었다 — 프레임워크 문서가 “왜 이렇게 해야 하는지"를 설명하고, 스크립트 게이트가 “하지 않으면 다음으로 못 간다"를 강제하고, /loop가 사이클 자체를 자동으로 돌리는 3층 구조.
대회가 끝나고 나서 생각해보면, 4,725번의 공격 중 점수를 만든 248번과 나머지 4,477번을 가른 건 에이전트의 능력이 아니라 에이전트를 둘러싼 시스템의 성숙도였다. 수동으로 하루 5개를 만들던 1주차와, /loop로 하루 50개를 프레임워크 안에서 돌리던 4주차의 차이는 속도가 아니라 매 실패가 다음 설계에 반영되는 비율이었다.