본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 22. 15:52

Codex가 Codex를 수정하기: 스스로 논쟁하고, 판단하며, 자신의 PR을 병합하는 합의 루프 (Consensus Loop)

요약

Codex CLI의 포크 버전을 대상으로 버그를 스스로 수정하고 PR을 병합하는 '합의 루프(Consensus Loop)' 에이전트 메커니즘을 소개합니다. 여러 솔버 에이전트가 서로 다른 관점에서 코드를 제안하고 논쟁하며, 판사가 이를 중재하여 최적의 해결책을 도출하는 과정을 다룹니다.

핵심 포인트

  • 에이전트가 스스로 코드를 작성, 검토, PR 병합까지 수행하는 엔드 투 엔드 루프 구현
  • 최소/구조적/삭제 솔버 등 서로 다른 목적을 가진 다중 에이전트 간의 논쟁 메커니즘
  • 메타 판사가 에이전트 간의 의견 차이를 중재하여 설계 합의를 유도
  • 실제 오픈 소스 버그를 대상으로 작동하는 도그푸딩 실험 사례 제시

지난 금요일, 저는 단순히 코드를 제안하는 것을 넘어 실제로 코드를 작성하고, 에이전트가 이를 검토하며, 자신의 PR (Pull Request)을 직접 병합하는, 저희가 구축하여 오픈 소스로 공개한 에이전트 루프인 합의 루프 (consensus-loop)에 대해 이곳에 글을 썼습니다 (해당 포스트는 여기 있습니다). 몇몇 분들이 저희가 일상적으로 실제로 무엇을 대상으로 이 루프를 실행하는지 물어보셨습니다. 그래서 제가 계속해서 다시 시도하게 되는 실험을 소개하고자 합니다. 저희는 동일한 루프를 codex CLI의 포크 (fork) 버전에 적용하여 codex를 수정하게 했습니다. codex가 codex를 수정하는 것입니다.

이 버전은 리포지토리 (repository) 링크가 포함되어 있으므로, 제 말을 믿는 대신 직접 그것이 실제인지 판단하실 수 있습니다.

설정: 오픈 소스인 codex CLI의 공개 포크 버전을 가져와서, 저희의 합의 루프를 그곳으로 지정합니다. 루프의 임무는 패치를 작성하는 사람이 아무도 없는 상태에서, 해당 포크 내의 작은 업스트림 (upstream) 버그들을 엔드 투 엔드 (end-to-end)로 해결하여 종료하는 것입니다. 이 모든 과정은 도그푸딩 (dogfood)입니다. 이 포크는 별 (star)도 없고, 포크 (fork)도 없으며, 외부 사용자도 없습니다. 제가 이 점을 미리 말씀드리는 이유는, 이후의 내용이 "이것은 제품이다"가 아니라 "이것은 하나의 메커니즘이다"로 읽히길 원하기 때문입니다.

리포지토리는 공개되어 있습니다: github.com/ChronoAIProject/codex. 이는 openai/codex의 포크입니다. 아래의 내용은 저를 믿을 필요가 없습니다. 모든 주장은 클릭 가능한 이슈 (issue) 또는 PR입니다.

버그가 루프를 통해 이동하는 방식

1. 인테이크 (Intake). 실제 업스트림 codex 버그가 이슈 (issue)로서 포크로 미러링됩니다. 제목에는 포인터가 포함됩니다. 예: "Upstream openai/codex#29131: 인식되지 않는 슬래시 명령어로 인해 메시지 전송이 방지됨." 이슈 본문에는 선택 기준이 명시됩니다: 식별 가능한 파일로 한정되고, 이 리포지토리가 소유한, 작거나 중간 규모의 기계적인 버그. 인증 (auth), 앱 서버 (app-server), 데스크톱 (desktop), iOS, 광범위한 샌드박스 정책 (sandbox policy)은 명시적으로 제외됩니다. 따라서 루프는 영웅적인 유지 관리자가 되려고 노력하는 것이 아니라, 자신이 끝낼 수 있는 싸움을 선택하는 것입니다.

