direcf Loop Engineering Ch02 ReAct: The Origin of the Agentic Loop
Chapter 02 · 10 chapters
🔁

ReAct: The Origin of the Agentic Loop

ReAct — 에이전트 루프의 기원
생각(reason)과 행동(act)과 관찰(observe)을 한 루프 안에서 번갈아 돌리는 패턴. 오늘날 거의 모든 에이전트(Claude Code, Cursor, LangGraph)의 유전자가 여기서 나왔다. framework에서 loop로 넘어가는 바로 그 기점.
Chapter 2 Cheat Sheet
🍌 Nano Banana — Chapter 2 Visual Summary

🎯 학습 목표

5 goals
Learning Goals
  • ReAct의 Thought→Action→Observation 루프를 정확히 설명하고 직접 구현할 수 있다
  • 순수 Chain-of-Thought가 왜 환각과 오류 전파에 취약한지, ReAct가 이를 어떻게 막는지 안다
  • '추론을 외부 도구에 그라운딩한다'는 말의 의미를 이해한다
  • ReAct가 왜 loop engineering의 출발점으로 불리는지 설명한다
  • ReAct 루프의 실패 모드(무한 반복, 잘못된 관찰 신뢰)를 진단할 수 있다

2022년 10월, Yao 등이 발표한 ReAct: Synergizing Reasoning and Acting in Language Models(ICLR 2023)는 오늘날 우리가 '에이전트'라고 부르는 거의 모든 것의 씨앗이다. 제목 그대로 Reason(추론) + Act(행동) 의 합성어다.

그 전까지 LLM의 추론은 Chain-of-Thought(CoT), 즉 '머릿속으로 단계별로 생각하기'가 전부였다. 문제는 이 생각이 순전히 모델 머릿속에서만 일어난다는 것이다. 중간에 사실이 틀리면 그 오류가 눈덩이처럼 다음 단계로 굴러간다(오류 전파). 세상과 대조할 방법이 없으니 그럴싸한 거짓말(환각)을 지어내도 스스로 못 잡는다.

ReAct의 통찰은 단순하면서 결정적이다. 생각만 하지 말고, 생각 사이사이에 실제 행동을 끼워 넣어 세상과 대조하라. 위키피디아를 검색하고(Action), 그 결과를 읽고(Observation), 그걸 바탕으로 다시 생각한다(Thought). 이 Thought→Action→Observation의 반복이 바로 1장에서 본 while 루프의 원형이다. 이 장에서 우리는 에이전트 루프의 DNA를 해부한다.

핵심 내용

CoT의 병: 머릿속에만 갇힌 추론

Chain-of-Thought는 강력했지만 치명적 약점이 있었다. 추론의 모든 단계가 모델 파라미터라는 닫힌 세계 안에서만 벌어진다는 것이다.

여기서 두 가지 병이 생긴다. 환각(hallucination) — 모델이 모르는 사실을 그럴싸하게 지어낸다. 오류 전파(error propagation) — 중간 한 단계가 틀리면, 이후 모든 추론이 그 틀린 전제 위에 쌓여 통째로 무너진다.

"ReAct overcomes issues of hallucination and error propagation prevalent in chain-of-thought reasoning by interacting with a simple Wikipedia API." — Yao et al., 2022

핵심 진단은 이렇다. CoT에는 현실과 대조하는 순간(reality check) 이 없다. 시험지에 계산 과정을 적되, 계산기를 한 번도 안 두드리고, 참고서를 한 번도 안 펼치는 학생과 같다. ReAct는 바로 이 '대조하는 순간'을 추론 루프 안에 심는다.

해법: 생각과 행동을 번갈아 짜기(interleaving)

ReAct의 한 스텝은 세 박자로 이루어진다.

  1. Thought — 지금 상황에서 무엇을 해야 하는지 언어로 추론한다. ("파리의 인구를 알아야겠다")
  2. Action — 그 추론에 따라 외부 도구를 호출한다. (Search["Paris population"])
  3. Observation — 도구가 돌려준 실제 결과를 받는다. ("약 210만 명")

그리고 이 관찰을 컨텍스트에 넣은 채 다시 Thought로 돌아간다. 논문의 표현을 빌리면:

"reasoning traces help the model induce, track, and update action plans as well as handle exceptions, while actions allow it to interface with external sources."

