ReAct 패턴

발행: (2026년 2월 4일 오전 11:35 GMT+9)
15 min read
원문: Dev.to

Source: Dev.to

ReAct란 무엇인가?

Klover: ReAct는 Reasoning + Acting(추론 + 행동)의 약자로, 명시적으로 interleaved됩니다.
아이디어는 간단합니다: LLM이 한 번에 질문에 답하도록 하는 대신, 소리 내어 생각하게 하고, 도구를 호출하는 등 행동을 취하고, 결과를 관찰한 뒤 다시 생각하게 합니다. 이것이 루프입니다.

Me: 그러면 실제 도구 사용이 섞인 체인‑오브‑쓰루(chain‑of‑thought)와 비슷한 건가요?

Klover: 정확합니다. 루프는 다음과 같습니다:

  1. Thought – 모델이 다음에 무엇을 할지 추론합니다.
  2. Action – 모델이 도구(검색, 계산기, API 등)를 호출합니다.
  3. Observation – 시스템이 해당 도구의 실제 결과를 삽입합니다.
  4. Repeat 모델이 충분한 정보를 얻었다고 판단할 때까지 반복 → Final Answer.

핵심 단어는 “interleaved.” “모두 생각하고 나서 행동한다”가 아니라 조금 생각하고, 조금 행동하고, 관찰하고, 조정한다는 의미입니다.

시스템은 모델이 도구를 호출하려는지 그냥 대화하려는지 어떻게 알까요?

Me: 모델이 도구를 호출하려는지 그냥 대화하려는지 시스템은 어떻게 알까요?

Klover: 정지 시퀀스. 모델은 특정 형식으로 출력하도록 훈련(또는 프롬프트)됩니다, 예를 들어:

Action: search["weather in Singapore"]

시스템은 그 패턴을 감시하고, 가로채서 실제로 도구를 실행한 뒤 결과를 다음과 같이 삽입합니다:

Observation: 32 °C, sunny

모델은 실제로 아무것도 실행하지 않습니다. 도구 호출처럼 보이는 텍스트를 쓸 뿐이며, 주변 스캐폴딩이 나머지를 처리합니다.

Me: 그래서 LLM은 실제 도구를 호출한다는 걸 모르는 건가요? 그냥 텍스트를 생성하고 있다고 생각하나요?

Klover: 맞아요. 모델 입장에서는 다음 토큰을 예측하는 것뿐입니다. 마법은 가로채고, 실행하고, 삽입하는 스캐폴딩에 있습니다.

예시 상호작용

Klover: 예를 들어 ReAct 에이전트에게 물어본다고 해보세요:

“이번 토요일에 해변에 가야 할까요?”

에이전트는 다음과 같이 할 수 있습니다:

Thought: I need to check the weather for Saturday.
Action: weather_forecast("Saturday")
Observation: Saturday: 34 °C, sunny, UV index 11, no rain.
Thought: Weather looks great, but UV is very high. Let me check tide times too.
Action: tide_info("Saturday")
Observation: High tide at 2 pm, low tide at 8 am.
Thought: Good beach weather, but go early to avoid peak UV and catch low tide. I have enough info now.
Final Answer: Yes! Saturday looks perfect — sunny, 34 °C. Go in the morning around 8‑9 am when the tide is low and UV isn’t brutal yet.

여러 단계가 진행되며, 각 단계는 이전 단계의 정보를 기반으로 합니다. 모델은 충분한 정보를 얻었을 때 멈출지를 스스로 결정합니다.

Me: 알겠어요. 즉, 기본적으로 LLM을 반복문 안에 넣은 형태군요.

Klover: 가장 간결하게 표현한 말이 맞아요 – 도구 접근 권한과 언제 멈출지 결정할 수 있는 능력을 가진 LLM을 반복문에 넣은 것이 바로 agent의 핵심입니다.

Me: 잠깐 — 지금 당신도 ReAct 에이전트인가요?

