에이전트 126개가 떴습니다 — 규율로 네 번 실패하고 hook으로 옮긴 판단
AI 에이전트를 여러 개 병렬로 띄워 일을 시킬 때, 몇 개가 적당한지는 매번 판단해야 합니다. 저는 그 판단을 에이전트 자신에게 맡기고 규율을 문서에 적어뒀습니다. 네 번 연속으로 실패했고, 네 번 다 제가 비용을 치르고 나서야 발견했습니다.
네 번의 기록
| 일자 | 띄운 에이전트 | 실제로 필요했던 규모 |
|---|---|---|
| 7월 09일 | 126개 | 12~15개 |
| 7월 17일 | 88개 | 10개 안팎 |
| 7월 18일 | 40개 | 사실 조회 하나 — 인라인으로 충분 |
| 8월 19일 | 10개 | 1개 |
세 번째 건이 특히 뼈아팠습니다. "이 API에 이런 기능이 있나"라는 사실 조회 한 건에 40개를 띄웠고, 그중 35개가 검증 담당이었는데 그 35개가 바꾼 결론은 0개였습니다. 워크플로를 던지기 전에 제가 이미 인라인으로 답한 내용이 그대로 최종 결론이었습니다.
규율은 이미 있었고, 마지막 건은 그 규율을 지켰습니다
세 번의 사고를 겪으며 규율을 계속 다듬었습니다. 예산을 미리 선언할 것, 검증 렌즈 수를 고정 예산에서 차감할 것, 임계를 넘으면 멈추고 물어볼 것. 문서는 점점 정교해졌습니다.
그런데 네 번째 사고에서 에이전트는 그 규율을 지켰습니다. 예산을 계산했고, 선언했고, 그 선언대로 진행했습니다. 문제는 입력 분류였습니다. 파일 아홉 개를 보고 "독립 발견 아홉 건"으로 셌는데, 그 아홉 개는 하나의 커밋 결정으로 수렴하는 것이었습니다. 결정은 하나였습니다.
규율이 지켜졌는데 결과가 틀렸다면, 규율 문구를 더 정교하게 고치는 것으로는 해결되지 않습니다. 다섯 번째 문장을 추가해도 여섯 번째 오분류가 나올 뿐입니다. "더 잘 판단하기"로는 안 고쳐지는 종류라는 것을 그때 인정했습니다.
판단할 에이전트를 하나 더 만들면 되지 않나
제가 처음 떠올린 답이 이것이었습니다. 규모가 타당한지 판정하는 에이전트를 두는 것이죠. 결국 쓰지 않았고, 이유가 셋이었습니다.
- 제 규율 문서에 이미 "선별을 LLM 에이전트에게 맡기지 말 것"이 안티패턴으로 적혀 있었습니다. 에이전트를 아끼려고 에이전트를 더 쓰는 모양이 됩니다.
- 더 결정적인 이유입니다. 메인이 부를 때만 도는 장치는, 과신할 때 안 부릅니다. 네 번의 사고는 전부 "이 정도는 괜찮다"고 판단한 순간에 났습니다. 바로 그 순간에 검사를 건너뛸 수 있는 장치는 그 순간을 못 막습니다.
- hook은 토큰을 쓰지 않고 건너뛸 수도 없습니다. 이미 비슷한 선례를 하나 갖고 있기도 했습니다.
두 번째가 이 글의 핵심입니다. 자기점검이든 보조 에이전트든, 호출 여부가 판단하는 쪽 손에 있으면 같은 실패로 끝납니다. 판단을 개선하는 게 아니라 판단의 위치를 옮겨야 했습니다.
hook이 하는 일
워크플로 실행 직전에 걸리는 hook을 하나 만들었습니다. 넘어온 스크립트를 정적으로 파싱해 스폰될 에이전트 수를 세고, 임계(기본 6) 이상이면 거절합니다.
- 중첩은 곱으로 셉니다. 5개 항목 각각이 3개 렌즈를 띄우면 15입니다.
- 길이를 모르는 컬렉션은 8로 보수 가정합니다.
- 루프 안의 호출은 총량 상한이 없으므로 무조건 차단합니다.
- 파싱에 실패하면 통과시킵니다. 가드가 정상 작업을 막는 쪽이 더 나쁩니다.
통과하려면 제 승인을 받은 뒤 스크립트에 주석을 답니다.
// [fanout-approved: 18] 전 레포 감사 — 사용자 승인 (8/20)
선언한 숫자가 추정치보다 작으면 다시 막힙니다. 승인 토큰을 형식적으로 붙여 빠져나가는 길을 하나 줄인 셈입니다.
가드의 버그는 대칭이 아닙니다
만들면서 제 코드에서 버그를 하나 잡았는데, 그 방향이 중요했습니다. 처음 구현은 중첩 팬아웃을 곱이 아니라 바깥 개수만으로 세고 있었습니다. 5×3을 15가 아니라 5로 읽는 것이죠.
일반적인 버그였다면 "고치면 되는 일"입니다. 그런데 가드에서는 다릅니다. 과대 계수는 짜증나고, 과소 계수는 가드를 무력화합니다. 실수의 방향이 한쪽으로 위험하게 정렬되어 있습니다. 그래서 팬아웃 범위를 전부 모아 각 호출을 감싸는 배수를 곱하는 방식으로 다시 짰고, 케이스를 나눠 통과와 차단 양쪽을 다 확인했습니다.
못 막는 것을 문서에 적어둡니다
이 hook에는 구멍이 있습니다. 워크플로가 아니라 에이전트를 한 메시지에서 여러 개 직접 띄우는 경우는 못 셉니다. hook은 호출을 하나씩 보기 때문에 한 번에 세 개가 떠도 각각 하나로 보입니다.
일부러 막지 않았습니다. 리뷰 루프처럼 정당하게 두세 개를 띄우는 흐름과 구분할 방법이 마땅치 않아서, 무리하게 막으면 정상 작업이 자주 걸립니다. 다만 못 막는다는 사실 자체는 적어뒀습니다. 가드가 있으니 다 막힌다고 믿는 것이 가드가 없는 것보다 위험합니다.
배선에도 함정이 하나 있습니다. hook을 등록하는 설정 파일이 버전 관리에서 제외돼 있어서, 레포를 새 컴퓨터에 받아도 hook 파일만 오고 실제로는 안 돕니다. 파일이 있는 것과 걸려 있는 것이 다르다는 걸 확인 한 번으로 알 수 있는데, 저는 이걸 나중에야 알았습니다.
같이 고친 판정 함정 둘
hook을 만들면서 규율 문서도 손봤습니다. 문구를 더 정교하게 만드는 대신, 네 번의 사고에서 실제로 틀렸던 두 지점을 적었습니다.
첫째, "N개의 독립 발견"은 개수가 아니라 행동의 수로 셉니다. 아홉 개가 하나의 결정으로 수렴하면 그건 결정 하나입니다. 개수를 세지 말고 분기하는 행동을 세라는 것이죠.
둘째, 팬아웃 전에 전제를 결정적으로 확인합니다. 네 번째 사고에서 그 아홉 개 파일은 사실 이미 전부 버전 관리에 들어가 있었습니다. 실제로 빠진 건 문서 한 개였습니다. 상태 출력의 앞부분만 보고 "이 디렉토리가 통째로 빠졌다"고 일반화했고, 확인용 명령을 한 번도 돌리지 않았습니다. 틀린 전제 위에 열 개를 띄운 것입니다.
남는 생각
혼자 개발하면서 AI에게 일을 맡길 때, 저는 대체로 "더 좋은 지시를 쓰는 것"으로 문제를 풀어왔습니다. 이번 건은 그 방법이 통하지 않는 부류였습니다. 지시는 이미 충분히 좋았고, 마지막 사고에서는 지시대로 했는데도 틀렸으니까요.
같은 실패가 반복될 때 문구를 고칠지 구조를 고칠지는, "규율을 지켰는데도 틀렸는가"로 갈리는 것 같습니다. 안 지켜서 틀렸으면 규율을 더 눈에 띄게 만들면 되고, 지켰는데 틀렸으면 그 판단은 그 자리에 있으면 안 되는 것입니다.
그리고 토큰을 쓰지 않고 도는 결정적인 검사가 LLM 판정자보다 나은 자리가 분명히 있다는 생각이 들었습니다. 세는 일은 세는 도구에게 맡기는 편이 낫습니다.
관련 글
AI 코딩 에이전트의 메모리도 결국 부채가 됩니다 — 감사와 정리의 기록
AI 코딩 에이전트에 규칙과 메모리를 부지런히 쌓았지만, 어느 순간부터 그 기억이 되려 발목을 잡기 시작했습니다. 가장 위험했던 '폐기된 정책의 화석'부터 메모리 부채의 유형을 정리하고, 적대적 검증·3진 판정·근거 부담 차등으로 감사해 걷어낸 과정을 돌아봅니다.
사람 손 없이 PR→머지→배포: AI 에이전트 자동 연쇄를 설계하며 배운 것
코드 리뷰·머지 게이트·배포는 다 자동화했는데, 사슬의 진입점만 수동 호출로 남아 있었습니다. PR 생성 직후 자동 트리거, 보고가 아닌 GitHub를 직접 조회하는 머지 게이트, 배포 보류 게이팅까지 — 자동 연쇄를 직접 설계하며 겪은 시행착오를 정리했습니다.
한 레포에만 살던 개발 규율을, Claude Code가 어디서든 따르게 만들기
한 레포에만 쌓여 있던 개발 규율이 왜 다른 레포로 옮겨지지 않는지 따져보니, 방법론을 떠받치던 세 기둥이 전부 한 레포에 결합돼 있었습니다. 이를 글로벌 레이어로 끄집어내 opt-in으로 확장하고, 직접 만든 차단 hook을 적대적으로 검증해 우회와 과차단을 함께 잡은 기록입니다.