Reflexion: 언어적 강화학습(Verbal Reinforcement Learning)
경사 기반 강화학습(gradient-based RL)은 하나의 실패 양식을 고치기 위해 수천 번의 시도와 GPU 클러스터를 필요로 합니다. Reflexion(Shinn et al., NeurIPS 2023)은 이를 자연어로 처리합니다. 각 실패한 시도 뒤에 에이전트가 회고(reflection)를 작성하고, 이를 에피소드 기억(episodic memory)에 저장한 뒤, 다음 시도에서 그 기억을 조건으로 사용합니다. Letta의 수면 시간 계산(sleep-time compute), Claude Code의 CLAUDE.md 학습 내용, pro-workflow의 learn-rule 뒤에 있는 패턴이 바로 이것입니다.
유형: Build
언어: Python (stdlib)
선수 학습: Phase 14 · 01 (에이전트 루프), Phase 14 · 02 (ReWOO)
소요 시간: 약 60분
학습 목표
- Reflexion의 세 구성 요소인 행위자(Actor), 평가자(Evaluator), 자기 회고기(Self-Reflector)와 에피소드 기억의 역할을 말할 수 있습니다.
- 이진 평가자(binary evaluator), 회고 버퍼(reflection buffer), 새로운 재시도(fresh re-attempt)를 포함한 표준 라이브러리 기반 Reflexion 루프를 구현합니다.
- 주어진 작업에 대해 스칼라(scalar), 휴리스틱(heuristic), 자기 평가(self-evaluated) 피드백 원천 중 무엇을 선택할지 판단합니다.
- 언어적 강화(verbal reinforcement)가 왜 경사 기반 강화학습이라면 수천 번의 시도가 필요할 오류를 잡아낼 수 있는지 설명합니다.
문제
에이전트가 작업에 실패했습니다. 표준 강화학습(RL)에서는 수천 번의 추가 시도를 실행하고, 경사를 계산하고, 가중치를 업데이트해야 합니다. 비싸고 느립니다. 그리고 대부분의 프로덕션 에이전트는 모든 실패마다 학습 예산을 갖고 있지 않습니다.
Reflexion(Shinn et al., arXiv:2303.11366)은 다른 질문을 던집니다. 에이전트가 왜 실패했는지 생각하고, 그 생각을 프롬프트에 넣어 다시 시도하면 어떨까요? 가중치 업데이트도 없습니다. 경사도 없습니다. 시도 사이에 저장되는 자연어만 있습니다.
결과는 인상적입니다. ALFWorld에서는 ReAct와 다른 비미세조정(non-fine-tuned) 기준선을 이겼습니다. HotpotQA에서는 ReAct보다 개선되었습니다. 코드 생성(HumanEval/MBPP)에서는 당시 최고 수준(state of the art)을 달성했습니다. 이 모든 것이 단 한 번의 경사 단계 없이 이루어졌습니다.
개념
세 가지 구성 요소
행위자(Actor) : 궤적을 생성합니다 (ReAct 스타일 루프)
평가자(Evaluator) : 궤적을 채점합니다 — 이진, 휴리스틱, 자기 평가
자기 회고기(Self-Reflector): 실패에 대한 자연어 회고를 작성합니다
여기에 하나의 자료 구조가 더해집니다.
에피소드 기억(Episodic memory): 이전 회고의 목록이며, 다음 시도 프롬프트 앞에 붙습니다
한 번의 시도(trial)는 행위자가 실행합니다. 평가자는 그 시도를 채점합니다. 점수가 낮으면 자기 회고기가 실패에 대한 회고를 만듭니다. 예를 들면 "질문이 X를 묻는다고 잘못 읽었지만 실제로는 Y를 묻고 있었기 때문에 잘못된 도구를 골랐다"와 같은 문장입니다. 이 회고는 에피소드 기억에 들어갑니다. 다음 시도는 새로 시작하지만, 그 회고를 본 상태에서 출발합니다.
세 가지 평가자 유형
- 스칼라(Scalar) - 외부의 이진 신호입니다. ALFWorld는 성공하거나 실패합니다. HumanEval 테스트는 통과하거나 실패합니다. 가장 단순하고 신호가 가장 강합니다.
- 휴리스틱(Heuristic) - 미리 정의한 실패 신호입니다. "에이전트가 같은 행동을 두 번 연속 만들면 막힘(stuck)으로 표시한다." "궤적이 50단계를 넘으면 비효율로 표시한다."
- 자기 평가(Self-evaluated) - LLM이 자기 자신의 궤적을 채점합니다. 정답(ground truth)이 없을 때 필요합니다. 신호는 약하지만, 도구 기반 검증(tool-grounded verification)과 함께 쓰면 좋습니다(05강 CRITIC).
2026년의 기본값은 혼합입니다. 스칼라 신호가 있으면 스칼라를 쓰고, 없으면 자기 평가를 쓰며, 휴리스틱은 안전 난간(safety rail)으로 둡니다.
왜 일반화되는가
Reflexion은 완전히 새로운 알고리즘이라기보다 이름 붙은 패턴에 가깝습니다. 거의 모든 프로덕션 "자가 치유(self-healing)" 에이전트는 어떤 변형을 실행합니다.
- Letta의 수면 시간 계산(08강): 별도 에이전트가 과거 대화를 회고하고 기억 블록(memory block)에 씁니다.
- Claude Code의
CLAUDE.md / "save memory" 패턴: 회고가 학습 내용(learnings)으로 포착되어 이후 세션 앞에 붙습니다.
- pro-workflow의
/learn-rule 명령: 수정 사항이 명시적 규칙으로 저장됩니다.
- LangGraph의 회고 노드(reflection node): 출력을 채점하고 필요하면 개선(refine) 경로로 라우팅하는 노드입니다.
이 모든 방식은 같은 통찰에서 나옵니다. 자연어는 "실패에서 배운 것"을 실행 사이에 전달하기에 충분히 풍부한 매체입니다.
언제 잘 작동하고, 언제 그렇지 않은가
Reflexion은 다음 조건에서 잘 작동합니다.
- 명확한 실패 신호가 있습니다. 테스트 실패, 도구 오류, 오답 같은 신호입니다.
- 작업 유형이 재현 가능합니다. 같은 유형의 질문이 다시 나올 수 있습니다.
- 회고가 궤적을 개선할 여지가 있습니다. 즉, 행동 예산(action budget)이 충분합니다.
Reflexion이 도움이 되지 않는 경우도 있습니다.
- 에이전트가 이미 첫 시도에 성공합니다.
- 실패 원인이 외부에 있습니다. 네트워크가 내려갔거나 도구가 고장난 경우입니다. "네트워크가 내려갔다"는 회고는 향후 실행에 별 도움이 되지 않습니다.
- 회고가 미신(superstition)으로 변합니다. 한 번만 발생한 불안정한 실행에 대한 서사를 저장하는 경우입니다.
2026년의 함정은 기억 부패(memory rot)입니다. 회고가 쌓입니다. 일부는 오래되었거나 틀렸습니다. 에피소드 버퍼가 커질수록 재실행은 느려집니다. 완화 방법은 주기적 압축(compaction, 06강), 회고에 대한 TTL(Time To Live), 또는 별도의 수면 시간 정리 에이전트(Letta)입니다.
만들어보기
code/main.py는 장난감 퍼즐 위에 Reflexion을 구현합니다. 목표 합계가 되도록 3개 원소 리스트를 만드는 문제입니다. 행위자는 후보 리스트를 만들고, 평가자는 합계를 확인하며, 자기 회고기는 무엇이 잘못되었는지 한 줄로 씁니다. 이 회고는 다음 시도를 위한 에피소드 기억으로 들어갑니다.
구성 요소는 다음과 같습니다.
Actor - 회고를 보면 개선되는 스크립트형 정책입니다.
Evaluator.binary() - 목표 합계에 대한 통과/실패 평가입니다.
SelfReflector - 실패에 대한 한 줄 진단을 생성합니다.
EpisodicMemory - TTL 의미론을 가진 제한된 길이의 목록입니다.
실행합니다.
python3 code/main.py
실행 추적(trace)은 세 번의 시도를 보여줍니다. 1차 시도는 실패하고 회고가 저장됩니다. 2차 시도는 그 회고를 보고 개선되지만 아직 실패합니다. 3차 시도는 성공합니다. 기준 실행(baseline, 회고 없음)과 비교해보세요. 기준 실행은 1차 시도의 답에 계속 멈춰 있습니다.
사용해보기
LangGraph는 회고를 노드 패턴으로 제공합니다. Claude Code의 /memory 명령과 pro-workflow의 /learn-rule은 에피소드 버퍼를 마크다운 파일로 외부화합니다. Letta의 수면 시간 계산은 자기 회고기를 유휴 시간(downtime)에 실행해 기본 에이전트가 지연 시간(latency)에 묶인 상태를 유지하게 합니다. OpenAI Agents SDK는 Reflexion을 직접 제공하지 않습니다. 대신 점수에 따라 궤적을 거부하는 사용자 정의 가드레일(Guardrail)과 실행 사이에 살아남는 메모리 Session을 사용해 직접 만듭니다.
산출물 만들기
outputs/skill-reflexion-buffer.md는 회고 포착, TTL, 중복 제거(deduplication)를 포함한 에피소드 버퍼를 만들고 유지합니다. 작업 유형과 실패를 받으면 다음 시도에 실제로 도움이 되는 회고를 생성합니다. 단순히 "더 조심하라"는 일반 문장이 아니라, 다음 실행을 바꾸는 문장이어야 합니다.
연습문제
- 이진 평가자 대신 거리 척도(distance metric)를 반환하는 스칼라 평가자로 바꿔보세요. 목표에서 얼마나 떨어졌는지를 반환하면 더 빨리 수렴하나요?
- 회고에 10회 시도의 TTL을 추가해보세요. 그 이후에는 오래된 회고가 방해가 되나요, 도움이 되나요?
- 휴리스틱 평가자를 구현해보세요. 같은 행동이 반복되면 시도를 막힘으로 표시합니다. 이것은 자기 회고기와 어떻게 상호작용하나요?
- 회고를 무시하는 적대적 행위자(adversarial Actor)로 Reflexion을 실행해보세요. 행위자가 회고를 알아차리도록 강제하는 최소한의 회고 프롬프트 엔지니어링은 무엇인가요?
- Reflexion 논문의 AlfWorld 관련 4장을 읽어보세요. 성공률 130% 개선을 개념적으로 재현하려면 무엇이 핵심 차이인가요? 일반 ReAct(vanilla ReAct)와의 차이를 설명하세요.
핵심 용어
| 용어 | 흔한 설명 | 실제 의미 |
|---|
| Reflexion | "자기 수정(Self-correction)" | Shinn et al. 2023의 패턴입니다. 행위자, 평가자, 자기 회고기, 에피소드 기억으로 구성됩니다. |
| 언어적 강화(Verbal reinforcement) | "경사 없는 학습" | 자연어 회고를 다음 시도의 프롬프트 앞에 붙이는 방식입니다. |
| 에피소드 기억(Episodic memory) | "작업별 회고" | 하나의 작업 유형에 대한 이전 회고를 담는 제한된 버퍼입니다. |
| 스칼라 평가자(Scalar evaluator) | "이진 성공 신호" | 정답 기준에서 나오는 통과/실패 또는 수치 점수입니다. |
| 휴리스틱 평가자(Heuristic evaluator) | "패턴 기반 탐지기" | 막힘 루프, 과도한 단계 수 같은 미리 정의된 실패 신호입니다. |
| 자기 평가자(Self-evaluator) | "자기 추적에 대한 LLM-as-judge" | 정답이 없을 때 쓰는 낮은 신호의 대안입니다. 도구 기반 검증과 함께 쓰는 편이 좋습니다. |
| 기억 부패(Memory rot) | "오래된 회고" | 에피소드 버퍼가 쓸모없어진 항목으로 차는 현상입니다. 압축이나 TTL로 고칩니다. |
| 수면 시간 회고(Sleep-time reflection) | "비동기 자기 회고" | 기본 에이전트를 빠르게 유지하기 위해 자기 회고기를 주요 실행 경로 밖에서 실행하는 방식입니다. |
더 읽을거리