direcf Loop Engineering Ch03 Reflexion & Self-Refine: The Self-Reflection Loop
Chapter 03 · 10 chapters
🪞

Reflexion & Self-Refine: The Self-Reflection Loop

Reflexion & Self-Refine — 자기성찰 루프
루프에 '반성'을 넣는다. 실패한 시도를 언어로 되짚어 메모리에 적어두고(Reflexion), 혹은 자기 출력을 스스로 비평해 고쳐 쓴다(Self-Refine). 가중치를 건드리지 않고 언어만으로 스스로 나아지는 루프.
Chapter 3 Cheat Sheet
🍌 Nano Banana — Chapter 3 Visual Summary

🎯 학습 목표

5 goals
Learning Goals
  • Reflexion의 '언어로 강화학습' 아이디어와 episodic memory buffer를 설명한다
  • Self-Refine의 generate→critique→refine 단일 LLM 루프를 구현할 수 있다
  • 두 방법이 가중치 업데이트 없이 성능을 올리는 원리를 이해한다
  • 자기성찰 루프의 한계(self-bias, 수렴 실패)를 진단한다
  • 언제 반성을 메모리에 남기고(Reflexion) 언제 즉석에서 고쳐쓸지(Self-Refine) 판단한다

ReAct가 '행동하는 루프'를 만들었다면, 다음 질문은 자연스럽다. 실패하면 어떻게 배우지? 사람은 시험을 망치면 '아, 이 부분을 놓쳤구나' 반성하고 다음엔 다르게 한다. 그런데 LLM을 다시 훈련(가중치 업데이트)하는 건 비싸고 느리다. 더 가벼운 방법은 없을까?

Reflexion(Shinn et al., NeurIPS 2023)의 답은 우아하다. 가중치 대신 언어로 강화하라. 에이전트가 작업에 실패하면, 무엇이 왜 잘못됐는지를 스스로 문장으로 적어 episodic memory buffer(일화 기억 버퍼) 에 저장한다. 다음 시도 때 이 반성문을 컨텍스트에 넣어주면, 같은 실수를 피한다. 훈련 없이, 오직 텍스트로 학습이 일어난다.

Self-Refine(Madaan et al., 2023)은 이걸 더 미니멀하게 만든다. 별도 메모리도, 여러 시도도 필요 없다. 하나의 LLM이 답을 쓰고 → 자기 답을 비평하고 → 그 비평으로 고쳐 쓰기를 반복한다. 생성자·비평가·수정자가 전부 같은 모델이다. 이 장에서는 '루프에 반성을 심는' 두 가지 방식을 배운다 — 시도 간(Reflexion) 반성과 시도 내(Self-Refine) 반성.

핵심 내용

Reflexion: 언어로 하는 강화학습

전통적 강화학습(RL)은 보상 신호로 가중치를 조금씩 조정한다. 느리고, 많은 시도가 필요하고, 왜 그렇게 바뀌었는지 해석하기 어렵다. Reflexion은 이 루프를 통째로 언어 공간으로 옮긴다.

"Reflexion, a novel framework to reinforce language agents not by updating weights, but instead through linguistic feedback." — Shinn et al., 2023

동작은 이렇다. 에이전트가 작업을 시도한다 → 실패 신호(테스트 불통과, 목표 미달)를 받는다 → 그 실패를 언어로 성찰한다: "내가 X를 가정했는데 그게 틀렸다. 다음엔 먼저 Y를 확인해야 한다." → 이 반성문을 episodic memory에 저장 → 다음 시도 때 이 메모리를 컨텍스트에 주입.

핵심은 반성문이 다음 시도의 지침이 된다는 것이다. 보상이라는 숫자 한 개(scalar)보다, '무엇을 왜 틀렸는지'라는 문장이 훨씬 정보가 풍부하다. 그래서 단 몇 번의 시도만으로도 극적으로 개선된다. 가중치는 그대로인데, 컨텍스트에 쌓인 반성이 에이전트를 똑똑하게 만든다.

Self-Refine: 혼자서 쓰고, 까고, 고치기

Self-Refine은 더 급진적으로 단순하다. 외부 도구도, 별도 훈련 데이터도, 여러 에피소드도 없다. 단 하나의 LLM이 세 가지 역할을 번갈아 한다.

"the same LLM provides feedback for its output and uses it to refine itself, iteratively." — Madaan et al., 2023

루프는 세 박자다.

  1. Generate — 초안을 쓴다.

  2. Feedback — 같은 모델이 그 초안을 비평한다. ("이 함수는 엣지 케이스를 놓쳤다")

  3. Refine — 그 피드백을 반영해 고쳐 쓴다.

