Agentic AI Design Patterns

CH04-스스로 고쳐 쓰는 AI — 리플렉션(Reflection) 패턴 완벽 정리

빌드 피벗(Build Pivot) 2026. 6. 2. 22:15
반응형
AGENTIC AI DESIGN PATTERNS · 04
스스로 고쳐 쓰는 AI — 리플렉션(Reflection) 완벽 정리
생성-평가-개선 루프부터 Producer-Critic, 3개 프레임워크, 멀티크리틱·구조화비평·코드실행 grounding, Reflexion(55%→81%), CRM·ERP·Groupware 적용까지
🟢 입문~중급⏱️ 약 17분🔄 LangChain·LangGraph·ADK🏢 엔터프라이즈

TL;DR — 리플렉션은 에이전트가 자신의 출력을 스스로 평가하고 다시 고쳐 쓰는 패턴입니다. 한 번에 끝내지 않고 생성 → 평가 → 개선 → (반복)의 피드백 루프를 돕니다. 핵심은 생성자와 비평가의 역할 분리(Producer-Critic)로 자기 평가의 편향을 줄이고, 키워드·점수·최대 반복 횟수로 루프를 안전하게 끝내는 것입니다. 내부 평가만 하는 기본 Reflection을 외부 도구(코드 실행·테스트)로 검증하는 Reflexion으로 끌어올리면 코드 생성 성공률이 약 55%→81%로 뜁니다. 대신 비용·지연이 늘므로 품질이 속도보다 중요할 때 씁니다.

1. 왜 리플렉션인가 — 한 번에 쓴 글은 들쭉날쭉하다

1편 프롬프트 체이닝은 작업을 순서대로 쪼갰고, 2편 라우팅은 입력에 따라 길을 나눴으며, 3편 병렬화는 독립 작업을 동시에 던졌습니다. 셋 다 “한 번 생성하면 그게 결과”라는 점은 같았습니다. 그런데 LLM에게 똑같은 요청을 세 번 하면 이런 일이 벌어집니다.

단일 호출 — 한 번 쓰고 끝
입력
LLM
출력 (끝)
같은 요청, 매번 다른 품질:
응답 ①9/10 전문적
응답 ②5/10 평범
응답 ③3/10 오류 포함
품질 편차 크고 예측 불가
리플렉션 — 평가하고 다시 쓴다
입력
생성평가개선
반복할수록 품질이 수렴:
반복 15/10 → 피드백
반복 27/10 → 피드백
반복 38/10 ✅ 승인
항상 기준 이상으로 수렴
그림 1. 단일 호출은 품질이 들쭉날쭉, 리플렉션은 반복으로 기준점까지 끌어올린다

사람도 초안을 한 번에 완성하지 않습니다. 쓰고, 읽어 보고, 고칩니다. 리플렉션은 바로 이 “쓰고-검토하고-고치는” 인간의 작업 방식을 에이전트에 이식한 패턴입니다. 초기 응답의 부정확성, 빠진 정보, 논리적 비약, 요구사항 미충족, 코드 버그 — 이 모든 결함을 스스로 잡아내고 보완합니다.

🔄
리플렉션의 단 하나의 전제 — 품질 향상의 여지. 결과물에 “더 나은 버전”이 존재할 때만 의미가 있습니다. 장문 콘텐츠·코드·복잡한 계획처럼 평가 기준이 분명하고 개선 폭이 큰 작업에서 효과가 크고, 단순 변환이나 사실 한 줄 답변에는 과한 패턴입니다.

2. 리플렉션이란 — 생성·평가·개선의 4단계 루프

리플렉션의 본체는 피드백 루프입니다. 일반적인 AI 호출이 입력 → LLM → 출력(끝)이라면, 리플렉션은 출력을 다시 입력으로 되먹여 4단계를 돕니다.

1
실행
Execution
초기 출력물 생성
2
평가
Critique
기준 대비 분석
3
개선
Refinement
피드백 반영·재생성
4
반복
Iteration
만족 / 최대 횟수까지
2~3단계가 피드백 루프를 이루고, 4단계가 종료 조건을 본다 — 만족스러우면 빠져나가 최종 출력
그림 2. 리플렉션 4단계 — 실행 → 평가 → 개선 → 반복

2단계 평가는 막연한 “잘했나?”가 아니라 구체적 기준 — 사실 정확성·논리 일관성·스타일/톤·완성도·요구사항 충족도 — 를 항목별로 따집니다. 평가가 구체적일수록 3단계 개선이 정확해집니다. 이 점에서 리플렉션은 1편 체이닝의 사촌입니다. 체이닝이 작업을 공간적으로 쪼갰다면, 리플렉션은 같은 작업을 시간적으로 반복하며 다듬는 셈이죠.

