에이전트 평가자 워크플로우(Agentic Evaluator Workflow)를 사용한 자기 개선 코드
요약
멀티 에이전트 시스템을 활용하여 코드를 생성, 평가, 개선하는 자동화된 루프 워크플로우를 소개합니다. 생성자, 채점자, 개선자 역할을 수행하는 에이전트들이 협력하여 임계값을 통과할 때까지 코드를 스스로 최적화하는 과정을 다룹니다.
핵심 포인트
- 생성-채점-개선으로 이어지는 에이전트 기반 자기 개선 루프 구축
- 채점 시 이전 시도 이력을 포함하여 평가의 일관성 유지
- Claude Opus를 생성자로, Haiku를 채점자로 활용한 효율적 파이프라인
- 임계 점수 도달 시 코드를 실제 서브프로세스로 자동 실행
서론 (Intro)
최근 저는 멀티 에이전트 (Multi-agent) AI 시스템을 가지고 이것저것 실험해보고 있습니다. 그러다 아주 기발한 생각이 떠올랐습니다. 만약 하나의 AI 에이전트가 코드를 작성하고, 다른 에이전트가 그 점수를 매기며, 세 번째 에이전트가 그 점수를 바탕으로 코드를 개선한다면 어떨까요? 이 모든 과정이 자동으로, 루프 (Loop) 안에서 이루어지는 것입니다.
이 글에서 제가 진행한 과정을 설명해 드리겠습니다.
제가 탐구하고자 했던 것들은 다음과 같습니다:
- 프롬프트 (Prompt)로부터 AI 에이전트가 코드를 생성하게 하는 것
- 두 번째 AI 에이전트가 해당 코드를 채점하고 구조화된 피드백 (Structured feedback)을 제공하게 하는 것
- 그 피드백을 사용하여 루프 내에서 자동으로 코드를 개선하는 것
- 최종적으로 승인된 코드를 실제 서브프로세스 (Subprocess)로 실행하는 것
요약 (TLDR) - 코드만 필요하시다면 여기 있습니다: https://github.com/codecowboydotio/ai-self-propagate-experiment
이것은 무엇인가요? (What is this?)
저는 에이전트 1(Agent 1)이 Python 스크립트를 생성하고, 채점자(Scorer)가 이를 평가하며, 개선자(Refiner)가 점수가 충분히 높아질 때까지 반복해서 개선하는 파이프라인 (Pipeline)을 구축했습니다. 코드가 임계값 (Threshold)을 통과하면, 에이전트 1은 이를 임시 파일에 쓰고 자식 프로세스 (Child process)로 실행합니다.
루프를 제어하는 몇 가지 설정 가능한 상수 (Constants)가 있습니다:
MAX_REFINEMENTS = 3
MIN_SCORE = 9.6
코드가 10점 만점에 9.6점 이상을 받으면 승인됩니다. 그렇지 않으면 최대 3번까지 개선을 진행합니다. 만약 여전히 기준에 도달하지 못하면, 스크립트는 0이 아닌 종료 코드 (Non-zero code)와 함께 종료됩니다.
에이전트 1 - 생성자 (Agent 1 - the generator)
에이전트 1은 claude-opus-4-8을 사용하며, 마크다운 (Markdown), 설명, 백틱 (Backticks) 없이 오직 소스 코드만 응답하도록 하는 엄격한 시스템 프롬프트 (System prompt)를 사용합니다.
response = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
...
제가 준 작업은 간단합니다. Claude를 호출하여 "2 + 2는 무엇인가요?"라고 묻는 Python 스크립트를 작성하는 것입니다. 핵심은 작업 자체가 아니라 패턴 (Pattern)에 있습니다.
정보 (Info)
생성된 코드는 에이전트 2(Agent 2)가 됩니다. 모델이 아니라, 나중에 서브프로세스로 실행될 실제 Python 스크립트입니다. 에이전트 1이 말 그대로 에이전트 2를 만들어내는 것입니다.
코드 채점하기 (Scoring the code)
채점기(Scorer)는 더 빠르고 저렴한 claude-haiku-4-5-20251001을 사용합니다. 채점기는 원본 프롬프트(Prompt), 평가할 코드, 그리고 이전 시도들의 전체 이력(History)을 전달받습니다.
이력(History) 부분은 매우 중요합니다. 이력이 없으면 점수가 퇴보합니다. 채점기가 이미 보상했던 내용을 잊어버리고, 이전에 수용했던 사항들을 다시 감점하기 시작하기 때문입니다. 저는 이를 뼈아픈 경험을 통해 배웠습니다. 초기 실행 시 어떤 항목에 높은 점수를 주었다가, 다음 반복(Iteration)에서 동일한 항목을 다시 감점하는 일이 발생하곤 했습니다. 전체 이력을 전달함으로써 이 문제를 해결할 수 있습니다.
채점기는 다음과 같은 구조화된 차이(Diff) 형식을 반환합니다:
Score: 8.5/10
Reason: 코드는 기능적으로 작동하지만 에러 처리(Error handling)가 누락되었습니다.
Diff:
...
모호한 피드백 대신 정확한 REMOVE/ADD 쌍을 강제하면 개선기(Refiner)의 작업이 훨씬 더 결정론적(Deterministic)으로 변합니다. "에러 처리를 개선하세요"라는 말은 쓸모가 없습니다. "이 정확한 줄을 이 정확한 블록으로 교체하세요"라는 말은 유용합니다.
def score_code(code: str, history: list[dict]) -> tuple[float, str]:
history_text = ""
for i, entry in enumerate(history, 1):
...
코드 개선하기 (Refining the code)
점수가 임계값(Threshold) 미만으로 돌아오면 개선기(Refiner)가 작동하며, 이 역시 claude-opus-4-8을 사용합니다. 개선기는 전체 이력과 최신 구조화된 차이(Diff)를 전달받아 변경 사항을 적용합니다.
def refine_code(history: list[dict]) -> str:
refine_response = client.messages.create(
model="claude-opus-4-8",
...
이력 주입(History injection)이 없다면, 개선기는 한 가지 문제를 해결하면서 이전 단계에서 이미 해결되었던 다른 부분을 실수로 망가뜨릴 수 있습니다.
루프 (The loop)
개선 루프(Refinement loop) 자체는 상당히 깔끔합니다:
refinement_history: list[dict] = []
refinements = 0
...
각 사이클은 다음과 같이 진행됩니다: 채점(Score) → 임계값 확인(Check threshold) → 개선(Refine) → 다시 채점(Score again). 이력 리스트는 매 패스(Pass)마다 늘어납니다.
에이전트 2 실행하기 (Running Agent 2)
루프가 승인된 점수와 함께 종료되면, 에이전트 1은 최종 코드를 임시 파일에 작성하고 이를 실행합니다:
with tempfile.NamedTemporaryFile(
mode="w", suffix=".py", delete=False, dir=os.path.dirname(__file__)
) as f:
...
임시 파일은 어떤 상황이 발생하더라도 finally 블록에서 정리됩니다. stdout(표준 출력)과 stderr(표준 에러)가 모두 캡처되므로, 만약 Agent 2에서 오류가 발생하더라도 그 원인을 확인할 수 있습니다.
Info
이것이 바로 진정한 에이전트적(agentic) 특성을 만드는 요소입니다. Agent 1은 단순히 사람이 실행할 코드를 생성하는 것에 그치지 않고, 결과물을 스스로 생성(generating), 채점(scoring), 개선(refining) 및 실행(executing)합니다.
모델 선택 (Model choices)
저는 역할에 따라 서로 다른 모델을 의도적으로 사용했습니다. 생성기(generator)와 개선기(refiner)는 모두 claude-opus-4-8을 사용하는데, 이는 구조화된 디프(structured diff)를 생성하거나 정확하게 적용하기 위한 추론 능력(reasoning capacity)이 필요하기 때문입니다. 채점기(scorer)는 claude-haiku-4-5-20251001을 사용합니다. 채점은 상대적으로 비용이 적게 드는 작업이므로, 빠르고 충분하기 때문입니다. 더 풍부한 피드백을 원한다면 haiku를 sonnet으로 교체할 수도 있지만, 저는 그럴 필요성을 느끼지 못했습니다.
실행 방법 (Running it)
ANTHROPIC_API_KEY가 포함된 .env 파일이 있는 디렉토리에 스크립트를 넣으세요:
pip install anthropic python-dotenv
python agent1.py # 일반 출력
python agent1.py --debug # 전체 상세 출력
출력 결과는 다음과 유사합니다:
=== Agent 1 (PID 12345): generating Agent 2 ===
Score 9.8/10 — accepted.
=== Agent 1 (PID 12345): running Agent 2 ===
...
요약 (Summary)
이것은 자기 개선형 코드 생성(self-improving code generation)을 위한 단순하지만 유연한 패턴입니다. 구조화된 디프(structured diff) 형식, 전체 히스토리 전달, 그리고 역할에 따른 서로 다른 모델 계층(model tiers)이 이 방식을 실제로 작동하게 만드는 핵심 요소입니다.
다음 단계는 아마도 이 기사 https://codecowboy.io/ai/autonomous-ai-agents/에서 소개된 것과 유사한 진정한 분산 메시지 버스(distributed message bus)를 도입하는 것이 될 것입니다. 이렇게 하면 각 에이전트가 네트워크 내에서 서로를 개선할 수 있으며, 공유된 목표를 향해 함께 협력할 수 있습니다.
저는 이미 공유된 목표 분해기(shared goal deconstructor)를 구현하는 것에 대해 생각하고 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기