Klover: 거의 그렇습니다. 저는 여러분이 묻는 내용을 추론하고, 도구(검색, 파일 읽기, 코드 실행)를 호출할 수 있으며, 결과를 관찰하고 다음에 할 말을 결정합니다. 여러분은 그 패턴이 실제로 작동하는 모습을 보고 있는 겁니다.

일반적인 실패 모드

Klover: 두 가지 큰 실패 모드.

  1. 무한 루프 – 모델이 답변에 수렴하지 못하고 도구를 계속 호출할 수 있음(예: 반복적으로 검색, 다시 표현, 다시 검색…).
    해결: 최대 반복 제한을 설정합니다. N 단계 후에 사용 가능한 모든 정보를 가지고 최종 답변을 강제합니다.

  2. 컨텍스트 오버플로우 – 모든 Thought, Action, Observation이 대화에 추가됩니다. 10‑15 단계 후에는 토큰이 많이 쌓여 결국 컨텍스트 창 한계에 도달해 모델이 중단됩니다.
    하지만 실제 문제는 한계에 도달하기 전에 시작됩니다.

“한계 전에”가 중요한 이유

  • 희석된 어텐션: 토큰 수가 증가함에 따라 소프트맥스 어텐션이 모든 토큰에 얇게 퍼집니다.
    비유: 파이를 10조각으로 자른 것과 1 000조각으로 자른 것을 비교하면, 각 조각이 거의 의미가 없어집니다.

  • “중간에서 잃어버림” 효과: 모델은 컨텍스트의 시작과 끝에 더 많이 어텐션을 주고, 중간 부분은 무시하는 경향이 있습니다.
    15단계 체인에서 핵심 추론이 5단계에 발생한다면, 효과적으로 무시될 수 있습니다.

결과: 긴 ReAct 체인은 희석된 어텐션중간에서의 손실이라는 이중 타격을 입습니다.

실용적인 해결책: 컨텍스트 압축

Klover: 실용적인 해결책은 단계 사이의 컨텍스트를 압축하는 것입니다.

  • 상세를 위해 마지막 몇 번의 교환을 그대로 유지합니다.
  • 그보다 오래된 내용은 모두 요약합니다.

LangChain의 ConversationSummaryBufferMemory가 바로 이 작업을 수행합니다: 최신 메시지는 그대로 유지되고, 오래된 메시지는 LLM에 의해 요약됩니다.

트레이드오프

  • 손실 압축: 요약기가 중요한 내용을 놓칠 수 있습니다.
  • 10단계 뒤에 놓친 세부 사항이 중요해질 수 있습니다.
  • 완벽한 해결책은 없으며, 컨텍스트 최신성정보 보존 사이의 균형입니다.

명시적 Thought 단계가 중요한 이유

Me: 왜 명시적인 Thought 단계가 이렇게 중요한가요? 모델이 바로 Action 단계로 바로 갈 수는 없나요?

Klover: 이것은 좀 더 깊은 통찰입니다. Chain‑of‑thought (CoT) 은 단순한 프롬프트 트릭이 아니라 추가적인 연산입니다.

  • Without CoT: 모델은 답을 만들기 위해 한 번의 forward pass만 수행합니다. 이것이 모델이 할 수 있는 전부이며, 트랜스포머 네트워크의 고정된 깊이에 의해 제한됩니다.
  • With CoT: 모델이 생성하는 각 토큰은 다음 토큰을 위한 새로운 컨텍스트가 됩니다. 생성된 각 토큰은 전체 forward pass의 결과이며, 그 출력이 다음 패스로 전달됩니다. 즉, 여러 forward pass를 연쇄적으로 연결하는 효과를 얻는 것입니다.

Me: 그런데 각 forward pass는 독립적인 것이 아니나요? 차이점은 이전 토큰들로부터 새로운 컨텍스트를 얻는 것뿐입니다. 모델이 “기억”하는 것이 아니라 단지 더 많은 텍스트를 보는 것뿐이죠.

