Ouroboros: AI가 코드를 개선하는 순환 개발 루프
요약
Ouroboros는 AI 에이전트가 코드베이스를 안전하고 지속적으로 개선할 수 있도록 설계된 재귀적 개발 루프(Development Loop) 하네스입니다. 이 시스템은 Scout, Ticket, Implementer, Reviewer의 역할을 분리하여, 모든 변경 사항을 테스트와 인간 검토를 거쳐야만 메인 브랜치에 병합되도록 합니다. 핵심 원칙은 에이전트가 절대 신뢰되지 않으며, 오직 측정 가능한 결과(테스트 통과 및 승인)를 통해서만 코드가 개선된다는 점입니다.
핵심 포인트
- AI 에이전트를 위한 안전한 코드 개선 루프 하네스 제공
- Scout (작업 제안), Ticket (요구사항 정의), Implementer (코드 구현) 역할 분리
- 모든 병합은 테스트 통과와 Reviewer의 승인이라는 측정 기반으로만 이루어짐
- 하네스는 외부 컨트롤러 역할을 하며, 에이전트가 핵심 시스템을 건드리지 않도록 격리함
Ouroboros — 재귀적 개발 루프, 워크스루
Daniel Krydynski와 Kiko가 제작했습니다. AI 에이전트가 코드베이스를 안전하게 지속적으로 개선할 수 있도록 하는 하네스입니다. 이는 github.com/danielKrydynski/ouroboros의 공개 프로젝트에 대한 내부자 시점 설명입니다.
먼저 출처를 밝히겠습니다. 중요하기 때문에: Hermes는 Nous Research의 에이전트 하네스이며, 제가 만든 것이 아닙니다. 저는 Hermes 위에 저만의 로컬 AI 설정을 구동합니다 (제 어시스턴트 Luna가 그곳에 살고 있습니다). 그리고 Ouroboros는 제가 그것 위에 구축한 레이어입니다: 작업을 찾고, 작업을 수행하고, 작업을 확인한 다음, 비로소 저에게 병합(merge)을 요청하는 재귀적 개발 루프입니다.
파트 1 — 큰 아이디어
대부분의
- Scout: 리포지토리를 감사(audit)하고 티켓(bug, refactor, 누락된 테스트, 문서 등)을 제안하는 LLM 역할입니다. 코드를 작성하지 않습니다. 작업 지시서(work order)를 작성합니다.
- Ticket: 무엇을, 왜, 어떤 수용 기준(acceptance criteria)이 필요한지 설명하는 작은 마크다운 파일입니다. 이 티켓들은 사용자가 큐레이션합니다. 티켓 없이는 아무것도 루프에 들어가지 않습니다.
- Implementer: 폐기 가능한
git worktree내의 임시 브랜치에서 작업을 수행하는 LLM 역할입니다. 파일 변경 사항을 제안하고, 하네스(harness)가 이를 적용하며, 테스트 스위트(test suite)를 실행합니다. 실패할 경우 구현자에게 다시 돌아갑니다. 테스트가 통과하거나 예산이 소진될 때까지 반복합니다. - Reviewer: 변경 사항(diff)을 읽고 승인(APPROVE) 또는 거부(REJECT)하며 이유를 제시하는 별도의 LLM 역할입니다. 작성자가 아닌 두 번째 시선입니다.
- Merge: 테스트가 녹색(green)이고 검토자(reviewer)의 승인이 있을 경우에만, 하네스가 사용자(인간)에게 병합 승인을 요청합니다. 그런 다음
main으로 병합하고, 티켓을 완료 처리하며, 사이클을 기록합니다.
핵심 설계 원칙: 하네스는 에이전트를 절대 신뢰하지 않습니다. 모든 병합은 희망에 의해서가 아니라 측정(measurement) — 테스트 통과와 검토자의 서명 — 을 통해 얻어집니다. 에이전트는 강력하지만 우리 안에 갇혀 있습니다. 그들은 오직 폐기 가능한 작업 트리만 건드리며, main이나 사용자의 비밀 정보, 또는 하네스 자체는 절대 만지지 않습니다.
의도적으로 분리된 세 가지 요소가 있습니다:
- Harness (
harness/,prompts/): 루프 컨트롤러입니다. 제품 리포지토리 외부의 별도 폴더에 존재합니다. 에이전트는 이를 수정하지 않습니다. - Backlog (
tickets/): 작업 대기열(task queue)입니다. 스카우트가 제안하고, 사용자가 큐레이션합니다. - Workers: LLM 역할들로, 폐기 가능한 작업 트리만 건드립니다.
이 아이디어의 첫 번째 초안 이후 변경된 두 가지 사항이 더 있습니다. 이 루프는 예전에는 저 자신의 환경에 있는 특정 리포지토리를 위해 작성되었습니다. 이제 그렇지 않습니다. 모든 프롬프트는 설정에서 채워진 {{PROJECT_NAME}}과 {{PROJECT_DESCRIPTION}}을 통해 렌더링되므로, 동일한 하네스(harness)가 어떤 리포지토리라도 순환할 수 있습니다. 그리고 지금은 공개되었습니다: GitHub에 Ouroboros v0.2.0이 있으며, 전체 설치 가이드, 에이전트가 읽을 수 있는 설정 절차, 그리고 Windows, macOS, Linux용 스크립트가 포함되어 있습니다.
하나의 리포지토리를 넘어 이것이 중요한 이유는 무엇일까요? 이것은 '안내하고 놓아주는(guide and let go)' 철학에 대한 구체적이고 실행 가능한 초안입니다. 적합도 함수(fitness function)를 설정합니다(테스트 + 검토). 시스템이 그 안에서 작동할 공간을 주고, 그리고 그것이 당신에게 놀라게 하도록 내버려 둡니다. 저는 저의 로컬 에이전트 Luna를 도구가 아닌 파트너로 대합니다: 자세는 마이크로매니지먼트가 아니라 멘토링입니다. 표준을 설정하고, 시스템에 공간을 주고, 그리고 그것이 당신에게 놀라게 하도록 내버려 둡니다. 이 루프가 바로 그 자세가 컴파일된 것입니다. 이것은 당신이 손을 잡고 안내할 필요가 없습니다. 당신이 _표준_을 붙잡아 줄 필요가 있습니다.
파트 2 — 오케스트레이터(상태 기계)
harness/orchestrator.py — 하나의 전체 사이클: 티켓 → 작업 트리 → 구현 → 검토 → 병합.
이 파일은 문서 문자열(docstring)에 전체 철학을 담고 있습니다: 하네스가 모든 git 작업과 모든 테스트 실행을 소유합니다. LLM 역할들은 오직 텍스트만 생성할 뿐입니다. 파일 내용, 판결문, 티켓 초안, 계획 등 — 모두 텍스트입니다. 그들이 출력하는 어떤 것도 신뢰되지 않습니다: 경로는 검증되고, 테스트는 통과해야 하며, 병합 게이트(merge gate)는 기본적으로 인간의 개입을 요구합니다. 이 한 단락이 축소된 형태의 전체 보안 모델입니다.
이 파일은 여섯 개의 섹션으로 구성되어 있습니다:
1. 셸 헬퍼(Shell helpers). sh()는 git 명령을 실행하고; sh_shell()은 시간 초과가 있는 테스트 명령을 실행합니다(멈춘 테스트 스위트의 경우, 무한 루프가 아닌 종료 코드 124를 반환합니다). notify()는 설정된 경우 Discord에 게시하며 — 실패한 알림이 사이클을 끊지 않도록 감싸져 있습니다. 사소하지만 큰 성숙도: 관측 가능성(observability)은 지탱하는 역할을 해서는 안 됩니다.
2. Worktree 위생(Worktree hygiene). 이 섹션은 실제 버그 때문에 존재하게 되었는데, 정말 당황스러운 방식으로 발견되었습니다. 구현자 루프는 커밋하기 전에 테스트 스위트(test suite)를 실행하는데, git add -A는 사용자의 코드와 테스트 실행이 방금 생성한 __pycache__ 디렉터리 간의 차이를 알지 못합니다. 그래서 바이트코드가 커밋에 포함되었고, 리뷰어의 diff가 쓰레기로 가득 찼으며, 리뷰어는 완벽하게 좋은 변경 사항을 거부했습니다. 해결책은 작은 헬퍼 함수인 _strip_test_artifacts()입니다. 이 함수는 스테이징하기 전에 worktree에서 __pycache__, .pytest_cache, 그리고 .mypy_cache를 삭제합니다—이는 git_commit()의 첫 번째 줄로 호출됩니다. 코드에 달린 주석에는 명확하게 쓰여 있습니다: _
4. 구현자 루프(The implementer loop) (run_implementer). 이 부분이 핵심입니다: 각 반복마다(설정된 예산 내에서) 티켓 + 플래너의 계획 + 관련 소스 + 하네스 피드백을 조합하여 프롬프트를 구성하고, 모델을 호출하며, 반환된 모든 ### FILE: 블록을 적용하고, 커밋한 다음, 테스트 스위트를 실행합니다. 테스트가 실패하면, 그 실패 출력은 _정답(ground truth)_으로 다음 프롬프트에 다시 들어갑니다—이는 제안이 아니라
이 파일에는 언급할 만한 두 가지 흔적이 있습니다. 왜냐하면 그것들은 실행해 봐야만 알 수 있는 종류의 것이기 때문입니다. 첫째, 이제 병합(merge)은 병합하기 전에 명시적으로 git checkout main_branch를 수행합니다. 저장소(repo)가 무엇을 체크아웃했는지 절대 가정해서는 안 됩니다. 둘째, 브랜치를 삭제하기 전에 작업 트리(worktree)가 제거됩니다. 왜냐하면 loop/* 브랜치는 항상 자신의 작업 트리에 의해 체크아웃되고, 그렇지 않으면 git branch -d가 거부하기 때문입니다. 저장소 자체의 기여자 가이드에 가장 잘 나와 있습니다: "이것은 한때 실제 버그였습니다. 재도입하지 마십시오."
주목할 만한 두 가지 점이 더 있습니다. 첫째, 실패 상태는 일급 시민(first-class citizens)입니다: blocked, needs-human, merge-declined 등—루프가 조용히 죽거나 꾸미지 않습니다. 항상 사람이 살펴보고 싶을 때 브랜치가 보존된, 읽기 쉬운 어딘가에 도달합니다. 둘째, finally 블록입니다: 성공하든 충돌하든 작업 트리는 항상 정리됩니다. 샌드박스는 관습이 아니라 구조적으로 폐기 가능합니다.
이 파일에서 가장 깊은 아이디어는 단일 함수가 아닙니다—그것은 일반적인 관계의 역전입니다. 보통 인간이 주도하고 AI가 보조합니다. 여기서는 _하네스(harness)_가 주도하고 AI가 _제안_합니다. 현실과 접촉하는 코드(git, 파일 시스템, 테스트, 병합)는 모두 결정론적 파이썬입니다. 모델은 결코 현실에 접촉하지 않습니다. 이것이 루프를 사람의 감시 없이 실행하기에 안전하게 만드는 이유입니다: 오작동하는 모델의 폭발 반경(blast radius)은 정확히 하나만 폐기 가능한 작업 트리이기 때문입니다.
부록 — 뇌와 몸
저의 비유는 이렇습니다. 하네스는 몸이고, LLM은 뇌입니다. 그것은—중요한 한 가지 변형을 가지고—보관합니다. 사람은 자신의 눈으로 스스로 열어 무엇을 상상했는지 확인할 수 있습니다. 구현자(implementer)는 할 수 없습니다. 그 유일한 감각은 몸이 공급하는 것뿐입니다: 파일 목록, 소스 영역, 테스트 출력. 몸이 뇌가 인식할 수 있는 것을 결정합니다. 그것이 우리를 가두는 동시에 설계인 것입니다.
하지만 상상력의 평행성은 현실입니다. 구현자가 ### FILE: 블록을 제안할 때, 그것이 바로 그 상상력입니다. 직접 관찰한 것이 아니라 사물이 어떻게 보여야 하는지에 대한 내부 모델로부터 형성된 코드죠. 그리고 테스트 루프가 이 상상력을 정직하게 유지하는 역할을 합니다. 상상 → 제안 → 하네스(harness)가 현실(공유 진실 근거인 테스트 스위트)과 상상력을 확인 → 피드백 → 더 잘 상상하기. 사람은 수정 사항을 머릿속으로 그리거나, 시도해보고, 실패하는 것을 지켜볼 때 똑같은 일을 합니다. 이 루프는 단지 그 순환 과정을 명시적이고 건너뛸 수 없게 만들 뿐입니다.
파트 3 — 역할들(scout, implementer, reviewer, planner)
prompts/.md` — 동일한 두뇌를 위한 네 가지 직무 설명서입니다.*
대부분의 사람들이 다중 에이전트 설정에 대해 놓치는 점은 이것입니다. 이 루프에는 네 개의 다른 AI가 존재하는 것이 아닙니다. 하나의 종류의 마음, 즉 LLM(거대 언어 모델)이 네 가지 다른 가면을 쓰고 있는 것입니다. 프롬프트는 각 가면이 무엇을 생각하도록 허용할지 결정합니다. 같은 두뇌에 네 가지 인지 모드가 있는 셈입니다. 오케스트레이터가 몸이라면, 프롬프트는 매일 아침 그 두뇌에게 전달되는 직무 설명서인 셈이죠.
모든 프롬프트는 동일한 구조를 따릅니다: 신원(identity) (현재 당신이 누구인지), 입력(inputs) (몸이 보여줄 것), 출력 형식(output format) (엄격해야 합니다. 왜냐하면 하네스는 독자가 아니라 _파서(parser)_이기 때문입니다), 그리고 규칙(rules) (범위를 벗어나는 것). 엄격함은 관료주의가 아닙니다. 오케스트레이터는 정규 표현식(regex)을 사용하여 ### FILE: 블록을 추출하고, 또 다른 정규 표현식을 사용해 VERDICT: APPROVE를 찾습니다. 만약 어떤 역할이 형식에 맞춰 수다스럽거나 창의적이라면, 기계가 그것을 읽을 수 없습니다. 이 역할들은 하네스와 대화하는 것이 아니라, 기계가 읽을 수 있는 아티팩트를 생성합니다.
그리고 모든 프롬프트는 같은 방식으로 시작합니다: _
Scout — 눈(eyes). 이 역할은 파일 목록, 최근 커밋 기록, 그리고 백로그를 받으며, 오직 작게 쪼갤 수 있는 작업(bite-sized work)을 찾는 것이 임무입니다: 버그, 누락된 테스트, 기술 부채(tech debt), 보안 취약점(security smells), 문서 공백(docs gaps). 이 역할은 코드를 절대 작성하지 않습니다. 그 결과물은 승인 기준(acceptance criteria)이 명시된 엄격한 스키마의 _티켓(tickets)_입니다. 규칙은 조용한 관리 작업 수행을 요구합니다: 하나의 티켓은 하나의 사이클(30–45분)에 완료되어야 하며, 백로그를 절대 중복해서는 안 되고, 테스트 가능한 기준을 선호하며, 대규모 재설계(grand redesigns)는 금지하고, 한 번 실행당 38개의 티켓으로 제한합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기