3. Producer-Critic — 생성자와 비평가를 나누는 이유

한 에이전트가 자기 글을 자기가 평가하면 인지 편향(Cognitive Bias)이 생깁니다. 사람도 자기가 쓴 글의 오타를 잘 못 보죠. 그래서 리플렉션의 정석은 역할을 둘로 나누는 Producer-Critic(생성자-비평가) 모델입니다.

사용자 입력
🎨Producer (생성자)
콘텐츠·코드·계획 생성에만 집중 — 초안과 개선안을 만든다
▼  초안 전달
🔍Critic (비평가)
평가에만 집중 — 다른 페르소나(예: “시니어 엔지니어”)로 구조화된 피드백 제공
만족? (점수 ≥ 기준 또는 승인 키워드)
예 → 최종 출력 ✅
아니오 → 피드백을 Producer로 ↺
그림 3. 생성자와 비평가를 분리하면, 새로운 관점에서 더 객관적인 평가가 나온다

Critic의 품질이 곧 리플렉션의 품질입니다. 좋은 Critic 프롬프트에는 세 가지가 들어갑니다 — 전문가 페르소나(“당신은 PEP 8을 준수하는 시니어 Python 엔지니어”), 구체적 평가 기준(항목별 점수), 구조화된 출력 형식과 종료 키워드입니다.

PYTHON · Critic 프롬프트 설계
critic_instruction = """ 당신은 시니어 소프트웨어 엔지니어이자 Python 전문가입니다. # ① 페르소나 검토 항목: # ② 구체적 기준 1. 버그 및 오류 2. 스타일(PEP 8) 3. 누락된 엣지 케이스 4. 개선 가능 영역 5. 보안 취약점 출력 형식: # ③ 형식 + 종료 키워드 - 코드가 완벽하면: "CODE_IS_PERFECT" - 문제가 있으면: 구조화된 피드백 목록 """

4. 리플렉션의 진짜 함정 — 루프는 반드시 끝나야 한다

리플렉션 코드의 가장 위험한 버그는 무한 루프입니다. 비평가가 영원히 트집을 잡으면 LLM 호출이 끝없이 돌며 비용과 리소스를 태웁니다. 그래서 종료 조건은 선택이 아니라 필수입니다. 네 가지를 조합합니다.

  • 키워드 종료 — 비평가가 "APPROVED"·"CODE_IS_PERFECT"를 출력하면 종료
  • 점수 임계값 — 품질 점수 ≥ 8/10(또는 75/100) 충족 시 종료
  • 최대 반복(필수 안전장치)max_iterations 도달 시 무조건 종료
  • 개선 없음 감지 — 직전과 같은 피드백이 반복되면 조기 종료
PYTHON · should_continue 종료 판단
def should_continue(state) -> str: if state["iteration"] >= MAX_ITERATIONS: # ① 최대 반복(최후의 안전장치) return "end" if "PERFECT" in state["last_critique"]: # ② 품질 기준 충족 return "end" if state["last_critique"] == state["prev_critique"]: # ③ 개선 없음 return "end" return "continue" # 아직 부족 → 한 번 더
⚠️
비용은 반복에 비례한다. 3회 반복이면 생성 3번 + 비평 3번 = 최소 6번의 LLM 호출입니다. 거기에 히스토리가 누적되며 컨텍스트 윈도우를 압박하죠. 그래서 평균 반복 횟수를 2.5회 미만으로 관리하고, 최초 요구사항만 남기고 중간 히스토리는 요약·압축하는 컨텍스트 관리가 함께 가야 합니다.

5. 구현 ① LangChain — 메시지 히스토리 + while 루프

가장 직관적인 방식입니다. MessagesPlaceholder로 대화 히스토리를 프롬프트에 끼워 넣고, for 루프로 생성-비평을 반복합니다. 비평 결과를 HumanMessage로 히스토리에 쌓으면, 다음 생성이 자동으로 피드백을 반영합니다.

PYTHON · ReflectionLoop
history = [] # 생성물·피드백이 누적되는 대화 기억 for i in range(1, max_iterations + 1): # 1) Producer: 히스토리(피드백 포함)를 보고 생성/개선 content = producer_prompt | llm # MessagesPlaceholder("history")로 자동 주입 history.append(AIMessage(content=content)) # 2) Critic: 점수 + 판정([품질 점수]:8 / APPROVED) critique = critic_prompt | llm if "APPROVED" in critique.upper(): # 종료 키워드 break # 3) 피드백을 히스토리에 추가 → 다음 반복이 반영 history.append(HumanMessage(content=f"피드백:\n{critique}"))

