본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 4장: 리플렉션 패턴 - 자기 평가와 반복 개선
2026년 1월 23일·AI / ML·

4장: 리플렉션 패턴 - 자기 평가와 반복 개선

에이전트가 자신의 출력을 평가하고 반복적으로 개선하는 리플렉션 패턴의 원리, 구현 방법, 실전 활용 전략을 다룹니다.

18분751자7개 섹션
ai-agentllmarchitectureorchestration
공유
ai-agent-patterns4 / 10
12345678910
이전3장: 도구 사용 패턴 - 에이전트의 손과 발다음5장: 계획 수립 패턴 - 복잡한 작업의 분해와 실행

리플렉션이란

리플렉션(Reflection)은 에이전트가 자신의 출력을 스스로 평가하고, 부족한 점을 식별하여 개선하는 패턴입니다. 사람이 글을 쓴 후 다시 읽으며 퇴고하는 과정과 유사합니다. Andrew Ng은 이 패턴을 에이전틱 AI의 네 가지 핵심 설계 패턴 중 하나로 제시하며, 단순한 프롬프트 추가만으로도 눈에 띄는 품질 향상을 가져올 수 있다고 강조했습니다.

기본 아이디어는 단순합니다. LLM이 초기 응답을 생성한 후, 같은 모델이나 다른 모델이 그 응답을 평가하고, 평가 결과를 바탕으로 개선된 응답을 다시 생성하는 것입니다.

왜 리플렉션이 효과적인가

LLM이 단일 패스(single pass)로 생성한 출력에는 여러 문제가 있을 수 있습니다. 사실 오류, 논리적 비약, 불완전한 답변, 형식 불일치 등이 그 예입니다. 리플렉션은 이런 문제를 발견하고 수정할 기회를 제공합니다.

핵심적인 이유는 LLM이 생성 모드와 평가 모드에서 서로 다른 능력을 발휘한다는 점입니다. 생성할 때는 창의적이고 유창한 텍스트를 만들지만, 세부적인 정확성을 놓칠 수 있습니다. 반면 평가할 때는 구체적인 기준에 따라 체계적으로 검토할 수 있습니다. 리플렉션은 이 두 모드를 순환시켜 양쪽의 장점을 결합합니다.

기본 리플렉션 구현

가장 단순한 형태의 리플렉션을 구현해 보겠습니다.

단일 모델 리플렉션

하나의 LLM이 생성과 평가를 모두 수행하는 방식입니다.

basic_reflection.py
python
import anthropic
 
client = anthropic.Anthropic()
 
def generate(prompt: str) -> str:
    """초기 응답을 생성합니다."""
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        messages=[{"role": "user", "content": prompt}],
    )
    return response.content[0].text
 
def evaluate(original_prompt: str, response: str) -> dict:
    """응답을 평가하고 피드백을 반환합니다."""
    eval_prompt = (
        "다음 질문에 대한 응답을 평가하십시오.\n\n"
        "## 원래 질문\n" + original_prompt + "\n\n"
        "## 응답\n" + response + "\n\n"
        "## 평가 기준\n"
        "1. 정확성: 사실적으로 정확한가?\n"
        "2. 완전성: 질문의 모든 측면을 다루었는가?\n"
        "3. 명확성: 이해하기 쉽게 작성되었는가?\n"
        "4. 구조: 논리적으로 구성되었는가?\n\n"
        "## 출력 형식\n"
        "각 기준에 대해 1-5점으로 평가하고, 구체적인 개선 사항을 제시하십시오.\n"
        "마지막에 전체 점수(1-5)를 제시하십시오.\n\n"
        "JSON 형식으로 응답하십시오:\n"
        '{"scores": {"accuracy": N, "completeness": N, '
        '"clarity": N, "structure": N},\n'
        ' "overall": N,\n'
        ' "feedback": "구체적인 개선 사항",\n'
        ' "pass": true/false}'
    )
 
    result = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=2048,
        messages=[{"role": "user", "content": eval_prompt}],
    )
    import json
    try:
        return json.loads(result.content[0].text)
    except json.JSONDecodeError:
        return {"overall": 3, "feedback": result.content[0].text, "pass": False}
 