즉 추론과 행동이 서로를 돕는 시너지 구조다. 추론은 다음에 무슨 행동을 할지 계획을 세우고 예외를 처리하며, 행동은 그 추론을 실제 세계(위키피디아, 계산기, 코드 실행)에 붙들어 맨다. 이 '붙들어 맴'을 그라운딩(grounding) 이라 부른다 — 추론이 허공에 떠 있지 않고 관찰된 사실에 닻을 내린 상태다.

왜 이것이 loop engineering의 시작인가

ReAct 논문 자체는 '루프 엔지니어링'이라는 말을 쓰지 않는다. 하지만 후대가 이걸 출발점으로 삼는 이유가 있다.

ReAct는 에이전트를 정적인 프롬프트 한 방이 아니라 동적인 반복 과정으로 재정의했다. 답이 나올 때까지 Thought→Action→Observation을 계속 도는 것 — 이게 바로 1장의 for step in range(max_steps) 루프다. 오늘날 Claude Code가 파일을 읽고·수정하고·테스트를 돌리는 것도, Cursor가 코드베이스를 탐색하는 것도, 전부 이 루프의 후손이다.

중요한 건 ReAct가 열어젖힌 질문들이다. 루프를 언제 멈출까? 관찰이 너무 길면 어떻게 자를까? 같은 행동을 반복하면 어떻게 감지할까? 도구가 에러를 뱉으면? 이 질문들에 답하는 것이 곧 loop engineering이고, 3장부터의 모든 논문이 이 질문들 중 하나씩을 붙들고 발전시킨 결과물이다. ReAct는 '에이전트는 루프다'라는 명제를 최초로 실증했다.

💡 비유로 이해하기

Analogy
눈 감고 푸는 학생 vs 참고서 펴는 학생

순수 CoT는 눈을 감고 암산으로만 시험을 푸는 학생이다. 머릿속으로 '음, 이 나라 수도는 아마 이거고, 인구는 대략 저 정도일 거야' 하고 쭉 밀고 나간다. 똑똑하면 꽤 맞히지만, 한 번 잘못 기억하면 그 위에 쌓은 모든 답이 연쇄적으로 틀린다. 그리고 자기가 틀렸는지조차 모른다.

ReAct는 매 단계 참고서를 펼치고 계산기를 두드리는 학생이다. '수도가 뭐였지?' 싶으면 바로 찾아보고(Action), 찾은 값을 확인하고(Observation), 그제서야 다음 계산으로 넘어간다. 한 단계 한 단계가 현실에 검증받으니, 틀린 전제 위에 탑을 쌓는 일이 없다.

결정적 차이는 '생각의 속도'가 아니라 '대조의 유무'다. 두 학생 다 똑똑할 수 있다. 하지만 시험이 어려워지고 길어질수록, 매번 사실을 확인하며 나아가는 학생이 압도적으로 안정적이다. 에이전트 작업이 복잡할수록 ReAct 루프가 CoT를 이기는 이유가 정확히 이것이다.

💻 코드 예시

ReAct 루프를 직접 구현해보자. 1장의 골격과 거의 같지만, 이번엔 LLM이 명시적으로 'Thought:'와 'Action:'을 텍스트로 뱉게 하고, 우리가 그걸 파싱해 도구를 실행한 뒤 'Observation:'을 되먹인다. 이 텍스트 프로토콜이 원조 ReAct의 방식이다(요즘은 tool-calling API가 이걸 구조화해준다).

python
import re

SYSTEM = '''당신은 ReAct 에이전트다. 다음 형식을 반복하라:
Thought: <지금 무엇을 왜 해야 하는지>
Action: <tool_name>[<query>]
(도구 결과는 Observation:으로 주어진다)
충분히 알았으면 Action 대신 'Answer: <최종답>'을 출력하라.'''

def react(question, tools, llm, max_steps=8):
    scratchpad = f"Question: {question}\n"
    for _ in range(max_steps):
        out = llm(SYSTEM + scratchpad, stop=["Observation:"])  # 관찰 전까지만 생성
        scratchpad += out

        if "Answer:" in out:                       # 정지 조건
            return out.split("Answer:")[-1].strip()

        m = re.search(r"Action:\s*(\w+)\[(.*?)\]", out)   # Action 파싱
        if not m:
            scratchpad += "\nObservation: (형식 오류 — Action을 다시 출력하라)\n"
            continue
        tool, arg = m.group(1), m.group(2)
        obs = tools.get(tool, lambda a: f"알 수 없는 도구 {tool}")(arg)  # act
        scratchpad += f"\nObservation: {obs}\n"     # observe → 다음 루프
    return "(max_steps 도달)"