코드 생성에 특화하려면 비평가를 코드 리뷰어 페르소나로 바꾸고 종료 키워드를 CODE_APPROVED로 두면 됩니다. 가장 유연하지만 상태·종료를 직접 관리해야 해 코드량이 많은 게 단점입니다.

6. 구현 ② LangGraph — 조건부 엣지로 동적 분기

LangGraph는 리플렉션을 그래프로 표현합니다. generate(Producer)와 evaluate(Critic) 노드를 두고, 평가 뒤에 조건부 엣지로 “다시 생성”과 “종료”를 동적으로 가릅니다. 루프 횟수가 입력마다 달라지는 리플렉션에 가장 잘 맞는 구조입니다.

PYTHON · StateGraph + Conditional Edges
class ReflectionState(TypedDict): topic: str messages: Annotated[list, operator.add] # Reducer로 메시지 누적 score: int iteration: int is_approved: bool def should_continue(state) -> Literal["generate", "end"]: if state["is_approved"]: return "end" # 승인 → 종료 if state["iteration"] >= state["max_iterations"]: return "end" return "generate" # 부족 → 다시 생성 builder = StateGraph(ReflectionState) builder.add_node("generate", generate_content) # Producer builder.add_node("evaluate", evaluate_content) # Critic builder.add_edge(START, "generate") builder.add_edge("generate", "evaluate") builder.add_conditional_edges("evaluate", should_continue, # 동적 분기 {"generate": "generate", "end": END}) graph = builder.compile()

상태가 TypedDict로 명시되고 흐름이 그래프로 보이니 복잡한 워크플로우를 시각화·디버깅하기 좋습니다. graph.astream()으로 각 노드 실행을 실시간 추적할 수도 있습니다.

7. 구현 ③ Google ADK — LoopAgent로 선언적 반복

ADK는 반복 자체를 에이전트로 추상화합니다. SequentialAgent로 Producer→Critic을 한 스텝으로 묶고, 그 스텝을 LoopAgent에 넣으면 max_iterations까지 자동 반복합니다. 결과는 output_key로 세션 상태에 저장돼 다음 에이전트가 {변수}로 참조합니다.

PYTHON · LoopAgent + SequentialAgent
producer = LlmAgent(name="ContentProducer", instruction="주제 {topic}에 대해 작성. {previous_feedback} 반영.", output_key="current_content") # 세션 상태에 저장 critic = LlmAgent(name="ContentCritic", instruction="{current_content} 평가. 8점↑이면 'CONTENT_APPROVED' 출력.", output_key="previous_feedback") # 다음 반복에서 사용 step = SequentialAgent(sub_agents=[producer, critic]) # 생성→평가 loop = LoopAgent(sub_agents=[step], max_iterations=5) # 반복(키워드 시 조기종료)

코드량이 가장 적고 의도가 선언적으로 드러나 빠른 프로토타이핑에 좋습니다. Producer는 current_content, Critic은 previous_feedback처럼 서로 다른 output_key를 쓰는 게 상태가 덮어써지지 않게 하는 핵심입니다.

8. 세 프레임워크 — 같은 루프, 다른 추상화

셋 다 “생성-평가-개선-반복”이라는 원리는 동일합니다. 다른 건 상태를 어떻게 관리하고 루프를 어떻게 제어하느냐 뿐입니다.

LangChain LCEL
history + while 루프
상태: List[Message]
제어: if 조건문
코드량 많음 · 유연성 최고
LangGraph
StateGraph + 조건부 엣지
상태: TypedDict+Reducer
제어: should_continue
그래프 시각화 가능
Google ADK
LoopAgent(max_iterations)
상태: output_key 자동
제어: max_iterations
코드량 최소 · 선언적
그림 4. 같은 리플렉션, 세 가지 구현 — 추상화 수준만 다를 뿐 루프는 같다
특성 LangChain LCEL LangGraph Google ADK
상태 관리 List[Message] TypedDict + Reducer output_key 자동
반복 제어 if 조건문 should_continue max_iterations
코드량 많음 중간 적음
적합한 경우 세밀한 제어 복잡한 워크플로우 빠른 프로토타이핑

9. 한 걸음 더 — 리플렉션을 강하게 만드는 3가지

기본 루프를 익혔다면, 실무에서 위력을 발휘하는 세 가지 진화형이 있습니다. 모두 “평가를 더 객관적으로” 만드는 방향입니다.

