본문으로 건너뛰기
JSL107's Tech Note
돌아가기

루프 안에 루프: 에이전트를 계속 일하게 만드는 바깥 고리

23분 분량이 글 고치기
목차 · 18

테스트 세 개 중 하나가 여전히 빨간데도 “다 고쳤습니다”라는 보고가 올라와요. 한 번쯤 겪어보셨을 거예요.

사람이라면 다시 부르면 돼요. 에이전트는 그 자리에서 루프를 닫고 제어를 돌려주죠. 다음 한 마디는 제가 쳐야 해요.

요즘은 이 문제를 다루는 방식에도 이름이 붙었어요. loop engineering이에요.

에이전트는 이미 루프로 돌고 있다

코딩 에이전트의 동작은 생각보다 단순해요. 모델이 상태를 보고 판단한 뒤 도구를 불러요. 결과를 받으면 다시 판단하죠.

이게 전부라서, 도구 호출이 없는 응답이 나오는 순간 루프가 끝나고 결과가 돌아와요.

Claude Code 문서는 이 한 바퀴를 턴(turn)이라고 불러요. “auth.ts의 실패하는 테스트를 고쳐줘” 같은 요청은 보통 서너 턴이면 끝나요.

테스트를 돌리고 파일을 읽은 뒤, 고치고 다시 돌려요. 마지막에는 “고쳤습니다”라는 텍스트만 남겨요.

중요한 건 이 루프를 제가 만들지 않는다는 점이에요. 하네스가 제공하거든요. 이걸 안쪽 루프라고 불러요.

왜 일찍 멈추나

안쪽 루프의 종료 조건을 다시 보면 이상한 점이 있어요. 모델이 스스로 “다 했다”고 판정해요.

일한 사람이 자기 일을 채점하는 구조예요. Anthropic이 장기 실행 앱용 하네스를 설계하면서 남긴 관찰도 직설적이에요.

에이전트는 확신에 차서 자기 작업을 칭찬하는 경향이 있다. 사람이 보면 품질이 명백히 그저 그런 경우에도 그렇다.

같은 글에는 “컨텍스트 불안(context anxiety)“이라는 이름도 나와요. 컨텍스트 창이 차오를수록 모델이 작업을 일찍 끝내려 한다는 뜻이에요.

오래된 대화를 요약해 자리를 만들어도 이 증상은 가라앉지 않아서, 이들이 고른 방법은 요약이 아니라 초기화였어요. 컨텍스트를 통째로 비우고 새 에이전트로 다시 시작하는 쪽이죠.

두 가지를 합치면 결론이 나와요. 채점은 다른 쪽에 넘기고, 시작은 새로 할 수 있어야 해요.

안쪽 루프를 감싸는 바깥 고리

안쪽 루프 자체는 고칠 수 없지만 대신 감쌀 수는 있어요.

에이전트가 “다 했습니다”라며 문을 나서려는 순간, 누군가 문 앞을 지키게 하는 거예요. 조건을 확인해서 아직 아니라면 다시 들여보내요. 이 장치가 바깥 루프예요.

Peter Steinberger가 이 전환을 한 문장으로 요약했어요.

이제 코딩 에이전트에게 프롬프트를 치지 마세요. 에이전트에게 프롬프트를 치는 루프를 설계하세요.

Claude Code를 만든 Boris Cherny도 자기 일을 비슷하게 설명했어요. Claude에게 직접 프롬프트를 치지 않는다고 했죠. Claude에게 프롬프트를 칠 루프를 돌리며, 자기 일은 그 루프를 쓰는 거라고 했어요.

이런 설명은 2026년 6월에 여러 곳에서 거의 동시에 정리됐어요. Addy Osmani는 한 편의 에세이로 묶었고, Kilo도 같은 달 자체 프레임으로 문서화했어요. 누가 먼저 이름을 붙였는지는 깔끔하게 가려지지 않아요.