그리고 만족스러울 때까지 2→3을 반복한다. 훈련이 전혀 없다는 게 논문의 자랑이다: "does not require any supervised training data, additional training, or reinforcement learning."

이게 통하는 이유는 비평이 생성보다 쉽다는 비대칭성 때문이다. 처음부터 완벽한 에세이를 쓰긴 어렵지만, 남이 쓴 에세이에서 어색한 문장을 찾긴 쉽다. LLM도 마찬가지여서, '평가자 모드'로 자기 출력을 보면 생성 때 놓친 결함을 종종 잡아낸다.

두 방식의 차이와 공통의 함정

둘 다 '언어 기반 자기개선 루프'지만 결이 다르다.

구분 Reflexion Self-Refine
반성의 위치 시도와 시도 사이 한 답 안에서
메모리 episodic buffer에 축적 없음 (즉석)
외부 신호 필요 (성공/실패 판정) 불필요 (자기 비평만)
적합한 상황 명확한 성공 판정이 있는 반복 과제 한 번에 품질을 올리고 싶은 생성 과제

그리고 둘 다 같은 함정에 빠진다. self-bias(자기편향) — 모델이 자기 답을 후하게 평가해서 진짜 결함을 못 보는 것이다. 특히 수학처럼 정답이 딱 떨어지는 영역에서, 틀린 답을 '괜찮아 보인다'며 그냥 확정해버리는 실패가 잦다.

그래서 실무에서는 비평 신호를 외부에서 주입하는 식으로 보강한다 — 테스트 실행 결과, 컴파일러 에러, 별도 검증 모델의 판정 등. 자기 자신만 거울로 삼으면 편향에서 못 벗어나지만, 외부 관찰(2장의 그라운딩!)을 반성의 재료로 쓰면 훨씬 튼튼해진다. 이 지점에서 ReAct의 그라운딩과 Reflexion의 반성이 한 루프 안에서 만난다.

💡 비유로 이해하기

Analogy
오답노트 vs 초고 퇴고

Reflexion은 오답노트다. 시험을 한 번 치르고 나서, 틀린 문제마다 '나는 여기서 이런 착각을 했다. 다음엔 이렇게 접근하자'를 노트에 적는다. 다음 시험 전에 이 노트를 다시 읽으면, 같은 함정에 두 번 빠지지 않는다. 시험(시도)과 시험 사이에 반성이 쌓이고, 그 축적이 곧 실력이 된다. 핵심은 노트가 '점수'가 아니라 '문장으로 된 교훈'이라는 점 — 65점이라는 숫자보다 '분수 통분을 빼먹었다'는 문장이 훨씬 쓸모 있다.

Self-Refine은 초고 퇴고다. 에세이 한 편을 쓰는 그 자리에서, 초고를 쓰고 → 스스로 소리 내어 읽어보고 → '이 문단 논리가 약하네' 고치고 → 다시 읽고를 반복한다. 다음 에세이를 위한 노트를 남기는 게 아니라, 지금 이 한 편을 그 안에서 갈고닦는다.

둘 다 '스스로를 거울에 비춰 고친다'는 점은 같다. 하지만 오답노트는 여러 시험에 걸친 학습이고, 퇴고는 한 작품 안의 완성이다. 그리고 둘 다 위험이 같다 — 자기 눈이 후하면 오답노트에 틀린 교훈을 적거나, 퇴고에서 결함을 못 본다. 그래서 진짜 고수는 채점표(외부 신호)를 옆에 두고 반성한다.

💻 코드 예시

Self-Refine 루프를 구현해보자. 하나의 LLM이 생성자·비평가·수정자를 번갈아 맡는다. 실전 감각을 위해 '외부 신호로 비평을 보강'하는 훅(테스트 실행)도 함께 넣었다 — 순수 자기비평의 self-bias를 완화하는 실무 패턴이다.

python
def self_refine(task, llm, run_tests=None, max_iters=4):
    draft = llm(f"다음 작업의 코드를 작성하라:\n{task}")  # 1) Generate

    for i in range(max_iters):
        # 외부 신호로 비평을 그라운딩 (self-bias 완화)
        test_signal = run_tests(draft) if run_tests else "(테스트 없음)"
        if test_signal == "PASS":
            return draft                       # 정지: 객관적 통과

        # 2) Feedback — 같은 모델이 자기 출력을 비평
        critique = llm(
            f"작업:\n{task}\n\n현재 코드:\n{draft}\n\n"
            f"테스트 결과: {test_signal}\n"
            "구체적 결함을 항목별로 지적하라. 결함이 없으면 'NONE'."
        )
        if critique.strip() == "NONE":
            return draft

        # 3) Refine — 비평을 반영해 고쳐 씀
        draft = llm(
            f"작업:\n{task}\n\n이전 코드:\n{draft}\n\n"
            f"지적된 결함:\n{critique}\n\n결함을 고친 전체 코드를 출력하라."
        )
    return draft   # max_iters 도달 — 마지막 초안 반환