def refine(original_prompt: str, response: str, feedback: str) -> str:
    """피드백을 바탕으로 응답을 개선합니다."""
    refine_prompt = (
        "다음 응답을 피드백에 따라 개선하십시오.\n\n"
        "## 원래 질문\n" + original_prompt + "\n\n"
        "## 현재 응답\n" + response + "\n\n"
        "## 피드백\n" + feedback + "\n\n"
        "피드백에서 지적된 모든 사항을 반영하여 개선된 응답을 작성하십시오.\n"
        "개선된 응답만 출력하십시오."
    )
 
    result = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=4096,
        messages=[{"role": "user", "content": refine_prompt}],
    )
    return result.content[0].text
 
def reflect_and_improve(prompt: str, max_iterations: int = 3) -> str:
    """리플렉션 루프를 실행합니다."""
    response = generate(prompt)
    print(f"[초기 생성] 완료")
 
    for i in range(max_iterations):
        evaluation = evaluate(prompt, response)
        print(f"[평가 {i + 1}] 전체 점수: {evaluation.get('overall', 'N/A')}")
 
        if evaluation.get("pass", False):
            print(f"[완료] {i + 1}번째 평가에서 통과")
            return response
 
        feedback = evaluation.get("feedback", "")
        response = refine(prompt, response, feedback)
        print(f"[개선 {i + 1}] 완료")
 
    return response

이중 모델 리플렉션

생성과 평가에 서로 다른 모델을 사용하는 방식입니다. 동일한 모델의 편향을 줄이는 효과가 있습니다.

dual_model_reflection.py
python
class DualModelReflector:
    def __init__(self):
        self.client = anthropic.Anthropic()
        self.generator_model = "claude-sonnet-4-20250514"
        self.evaluator_model = "claude-sonnet-4-20250514"
 
    def generate(self, prompt: str) -> str:
        response = self.client.messages.create(
            model=self.generator_model,
            max_tokens=4096,
            messages=[{"role": "user", "content": prompt}],
        )
        return response.content[0].text
 
    def evaluate(self, prompt: str, response: str) -> dict:
        eval_prompt = (
            "당신은 엄격한 품질 검토자입니다.\n"
            "다음 응답을 비판적으로 평가하십시오.\n\n"
            "질문: " + prompt + "\n"
            "응답: " + response + "\n\n"
            "1. 사실 오류가 있는가? 있다면 구체적으로 지적하십시오.\n"
            "2. 누락된 중요한 정보가 있는가?\n"
            "3. 논리적 비약이나 모순이 있는가?\n"
            "4. 개선이 필요한 부분을 구체적으로 기술하십시오.\n\n"
            'JSON으로 응답: {"issues": [...], "suggestions": [...], "pass": true/false}'
        )
 
        response = self.client.messages.create(
            model=self.evaluator_model,
            max_tokens=2048,
            messages=[{"role": "user", "content": eval_prompt}],
        )
        import json
        try:
            return json.loads(response.content[0].text)
        except json.JSONDecodeError:
            return {"issues": [], "suggestions": [], "pass": False}

특화된 리플렉션 패턴

코드 리뷰 리플렉션

코드 생성에 리플렉션을 적용한 예입니다.

code_reflection.py
python
CODE_EVAL_PROMPT = (
    "다음 코드를 검토하십시오.\n\n"
    "## 요구 사항\n{requirements}\n\n"
    "## 생성된 코드\n```python\n{code}\n```\n\n"
    "## 검토 기준\n"
    "1. 정확성: 요구 사항을 올바르게 구현하는가?\n"
    "2. 엣지 케이스: 경계 조건을 처리하는가?\n"
    "3. 오류 처리: 예외 상황을 적절히 처리하는가?\n"
    "4. 성능: 비효율적인 부분이 있는가?\n"
    "5. 보안: 보안 취약점이 있는가? (SQL 인젝션, XSS 등)\n"
    "6. 가독성: 명확하고 이해하기 쉬운가?\n\n"
    "각 기준에 대해 구체적인 문제와 개선 방안을 JSON으로 제시하십시오."
)
 
