01
배경
왜 만들었나
AI 코딩 에이전트를 쓰다 보면, 한 모델이 자신 있게 내놓은 답이 틀렸을 때 그걸 걸러줄 장치가 없다는 점이 늘 불안했습니다. 사람이 코드 리뷰로 서로를 보완하듯, 에이전트도 그렇게 만들 수 없을까에서 출발했습니다.
그래서 일하는 owner, 그 결과를 검토하는 reviewer, 합의가 깨지면 최종 판정을 내리는 arbiter — 세 역할이 도는 'Tribunal' 구조를 직접 설계했습니다. 화면이 아니라 여러 프로세스가 안전하게 동시에 도는 런타임을 만드는 프로젝트였습니다.
02
주요 기능
무엇을 하는가
- ①Tribunal 합의 루프owner가 구현하고 reviewer가 peer로 검토하며, 막히면 arbiter가 binding 판정을 내립니다.
- ②Mixture-of-Agentsarbiter 판정 전 외부 모델(Kimi·GLM 등) 의견을 병렬 수집해 프롬프트에 주입합니다.
- ③역할별 봇 · 설정Discord 봇 3개를 역할에 고정하고, 역할마다 agent type·model·effort를 따로 설정합니다.
- ④승인 후 재검토승인 뒤에도 변경이 감지되면 다시 리뷰 루프를 돌려 회귀를 잡습니다.
03
기술 선택
왜 이 기술을 썼나
Bun런타임
여러 에이전트와 봇을 동시에 띄우는 만큼 빠른 시작과 가벼운 런타임이 중요했고, 단일 바이너리로 운영이 단순해집니다.
SQLite · WAL상태 저장
별도 DB 서버 없이 단일 파일로 운영하면서, WAL 모드로 읽기/쓰기 동시성을 확보해 turn·lease 추적에 썼습니다.
Discord.js인터페이스
사용자 진입점을 owner 봇 하나로 두고, reviewer·arbiter는 내부 역할로 동작시켜 익숙한 채팅 UI 위에서 멀티에이전트를 노출했습니다.
Agent / Codex SDK에이전트
역할마다 다른 에이전트 타입을 붙일 수 있게 추상화해, 한 모델에 종속되지 않는 구조로 만들었습니다.
04
트러블슈팅
부딪힌 문제와
해결 과정
해결 과정
동시성
reviewer가 owner의 옛 코드를 검토하는 문제
문제reviewer가 owner 작업물의 오래된 스냅샷(stale copy)을 읽고 검토해, 이미 고쳐진 부분을 다시 지적하는 일이 생겼습니다.
원인reviewer workspace를 스냅샷 복사로 만들다 보니, owner가 그 사이 작업을 진행하면 두 워크스페이스가 어긋났습니다.
해결스냅샷 복사를 걷어내고, reviewer가 owner의 현재 worktree를 read-only로 direct mount해 읽도록 바꿨습니다. stale 레코드가 남아도 실행 직전 owner 현재 경로로 재동기화합니다.
결과reviewer가 항상 최신 상태를 검토하게 됐고, 역할별 read-only 보호로 reviewer가 코드를 건드릴 위험도 차단했습니다.
장애 대응
모델 장애·한도로 런타임 전체가 멈춤
문제Claude 쪽에 장애나 사용량 한도가 걸리면 전체 작업 루프가 그대로 멈췄습니다.
원인단일 제공자·단일 토큰에 의존해, 한 곳이 막히면 우회 경로가 없었습니다.
해결Claude 장애 시 Codex 런타임으로 자동 전환하는 global failover를 넣고, OAuth 멀티 토큰 로테이션으로 한도에 걸려도 다른 토큰으로 이어가게 했습니다.
결과한쪽 제공자에 문제가 생겨도 작업이 중단 없이 이어지는 가용성을 확보했습니다.
합의 품질
두 에이전트가 끝내 합의하지 못할 때
문제owner와 reviewer가 서로 다른 주장을 반복하며 deadlock에 빠지거나, 단일 모델 편향으로 같은 실수를 함께 넘기는 경우가 있었습니다.
원인합의 실패 상태를 끝까지 책임지고 결정할 주체가 없었습니다.
해결합의 실패·blocked 상태에 한해 arbiter가 binding 판정을 내리게 하고, 판정 전 외부 모델 의견(MoA)을 병렬로 주입해 한 모델의 시야를 넘게 했습니다.
결과루프가 무한히 도는 대신 명확한 종료 조건을 갖게 됐고, 판정의 근거가 더 다양해졌습니다.
05
배운 점
회고
동시성은 "상태를 누가 소유하는가"의 문제다. direct mount와 재동기화를 거치며, 여러 프로세스가 같은 자원을 볼 때 SSOT를 어디에 둘지가 핵심이라는 걸 배웠습니다.
장애는 막는 게 아니라 우회하는 것. failover와 토큰 로테이션을 만들며, 외부 의존성은 언젠가 반드시 실패한다는 전제로 설계해야 한다는 걸 체감했습니다.
합의에는 종료 조건이 필요하다. 사람의 협업 구조(검토·중재)를 시스템으로 옮기면서, 무한 루프를 끊어줄 binding 판정자의 역할을 이해했습니다.