콘텐츠로 이동

Reflection과 Self-Correction

에이전트는 첫 번째 시도에서 항상 최선의 결과를 내지 않습니다. 코드를 생성했지만 테스트가 실패하거나, 계획을 세웠지만 일부 단계가 비현실적이거나, 응답을 작성했지만 요구사항을 놓치는 경우가 흔합니다.

Reflection은 에이전트가 자신의 출력을 외부 기준 또는 내부 기준으로 평가하고 개선하는 메커니즘입니다. 별도의 인간 검토 없이도 품질을 높일 수 있습니다.

┌────────────────────────────────────────┐
│ 1. Generate (초안 생성) │
│ 에이전트가 첫 번째 결과물 생성 │
└──────────────────┬─────────────────────┘
┌────────────────────────────────────────┐
│ 2. Critique (비판) │
│ 동일 또는 별도 에이전트가 평가 │
│ "이 코드에서 잠재적 문제는 무엇인가?" │
└──────────────────┬─────────────────────┘
┌────────────────────────────────────────┐
│ 3. Refine (개선) │
│ 비판을 반영하여 수정 │
└──────────────────┬─────────────────────┘
품질 기준 충족?
YES → 완료
NO → 2번으로 돌아감 (최대 N회)
class ReflectionAgent:
def generate_with_reflection(
self,
task: str,
max_iterations: int = 3
) -> str:
# 초안 생성
output = self.llm.complete(task)
for i in range(max_iterations):
# 비판 단계
critique = self.llm.complete(f"""
다음 출력을 비판적으로 평가하세요.
태스크: {task}
현재 출력:
{output}
평가 기준:
1. 요구사항을 모두 충족하는가?
2. 엣지 케이스를 처리하는가?
3. 오류나 논리적 결함이 있는가?
문제가 없다면 "LGTM"을 반환하세요.
문제가 있다면 구체적으로 설명하세요.
""")
if critique.strip() == "LGTM":
break
# 개선 단계
output = self.llm.complete(f"""
다음 피드백을 반영하여 출력을 개선하세요.
원래 태스크: {task}
현재 출력: {output}
피드백: {critique}
""")
return output

오류 트레이스 보존: Manus의 교훈

섹션 제목: “오류 트레이스 보존: Manus의 교훈”

Manus 팀이 발표한 에이전트 운영 사례에서 중요한 통찰이 있습니다: 오류 메시지를 절대 버리지 말라.

에이전트가 실패하면 오류 메시지를 컨텍스트에 유지해야 합니다. 이를 삭제하거나 요약하면 에이전트가 같은 실수를 반복합니다.

# 나쁜 패턴: 오류를 삭제하고 재시도
def bad_retry(task):
try:
return execute(task)
except Exception:
context.clear_errors() # 오류 흔적 삭제
return execute(task) # 같은 실수 반복 가능
# 좋은 패턴: 오류를 컨텍스트에 보존
def good_retry(task, context):
try:
return execute(task, context)
except Exception as e:
context.append_error(str(e)) # 오류 보존
return execute(task, context) # 오류를 참고하여 다른 접근
전략방식적합한 상황
즉시 재시도동일 입력으로 즉시 재실행일시적 네트워크 오류
수정 후 재시도오류를 컨텍스트에 추가 후 재실행논리 오류, 잘못된 도구 사용
단순화 후 재시도태스크를 더 작게 분해 후 재실행복잡도로 인한 실패
다른 도구로 재시도대안 도구나 방법 사용특정 도구의 한계
에스컬레이션인간이나 상위 에이전트에게 위임반복 실패 시

Reflection은 누가 비판하는가에 따라 두 가지로 나뉩니다:

Self-Reflection: 같은 에이전트가 자신의 출력을 평가합니다.

  • 장점: 추가 비용 없음, 빠른 반복
  • 단점: 동일한 편향이 비판에도 적용될 수 있음

External Critique: 별도의 에이전트나 도구가 평가합니다.

  • 장점: 더 객관적인 평가 가능
  • 단점: 추가 LLM 호출 비용
# 코드 품질의 경우 테스트 실행이 최선의 외부 검증
def code_with_test_reflection(task: str) -> str:
code = generate_code(task)
for _ in range(3):
test_result = run_tests(code) # 외부 검증
if test_result.passed:
return code
# 실패한 테스트를 컨텍스트로 활용
code = fix_code(code, test_result.failures)
raise MaxRetriesExceeded("테스트 통과 실패")

Reflection 패턴은 에이전트가 생성-비판-개선 사이클을 통해 스스로 품질을 높이게 합니다. 핵심 원칙은 오류 트레이스를 보존하는 것입니다. 외부 도구(특히 테스트)를 검증에 활용하면 자기 비판의 한계를 보완할 수 있습니다. 다음 챕터에서는 계획 모드와 실행 모드를 명시적으로 분리하는 Dual-Mode 실행 패턴을 다룹니다.