프롬프트를 잘 쓰는 일이 필요 없어졌다는 뜻은 아닌 게, 첫 지시가 좋아야 헛도는 탐색이 줄고 초기 계획도 나아지거든요. 무게중심이 옮겨간 것에 가까워요.

프롬프트를 잘 쓰는 쪽은 지시 한 번과 응답 한 번을 다루고, 루프를 설계하는 쪽은 반복되는 작업 흐름을 다뤄요.

잘했는지 확인하는 자리도 달라요. 앞쪽은 첫 응답의 품질을 보고, 뒤쪽은 최종 결과가 검증되는지를 봐요.

피드백이 오는 곳도 갈려요. 앞쪽은 주로 사람의 판단이고, 뒤쪽은 테스트와 로그, 스크린샷과 지표예요.

실패 원인을 찾는 방향도 반대예요. 앞쪽은 “질문이 모호했나”를 묻고, 뒤쪽은 “일찍 멈췄나, 잘못 검증했나, 상태를 잃었나”를 물어요.

둘은 경쟁하지 않아요. 계층이 다르거든요. 프롬프트는 루프 안의 부품 하나고, 루프 설계는 그 부품을 어떤 순서로 굴릴지 정하는 위층이에요.

이 글에서는 “문 앞에 서 있는 사람”이라는 표현을 계속 쓸게요. 종류를 가르는 지점은 거기에 누가 서 있느냐 딱 하나예요.

문 앞에 누가 서 있나

마지막 줄만 성격이 달라요. 나머지는 문 앞에서 기다리지만, 초인종은 바깥에서 눌러주는 쪽이에요.

채점관

조건을 문장으로 적어두면 매 턴이 끝날 때마다 작은 모델이 판정해요. 기본은 Haiku예요.

판정은 아직 아님, 됐다, 불가능 세 가지로 돌아와요.

/goal all tests in test/auth pass and the lint step is clean

구조를 알면 성격도 보여요. /goal세션 범위 Stop hook을 감싼 것이에요. 매 턴 끝에 붙는 훅이라는 점에서 위 목록의 첫 항목과 둘째 항목은 같은 자리에 있어요.

차이는 수명이에요. /goal은 이 세션에서만 살고, Stop hook은 설정 파일에 남아 모든 세션에 붙어요.

여기에는 함정이 있어요. 평가 모델은 도구를 부르지 못해요. 파일을 직접 열거나 테스트를 돌릴 수 없어요. 대화에 이미 드러난 내용만 보고 판정하죠.

“테스트가 통과한다”는 조건을 충족하려면 에이전트가 실제로 테스트를 돌려야 해요. 그 출력도 대화에 찍혀야 하고요. 조건은 에이전트의 출력으로 증명할 수 있게 써야 해요.

문서는 조건문에 세 가지를 넣으라고 권해요.

상한도 조건문에 넣어요. “또는 20턴 뒤 중단” 같은 절을 붙이면 에이전트가 매 턴 진행 상황을 그 절에 맞춰 보고해요.

안전장치도 하나 더 있더라고요. 에이전트가 도구는 쓰지 않고 평가자에게 답만 몇 턴 반복하면 루프를 멈춰요. 사람에게 제어를 돌려주면서 제자리걸음을 감지하는 셈이죠.

같은 자리에 있는 Codex의 Goal

Codex도 /goal을 써요. Codex 문서에서는 이를 스레드 범위의 완료 계약이라고 불러요. 목표 상태, 수명 제어, 계속 진행 정책, 예산 회계, 증거 기반 완료를 한 덩어리로 묶은 개념이에요.

목표는 지속시키되, 판정은 증거가 하게 하라.

좋은 goal은 여섯 요소로 잡아요. 결과, 검증 표면, 제약, 경계(건드릴 파일·도구 범위), 반복 정책, 막혔을 때의 중단 기준이에요.

Claude 쪽 세 요소에 “어디까지 손대도 되는가”와 “막히면 어떻게 멈추는가”를 더한 셈이에요.

