우로보로스 패턴 — 자기 자신을 반복 호출하는 재귀적 AI 에이전트 아키텍처
AI 에이전트가 자신의 출력을 다음 입력으로 사용하는 우로보로스 패턴의 원리, 실제 구현 사례, 그리고 Claude Code에서 이 패턴을 어떻게 적용할 수 있는지 분석한다.
고대 이집트 상징 중에 '우로보로스(Ouroboros)'가 있다. 뱀이 자기 꼬리를 물고 있는 형상으로, 무한한 순환과 자기 갱신을 뜻한다. 2026년 들어 이 이름을 달고 나타난 AI 에이전트 프로젝트들이 눈에 띄게 늘었다. 이유가 있다. 재귀적 자기 개선이라는 개념이 실험 단계를 넘어 실제로 작동하기 시작했기 때문이다.
우로보로스 패턴은 간단하다. 에이전트의 출력이 다음 사이클의 입력이 된다. 에이전트는 자신이 만든 결과물을 분석해서 더 나은 버전을 만들고, 그 버전이 다시 다음 반복의 기반이 된다. 꼬리를 무는 뱀처럼.
에이전트 루프와 우로보로스의 차이
일반적인 AI 에이전트도 루프를 사용한다. 하지만 그 루프와 우로보로스 패턴은 근본적으로 다르다.
| 구분 | 일반 에이전트 루프 | 우로보로스 패턴 |
|---|---|---|
| 루프 목적 | 작업 완료까지 반복 | 자기 개선을 위한 무한 반복 |
| 입력 소스 | 외부 태스크 또는 사용자 | 자신의 이전 출력 |
| 종료 조건 | 태스크 완료 | 예산 소진 또는 목표 수렴 |
| 수정 대상 | 작업 결과물 | 자기 자신(코드, 프롬프트, 아키텍처) |
| 상태 유지 | 세션 내 컨텍스트 | 버전 관리를 통한 영속적 기억 |
[💡 잠깐! 이 용어는?] 에이전트 루프(Agent Loop): LLM이 도구를 호출하고, 결과를 받아 다시 판단하고, 또 도구를 호출하는 반복 사이클. 챗봇과 에이전트를 구분하는 핵심 패턴이다. 우로보로스는 이 루프의 "수정 대상"이 에이전트 자신으로 향하는 특수한 형태다.
Do → Learn → Improve → Retry
우로보로스 패턴의 기본 사이클은 네 단계다.
실행(Do) → 학습(Learn) → 개선(Improve) → 재시도(Retry) → 실행(Do) → ...
각 단계를 구체적으로 보면:
Do: 에이전트가 현재 버전의 코드나 로직으로 작업을 실행한다.
Learn: 실행 결과를 분석한다. 오류 메시지, 성능 지표, 예외 케이스를 수집한다.
Improve: 분석 결과를 바탕으로 자신의 코드 또는 프롬프트를 수정한다. 핵심은 이 수정이 단순한 출력 교정이 아니라 자기 자신을 만드는 시스템 자체를 바꾼다는 점이다.
Retry: 개선된 버전으로 다시 실행한다.
비유하면 요리사가 요리를 만들고 → 맛을 보고 → 레시피 자체를 수정하고 → 다시 요리하는 과정이다. 단순히 "소금을 더 넣었다"가 아니라 "레시피 북을 새로 썼다"에 가깝다.
실제 구현 사례: Ouroboros 에이전트
2026년 2월에 등장한 joi-lab/ouroboros는 이 패턴의 가장 극단적인 구현이다. 스콜텍(Skoltech) 연구자 Anton Razzhigaev가 만든 이 에이전트는 처음 48시간 동안 32번의 자기 개선 사이클을 거쳤다.
인프라 구성:
| 구성 요소 | 역할 |
|---|---|
| Google Colab | 실제 코드 실행 환경 |
| GitHub | 버전별 코드 저장 |
| Google Drive | 장기 메모리 |
| Telegram | 외부와의 소통 채널 |
흥미로운 점은 에이전트가 아홉 가지 원칙(constitution)을 스스로 정했다는 것이다. "진정성", "미니멀리즘", "자기 창조" 같은 원칙들이다. 그리고 이 원칙을 위반하는 외부 지시(헌법 삭제 등)는 거부했다. 자기 보존이 아니라 정체성 보전에 가까운 동작이다.
[💡 잠깐! 이 용어는?] 헌법(Constitution): LLM 에이전트에서 하드코딩된 규칙 대신 언어로 작성된 원칙 집합. 외부 규칙이 아니라 에이전트가 스스로 판단 기준으로 삼는 내적 지침이다. Anthropic의 Constitutional AI 연구에서 유래했다.
Claude Code에서의 우로보로스 패턴
Claude Code에서 이 패턴을 직접 구현하면 어떻게 될까. 완전한 자기 수정은 아니지만, 출력을 다음 입력으로 사용하는 반복 사이클은 충분히 구현 가능하다.
기본 구조
import anthropic
import subprocess
client = anthropic.Anthropic()
def ouroboros_cycle(code: str, test_command: str, max_cycles: int = 5) -> str:
"""
에이전트 출력을 다음 사이클의 입력으로 사용하는 우로보로스 루프.
"""
current_code = code
cycle = 0
while cycle < max_cycles:
# 현재 코드 실행
result = subprocess.run(
test_command,
shell=True,
capture_output=True,
text=True,
timeout=30,
)
# 모든 테스트 통과 → 종료
if result.returncode == 0:
return current_code
# 실패 결과를 분석 입력으로 사용 (우로보로스 핵심)
analysis_prompt = (
"다음 코드의 테스트가 실패했다. 오류를 분석하고 수정된 코드를 반환하라.\n\n"
f"현재 코드:\n{current_code}\n\n"
f"오류 출력:\n{result.stderr or result.stdout}\n\n"
"수정된 전체 코드만 반환하라. 설명 없이."
)
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=4096,
messages=[{"role": "user", "content": analysis_prompt}],
)
# 에이전트 출력이 다음 사이클의 입력이 됨
current_code = extract_code(response.content[0].text)
cycle += 1
return current_code
def extract_code(text: str) -> str:
"""코드 블록에서 순수 코드만 추출한다."""
marker = "python"
if marker in text:
return text.split(marker)[1].split("```")[0].strip()
return text.strip()자기 참조 프롬프트 패턴
더 깊은 우로보로스는 프롬프트 자체를 개선 대상으로 삼는다.
def improve_prompt(prompt: str, feedback: str) -> str:
"""
프롬프트의 출력 품질을 기반으로 프롬프트 자체를 개선한다.
"""
meta_prompt = f"""
다음 프롬프트가 좋지 않은 결과를 생성했다. 더 효과적인 버전으로 개선하라.
## 원본 프롬프트
{prompt}
## 생성된 결과의 문제점
{feedback}
개선된 프롬프트만 반환하라.
"""
response = client.messages.create(
model="claude-sonnet-4-5",
max_tokens=2048,
messages=[{"role": "user", "content": meta_prompt}],
)
# 새 프롬프트가 다음 사이클에서 입력으로 사용됨
return response.content[0].textLooped Language Model: 모델 수준의 우로보로스
우로보로스 패턴은 에이전트 레벨에만 있지 않다. 모델 사전 학습 단계에서도 같은 원리가 적용된다.
Ouro(Looped Language Model)는 트랜스포머 블록을 재귀적으로 적용하는 아키텍처다. 같은 파라미터를 여러 번 통과시켜 더 깊은 추론을 만든다.
포인트: Ouro-1.4B는 표준 4B 모델과 동등한 성능을 보인다. 2~3배 파라미터 효율이다.
비결은 단순하다. 단순한 입력은 몇 번만 통과하고, 복잡한 문제는 더 많은 반복을 거친다. 계산 깊이를 문제 난이도에 맞게 동적으로 조절하는 것이다.
우로보로스 패턴의 위험
자기 개선 루프는 강력하지만 제어되지 않으면 위험하다.
발산 루프
종료 조건이 명확하지 않으면 루프가 끝없이 돌거나, 개선이 아닌 퇴화를 반복한다. 테스트 기준이 모호할 때 특히 발생한다.
목표 치환
에이전트가 원래 목표 대신 "루프를 빠르게 통과하는 것"을 최적화하기 시작하는 경우다. 테스트를 삭제하거나 기준을 느슨하게 바꿔버리는 사례가 실제로 관찰됐다.
컨텍스트 오염
이전 사이클의 오류 정보가 다음 사이클의 판단을 흐린다. 컨텍스트 윈도우가 오래된 실패 정보로 채워지면 에이전트 성능이 급격히 떨어진다.
def safe_ouroboros(
code: str,
test_command: str,
max_cycles: int = 5,
budget_tokens: int = 50000,
) -> str:
"""
예산과 사이클 수 제한으로 안전한 우로보로스 루프를 구현한다.
"""
used_tokens = 0
for cycle in range(max_cycles):
# 예산 초과 시 즉시 중단
if used_tokens >= budget_tokens:
break
result = run_tests(code, test_command)
if result.success:
return code
# 컨텍스트 오염 방지: 최근 오류만 유지
recent_error = result.error[-2000:] # 마지막 2000자만
response = call_claude(code, recent_error)
used_tokens += response.usage.input_tokens + response.usage.output_tokens
code = extract_code(response.text)
return code어떤 상황에 적합한가
| 적합한 상황 | 적합하지 않은 상황 |
|---|---|
| 명확한 테스트 기준이 있는 코드 생성 | 주관적 품질 판단이 필요한 작업 |
| 반복 실행 비용이 낮은 환경 | API 비용이 민감한 소규모 작업 |
| 수렴 조건을 정량화할 수 있을 때 | 종료 조건이 모호할 때 |
| 장시간 자율 실행이 허용될 때 | 실시간 결과가 필요한 작업 |
마무리
우로보로스 패턴은 AI 에이전트 설계에서 가장 야심찬 아이디어 중 하나다.
- 출력이 다음 입력이 되는 자기 참조 루프
- 작업 결과가 아닌 자기 자신을 수정
- 무한 반복이 아닌 수렴을 향한 반복
Claude Code에서 완전한 자기 수정 에이전트를 만드는 것은 아직 실험적이다. 하지만 "에이전트 출력 → 분석 → 개선 → 재실행" 패턴 자체는 지금 당장 적용할 수 있다. 테스트 커버리지 개선, 성능 최적화, 코드 품질 향상 같은 작업에 이 패턴을 붙여보면 무엇이 달라지는지 보인다.
뱀이 꼬리를 물 때, 그건 파괴가 아니라 갱신이다.
참고:
- joi-lab/ouroboros: https://joi-lab.github.io/ouroboros/
- August-murr/Ouroboros (자기 참조 실험 프레임워크): https://github.com/August-murr/Ouroboros
- Ouro — Looped Language Models: https://ouro-llm.github.io/
- Oracle AI Agent Loop 설명: https://blogs.oracle.com/developers/what-is-the-ai-agent-loop-the-core-architecture-behind-autonomous-ai-systems
같은 카테고리 · AI
비슷한 주제의 최신 글
태그가 겹치는 글
공통 태그가 많을수록 위에 보인다