① 멀티크리틱 병렬 — 리플렉션 × 병렬화

한 명의 만능 비평가 대신 품질·보안·성능 전문 비평가 3명이 동시에 같은 코드를 평가합니다(3편 Fan-Out/Fan-In의 응용). 각 점수를 가중 평균(품질 40% · 보안 35% · 성능 25%)해 75점 임계값으로 판정하죠.

PYTHON · Multi-Critic Fan-Out/Fan-In
class MultiCriticState(TypedDict): critic_feedbacks: Annotated[list, operator.add] # 병렬 결과 자동 수집 # Fan-Out: producer → 3개 Critic 동시 실행 for c in ["quality_critic", "security_critic", "performance_critic"]: workflow.add_edge("producer", c) workflow.add_edge(c, "feedback_merger") # Fan-In: 가중평균 통합

② 구조화 비평 — 자유 텍스트 대신 JSON 스키마

“개선이 필요합니다” 같은 자유 서술은 파싱이 불안정합니다. Pydantic 스키마로 비평을 강제하면 점수·심각도(critical/major/minor)·이슈 ID가 정량화되어 CI/CD 품질 게이트·대시보드에 그대로 연결됩니다. 형식이 깨지면 자동 재시도까지 합니다.

PYTHON · Pydantic 구조화 비평
class CritiqueResult(BaseModel): overall_status: Literal["PASS", "FAIL"] overall_score: int = Field(ge=0, le=100) issues: list[Issue] # severity·category·suggestion 구조화 improvement_priorities: list[str] # 개선 우선순위(중요도 순) result = CritiqueResult.model_validate(parsed_json) # 스키마 자동 검증

③ 코드 실행 grounding — 의견이 아니라 사실로 평가

가장 강력한 형태입니다. LLM의 주관적 비평 대신, 생성된 코드를 실제로 실행해 테스트 통과/실패라는 객관적 사실을 피드백으로 씁니다(TDD 방식). “그럴듯한데 틀린” 비평이 끼어들 여지가 없죠.

Producer
코드 생성
Code Runner
실제 실행·테스트
Critic
결과 분석
테스트 pass → 출력 · fail → 에러 메시지를 피드백으로 재생성
💡
기본 Reflection vs Reflexion. 내부 평가만 하는 기본 리플렉션과, 외부 도구·장기 메모리로 검증하는 Reflexion은 성능 차이가 큽니다. 위 ③번이 바로 Reflexion의 핵심인 외부 환경 grounding입니다.
기본 Reflection
생성 → 비평 → 개선
내부 평가만 · 메모리 없이 독립 반복
~55%코드 생성 성공률
✓ 저비용 · 저지연 · 구현 간단
Reflexion (고급)
Actor → Evaluator(외부도구) → Self-Reflection → Memory
실행·테스트로 검증 · 성찰을 저장·재사용
~81%코드 생성 성공률
△ 고비용 · 외부 도구 통합 필수
그림 5. 외부 도구로 평가를 ‘grounding’하면 코드 생성 성공률이 55%→81%로 뛴다

10. 엔터프라이즈 적용 — CRM · ERP · Groupware

기업에서 리플렉션의 비평가는 곧 자동 품질 보증(QA)·컴플라이언스 검증기가 됩니다. Producer가 만든 문서를 Critic이 평가하고, 그 뒤 Validator가 비즈니스 규칙·규정 준수를 100% 통과시킨 뒤에만 내보냅니다. 인간은 최종 승인이나 예외만 봅니다.

CRM고객 관계 관리
영업 제안서승인율 65→85%
고객 응대만족도 3.8→4.4
리드 스코어링편향 자동 검증
ERP전사 자원 관리
재무 보고서감사 지적 -80%
구매 요청예산·승인 기준 검증
생산 계획제약·효율성 검증
Groupware협업 시스템
회의록액션아이템 자동 검증
결재 문서반려율 25→5%
공지사항명확성·일관성 평가
도입 효과 · ROI
−70%
인간 검토 시간
자체 QA 수행
−80%
오류 수정 비용
사전 차단
+20%p
제안 승인율
65→85%
100%
컴플라이언스
자동 검증
그림 6. 시스템별 리플렉션 적용과 도입 효과 — 품질 보증과 자동화를 동시에

한 번에 전사 적용하지 않습니다. 파일럿(1개 사용 사례) → 확장(CRM·ERP·GW) → 고도화의 3단계로 가며, 핵심은 Producer-Critic-Validator를 공통 오케스트레이터로 두고 시스템 커넥터만 갈아끼우는 것입니다.