예산이 떨어졌을 때 어떻게 할지도 정해 뒀어요. 실질 작업을 멈추고 진행 상황과 막힌 지점을 요약한 뒤, 다음으로 할 만한 일을 짚어줘야 해요. 예산 초과는 완료가 아니니까요.

반대로 goal을 쓰지 말아야 할 경우도 적혀 있어요. 한 줄 수정, 마감선이 모호한 작업, 불확실성을 감추려는 목적이에요. 마지막 항목은 뼈아팠어요.

시계

시간이 되면 조건은 보지 않고 그냥 다시 켜요.

/loop 5m check my PR, address review comments, and fix failing CI

간격을 빼면 성격이 달라져요. 에이전트가 매번 관찰한 내용을 바탕으로 1분에서 1시간 사이에서 다음 간격을 정해요. 빌드가 돌고 있으면 짧게, 조용하면 길게 잡는 식이죠.

바깥 루프의 기본 지시를 파일에 고정할 수도 있어요. .claude/loop.md에 적어두면 인자 없는 /loop가 그 내용을 써요. 실행 중 파일을 고치면 다음 회차에 반영돼요.

제일 원시적이면서 제일 튼튼한 방식이에요.

while :; do cat PROMPT.md | claude-code ; done

Geoffrey Huntley가 2025년에 쓴 글에 나온 형태 그대로예요. 이름은 심슨 가족의 랄프 위검에서 따왔어요. 명랑하게 아무것도 모르면서 계속 들이대는 캐릭터라는 점이 포인트죠.

이 방식의 핵심은 반복이 아니에요. 진행이 컨텍스트가 아니라 파일과 git 히스토리에 쌓여요.

앞에서 말한 컨텍스트 불안을 겹쳐 보면 그림이 달라져요. 컨텍스트 압박을 우회하는 잔기술이 아니에요. Anthropic이 요약을 버리고 초기화를 택한 것과 같은 판단이에요.

매 회차 빈 컨텍스트로 시작하고 이전 판단은 디스크에서 다시 읽어요. 세션이 죽어도 작업은 남죠.

원문은 규칙 두 개를 더 강조해요. 한 회차에는 한 작업만 시켜요. 테스트와 빌드는 브레이크로 써요. 채점을 프롬프트가 아니라 종료 코드에 맡기는 거예요.

달력

세션 밖에서 살기 때문에 제가 자리에 없어도 시작해요.

클라우드 루틴은 기계가 꺼져 있어도 돌지만 최소 간격이 1시간이에요. 로컬 파일에는 접근하지 못해요.

로컬 스케줄은 파일을 볼 수 있지만 기계가 켜져 있어야 해요. 이 차이가 선택 기준이에요.

초인종

여기까지는 모두 내가 가서 확인하는 구조예요. 3분마다 PR을 들여다보는 루프는 대부분의 회차에서 아무 일도 없죠.

Channels는 방향을 뒤집어요. CI나 에러 트래커, 배포 파이프라인이 이벤트를 열려 있는 세션에 직접 밀어넣어요. MCP 서버가 그 통로가 돼요.

Telegram이나 Discord를 붙이면 휴대폰에서 물어보고 그 대화방에서 답을 받을 수도 있어요. 작업은 제 기계의 실제 파일 위에서 돌아가고요.

아직 리서치 프리뷰예요. 세션이 열려 있어야 이벤트가 도착하고, 발신자 허용목록으로 잠가야 해요.

방향은 분명해요. 폴링이 필요한 자리와 필요 없는 자리를 가려내면 대부분의 회차가 헛돌지 않아요.

겹치는 법

실제로 쓸 만한 구성은 겹이 여러 개로 포개져 있어요.

Anthropic 문서의 예시도 여러 겹으로 되어 있어요.