2. 솔버 (Solvers)들이 논쟁합니다. 여러 솔버 에이전트 (solver agents)가 검토를 수행하고 그들의 제안을 이슈 댓글로 게시합니다. 그들은 서로 다른 사전 확률 (priors)을 가지고 있습니다:

  • 재현(repro)을 만족하는 가장 작은 변경을 원하는 최소 솔버 (minimal solver),
  • 깔끔한 경계 (clean boundary)를 원하는 구조적 솔버 (structural solver),
  • 코드를 추가하기보다 삭제하는 쪽을 주장하는 삭제 솔버 (delete-solver). 이들은 진심으로 의견이 다릅니다. 이슈 #34에서 최소 솔버는 "pre-dispatch validation" 수정을 제안했고, 구조적 솔버는 "batch validation boundary"를 제안했으며, 삭제 솔버는 삭제에 대해 기권했습니다. 이 세 가지 제안을 모두 읽어보실 수 있습니다.

3. 판사 (judge)가 라운드를 중재합니다. 메타 판사 (meta-judge)가 솔버들의 출력값을 읽습니다. 의견이 갈릴 경우, 승자를 선택하는 대신 "설계 합의를 위해 한 차례 더 좁은 범위의 라운드가 필요합니다"와 같은 메시지를 게시하고 다시 돌려보냅니다. 이슈 #34는 세 라운드까지 진행되었습니다. 마지막 댓글은 "Round-3 meta-judge arbitration"이라는 제목으로 되어 있으며 다음과 같이 결정을 명시합니다:

"최소 솔버와 구조적 솔버가 이제 동일한 구체적인 구현 경계에 동의했으며, 삭제 솔버는 동일한 경계를 수용하면서 삭제에 대해서는 기권합니다."

심지어 무엇이 거절되었는지도 기록합니다: 새로운 ToolCallBatch 모듈 ("정확성을 위해 새로운 단일 호출자 codex-core 추상화는 필요하지 않음"). 이 부분이 제가 진정으로 유용하다고 생각하는 지점입니다. 판사는 선택되지 않은 길(the road not taken)을 기록합니다.

4. 구현, 테스트, 병합 (Implement, test, merge). 합의에 도달하면 루프는 브랜치(refactor/iter34-issue-34)를 열고, 패치(patch)를 작성하며, 보호된 빌드/테스트(guarded build/test)를 실행한 뒤, consensus-rnd/issues 브랜치를 대상으로 PR을 생성합니다. #34의 경우 그것은 PR #37이며, codex-rs/core/src/session/turn.rs 및 codex-rs/core/src/stream_events_utils.rs를 수정하고 codex-rs/core/tests/ 아래에 회귀 테스트(regression test)를 추가했습니다. 그 후 스스로를 병합하고 이슈에 다음과 같이 게시합니다: ✅ PR #37을 통해 자동 병합됨.

상태(state)는 GitHub에 존재합니다. 이슈(Issue)는 작업 큐(work queue)이며, 솔버(solver)의 댓글은 토론 기록(debate transcript), 판사(judge)의 댓글은 결정 기록(decision record), PR은 결과물(artifact)입니다. 레이블(label)은 라이프사이클(lifecycle)을 추적합니다: crnd:lifecycle:managed, crnd:phase:design-solvingcrnd:phase:consensus-reachedcrnd:phase:merged, 그리고 crnd:human:auto는 컨트롤러(controller)가 관리자(maintainer) 없이 진행할 수 있음을 의미합니다. 루프(loop)가 작성한 모든 PR 본문은 ⟦AI:AUTO-LOOP⟧로 끝납니다. 누가 작성했는지를 알려주는 것은 사람이 아니라 바로 그 마커입니다.

실제 수정 사항, 엔드 투 엔드 (end to end)

이슈(Issue) #34는 실제 Codex의 동시성(concurrency) 버그를 반영합니다: 하나의 모델 응답에 여러 개의 병렬 도구 호출(tool calls)이 포함되었을 때, 유효한 apply_patch 형제 호출이 동일한 응답 내의 잘못된 형식을 가진 형제 호출이 거부되기 전에 부작용(side effects)을 시작할 수 있는 문제입니다. 판사(judge)는 이를 "부작용을 일으키는 배치(batches)에 대한 fail-fast 검증"으로 정의했습니다. 즉, 쓰기 작업을 실행하기 전에 전체 배치가 잘 구성되었는지 먼저 수락하는 것입니다.

