Agent Engineering
루프 엔지니어링: 당신에게 유죄를 선고할 루프를 지어라
프롬프트 엔지니어링은 무엇을 말할지를 묻는다. 루프 엔지니어링은 시스템이 일을 어떻게 끝맺는지를 묻는다 — 그리고 운이 좋은 날에는, 시스템이 당신에게 유죄를 선고하는지를 묻는다.
한 줄로 요약하면
지난 2년간 우리는 더 나은 프롬프트를 쓰는 법을 배웠다. 프롬프트는 운전대다. 자동차가 아니다.
진짜 일에는 나머지가 필요하다: 확인 가능한 목표, 기록해둘 곳, 브레이크, 온도계, 정비공, 승인해줄 어른, 그리고 오늘보다 내일이 더 똑똑해지도록 해줄 노트. 이 전체가 루프이며, 루프를 만드는 일이 곧 엔지니어링이다.
아무도 글로 쓰지 않는 부분이 사실은 좋은 부분이다: 제대로 지은 루프는 언젠가 당신에게 당신이 틀렸다고 말해준다. 내 루프는 일주일 만에 그 일을 네 번이나 해냈다. 이 글의 증거는 바로 그 네 가지 이야기이며, 그것들은 다이어그램보다 훨씬 값비싼 대가를 치르고 얻은 것이다.
트리하우스
아빠가 이렇게 말한다: 안전한 트리하우스를 만들어라.
눈을 감고 마법의 주문 한 마디를 외운 다음 눈을 떠보니 트리하우스가 완성돼 있는 일은 없다. 당신은 루프를 돌린다. "안전하다"는 것은 무슨 뜻인가? 나에게 어떤 목재가 있는가? 판자는 잘 맞았는가? 사다리가 흔들리는가? 무엇이 부서졌는가? 내 동생이 올라갈 수 있는가?
이제 명사를 바꿔보자. 트리하우스는 소프트웨어가 되고, 판자는 코드가 되며, 나사는 API 호출이 된다. 안전 점검은 테스트가 되고, 아빠는 휴먼 리뷰가 되고, 공책은 메모리가 되고, 실수는 피드백이 된다.
에이전트가 강력한 이유는 한 번에 답을 내놓아서가 아니다. 시도하고, 살펴보고, 판단하고, 고치고, 다시 시도할 수 있어서 강력한 것이다.
일대일 대응표
| 멘탈 모델 | 기술 용어 | 중요한 이유 |
|---|---|---|
| "안전한 트리하우스를 지어라" | 목표 계약(Goal Contract) | 에이전트에게는 목표와 완료의 정의가 필요하다. |
| 밑그림부터 그리기 | 계획(Plan) | 행동에 앞서 제안되는 경로. |
| 작업대 위의 도구들 | 도구 접근권(Tool Access) | 의도적으로 경계가 설정된 능력. |
| 목재 측정하기 | 관찰(Observation) | 가정이 아니라 실제 피드백. |
| 사다리가 흔들리는지 확인하기 | 평가(Evaluation) | 이것으로 정말 충분한가? |
| 헐거운 나사 조이기 | 복구(Recovery) | 실제로 마주친 실패를 고쳐라. |
| 아이들이 오르기 전에 아빠가 확인한다 | 휴먼 게이트 | 영향이 큰 행동에는 서명이 필요하다. |
| 무엇이 잘못됐는지 기록하기 | 메모리 | 다음 실행은 오늘보다 똑똑하게 시작한다. |
| 안전할 때 멈추기 | 정지 기준 | 언제 끝낼지 아는 것. |
| 체인소를 혼자 쓰지 않기 | 정책 게이트 | 위험한 행동에는 결정론적 통제가 필요하다. |
그 표가 아키텍처의 전부다. 아래는 전부 세부 사항일 뿐이다.
왜 프롬프트만으로는 부족한가
프롬프트는 이렇게 말할 수 있다: 이 버그를 고쳐줘. 코딩 에이전트는 이슈를 읽고, 레포를 살피고, 파일을 찾고, 가설을 세우고, 수정하고, 테스트를 돌리고, 실패 내용을 읽고, 다시 패치하고, 재실행하고, 근거를 요약하고, 풀 리퀘스트를 열고, 리뷰를 기다려야 한다.
그것은 프롬프트 하나가 아니다. 운전대가 달린 운영체제다.
척추
User intent → Goal Contract → State Ledger → Planner → Tool Router
→ Policy Gate → Executor → Observer → Evaluator → Recovery
→ Human Gate → Memory → next loop or delivery
목표 계약 — 주문서
레스토랑 주문서는 이렇게 말한다: 7번 테이블, 치킨, 땅콩 빼고, 중간 매운맛, 20분. 이게 없으면 주방은 그냥 추측할 뿐이다.
나쁜 예: 결제 문제 좀 고쳐줘. 좋은 예:
{
"objective": "Fix timeout during checkout payment confirmation",
"success_criteria": [
"Payment confirmation completes under 3 seconds",
"Existing payment tests pass",
"No duplicate charges are created",
"Pull request includes test evidence"
],
"constraints": {
"max_iterations": 8,
"max_cost_usd": 10,
"forbidden_actions": ["deploy_to_production"],
"requires_human_approval": ["database_migration", "payment_logic_change"]
}
}
대부분의 에이전트 시스템은 모델이 멍청해서 실패하는 게 아니다. 목표가 모호해서 실패한다. 목표가 모호하면 계획도 모호해지고, 도구 사용은 무작위가 되며, 비용만 잡아먹는 난장판이 된다.
상태 원장 — 화이트보드
자전거를 고칠 때 우리는 무엇을 시도했는지 적어둔다. 모델은 두뇌이고, 원장은 화이트보드다. 두뇌에게 화이트보드 역할까지 맡기지 말라 — 맥락은 열화되고, 도구 출력은 잡음투성이며, 오래된 추론은 낡아버린다.
모든 도구 호출을 기록하라: 상태, 반복 횟수, 현재 가설, 완료된 단계, 미해결 질문, 마지막 오류.
정책 게이트 — 브레이크
10대는 부엌칼을 쓸 수 있다. 10대가 체인소를 혼자 쓰게 해서는 안 된다. 정책 게이트는 체인소 옆에 서 있는 어른이며, 반드시 code:
def policy_gate(tool, args, state):
if tool == "process_refund":
if not state.get("verified_customer_id"):
return {"allowed": False, "next_action": "lookup_verified_customer"}
if args["amount"] > 500:
return {"allowed": False, "next_action": "escalate_to_human"}
if tool == "deploy_to_production":
return {"allowed": False, "next_action": "request_approval"}
return {"allowed": True}
여야 한다. 프롬프트 지시는 확률적이다. 게이트는 결정론적이다. 행위가 돈, 프로덕션, 프라이버시, 안전, 법적 위험에 닿는다면 확률만으로는 부족하다.
평가자 — 온도계
닭이 다 익었는지 짐작으로 판단하지 않는다. 결정론적 검사 — 테스트 통과, 스키마 검증, 타입 체크, 숫자가 맞아떨어지는지 — 를 우선하고, 정말로 판단이 필요한 부분에만 모델을 쓰라.
묻지 말라, 이게 좋은가? 대신 물어라, 어떤 기준이 통과했고, 어떤 기준이 실패했으며, 그 판정을 뒷받침하는 증거는 무엇인가? 다음 행동을 낳지 못하는 평가는 그저 의견일 뿐이다.
복구 — 정비공
체인이 빠지면 새 자전거를 사지 않는다. 먼저 분류부터 하고, 그다음에 움직인다. 일시적 실패는 백오프를 두고 재시도하고, 검증 실패는 입력을 수정하고, 권한 실패는 접근 권한을 요청하고, 비즈니스 규칙 실패는 멈추거나 에스컬레이션한다. 같은 실패가 세 번 반복되면 전략을 바꿔야 한다.
재시도는 전략이 아니다. 그것은 도구다. 좋은 에이전트는 더 열심히 시도하지 않는다, 다르게.
시도한다.
정지 — 실패가 아니라 통제
뼈대가 안정되고, 사다리가 안전하고, 아빠가 승인했을 때 짓기를 멈춘다 — 지쳤을 때가 아니다. 무엇이든 실행하기 전에 다섯 가지 정지 조건을 정의하라: 성공, 실패, 에스컬레이션, 예산, 안전. 멈출 수 없는 루프는 자율적인 게 아니다. 길을 잃은 것이다.
휴먼 게이트 — 책임지는 어른"need human help"는 쓸모없는 에스컬레이션이다. 좋은 에스컬레이션은 필요한 결정, 권고안, 근거, 위험, 대안까지 담고 있어서 인간이 2분 안에 판단을 내릴 수 있어야 한다.
메모리 — 큐레이션되어야 할 뿐, 결코 자동이어서는 안 된다
나쁜 메모리는 오류를 누적시키고, 좋은 메모리는 지혜를 누적시킨다. 그래서 메모리는 제안되고 인간이나 게이트가 승인한다. 구체적이고, 근거가 있고, 재사용 가능하며, 미래의 행동에 결부된 교훈만 저장한다.
루프가 나에게 유죄를 선고한 네 번
여기서부터가 1년 전의 나라면 쓸 수 없었던 부분이다. 이 사례들은 내가 직접 운영하는 함대를 구축하던 어느 한 주에서 나온 것이고, 하나하나가 내가 만든 장치가 나에게 틀렸다고 말해준 경우다. 루프 엔지니어링이 주는 보상은 바로 이것이다. 속도가 아니라 교정.
1. 게이트가 처음 유죄를 선고한 상대는 다름 아닌 그것을 만든 사람이었다
나는 검증기에 새로운 차단 규칙을 추가하고 배포했는데, 빌드가 빨간불이 됐다. 규칙 자체가 잘못돼서가 아니라 — 그 규칙을 코드에는 넣었지만 정작 그 코드가 대조하는 스펙에는 반영하지 않았고, 패리티 테스트가 그 어긋남을 잡아냈기 때문이었다. 내 새 게이트가 맨 처음 차단한 것은 다름 아닌 나였다.
법칙: 자신을 만든 사람에게 한 번도 작동한 적 없는 검증기는 아마 아무것도 검사하고 있지 않은 것이다.
2. 모든 어간을 조용히 놓쳐버린 라우터
나는 새로운 글이나 레포가 내 레포 중 어느 것에 도움이 될지 판단하는 결정론적 라우터를 만들었다. 겉보기엔 맞아 보였고 테스트도 통과했다. 그런데 실제 소스 네 개에 돌려봤더니 하나만 라우팅했다.
원인은 나에게 있었다: 이 라우터가 참조하는 레지스트리에는 트리거 단어가 부분 문자열 매칭으로 등록되어 있었고, 그중 여럿은 의도적으로 어간만 남긴 것들이었다 — self-improv, portfolio rebalanc. 그런데 나는 완전 단어 매칭을 구현해 놓았고, 그래서 "self-improving pipelines"를 설명하는 페이지는 아무것도 매칭되지 않았다. 테스트가 데이터가 아니라 내 구현에 맞춰져 있었으니, 통과한 것도 당연했다.
법칙: 자신의 초록불을 믿기 전에 실제 입력값으로 돌려봐라.
3. 채점자가 내 성적표를 거부했다
내 시스템에서는 모든 결과물이 독립적인 채점자에게 채점된 성적표를 받는다. 나는 하나를 제출했는데, 돌아온 결과는 0.00 — "significance unstated"(번역: 중요성이 진술되지 않음)라는 이유로 상한이 걸린 채였다: "significance unstated"(번역: 유의미성이 진술되지 않음). 나는 내가 무엇을 했는지는 설명했지만, 그것이 왜 중요한지, 어떤 수치를 움직였는지는 한 번도 말하지 않았다.
나중에 같은 채점자는 어떤 작은 변경에 후한 점수를 주었는데, 그러면서도 이렇게 지적했다:1.00 (sandbagged?)내가 스스로에게 세운 기준이 너무 낮았다는 뜻이었다. 두 판정 모두 옳았고, 둘 다 듣기 좋은 말은 아니었다.
법칙: 평가자가 나쁜 점수를 줄 수 없다면, 그것은 장식품일 뿐이다.
4. 자신의 목표조차 설명하지 못한 계획
나는 360일짜리 매출 계획을 위한 critical-path 라우터를 만들었다. 이 라우터는 실제 매출이 0인 상황에서도 계획을 충분히 실현 가능한 것으로 계산해냈다 — 90일 마일스톤 대비 40일 만에 끝난다는 식이었다.
계산 자체는 문제가 없었다. 문제는 그래프였다: 그 안의 어떤 노드도 자신이 매출을 창출한다고 선언하지 않았다. 계획을 가장한, 레포지토리들의 의존성 그래프에 불과했던 셈이다. 그래서 이제는 차단 규칙이 하나 생겼다: 목표가 측정 가능한데도 거기에 기여하는 것이 아무것도 없다면, 어떤 경로도 제공하지 않는다.
법칙: 자신의 목표조차 설명하지 못하는 계획은 간트 차트를 두른 소망일 뿐이다.
열두 가지 법칙
- 명확한 목표가 없으면 신뢰할 수 있는 에이전트도 없다. 모호한 목표는 방황하는 루프를 낳는다.
- 상태는 모델 바깥에 있어야 한다. 모델은 생각하고, 시스템은 기억한다.
- 도구에는 계약이 필요하다. 목적, 입력, 출력, 경계, 실패 양상.
- 결정적인 규칙에는 게이트가 필요하다. 프롬프트로 하는 지시는 통제가 아니다.
- 평가는 반드시 증거를 만들어내야 한다. 증거가 없으면 신뢰도 없다.
- 재시도는 재시도가 통하는 실패에만. 더 세게 시도한다고 더 똑똑하게 복구되는 것은 아니다.
- 정지 조건은 아키텍처다. 멈출 줄 모르는 루프는 버그다.
- 메모리는 큐레이션되어야 한다. 나쁜 메모리는 오류를 복리로 키운다.
- 인간은 레버리지가 큰 게이트에 있어야 한다. 그것은 약함이 아니라 책임이다.
- 관찰 가능성이 신뢰를 만든다. 들여다볼 수 없으면 운영할 수 없다.
- 에이전트가 많다고 지능이 높아지는 것은 아니다. 어려운 것은 조율이다.
- 루프는 복리로 쌓여야 한다. 모든 실행은 다음 실행을 더 낫게 만들어야 한다.
그리고 위의 네 이야기가 비싼 대가를 치르고 사 온 열세 번째 법칙: 루프를 지어서 스스로가 유죄 판결의 대상이 되게 하라. 자기 자신만 검증하는 시스템은 거울에 지나지 않는다.
30일, 정직하게 라벨 붙이기
이 섹션의 모든 내용은 목표로 삼은 기준선이지, 내가 보고하는 결과가 아니다. 나는 통제된 연구를 수행한 적이 없다. 이것은 내가 스스로의 빌드를 위해 세운 기준일 뿐이다.
1주 차 — 최소 루프. 목표 계약, 상태 스키마, 도구 결과 스키마, 컨트롤러, 플래너 하나, 도구 둘, 평가자 하나, 반복 상한. 목표: 손대지 않아도 단순한 작업을 끝낸다.
2주 차 — 신뢰성. 오류 범주, is_retryable 플래그, 복구 정책, 반복 오류 탐지. 목표는 일시적 오류, 검증 오류, 권한 오류, 비즈니스 규칙 오류를 구분해내는 것이다.
3주 차 — 브레이크. 위험 등급, 코드로 구현된 정책 게이트, 승인 패킷, 감사 로그. 목표는 테스트 스위트 안의 제한된 호출을 전부 차단하는 것이다.
4주 차 — 축적. 메모리 후보군, 무언가가 영구화되기 전 거치는 인간 검토, 실패 유형 추적. 목표는 같은 실패가 더 이상 반복되지 않는 것이다.
이 글이 주장하지 않는 것
- 벤치마크는 없다. 나는 이 아키텍처를 다른 대안과 비교 측정한 적이 없다. 앞의 네 가지 사건은 그저 사건일 뿐, 연구가 아니다.
- 위 수치들은 지향점일 뿐이다. 이 글의 초고에는 이것들이 마치 실측 결과인 양 쓰여 있었다. 그것은 실수였고, 조용히 지우는 대신 여기서 바로잡는다.
- 하나의 플릿, 한 명의 운영자. 내가 배운 모든 것은 오직 내 레포와 내 에이전트에서 나온 것이다. 당신의 실패 양상은 다를 것이다.
출처
내 원칙은 이렇다: 출처가 없으면 주장도 없다. 이 글의 초고는 그 원칙을 어겼다 — Tree-of-Thoughts 논문에 대해 출처 없이 주장을 내놓았으니, 원칙이 막으려던 바로 그 실패를 저자 스스로 저지른 셈이다. 그래서 다음을 밝힌다:
- Tree of Thoughts: Deliberate Problem Solving with Large Language Models, Yao et al., 2023 — arXiv:2305.10601. 이 논문은 탐색·계획 과제에서 chain-of-thought 대비 상당한 우위를 보고한다. 발행 시점에 수치를 재검증하지 않았기에, 여기서 정확한 수치는 일부러 재현하지 않았다.
- 이 네 가지 사건은 2026-08-05부터 2026-08-11 사이, 내 레포지토리들의 커밋 기록에서 가져온 것이다.
결론
프롬프트 엔지니어링은 AI를 쓸모 있게 만들었다. 루프 엔지니어링은 그것을 믿을 만하게 만든다.
프롬프트는 답을 낼 수 있지만 루프는 일을 해낼 수 있다. 프롬프트는 똑똑해 보일 수 있지만 루프는 검증될 수 있다. 프롬프트는 감명을 줄 수 있지만 루프는 쌓인다.
목표, 상태, 도구, 게이트, 평가자, 복구, 휴먼, 메모리를 만들어라. 그리고 대부분의 사람이 건너뛰는 단 하나를 만들어라: 당신이 틀렸다고 말해줄 수 있는 부분이다.