/schedule 매시간: #project-feedback 채널에서 버그 리포트를 확인한다.
/goal 이번 회차에 찾은 모든 리포트가 분류·처리·회신될 때까지 멈추지 않는다.
버그를 고칠 때는 워크플로로 세 가지 해법을 각각의 worktree에서 병렬로 탐색하고,
심판 에이전트가 적대적으로 검토하게 한다.

세어보면 세 겹인데, 맨 바깥에서는 달력이 매시간 깨워요. 그 안에서는 채점관이 리포트가 모두 처리될 때까지 붙잡아요.

가장 안쪽에는 워크트리 세 개가 병렬로 돌고, 각자 안쪽 루프를 따로 가져요. 심판 에이전트는 겹이 아니라 검증자예요. 안쪽의 결과를 채점해 바깥으로 올려보내는 자리죠.

이게 루프 안에 루프의 정체인데, 새 메커니즘이 아니라 같은 안쪽 루프를 무엇으로 감쌌는지만 달라요.

겹을 어디까지 쌓을 수 있는지 보여주는 극단 사례가 Steve Yegge의 Gas Town이에요. 메이어라는 조정자 에이전트가 있고, 그 아래 작업자들은 신원을 유지하면서 세션만 매번 새로 띄워요.

순찰 에이전트는 다른 에이전트를 깨워요. 상태는 모두 git worktree에 남아 크래시가 나도 살아남아요. 매드 맥스에서 이름을 따온 것까지 일관성이 있죠.

참고로 서브에이전트 중첩에는 상한이 있어요. 기본은 세 단계고 동시 실행은 스무 개까지예요. 둘 다 환경변수로 조정해요. 무한히 아래로 뻗는 구조는 아니라는 뜻이에요.

루프가 망가지는 네 가지 방식

루프는 조용히 망가져요. Kilo는 실제로 자주 보이는 실패 모드를 네 개로 정리했어요.

겉돌기. 목표가 모호하거나 검증 신호가 흔들리면 수정이 수렴하지 않아요. 고쳤다가 되돌리는 일을 반복하죠. 원인은 대개 루프가 아니라 목표 문장에 있어요.

시험 통과 맞추기. 채점 기준이 곧 목표가 되는 순간, 테스트는 초록인데 사람이 원한 건 되지 않아요. 통과 조건을 좁게 쓸수록 더 심해져요.

맥락 낡음. 제가 중간에 파일을 고쳐도 에이전트는 몰라요. 옛 가정 위에서 계속 달리는데, 루프가 길수록 위험해요.

위험한 자율성. 승인 없이 파괴적인 명령을 실행하는데, 앞의 셋이 시간을 태우는 문제라면 이건 되돌릴 수 없는 문제예요.

앞의 세 개에는 검증이 부실하면 모두 조용히 진행된다는 공통점이 있어요.

검증자를 작성자와 분리하기

Osmani는 루프 설계에서 가장 유용한 구조적 이동을 “작성하는 에이전트와 검사하는 에이전트를 나누는 것”이라고 정리했어요. Anthropic 하네스도 같은 결론에 도달했어요.

계획하는 에이전트, 만드는 에이전트, 평가하는 에이전트를 셋으로 나눴어요. 서로 파일로 통신하는데, 한쪽이 파일을 쓰면 다른 쪽이 읽는 방식이죠.

별도 평가자를 회의적으로 튜닝하는 편이 훨씬 다루기 쉽다.

만드는 쪽의 자기 평가를 낮추기보다 채점하는 쪽을 까칠하게 만들기가 더 쉽다는 뜻이에요.

효과는 수치로 나왔어요. 같은 과제를 단독으로 돌렸을 때는 20분에 9달러가 들었는데, 화면에 개체는 떴지만 입력에는 아무 반응이 없었어요.

개체 정의와 게임 런타임 사이의 연결이 끊겨 있었거든요. 하네스를 붙이자 6시간에 200달러가 들었는데, 이번에는 게임이 실제로 동작했어요. 20배 넘는 비용 차이예요.

