에이전트에게 프롬프트를 입력하는 것을 그만두고, 대신 에이전틱 루프(Agentic Loops)를 작성하는 이유
요약
단순한 프롬프트 입력을 넘어, AI가 목표를 달성할 때까지 스스로 반복 수행하는 '에이전틱 루프(Agentic Loops)' 설계의 중요성을 다룹니다. 개발자가 직접 도구를 다루는 대신, 작업을 배분하고 검토하는 시스템을 구축하는 방식으로의 패러다임 전환을 제안합니다.
핵심 포인트
- 프롬프팅에서 에이전틱 루프 설계로의 역할 변화
- 루프는 재귀적 목표를 달성하기 위한 반복 시스템
- 단순 도구 사용자가 아닌 시스템 설계자로서의 개발자
- 파이썬 스크립트를 활용한 실질적인 루프 구현 사례
원문은 dragosroua.com에서 게시되었습니다.
Claude Code의 책임자인 Boris Cherny가 한 말이 지난 한 주 동안 제 머릿속을 계속 맴돌았습니다. 의역하자면, 그는 더 이상 AI에게 프롬프트(Prompt)를 입력하지 않는다는 것입니다. 그는 루프(Loop)를 작성하며, 그 루프가 프롬프팅을 수행합니다. 그의 역할은 루프를 작성하는 것입니다.
코딩 에이전트(Coding agent)를 만든 사람이 우리와 같은 방식으로 사용하는 것을 멈췄다면, 이는 충분히 고민해 볼 가치가 있습니다. 그래서 저는 이 생각을 깊이 파고들었습니다.
그 후, 저는 이 아이디어를 가지고 가장 화려하지 않은 일을 시작했는데, 바로 이것이 제가 이 글을 쓰는 이유이기도 합니다.
루프(Loop)란 실제로 무엇인가
Gemini AI와 밀접한 관계를 맺고 있는 Google 엔지니어 Addy Osmani는 지금까지 가장 훌륭한 정의를 내렸습니다. 루프란 '재귀적인 목표(Recursive goal)'입니다. 목적을 정의하면, AI는 그것이 완료될 때까지 반복(Iterate)합니다. 당신은 에이전트에게 프롬프트를 입력하는 사람이 아니라, 당신을 대신해 프롬프팅을 수행하는 시스템을 설계하는 사람이 됩니다.
약 2년 동안 코딩 에이전트와 작업한다는 것은 한 가지를 의미했습니다. 좋은 프롬프트를 작성하고, 충분한 컨텍스트(Context)를 공유하고, 결과물을 읽고, 다음 내용을 입력하는 것이었습니다. 에이전트는 도구였고, 당신은 매 턴마다 그 도구를 직접 잡고 있었습니다. 마치 조각가가 정을 사용하는 것과 같았습니다.
루프는 그 '잡고 있는 역할'을 작업을 찾아내고, 배분하고, 검토하고, 완료된 내용을 기록하며, 다음 단계를 결정하는 작은 시스템에 넘겨줍니다. 그리고 당신 대신 그 시스템이 에이전트를 자극하게 합니다. 이제 당신의 직업은 조각가를 만드는 것, 더 정확히 말하면 조각가 군단을 만드는 것과 같습니다.
테스트 사례 찾기
저는 지난 4개월 동안 9개의 앱을 출시하여 App Store에 총 10개의 앱을 올렸습니다. 이 앱들은 서로 교차 홍보를 하는데, 이는 모든 앱의 설정(Settings) 섹션에 다른 앱으로 연결되는 "동일 개발자 제작" 섹션이 있어야 함을 의미합니다. 이는 여러 코드베이스(Codebase)에 걸쳐 있는 아주 작은 문제로서, 루프를 적용하기에 완벽한 후보입니다.
상황을 조금 더 흥미롭게 만들기 위해, 저는 그 위에 한 가지 규칙(convention)을 추가하기로 했습니다. 각 앱은 설정(Settings)에 1.4.2 (28)와 같은 버전 문자열을 표시합니다. 즉, 시맨틱 버전(semantic version) 뒤에 괄호로 빌드 번호(build number)가 붙는 방식입니다. 매일 6~7개의 앱을 다루다 보면 어떤 빌드가 어떤 역할을 하는지 놓치기 쉬우므로, 모든 앱이 이를 정확하게 표시하도록 만들고 싶었습니다.
따라서 이 두 가지 간단한 사항을 바탕으로, 저는 저의 첫 번째 루프(loop)를 작성하기로 했습니다.
내가 만든 것
이 두 가지 문제를 해결하기 위해 제가 만든 루프는 총 50줄의 파이썬(Python) 코드로 이루어져 있습니다. 이는 단순히 제 앱 폴더들을 읽고, 필요한 파일들(즉, Settings.swift 또는 SettingsView.swift)을 가져온 다음, 제가 평이한 영어로 작성한 두 가지 규칙(프롬프트, prompt)과 함께 모델에 전달하여 앱별로 통과/실패(pass/fail) 판정을 출력하는 순수 파이썬(vanilla Python) 스크립트일 뿐입니다.
가장 중요한 부분은 다음과 같습니다 (단순화됨):
for app in APPS: state = collect_state(app) # 중요한 파일들을 읽음 result = audit(app, state) # 모델에게 판단을 요청함 print("✅" if result["ready"] else "❌", app)
그리 복잡하지 않습니다. 더 많은 앱을 추가할 수 있는 폴더 맵(folder map), 평이한 영어로 작성된 조건들("By the same dev" 섹션이 있는지, 그리고 버전이 특정 형식으로 포함되어 있는지 확인), 그리고 각 앱을 모델에 입력하고 결과를 출력하는 작은 루프 하나로 구성되어 있습니다.
루프를 구성하는 5가지 기본 요소 (그리고 내 루프의 위치)
하지만 진정한 루프는 이보다 훨씬 더 많은 것을 포함합니다. 움직이는 부품이 매우 많기 때문에 정의가 다양하지만, 아주 간단히 말하자면 다음과 같습니다.
자동화 (Automations) — 특정 일정에 따라 실행되어 당신을 대신해 탐색(discovery)을 수행하므로, 당신이 직접 돌아다니며 확인할 필요가 없습니다. 이것이 심장 박동(heartbeat)입니다. 이것이 루프를 단 한 번 실행한 작업이 아닌, 실제 루프로 만들어 주는 요소입니다.
Worktrees — 병렬로 작업하는 두 에이전트가 서로의 파일을 덮어쓰지 않도록 하는 별도의 체크아웃(checkouts)입니다. 에이전트를 하나 이상 실행하는 순간, 이것은 병렬 작업이 혼돈으로 변하는 것을 막아주는 요소가 됩니다. 저의 경우, 제 루프는 순수하게 보고만 하는 방식이었고 코드를 작성하지 않았기 때문에 이것은 필요하지 않았습니다.
Skills — 에이전트가 추측하는 대신 매 실행마다 읽을 수 있도록 한 번 작성해 둔 프로젝트 지식(SKILL.md 파일)입니다. 이것이 없다면, 루프는 매 사이클마다 프로젝트 전체를 처음부터 다시 유도(re-derives)해야 합니다.
Connectors — 루프가 실제 도구들(이슈 트래커, 데이터베이스, Slack, App Store Connect API 등)에 도달하는 것입니다. "여기에 수정 사항이 있습니다"라고 말하는 에이전트와, 스스로 PR(Pull Request)을 여는 루프 사이의 차이점입니다.
Sub-agents — 한 에이전트가 아이디어를 내면, 다른 에이전트가 이를 검토합니다. 모델들이 서로의 작업을 검토하게 하는 것이 대개 더 효과적입니다.
그리고 여섯 번째 요소는 memory (메모리) 입니다. 모델은 실행 사이에 모든 것을 잊어버리므로, 메모리는 디스크에 저장되어야 합니다.
저의 감사(audit) 루프는 이 다섯 가지 중 정확히 1.5개만을 사용합니다. 그것은 수동으로 실행되는 자동화(블록 1, 스케줄 제외)이며, 저의 조건문들이 스킬을 대신하고 있습니다(블록 3, 간신히). 워크트리(worktrees), 커넥터(connectors), 서브 에이전트(sub-agents), 지속성 메모리(persistent memory)는 없습니다.
하지만 이는 매우 좋은 학습 경험이었습니다.
무엇을 찾아냈는가
저는 제 모든 앱에 대해 스크립트를 실행했고, 대부분은 깨끗하게 통과했습니다. 진짜 발견된 사항들은 사소했으며, 제가 눈으로 절대 잡아내지 못했을 법한 종류의 것들이었습니다.
제 가장 오래된 앱 중 하나인 — 가장 오랫동안 유지되어 온 작업 관리자 앱 — 는 설정(Settings) 화면을 마케팅 버전으로만 배포해 왔습니다. 빌드 번호가 없었습니다. 다른 모든 앱이 1.4.2 (28)로 표시될 때, 이 앱은 1.4.2로 표시되었습니다. 제가 그 화면을 본 지 너무 오래되었기 때문에, 얼마나 많은 릴리스 동안 한 줄의 차이가 그대로 방치되어 있었던 것입니다.
정말 유용하고 실질적인 발견이었습니다. 또한 상상할 수 있는 가장 극적이지 않은 발견이었는데, 바로 그 점 때문에 루프는 찾아냈고 저는 찾지 못한 것입니다.
무엇이 틀렸는가, 그리고 왜 그것이 진짜 교훈인가
대부분의 "AI 루프를 구축했습니다!"라는 게시물들은 이 부분을 건너뛰곤 합니다. 그래서 저는 이 부분에 대해 깊이 파고들고 싶습니다. 루프는 저에게 여러 번 거짓말을 했고, 그 모든 거짓말은 저에게 무언가를 가르쳐 주었습니다.
루프는 제가 보낸 적도 없는 데이터가 누락되었다고 불평했습니다. 한 앱은 "Settings 화면이 없어 아무것도 검증할 수 없습니다"라고 보고했습니다. Settings 화면은 분명히 있었습니다. 하지만 제 파일 수집기(file collector)가 잘못된 경로를 가리키고 있었고, 아무런 오류 없이 조용히 아무것도 반환하지 않았으며, 모델은 받은 '무(nothing)'에 대해 충실하게 보고한 것이었습니다. 루프는 당신이 제공하는 상태(state)만큼만 정직합니다. 그리고 0개의 파일을 읽는 스크립트는 조용히 실패합니다. 오류는 발생하지 않으며, 단지 하류(downstream)에서 확신에 찬 오답을 내놓을 뿐입니다. 해결책은 더 똑똑한 프롬프팅(prompting)이 아니었습니다. 제가 언제 빈 공기만을 먹이고 있는지 확인할 수 있도록, 보내기 직전 데이터의 크기를 출력하는 것이었습니다.
루프는 정상적인 코드를 고장 난 것으로 표시하기도 했습니다. 여러 앱이 "컴파일되지 않음, 이 속성에 본문(body)이 없음"이라는 이유로 감점을 받았습니다. 그중 어느 것도 사실이 아니었습니다. 저는 토큰(token)을 아끼기 위해 각 파일을 잘라내고(truncating) 있었는데, 제가 중요하게 생각한 버전 코드는 파일의 맨 아래에 있었습니다. 즉, 제가 묻고 있는 바로 그 부분을 잘라버린 뒤, 모델이 그 파편은 컴파일되지 않는다고 정확하게 보고하는 것을 지켜보고 있었던 것입니다. 교훈: 감사(audit) 대상은 전체를 읽어야 합니다. 보조 증거는 잘라내더라도, 판단 대상 자체는 절대 잘라내지 마십시오.
루프는 문구(wording)를 가지고 검열을 하기도 했습니다. 한 앱은 교차 프로모션(cross-promo) 섹션의 제목을 "By the same dev(동일 개발자 제작)" 대신 "From the maker of...( ~의 제작자로부터)"라고 적었습니다. 루프는 이를 탈락시켰습니다. 해당 섹션은 문제가 없었습니다. 제가 규칙을 너무 문자 그대로 작성했고, 모델은 너무 문자 그대로 복종했을 뿐입니다. 해결책: 문구가 아니라 의도(intent)를 판단하라고 지시하는 것입니다.
이 중 어느 것도 모델이 멍청해서 발생한 일이 아니었습니다. 하나하나 모두 제가 잘못된 입력값이나 지침을 전달했고, 모델은 제가 준 것에 대해 정확하게 충실히 보고했을 뿐입니다. 그것이 바로 루프 엔지니어링(loop engineering)의 기술이며, 이는 프롬프팅이 아닙니다. 신뢰할 수 있는 입력을 구축하는 것, 그리고 루프가 어디에서 확신에 찬 오답을 내놓을지 아는 것이 바로 기술입니다.
참고로, 시니어 개발자(senior-dev)의 본능이 옳습니다. 35년 동안 소프트웨어를 출시하며 배운 점은 침묵하는 실패(silent failures)를 불신해야 한다는 것이며, "이것은 그저 API 호출이 포함된 for 루프일 뿐이다"라고 말하는 반사적인 직관이 정확하다는 것입니다. 그것은 실망스러운 결과가 아니라, 바로 그 기능(feature) 자체입니다. 흥미로운 부분은 루프 그 자체가 아니라, 무엇을 확인할지 결정하고 어디에서 답변을 불신해야 할지를 배우는 것이었습니다.
루프가 여전히 당신을 위해 해주지 못하는 세 가지
Osmani는 제가 하는 것보다 이 점들을 더 잘 명명했으며, 루프는 쉬워지는 것이 아니라 더 정교해짐에 따라 이 점들은 더욱 날카로워집니다.
검증(Verification)은 여전히 당신의 몫입니다. 관리되지 않은 채 실행되는 루프는 관리되지 않은 채 실수를 저지르는 루프이기도 합니다. "완료(Done)"는 주장일 뿐, 증명이 아닙니다. 결국 검사자 서브 에이전트(checker sub-agent)를 생성자(maker)로부터 분리하는 이유는 루프의 "완료되었다"는 말이 의미를 갖게 하기 위함이며, 그렇게 하더라도 당신의 업무는 제대로 작동함을 확인한 코드를 출시하는 것입니다.
방치하면 당신의 이해도는 부패합니다. 루프가 당신이 작성하지 않은 코드를 더 빨리 출시할수록, 존재하는 것과 당신이 실제로 파악하고 있는 것 사이의 간극은 더 커집니다. 매끄러운 루프는 당신이 그것이 만든 것을 읽지 않는 한, 그 간극을 더 빠르게 키울 뿐입니다.
안주하는 자세가 가장 위험한 자세입니다. 루프가 스스로 실행될 때, 의견을 갖기를 멈추고 루프가 돌려주는 결과물을 그대로 받아들이고 싶은 유혹에 빠지기 쉽습니다. 루프를 설계하는 것은 판단력을 가지고 임할 때는 해결책(cure)이 되지만, 사고를 피하기 위해 임할 때는 가속제(accelerant)가 됩니다. 같은 행동이지만 결과는 정반대입니다. 루프는 그 차이를 모르지만, 당신은 알고 있습니다.
그것이 바로 제 루프를 의도적으로 읽기 전용(read-only)으로 설정한 정확한 이유입니다. 루프는 보고만 할 뿐, 파일을 건드리지 않습니다. 루프가 할 수 있는 최악의 상황은 제 콘솔에 잘못된 의견을 출력하는 것뿐입니다. 제가 루프가 코드를 편집하도록 허용하는 순간, 위에서 언급한 모든 허위 양성(false positives) 사례들은 망가진 설정(Settings) 화면이 되어 돌아올 것입니다.
다음 단계
제가 의도적으로 저항하고 있는 명백한 다음 단계는, 단순히 격차(gap)를 보고하는 것에 그치지 않고 루프(loop)가 직접 수정안을 제안하도록 하는 것입니다. 바로 그 지점에서 검사기(checker)는 에이전트(agent)가 되며, 네 번째와 다섯 번째 블록(실행을 위한 커넥터, 검증을 위한 서브 에이전트)이 마침내 제 자리를 찾게 됩니다. 저도 그 단계에 도달하겠지만, 반드시 엄격한 승인 게이트(approval gate)를 거친 후에야 할 것입니다. 왜냐하면 앞서 언급한 허위 양성(false positives) 사례들은 방치했을 때 무엇이 잘못되는지를 보여주는 완벽한 목록이기 때문입니다.
그렇다고 해서 제가 IDE를 삭제하고 있는 것은 아닙니다. 잠을 자는 동안 열 개의 에이전트를 병렬로 실행하고 있는 것도 아닙니다. 저는 단 하나의 반복적인 업무를 가져와서 가능한 가장 작은 루프(loop)로 감싸고, 그 끝에 인간을 배치했습니다. 그것이 현재 제가 위치한 단계이며, 저는 이것이 지금 대부분의 현업 개발자들에게 적절한 단계라고 주장합니다. 더 높은 단계가 실재하지 않기 때문이 아니라, 여기서 쌓는 규율(discipline)이 나중에 더 높은 단계로 나아갈 때 안전을 보장해주기 때문입니다.
그저 작게 시작하여, 그 영향력을 이해하고 반복(iterate)하십시오. 먼저 조각가 한 명을 만들어 훌륭한 조각상을 깎게 만드십시오. 그런 다음 조각가 군단을 구축하는 것을 고민해도 늦지 않습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기