class CodeReflector:
    def __init__(self):
        self.client = anthropic.Anthropic()
 
    def generate_code(self, requirements: str) -> str:
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            messages=[{
                "role": "user",
                "content": "다음 요구 사항을 구현하는 Python 코드를 작성하십시오:\n" + requirements
            }],
        )
        return response.content[0].text
 
    def review_code(self, requirements: str, code: str) -> dict:
        prompt = CODE_EVAL_PROMPT.format(
            requirements=requirements, code=code
        )
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=2048,
            messages=[{"role": "user", "content": prompt}],
        )
        import json
        try:
            return json.loads(response.content[0].text)
        except json.JSONDecodeError:
            return {"pass": False, "feedback": response.content[0].text}
 
    def improve_code(self, requirements: str, code: str, review: dict) -> str:
        prompt = (
            "다음 코드를 리뷰 결과에 따라 개선하십시오.\n\n"
            "요구 사항: " + requirements + "\n"
            "현재 코드:\n```python\n" + code + "\n```\n"
            "리뷰 결과: " + json.dumps(review, ensure_ascii=False) + "\n\n"
            "개선된 코드만 출력하십시오."
        )
 
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=4096,
            messages=[{"role": "user", "content": prompt}],
        )
        return response.content[0].text
Info

코드 리플렉션에서는 정적 분석 도구(린터, 타입 체커)의 결과를 평가 프롬프트에 포함시키면 효과가 크게 향상됩니다. LLM만으로는 구문 오류나 타입 오류를 완벽히 잡아내기 어렵기 때문입니다.

글쓰기 리플렉션

문서나 기술 블로그 글 작성에 리플렉션을 적용한 예입니다.

writing_reflection.py
python
WRITING_CRITERIA = {
    "technical_accuracy": "기술적 내용이 정확한가?",
    "audience_fit": "대상 독자의 수준에 적합한가?",
    "structure": "도입-본문-결론 구조가 논리적인가?",
    "examples": "충분하고 적절한 예시를 포함하는가?",
    "actionability": "독자가 실제로 적용할 수 있는 내용인가?",
}
 
class WritingReflector:
    def __init__(self):
        self.client = anthropic.Anthropic()
        self.criteria = WRITING_CRITERIA
 
    def evaluate_writing(self, topic: str, draft: str) -> dict:
        criteria_text = "\n".join(
            "- " + k + ": " + v for k, v in self.criteria.items()
        )
        prompt = (
            "다음 글을 평가하십시오.\n\n"
            "주제: " + topic + "\n"
            "초안:\n" + draft + "\n\n"
            "평가 기준:\n" + criteria_text + "\n\n"
            "각 기준에 대해 1-5점과 구체적 피드백을 제시하십시오.\n"
            "전체 점수가 4점 이상이면 pass: true로 표시하십시오."
        )
 
        response = self.client.messages.create(
            model="claude-sonnet-4-20250514",
            max_tokens=2048,
            messages=[{"role": "user", "content": prompt}],
        )
        return {"feedback": response.content[0].text, "pass": False}

리플렉션의 한계와 대응

자기 평가의 편향

LLM은 자신이 생성한 텍스트에 대해 편향된 평가를 할 수 있습니다. 특히 자신이 작성한 내용의 사실적 오류를 발견하는 데는 취약합니다.

대응 전략:

  • 평가에 다른 모델을 사용합니다 (이중 모델 리플렉션).
  • 외부 도구(검색, 코드 실행, 린터)의 결과를 평가에 포함시킵니다.
  • 구체적이고 측정 가능한 평가 기준을 사용합니다.

수렴하지 않는 리플렉션

리플렉션을 반복해도 품질이 개선되지 않거나, 오히려 악화되는 경우가 있습니다. 특히 평가 기준이 모호하거나 서로 충돌할 때 이런 현상이 발생합니다.

대응 전략:

convergence_check.py
python
def check_convergence(scores: list[float], window: int = 3) -> bool:
    """점수 변화가 수렴했는지 확인합니다."""
    if len(scores) < window:
        return False
 
    recent = scores[-window:]
    # 최근 점수 변화가 미미하면 수렴으로 판단
    variance = sum((s - sum(recent) / len(recent)) ** 2
                    for s in recent) / len(recent)
    return variance < 0.1
 
def reflection_with_convergence(prompt: str, max_iterations: int = 5) -> str:
    """수렴 감지가 포함된 리플렉션 루프입니다."""
    response = generate(prompt)
    scores = []
 
    for i in range(max_iterations):
        evaluation = evaluate(prompt, response)
        score = evaluation.get("overall", 0)
        scores.append(score)
 
        # 통과 조건
        if evaluation.get("pass", False):
            return response
 
        # 수렴 감지: 더 이상 개선되지 않으면 중단
        if check_convergence(scores):
            print(f"[수렴] 점수가 더 이상 개선되지 않습니다: {scores}")
            return response
 
        # 점수 하락 감지
        if len(scores) >= 2 and scores[-1] < scores[-2]:
            print(f"[경고] 점수가 하락했습니다: {scores[-2]} -> {scores[-1]}")
 
        feedback = evaluation.get("feedback", "")
        response = refine(prompt, response, feedback)
 
    return response