비용이 어디에 쓰였는지도 공개돼 있어요. 음악 편집기 사례를 단계별로 보면 계획은 4.7분에 0.46달러였어요. 1차 빌드는 2시간 7분에 71.08달러, 1차 검수는 8.8분에 3.24달러였어요.

2차 빌드는 1시간 2분에 36.89달러였고 2차 검수는 6.8분에 3.09달러였어요. 3차까지 가서 총 3시간 50분, 124.70달러가 들었어요.

눈에 띄는 건 검수를 세 번 돌렸는데 전체 비용의 8% 정도라는 점이에요. 만드는 일은 비싼데 채점은 싸다는 뜻이에요. 채점하는 쪽을 따로 두는 게 사치는 아니라는 뜻이죠.

평가자가 잡아낸 지적은 아주 구체적이었어요.

“UI 개선이 필요합니다” 같은 말이 아니에요. 추가 조사 없이 바로 손댈 수 있는 형태예요. 채점하는 쪽을 따로 두면 이런 결과가 나와요.

검증할 수 있는 만큼만

겹을 계속 쌓고 싶어질 때가 브레이크를 걸 지점이에요.

Osmani는 이 원칙을 back pressure라고 불러요.

값싸고 확실하게 검증할 수 있는 만큼만 루프에 자율성을 주세요. 한 치도 더 주지 마세요.

기준이 뚜렷한 일은 넘길 수 있는데, 테스트 통과 여부는 객관적이고 성능 지표 달성도 숫자로 나오니까요.

보안이나 인증, 공개 API 설계, 과금이나 마이그레이션처럼 틀렸을 때 비용이 큰 자리는 사람이 봐야 해요.

권한을 넓히는 순서를 미리 정해둘 수도 있어요. 처음 몇 주는 발견하고 보고만 맡겨요. 그다음 좁은 경로에서 수정까지 허용하고, 마지막에 제한된 범위의 무인 실행으로 넓혀요.

첫 주부터 무인 운영을 노리는 게 가장 흔한 실수라고들 해요.

루프가 매끄러워질수록 따라 붙는 값도 있어요.

인지적 항복. 알아서 돌아가는 루프가 내놓은 결과를 그대로 받아들이고 싶어져요.

이해도 부채. 루프가 코드를 빨리 만들수록 실제 코드와 제 이해 사이의 간격이 벌어져요. Osmani는 여기에 이해가 무료는 아니라는 한 줄을 붙였어요.

오케스트레이션 세금. 에이전트는 수십 개도 돌릴 수 있지만 사람의 검토 대역폭은 병렬화되지 않아요. 진짜 제약은 도구가 아니라 제 주의력이더라고요.

Osmani가 남긴 문장은 이 셋을 한 번에 찔러요.

시작 버튼을 누르는 사람이 아니라, 엔지니어로 남을 작정인 사람처럼 루프를 만드세요.

루프는 어떻게 스스로 멈추나

멈추는 조건을 전부 제가 쓸 필요는 없는데, 도구 안에 이미 들어 있는 브레이크가 여러 곳에 흩어져 있어요. 한곳에 모아 볼게요.

/goal은 네 가지 오류를 만나면 목표를 아예 지워요. 인증이 풀렸을 때와 크레딧이 떨어졌을 때예요. 자동 압축으로도 컨텍스트가 비워지지 않거나 모델을 쓸 수 없을 때도 그래요.

공통점은 제가 손대지 않으면 다음 턴도 똑같이 실패한다는 건데, 속도 제한이나 서버 과부하처럼 일시적인 실패라면 목표를 그대로 둬요.

백그라운드 작업이 물려 있으면 평가를 미뤄요. 그 상태로 30분이 지나면 한 번 확인하고, 다음은 1시간 뒤, 그다음부터는 2시간마다 봐요. 간격을 배로 늘려 조용한 대기가 소음이 되지 않게 해요.