주목할 지점 셋. (1) stop=["Observation:"] — 모델이 스스로 관찰 결과까지 지어내지 못하게 생성을 끊는다. 관찰은 반드시 실제 도구에서 와야 그라운딩이 성립한다. 이걸 빠뜨리면 모델이 도구 결과를 환각하는 고전적 버그가 난다. (2) scratchpad — Thought·Action·Observation이 계속 누적되는 이 문자열이 곧 에이전트의 '작업 기억'이다. 길어지면 잘라야 하는데, 그게 7장 컨텍스트 엔지니어링의 문제로 이어진다. (3) 형식 오류 처리 — 파싱 실패 시 무너지지 않고 재시도를 유도한다. 이런 방어 코드가 loop engineering의 실전 기본기다. 이 30줄이 ReAct의 전부이며, 현대 tool-calling 에이전트는 이 텍스트 프로토콜을 JSON 스키마로 정형화한 것뿐이다.

🏭 현업에서의 평가

Industry Evaluation
ReAct는 너무 유명해서 '안다'고 말하기 쉽지만, 면접관은 이름이 아니라 '왜 그 구조여야 하는가'를 묻는다. 특히 CoT 대비 무엇이 근본적으로 달라졌는지, 그리고 프로덕션에서 ReAct 루프가 어떻게 깨지는지를 설명할 수 있느냐가 관건이다.

✅ 시니어가 보는 것

  • Thought/Action/Observation 각각의 역할과, 셋이 왜 함께 있어야 시너지가 나는지 설명
  • 그라운딩(추론을 관찰에 닻 내림)이 환각·오류전파를 어떻게 막는지 인과적으로 설명
  • 현대 tool-calling API가 결국 ReAct의 구조화된 버전임을 알아봄
  • 무한 반복·관찰 폭주·형식 오류 같은 실전 실패 모드에 대한 대비책

⚠️ 레드 플래그

  • ReAct를 그냥 '프롬프트 기법'으로만 이해하고 루프 구조를 못 봄
  • 관찰 결과를 모델이 지어내게 놔두는 stop-sequence 누락 버그를 모름
  • 정지 조건 없이 '답 나올 때까지' 돌리겠다고 함
  • CoT와의 차이를 '더 똑똑해서'로 설명 (그라운딩의 부재/존재를 못 짚음)

🎤 예상 인터뷰 질문

  1. ReAct와 순수 Chain-of-Thought의 근본적 차이는 무엇이며, 어떤 작업에서 그 차이가 결정적인가?
  2. ReAct 루프가 같은 행동을 무한 반복하는 상황을 어떻게 감지하고 끊겠는가?
  3. 현대 함수 호출(function calling) API는 원조 ReAct와 무엇이 같고 무엇이 다른가?
숙달 vs 익숙함: **표면**은 'ReAct = 생각하고 행동하기'라고 요약한다. **마스터**는 ReAct의 진짜 기여가 '추론을 외부 관찰에 그라운딩함으로써 오류 전파를 끊은 것'임을 짚고, 그 대가로 생긴 새 문제들(관찰 길이, 반복 감지, 정지 조건)이 이후 모든 에이전트 연구의 의제가 되었다는 계보까지 그린다.

✨ 핵심 요약

Reason + Act

생각(추론)과 행동(도구 호출)을 한 루프에서 번갈아 짜는 것이 ReAct의 핵심.

그라운딩

추론을 외부 관찰에 닻 내려, CoT의 환각과 오류 전파를 끊는다.

루프의 원형

Thought→Action→Observation 반복이 오늘날 모든 에이전트 while 루프의 조상이다.

stop sequence의 중요성

관찰까지 모델이 생성하지 못하게 끊어야 그라운딩이 진짜로 성립한다.

scratchpad = 작업 기억

누적되는 Thought/Action/Observation이 에이전트의 단기 기억이며, 길이 관리가 곧 컨텍스트 문제로 이어진다.

현대 API의 조상

tool-calling / function calling은 ReAct의 텍스트 프로토콜을 JSON으로 정형화한 버전이다.

새 질문의 문을 엶

정지 조건·반복 감지·관찰 관리 등 loop engineering의 의제 전부가 여기서 시작됐다.