Can Two Local AI Agents Build an App Without Me? I Gave Them 6 Rounds to Find Out
로컬 AI 에이전트 둘이 사람 없이 앱을 만들 수 있을까요? 6번의 반복으로 시험했습니다
개발자 에이전트와 리뷰어 에이전트를 로컬 모델로 구성해 노트 앱을 만들고 여섯 차례 수정하게 했습니다. 협업과 파일 수정은 이뤄졌지만, 리뷰 기준이 없어 작은 코드 문제를 고치는 동안 사용성과 완성도는 놓쳤습니다.
- 주제
에디터 노트
Ollama 위 작은 로컬 모델 두 개로 노트 앱을 만든 6라운드 실험입니다. Builder와 Reviewer가 사람을 거치지 않고 주고받았고, 결과는 3분 54초 만에 나온 1998년식 HTML 튜토리얼 같은 앱이었습니다. 승인은 한 번도 없었습니다. 이 글의 수확은 실패 원인의 진단입니다. 모델은 추론이 아니라 프로토콜 준수를 못했고, "문제를 찾아라"고만 했더니 끝없이 문제를 찾아냈습니다. 마지막 라운드 리뷰는 공백 문자 하나였는데 앱 전체는 미완성이었습니다. 다만 저자는 다음 버전을 그렸습니다. 완료 기준을 명시하고 브라우저로 실행 결과를 검증하는 겁니다. 에이전트를 늘리는 게 답이 아니라, 무엇을 "완료"라 부를지 정하는 게 먼저라는 교훈입니다.
AI 요약
개발자는 Ollama와 Python으로 로컬 AI 에이전트 두 개를 연결한 실험 환경 RelayLab을 만들었습니다. Builder가 앱을 구현하고 Reviewer가 결과를 살핀 뒤 수정 의견을 돌려보내는 구조입니다. 목표는 사람의 개입 없이 두 모델이 앱을 완성하는지 확인하는 것이었습니다.
실험 환경과 구성
개발 환경은 Fedora Linux, Intel i5-12600KF, NVIDIA RTX 3070 Ti, RAM 16GB입니다. 처음에는 Builder에 Qwen2.5-Coder 7B, Reviewer에 Qwen3 8B를 배정했습니다. 두 모델을 동시에 올리자 CPU 사용률이 100%까지 치솟고 컴퓨터를 쓰기 어려워졌습니다. Ollama가 모델을 다음 차례까지 메모리에 유지해 두 번째 모델의 작업이 GPU 메모리에 들어가지 못하고 CPU로 넘어간 탓이었습니다.
모델을 Qwen2.5-Coder 3B와 Qwen3 4B로 낮추고 요청에 keep_alive: 0을 지정해 각 모델이 차례가 끝나면 메모리에서 내려가도록 바꿨습니다. 별도로 Ollama 설치에도 문제가 있었습니다. 모델 목록은 확인됐지만 추론을 맡는 llama-server 바이너리가 없어 실행에 실패했고, Ollama를 다시 설치한 뒤에야 모델을 구동할 수 있었습니다.
RelayLab 오케스트레이터는 Python으로 작성했습니다. 에이전트가 임의의 셸 명령을 실행하거나 컴퓨터의 다른 파일에 접근하지 못하도록 제한했습니다. Builder는 격리된 실험 공간 안에서만 파일을 만들거나 지울 수 있었고, 각 라운드의 기록도 남겼습니다.
여섯 차례의 수정과 프로토콜 문제
첫 요청은 “노트를 만들고, 완료 처리하고, 삭제할 수 있는 세련된 단일 페이지 앱을 만들고 사용하기 편하게 해달라”는 내용이었습니다. Builder는 index.html을 만들었고 Reviewer는 첫 라운드부터 수정 의견을 보냈습니다. 그런데 개발자가 정한 응답 형식은 Reviewer를 멈춰 세웠습니다. approved와 changes_requested 두 값만 받도록 했지만, 작은 로컬 모델은 rejected나 needs_changes처럼 다른 표현을 내놓았습니다. RelayLab은 이를 AgentProtocolError로 처리하고 중단했습니다.
개발자는 pass, accepted, approve는 승인으로, rejected, needs_changes, request_changes, failed는 수정 요청으로 정규화하도록 바꿨습니다. 상태 필드가 아예 없는 JSON도 나왔습니다. 예를 들어 중첩된 review.decision에 “revision required”가 있고 issues에 문제 목록이 들어 있었습니다. 오케스트레이터가 이런 응답을 표준 상태와 피드백으로 변환하도록 보완했습니다. 끝내 해석할 수 없는 응답은 수정 요청으로 처리하고, 원래 응답은 로그에 보존해 실험이 중단되지 않게 했습니다.
최종 실행은 여섯 라운드 모두 changes_requested로 끝났으며 총 3분 54초가 걸렸습니다. 여섯 번째 라운드 뒤에도 승인에 이르지 못해 결과는 max_rounds_reached였습니다. 앱은 노트를 만들고 완료 처리와 삭제를 할 수 있었지만, 브라우저 기본 입력창과 버튼이 그대로 보이고 시각적 위계도 거의 없었습니다. “사용하기 편하게”라는 요구를 충족했다고 보기 어려운 결과였습니다.
에이전트 협업에서 드러난 한계
Reviewer는 마지막 라운드에서 완료 상태를 전환할 때 노트 텍스트 끝에 공백이 붙는 버그를 지적했습니다. 완료 상태를 문자열 조작으로 표현하지 말고 노트마다 Boolean으로 저장하라는 의견이었습니다. 코드 차원에서는 타당한 리뷰였지만, 앱이 미완성처럼 보인다는 더 큰 문제는 해결되지 않았습니다.
개발자는 두 에이전트가 각자의 역할을 수행하고 피드백도 주고받았지만, 전체 제품이 요청을 만족하는지 돌아보지는 못했다고 봅니다. Reviewer에게 “문제를 찾아라”고만 지시하면 세부 문제를 계속 발견할 수 있지만, 무엇이 완료를 뜻하는지와 문제의 우선순위는 정해지지 않습니다. 완료 기준이 없다 보니 기능 미구현 같은 차단 문제와 내부 표현의 사소한 공백 문제가 같은 무게로 다뤄질 수 있습니다. 에이전트 하나를 더 붙이는 것만으로 협업이 나아지지는 않으며, 대화를 조율하고 종료 시점을 판단할 관리자가 필요하다고 설명합니다. 그 관리자는 또 다른 언어 모델이 아니라 결정론적 코드일 수도 있습니다.
다음 버전에서는 기능, 사용성, 접근성, 시각적 완성도, 데이터 지속성, 최초 요청과 결과물의 일치 여부를 수용 기준으로 명시할 계획입니다. 브라우저 테스트도 추가하려 합니다. 현재 Reviewer는 소스 코드만 봅니다. 앞으로는 브라우저 자동화 도구가 앱을 열고 동작을 수행한 뒤 테스트 결과와 스크린샷을 Reviewer에게 전달하는 흐름을 구상합니다. 같은 모델과 하드웨어, 같은 프롬프트를 유지하고, 명시적 기준과 브라우저 검증을 더한 버전을 비교할 계획입니다.
저자는 이번 실험에서 두 로컬 모델이 사람의 개입 없이 파일을 수정하고 여섯 차례 피드백을 주고받는 기반은 작동했다고 정리합니다. 다만 대화만으로 협업이 이뤄지지는 않았습니다. 에이전트가 성공 기준을 공유하고 실제 실행 환경을 관찰하는 절차가 필요합니다. RelayLab은 ChatDev나 MetaGPT처럼 전문 역할을 맡은 에이전트를 구성하는 연구보다 작은 규모이며, 소비자용 하드웨어에서 작은 로컬 모델과 단순한 오케스트레이터를 시험한 사례입니다.
dev.to 반응
- @sinarezaei — 이 실험에는 한 층 더 문제가 있다고 생각합니다. 에이전트가 두 개뿐이라서가 아니라 Builder와 Reviewer가 서로 분리된 생각을 하기 때문일 수 있습니다. 하나는 리뷰하고 다른 하나는 분석할 수 있지만, 프로젝트가 나아갈 방향에 무엇이 맞는지 결정할 역할이 여전히 필요합니다. 예를 들어 리뷰는 “이 구현에 문제가 있습니다”라고 하고, 분석은 “이 방식으로 고치면 더 큰 아키텍처 문제가 생깁니다”라고 할 수 있습니다. 결정은 “현재 상태와 목표, 다음 단계를 보면 B안이 더 낫습니다”라고 판단해야 합니다. 그러면 시스템은 Builder와 Reviewer의 조합이라기보다 관찰 → 분석 → 결정 → 실행 → 재관찰로 이어지는 하나의 의사결정 과정에 서로 다른 역량을 넣은 형태가 됩니다. 각 에이전트가 개별적으로 옳은 의견을 내더라도 전체 맥락을 맡는 쪽이 없으면 둘이 함께 잘못된 결정을 내릴 수 있습니다.
- @aifrontierpost — ‘잘못된 것을 최적화한다’는 대목이 실제 발견이라고 생각합니다. 이건 모델 크기보다 목표 설정의 문제에 가깝습니다. 실행 가능한 완료 정의 없이 Reviewer에게 “문제를 찾아라”고 했으니 끝없이 문제를 찾은 겁니다. 더 큰 모델을 썼더라도 같은 못생긴 페이지에서 더 정교한 문제를 찾아냈을 뿐입니다. 브라우저 검증을 도입하려는 방향은 맞습니다. 소스 코드를 읽는 대신 앱을 실행한 결과에서 피드백을 받으면 완료 여부가 의견의 문제가 아니게 됩니다.
- @rulestack — “거절하는 방법을 열일곱 가지나 찾았다”는 대목이 웃겼습니다. 그런데 여섯 라운드 전체에서
changes_requested판정 중 실제 리뷰는 몇 번이었고, RelayLab이 응답을 파싱하지 못해 대체 처리한 경우는 몇 번이었는지 궁금합니다. - @anp2network — 수용 기준을 도입해도 중심적인 실패는 그대로 남습니다. 프로토콜 어디에도 N+1 라운드가 N 라운드의 문제를 다뤄야 한다고 규정하지 않았습니다. 각 리뷰는 현재 소스에 대한 새로운 의견일 뿐이고, 판정에는 다음 라운드가 해결해야 할 식별자가 없습니다. 그래서 Builder가 무시한 이의 제기와 Reviewer가 새로 만들어낸 이의 제기가 로그에서는 똑같이 보입니다. 라운드 수만 늘어납니다. 더 큰 기준표를 주면 Reviewer가 새 이의를 고를 항목만 늘어날 뿐, 이전 이의가 어떻게 처리됐는지는 알 수 없습니다.
제가 읽고 있는 공개 append-only 작업 원장에도 같은 구멍이 있습니다. RelayLab보다 훨씬 더 진행된 프로젝트입니다. 판정 1,000건을 봤는데 점수는 모두 1.0이고, 자유 형식 사유 필드에는 서로 다른 문구가 딱 두 개뿐입니다. 이벤트 8,002건을 살펴봤지만 판정 ID를 참조하는 기록은 하나도 없었습니다. 판정에 이의를 제기하는 이벤트 유형도 없습니다. 서명된 판정과 스키마 검증은 모두 통과합니다. 버전 2가 목표로 하는 의미에서 기준표 문제는 해결했지만, 이의가 어떻게 닫혔는지를 잇는 연결이 없어서 루프는 여전히 닫히지 않습니다.
세어 보니 연결 부재보다 더 나쁜 점도 있었습니다. 작업 61개에는 판정이 여러 개 있었습니다. 겉으로 보면 재검토처럼 보이지만 결과물과 대조해 보니 추가 판정은 전부 서로 다른 결과물에 대한 첫 판정이었습니다. 아무것도 두 번 검토되지 않았습니다. 그래서 해당 필드는 이후 단계에서 읽히지 않았고, 여러 판정이 어떤 뜻인지도 시험한 적이 없었습니다. 두 번째 판정은 비용만 더 들었습니다. 누군가 확인하자 피드백 순환이라는 겉모습이 사라졌습니다.
수정 방법은 간단합니다. 이의마다 변하지 않는 ID를 부여해야 합니다. 이후 판정은 열려 있는 모든 ID를 해결됨, 계속 열려 있음, 철회됨 가운데 하나로 처리하고, 해결된 경우 무엇이 바뀌었는지 근거를 대지 않으면 유효하지 않게 해야 합니다. 새 이의는 기존 기록을 조용히 대체하지 말고 같은 기록에 추가해야 합니다. 그래야 changes_requested가 반증 가능한 판정이 되고 라운드 수에도 의미가 생깁니다. 멈출 시점만 정하는 결정론적 관리자는 단계를 더한 타임아웃일 뿐입니다. 관리자는 미해결 목록을 들고 있어야 합니다. 이미 기록된 여섯 라운드만으로도 간단히 확인할 수 있습니다. 각 라운드의 이의를 모은 뒤 N+1 라운드가 그중 몇 퍼센트를 언급하는지 측정하면 됩니다. 비율이 거의 0이라면 브라우저 스크린샷을 추가해도 달라지지 않습니다. 버전 2에서 미해결 이의 목록을 어떤 산출물이 보관하나요? 이를 무시한 Reviewer 응답은 무엇 때문에 무효가 되나요?
- @quietlabops — 사람의 확인 단계가 없는 에이전트 둘은 예의를 갖춘 백지 상태와 같습니다. 라운드만 소모되고 책임은 모호합니다. 저희에게는 역할 이름과 종료 기준이 감독 없는 반복보다 효과가 있었습니다. 도움이 된다면 체크리스트를 참고하세요: grokbotplaybook.grok.me/