이 루프의 설계 포인트. (1) 외부 신호 우선run_tests가 PASS를 주면 자기비평을 건너뛰고 즉시 확정한다. 객관적 정답 판정이 있으면 그걸 self-bias보다 신뢰한다. (2) 비평을 테스트 결과에 그라운딩 — critique 단계에 test_signal을 넣어, 모델이 허공에 대고 자화자찬하지 못하게 실패의 물증을 들이민다. 이게 2장 그라운딩과 3장 반성이 만나는 지점이다. (3) 명시적 종료 신호 — 'NONE'이라는 탈출구를 줘서 불필요한 반복을 막는다. Reflexion으로 확장하려면 이 critique를 함수 밖 memory 리스트에 append해 다음 '시도'의 프롬프트에 주입하면 된다 — 즉 시도-내 루프가 시도-간 루프로 승격된다.

🏭 현업에서의 평가

Industry Evaluation
자기개선 루프는 데모에선 화려하지만 프로덕션에선 함정이 많다. 면접관은 지원자가 self-bias의 위험을 알고, '언제 자기비평이 실제로 도움이 되고 언제 비용만 늘리는지'를 판단할 수 있는지를 본다. 무작정 '반성 루프를 돌리면 좋아진다'는 순진한 낙관은 감점이다.

✅ 시니어가 보는 것

  • Reflexion(시도 간)과 Self-Refine(시도 내)의 구조적 차이와 각각의 적합 상황을 구분
  • self-bias를 인지하고, 외부 신호(테스트·검증 모델)로 비평을 그라운딩하는 설계
  • 반성 루프의 비용(추가 LLM 호출)과 이득을 저울질하는 감각
  • 언제 수렴하지 않는지(무한 퇴고, 진동)를 알고 정지 조건을 설계

⚠️ 레드 플래그

  • '자기비평을 반복하면 항상 좋아진다'는 무비판적 낙관
  • 정답이 딱 떨어지는 과제에서도 자기비평만 믿고 외부 검증을 안 붙임
  • 반성 루프의 추가 토큰/지연 비용을 고려하지 않음
  • Reflexion과 Self-Refine을 같은 것으로 뭉뚱그림

🎤 예상 인터뷰 질문

  1. 자기비평 루프에서 self-bias는 왜 생기며, 실무에서 어떻게 완화하는가?
  2. Reflexion의 episodic memory와 Self-Refine의 즉석 비평 중, 코드 생성 에이전트에는 어느 쪽이 맞고 왜인가?
  3. 자기개선 루프가 오히려 답을 망가뜨리는(진동·퇴행) 경우를 어떻게 감지하고 막겠는가?
숙달 vs 익숙함: **표면**은 'LLM이 자기 답을 고치게 하면 좋아진다'고 안다. **마스터**는 그 개선이 '비평이 생성보다 쉽다'는 비대칭성에서 나오되, 자기 자신만 거울로 삼으면 self-bias에 갇힌다는 한계를 알고, 외부 관찰을 반성의 재료로 끌어와(ReAct 그라운딩 + Reflexion 반성) 루프를 튼튼하게 만드는 설계까지 제시한다.

✨ 핵심 요약

언어로 하는 RL

Reflexion은 가중치 대신 언어적 반성으로 에이전트를 강화한다 — 숫자 보상보다 문장 교훈이 정보가 풍부하다.

episodic memory

실패의 반성문을 메모리에 쌓아 다음 시도에 주입 — 시도 간 학습이 일어난다.

generate-critique-refine

Self-Refine은 하나의 LLM이 쓰고·까고·고치기를 반복하는 시도 내 루프다.

비평 비대칭성

생성보다 비평이 쉽기에 자기비평이 통한다 — 하지만 만능은 아니다.

self-bias 함정

자기 답을 후하게 봐서 결함을 놓친다. 정답이 명확한 과제에서 특히 위험.

외부 신호로 그라운딩

테스트·컴파일러·검증 모델로 비평을 뒷받침하면 self-bias를 완화한다 — 2장 그라운딩과의 결합.

정지 조건 필수

무한 퇴고와 진동을 막기 위해 종료 신호(NONE/PASS)와 max_iters를 반드시 건다.