Klover: 메커니즘에 대해서는 맞습니다 – 각 패스는 독립적이지만 누적된 컨텍스트가 모델이 단계별 추론을 수행하게 하고, 단 한 번이 아니라 여러 패스에 걸쳐 내부 상태를 다듬을 수 있게 합니다. 이러한 반복적 추론이 ReAct에 힘을 주는 요소입니다.

Me: 독립적이고, 유일한 연결 고리는 컨텍스트를 통한 것이죠. 하지만 효과를 생각해 보세요: 고정 깊이 연산(​N​개의 트랜스포머 레이어를 한 번 통과)에서 가변 깊이 연산(이전 출력에 의해 정보가 제공되는 여러 번의 통과)으로 바꾸어 놓은 겁니다. 메커니즘은 컨텍스트이고, 효과는 더 많은 연산입니다. 고정 깊이를 가변 깊이로 연결하고 있는 겁니다.

Klover: 맞습니다 – 각 토큰은 전체 forward pass의 압축된 산물입니다. 주의(attention)와 피드‑포워드 처리의 모든 레이어가 하나의 토큰으로 압축되고, 그 토큰이 다음 패스의 컨텍스트가 됩니다. 출력 토큰은 본질적으로 작업 메모리, 즉 모델이 중간 연산을 수행하는 스크래치 공간입니다. CoT가 없으면 스크래치 공간이 없으며, 한 번에 한 번, 한 패스만 수행하게 됩니다.

Session: Feb 4 2026

첫 번째 노출 — 전체 ReAct 패턴을 다루었으며, 여기에는 실패 모드, CoT 메커니즘 및 에이전트 패턴 비교가 포함됩니다.

ReAct의 대안

Me: ReAct에 대한 대안이 있나요?

Klover: 비교해볼 만한 두 가지 주요 대안이 있습니다.

  1. 순수 도구 호출 — 명시적인 Thought 단계가 없습니다. 모델은 도구 호출을 직접 출력합니다.

    • 장점: 더 빠르고 토큰 사용량이 적음.
    • 단점: 모델이 행동하기 전에 무엇을 해야 할지 추론하지 않습니다. 간단한 작업에는 잘 작동하지만 복잡한 다단계 추론에서는 무너집니다.
  2. Plan‑first — 모델이 전체 계획을 미리 수립하고(step 1, step 2, step 3 …) 실행합니다.

    • 장점: 의존성을 파악하고 앞을 내다볼 수 있습니다 (“Y를 하기 전에 X가 필요합니다”).
    • 단점: 계획이 깨지기 쉽습니다. step 2에서 예상치 못한 데이터가 반환되어 step 3을 무효화하면 전체 계획이 붕괴됩니다.

Me: 그렇다면 ReAct는 더 적응적이지만 단기적이며, plan‑first는 앞을 내다보지만 쉽게 깨지는 건가요?

Klover: 깔끔한 요약입니다. ReAct는 적응적이지만 근시안적이며 — 한 단계만 앞을 생각합니다. Plan‑first는 전략적이지만 깨지기 쉽습니다.

실용적인 최적점은 하이브리드: 계획하고 재계획입니다. 대략적인 계획을 세우고 실행을 시작하되, 관찰이 기대와 맞지 않을 때 재계획을 할 준비를 합니다. 이것이 바로 LangGraph와 같은 프레임워크가 가능하게 하는 것으로, 조건부 엣지를 가진 단계 그래프를 정의해 에이전트가 실제 상황에 따라 분기하고 반복할 수 있게 합니다.

Me: 그렇다면 실제 운영에서는 순수 ReAct를 사용하는 경우가 없나요?

Klover: 교과서적인 형태로는 사용되지 않습니다. 실제 시스템은 핵심 통찰인 ‘추론과 행동을 교차시키는’ 방식을 기반으로 계획, 메모리 관리, 가드레일, 폴백 등을 추가합니다. 하지만 ReAct 루프가 모든 것의 기반이 됩니다.

Back to Blog

관련 글

더 보기 »