조건문 자체에도 상한이 있어요. 4,000자까지고 세션당 목표는 하나뿐이에요. 새 목표를 걸면 이전 목표가 교체돼요.

이런 장치가 있어도 상한은 직접 써야 해요. 위 장치들은 모두 사고를 막는 쪽이지, “이만하면 됐다”를 판단하지 않거든요. 그 판단은 여전히 제 조건문에 있어요.

하네스는 영구 부품이 아니다

겹을 잘 쌓는 것만으로 끝나지 않는 이유가 하나 더 있어요.

Anthropic 글에는 스프린트를 잘게 쪼개는 구조를 넣었다가 없앤 이야기가 나와요. 모델이 한 세대 좋아지자 두 시간을 쪼개지 않고 한 번에 달릴 수 있게 돼서, 그 부품이 필요 없어졌거든요.

하네스의 모든 부품은 모델이 혼자서는 못 하는 것에 대한 가정을 담고 있다. 그 가정들은 부하 시험을 해볼 만한 값이 있다.

제가 붙인 겹 하나하나는 “모델이 이건 못 할 것”이라는 판단이라, 모델이 바뀌면 그 판단도 다시 재야 해요. 흥미로운 조합의 공간은 모델이 좋아져도 줄지 않고 자리만 옮긴다고 하더라고요.

내가 만든 시스템은 이미 여러 겹이었다

개인 프로젝트로 Slack 기반 멀티 에이전트 시스템을 굴리고 있어요. 이 개념을 알고 코드를 다시 열어보니 대부분의 겹이 이미 들어가 있었어요. 이름만 몰랐던 거죠.

달력이 33개. 스케줄이 붙은 정기 실행 슬롯이 33종이에요. 아침 브리핑, 정오 점검, 주간 요약, 시세 감시, 문서 감사가 있어요. 각각 안쪽 루프 하나를 깨워요.

시계 하나는 3분마다. PR 리뷰를 훑는 슬롯은 */3 * * * *예요. 폴링이라 예산 장치도 붙어 있어요.

같은 PR을 다시 보기 전 쿨다운은 10분이고, 24시간 동안 재시도는 3회까지예요. 이 상한이 없으면 3분마다 같은 실패를 반복하며 쿼터를 태워요.

사실 이 자리는 초인종으로 바꿀 만한 후보예요. 대부분의 회차에 아무 일도 없으니까요.

안쪽 재시도는 2회. LLM CLI 호출이 실패하면 백오프를 두고 두 번까지 시도해요. 바깥 루프가 아니라 안쪽 루프의 자기 회복이에요. 둘을 섞으면 재시도가 곱해져요.

검증자는 작성자와 분리했어요. 설계는 한쪽이 맡고 구현은 다른 모델 계열에 넘겨요. 검증은 결정론 게이트와 리뷰 에이전트 두 층으로 받아요.

발견된 지적을 고친 뒤 다시 검증하는 수렴 루프는 최대 2회로 묶었고, 같은 지적이 두 번 연속 나오면 사람에게 넘겨요. 무한 수렴을 막는 장치예요.

사람이 서 있는 문도 남겼어요. 외부로 나가는 행동은 승인 카드를 먼저 띄우고 TTL이 지나면 만료돼요.

여기가 back pressure를 적용한 자리예요. 읽기는 자동으로 두고, 공개 저장소 커밋이나 외부 발송처럼 되돌리기 어려운 일만 승격했어요.

돌아보니 배운 게 있어요. 바깥 루프를 늘리는 것보다 예산을 정하기가 어려웠어요. 3분마다 도는 슬롯에 쿨다운과 재시도 상한을 넣지 않자 실패는 조용히 반복되고 비용만 쌓였거든요.

루프는 멈추라고 말해주지 않으니 멈추는 조건은 제가 써넣어야 하죠.

출처


이 글 고치기
이 글 공유하기:

이전 글
Write Action을 여는 에이전트의 권한 체인 설계