병합된 수정 사항(PR #37)은 도구 호출(tool calls)을 스테이징(stages)하고, 전체 응답 배치가 유효함이 확인된 후인 ResponseEvent::Completed 시점에만 실행 큐(run queue)로 플러시(flushes)합니다. 이 수정 사항은 회귀 테스트(regression)와 함께 배포되었습니다: 동일한 응답 내에서 유효한 형제 호출 뒤에 잘못된 형식이 올 경우, 유효한 호출이 실행되지 않음을 보장합니다. 해당 PR은 대상 테스트에 대해 test -p codex-core를 실행하여 통과(green)했음을 보고했습니다. 이는 제가 참여하지 않은 토론을 통해 생성된, 실제 검토 가능한 패치(patch)가 포함된 진짜 버그입니다.

아무것도 하지 않았음을 솔직하게 밝히는 부분

회의적인 시각을 가진 사람에게는 PR #16을 보여주고 싶습니다. 루프(loop)는 이슈(Issue) #15(apply_patch 버그)를 가져와 현재 체크아웃(checkout) 상태에서 재현을 시도했으나 실패했습니다. 생산적으로 보이기 위해 억지로 수정 사항을 만들어내는 대신, PR 본문은 다음과 같이 명시합니다:

"운영 환경을 위한 폴백(fallback)은 추가되지 않았습니다. 회귀 테스트를 통과했으므로, 현재 체크아웃에서는 네이티브 도구 호출(tool-call) 경로가 실행 가능한 조회(lookup) 버그를 증명하지 못했습니다."

따라서 Codex는 해당 동작을 고정하기 위해 PATH가 격리된 회귀 테스트(regression test)를 추가하고 작업을 중단했습니다. 이는 올바른 엔지니어링적 결정이며, 추론 과정을 읽기 전까지는 아무런 동작도 하지 않은(no-op) 것처럼 보이는 결과이기도 합니다. 언제 패치를 하지 말아야 할지를 아는 루프는, 항상 디프(diff)를 생성해내는 루프보다 저에게 더 흥란스럽게 다가옵니다.

정직한 경계

이것은 포크(fork)이며, 도그푸딩(dogfood, 자사 제품 사용)이고, 사용자는 없습니다. 여기서 제안된 사항 중 업스트림(upstream)으로 전달된 것은 없으며, 이것은 OpenAI의 프로젝트가 아닙니다. 우리는 그들의 오픈 소스 CLI를 포크한 우리만의 버전에 루프를 적용하고 있을 뿐입니다.

버그들은 설계상 작습니다. 상태 점(status-dot)의 대비 문제, apply-patch에서의 UTF-8 BOM 처리, call_id를 통한 도구 호출(tool calls)의 중복 제거, 슬래시 명령(slash-command) 수정 등입니다. 제한된 기계적 작업들입니다. "AI가 코드베이스를 유지 관리한다"라고 말한다면 거짓말이겠지만, "루프가 작고 제한된 버그들을 엔드 투 엔드(end-to-end)로 해결한다"는 것은 실제로 일어난 일이며, 현재까지 약 16개의 PR이 병합되었습니다.

여전히 인간이 개입하고 있습니다. 누군가가 업스트림의 이슈(issue)를 미러링하고 기준(rubric)을 설정하며, 우리는 모든 PR을 열어 읽습니다. 우리의 상태를 인용하자면, 우리는 여전히 절반 정도는 쓰레기 같은 결과물이 나올 것을 예상하며 PR을 엽니다. 코드는 자동이지만, 주의(attention)는 자동이 아닙니다.

판단자(judge)는 때때로 형식적인 절차에 불과합니다. 쉬운 버그의 경우 세 명의 해결사(solvers)가 기본적으로 동의하며, 판단자는 이를 승인(rubber-stamps)합니다. 이슈 #34에서 진행된 3라운드 중재(arbitration)는 의견 불일치가 결정적인 역할을 했던 유일한 사례입니다. 아직 판단자가 쉬운 80%의 문제에서 단일 우수한 에이전트(agent)보다 뛰어나다는 명확한 증거는 확보하지 못했습니다.

직접 파헤쳐 보고 싶다면 저장소는 공개되어 있습니다: github.com/ChronoAIProject/codex. 이슈 #34와 PR #37부터 시작해 보세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0