비용과 지연

리플렉션은 여러 번의 LLM 호출을 수반하므로 비용과 지연이 증가합니다. 3회 반복이면 최소 7번의 API 호출(생성 1 + 평가 3 + 개선 3)이 필요합니다.

대응 전략:

  • 중요한 출력에만 리플렉션을 적용합니다.
  • 평가 모델로 더 작고 빠른 모델을 사용합니다.
  • 1-2회 반복만으로도 상당한 품질 개선을 얻을 수 있습니다.
  • 평가와 개선을 하나의 프롬프트로 합칩니다.
Tip

실무에서는 "항상 리플렉션을 사용한다"보다 "리플렉션이 필요한 상황을 식별한다"가 더 중요합니다. 단순한 질답에는 리플렉션이 불필요하지만, 코드 생성, 보고서 작성, 복잡한 분석 등에는 큰 효과를 발휘합니다.

리플렉션과 다른 패턴의 결합

리플렉션은 독립적으로도 유용하지만, 다른 패턴과 결합하면 더욱 강력해집니다.

ReAct + 리플렉션

ReAct 에이전트의 최종 답변에 리플렉션을 적용하여, 도구를 사용해 수집한 정보가 올바르게 종합되었는지 검증합니다.

도구 사용 + 리플렉션

코드를 생성한 후 실제로 실행하여 결과를 검증하고, 오류가 있으면 수정하는 패턴입니다. 이는 "도구 기반 리플렉션"이라고 할 수 있습니다.

tool_based_reflection.py
python
def code_generate_and_test(requirements: str) -> str:
    """코드를 생성하고 테스트로 검증하는 리플렉션 루프입니다."""
    code = generate_code(requirements)
 
    for attempt in range(3):
        # 코드 실행으로 검증
        test_result = run_tests(code)
 
        if test_result["all_passed"]:
            return code
 
        # 실패한 테스트 정보로 코드 개선
        feedback = format_test_failures(test_result)
        code = improve_code(requirements, code, feedback)
 
    return code

다음 장 미리보기

5장에서는 계획 수립(Planning) 패턴을 다룹니다. 복잡한 작업을 하위 단계로 분해하고, 실행 계획을 수립하며, 실행 중에 계획을 동적으로 수정하는 방법을 살펴보겠습니다. Plan-and-Execute 아키텍처와 적응적 계획 수립 전략을 집중적으로 다룹니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#ai-agent#llm#architecture#orchestration

관련 글

AI / ML

5장: 계획 수립 패턴 - 복잡한 작업의 분해와 실행

Plan-and-Execute 아키텍처의 원리와 구현, 적응적 재계획 전략, 그리고 계획 수립 패턴이 에이전트 성능에 미치는 영향을 다룹니다.

2026년 1월 25일·16분
AI / ML

3장: 도구 사용 패턴 - 에이전트의 손과 발

AI 에이전트의 도구 정의, 호출, 결과 통합의 전 과정을 다루고, 효과적인 도구 스키마 설계와 복합 도구 조합 전략을 살펴봅니다.

2026년 1월 21일·16분
AI / ML

6장: 멀티 에이전트 패턴 - 협업과 조율의 아키텍처

여러 전문화된 에이전트가 협업하는 멀티 에이전트 시스템의 설계 패턴, 감독자/토론/파이프라인 아키텍처를 코드와 함께 다룹니다.

2026년 1월 27일·15분
이전 글3장: 도구 사용 패턴 - 에이전트의 손과 발
다음 글5장: 계획 수립 패턴 - 복잡한 작업의 분해와 실행

댓글

목차

약 18분 남음
  • 리플렉션이란
  • 왜 리플렉션이 효과적인가
  • 기본 리플렉션 구현
    • 단일 모델 리플렉션
    • 이중 모델 리플렉션
  • 특화된 리플렉션 패턴
    • 코드 리뷰 리플렉션
    • 글쓰기 리플렉션
  • 리플렉션의 한계와 대응
    • 자기 평가의 편향
    • 수렴하지 않는 리플렉션
    • 비용과 지연
  • 리플렉션과 다른 패턴의 결합
    • ReAct + 리플렉션
    • 도구 사용 + 리플렉션
  • 다음 장 미리보기