11. 케이스 스터디

① CRM 영업 제안서 — 품질 점수 8점 게이트

CRM의 고객·기회 정보로 Producer가 제안서 초안을 만들면, Critic이 니즈 매칭 30% · 가치 제안 25% · 차별화 20% · 완성도 15% · 가격 10%로 채점합니다. 8점 미만이면 피드백과 함께 되돌리고, 8점 이상이면 Compliance Validator(가격 정책·약관·브랜드 가이드)를 통과한 뒤에야 출력합니다.

PYTHON · 제안서 리플렉션 (점수+컴플라이언스 이중 게이트)
for iteration in range(1, max_iterations + 1): proposal = await produce_proposal(customer, feedback=last_feedback) evaluation = await evaluate_proposal(proposal) # 품질 점수 if evaluation["score"] >= 8: compliance = await validate_compliance(proposal) # 규정 검증 if compliance["passed"]: return {"status": "approved", "iterations": iteration} last_feedback = evaluation["feedback"] # 피드백 되먹임

② ERP 재무 보고서 — 다중 Validator

재무 보고서는 한 가지 평가로 부족합니다. 수치 정확성(합계·전기 일치) → 회계 기준(K-IFRS·주석 공시) → 품질(가독성·분석 깊이) 세 Validator를 모두 통과해야 합니다. 멀티크리틱(9장)의 엔터프라이즈판으로, 감사 지적 건수를 월 15건 → 3건(-80%)으로 줄입니다.

③ Groupware 회의록 — 완성도·정확성 검증 후 자동 등록

녹취를 요약해 초안을 만들고, Completeness Checker(모든 안건·결정·액션아이템·담당자/기한)와 Accuracy Reviewer(발언자 매칭·수치·고유명사)를 통과하면, 액션 아이템을 업무 관리 시스템에 자동 등록하고 참석자에게 검토를 요청합니다.

12. 베스트 프랙티스 & 트레이드오프

리플렉션을 쓸지 말지 — 의사결정

✅ 쓰면 좋은 곳
  • 품질·정확성 > 속도
  • 장문 콘텐츠(보고서·문서)
  • 코드 생성·디버깅
  • 복잡한 계획 수립
  • 전문적 평가가 필요한 작업
❌ 피해야 할 곳
  • 실시간 응답 필요
  • 단순 질의응답
  • 비용에 민감한 대용량 처리
  • 간단한 변환 작업

트레이드오프 — 공짜 품질은 없다

리플렉션은 품질을 비용·지연·컨텍스트와 맞바꿉니다. 3회 반복은 LLM 호출을 최소 6번으로 늘리고, 응답 시간을 키우며, 누적 히스토리로 토큰을 잡아먹습니다. 그래서 “품질 향상의 한계 효용이 비용을 넘는 구간”까지만 반복하는 게 핵심입니다.

체크리스트

  • Critic은 다른 페르소나로 — 자기 평가의 인지 편향 방지
  • 평가 기준을 항목별로 명시 — 점수·근거를 구조화(가능하면 Pydantic)
  • 종료 키워드 + 점수 + max_iterations 삼중 종료 — 무한 루프 금지
  • 가능하면 외부 도구로 grounding — 코드는 실행·테스트로 검증(Reflexion)
  • 컨텍스트 관리 — 최초 요구사항 유지 + 중간 히스토리 요약·압축
  • 에스컬레이션 — 최대 반복까지 미달이면 인간 검토로 넘긴다
★ 마치며

체이닝(1편)이 “순서대로 쪼개기”, 라우팅(2편)이 “입력에 따라 길 나누기”, 병렬화(3편)가 “독립 작업 동시에 던지기”였다면, 리플렉션은 “자기 출력을 스스로 평가하고 다시 쓰기”입니다. 네 패턴은 경쟁이 아니라 조합됩니다 — 라우터가 복잡한 작업만 리플렉션으로 보내고, 다양한 버전을 병렬 생성한 뒤 비평가가 최선을 고르고, 코드 실행 결과로 평가를 grounding합니다. 핵심은 생성과 평가를 분리하고, 루프를 반드시 끝맺는 것. 그리고 평가가 객관적일수록(외부 도구) 리플렉션은 강해집니다. 다음 편에서는 에이전트가 외부 세계와 상호작용하는 도구 사용(Tool Use)을 다룹니다.

#리플렉션#Reflection#ProducerCritic#Reflexion#LangGraph#LoopAgent#GoogleADK#AgenticAI
반응형