1 ReAct 패턴의 핵심
ReAct는 Reasoning(추론)과 Acting(행동)의 결합이다. LLM이 스스로 “생각”하고 “행동”하며 그 결과를 “관찰”하여 다음 단계를 결정하는 순환형 구조를 갖추고 있다.
모델이 복잡한 문제를 해결할 때, 매 단계마다 추론(Reason) -> 행동(Act) -> 관찰(Observe)을 반복하며 자율적으로 태스크를 완수하는 Agent 패턴이다. 단일 도구 호출로 끝나지 않고, 여러 단계의 도구 호출을 거치며 복잡한 다단계 문제를 유연하게 해결한다.
1.1 추론-행동-관찰 루프
ReAct Agent의 동작은 세 단계의 반복적 사고 과정으로 이루어진다.
┌─────────────────────────────────────────────────┐
│ │
│ ┌──────────┐ │
│ │ Reason │ 입력된 작업/현재 상황을 분석하여│
│ │ (추론) │ 문제를 분해하고 다음 행동을 계획│
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Act │ 추론을 바탕으로 외부 도구를 │
│ │ (행동) │ 선택하여 실행 │
│ └────┬─────┘ │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Observe │ 도구 실행 결과를 확인하고 │
│ │ (관찰) │ 다음 추론에 반영 │
│ └────┬─────┘ │
│ │ │
│ └──────── 목표 달성까지 반복 ────────────┘
- Reason (추론): 현재 상황을 분석하여 문제를 더 작은 단계로 분해하고, 목표 달성을 위해 어떤 행동이 필요한지 판단한다.
- Act (행동): 추론을 바탕으로 검색 엔진, 데이터베이스, API 등 적절한 외부 도구(Tool)를 선택하여 실행한다.
- Observe (관찰): 도구 실행 결과를 확인하고, 새로운 정보를 바탕으로 다음 반복에서 추론을 더 정교하게 다듬는다.
Agent는 태스크가 완료되거나 만족스러운 결론에 도달할 때까지 이 루프를 자율적으로 반복한다. 핵심은 외부 개입 없이 모델 스스로 도구 사용 여부와 종료 시점을 결정하는 자율적 제어 능력이다.
1.2 ReAct의 성능 검증
프린스턴 대학교와 Google Research Brain 팀의 공동 연구(Yao et al., 2023)에서 ReAct Agent를 질의응답, 팩트 검증, 텍스트 기반 게임, 웹 탐색 등 4가지 벤치마크로 평가한 결과는 다음과 같다.
| 평가 항목 | 결과 |
|---|---|
| 환각(Hallucination) 감소 | CoT(Chain-of-Thought)에서 빈번했던 환각과 오류 전파를 극복. 해석 가능성과 신뢰성이 크게 향상 |
| 외부 정보 활용 | Wikipedia API 상호작용 시 기존 행동 생성 모델보다 우수한 성능. ReAct + CoT 결합 시 최고 성능 달성 |
| 데이터 효율성 | 1~2개의 예시(few-shot)만으로 대량 데이터 학습 기반 모방 학습/강화 학습의 성능을 능가. 성공률 34%, 10% 향상 |
ReAct의 핵심 장점은 모델의 내부 지식과 도구를 통해 얻은 외부 정보를 모두 활용할 수 있다는 점이다. CoT가 추론에만 의존하여 최신 정보나 사실 검증에 한계가 있는 반면, ReAct는 추론 중간에 외부 도구로 사실을 확인하고 결과를 다시 추론에 반영한다.
ReAct 논문: Yao, S., et al. (2023). “ReAct: Synergizing Reasoning and Acting in Language Models.” ICLR 2023.
2 LangChain v1의 create_agent
LangChain v1 릴리즈를 기점으로 ReAct Agent 구축 방식에 큰 변화가 있었다. 기존 LangGraph의 create_react_agent 대신 langchain.agents 패키지의 create_agent 함수가 새로운 권장 표준이 되었다.
2.1 create_react_agent vs create_agent
| 항목 | create_react_agent (LangGraph) |
create_agent (LangChain v1) |
|---|---|---|
| 패키지 | langgraph.prebuilt |
langchain.agents |
| 내부 구조 | LangGraph StateGraph 기반 | LangGraph 기반이나 더 높은 추상화 |
| 시스템 프롬프트 | state_modifier 파라미터 |
system_prompt 파라미터 |
| 메모리 | checkpointer 명시 필요 |
내장 관리 |
| 권장 시점 | LangChain v0.x | LangChain v1+ |
기존
create_react_agent기반 구현은 ReAct Agent에서 다룬다. 본 포스트는 v1의create_agent에 초점을 맞춘다.
2.2 환경 설정
2.3 기본 구현
다음은 create_agent를 사용한 최소 구현 예제이다. 간단한 랜덤 숫자 도구를 정의하고, Agent가 이를 활용하여 조건부 반복 태스크를 수행한다.
import asyncio
import random
import nest_asyncio
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
nest_asyncio.apply()
load_dotenv()
# LLM 초기화
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)도구를 정의한다. @tool 데코레이터와 docstring이 핵심이다. Agent는 docstring을 읽고 이 도구를 언제 사용할지 판단한다.
@tool
def random_number() -> int:
"""Returns a random number between 1 and 10."""
return random.randint(1, 10)Agent를 생성하고 실행한다.
async def main():
# create_agent로 ReAct Agent 생성
agent = create_agent(model=llm, tools=[random_number])
# 조건부 반복 태스크 프롬프트
prompts = {
"messages": [
{
"role": "user",
"content": """
숫자가 30이 넘지 않을 때까지 랜덤한 숫자를 받아서 더해줘.
30이 넘어가는 순간 멈추고 최종 합계를 알려줘.
""",
}
]
}
# recursion_limit: 추론-행동-관찰 루프의 최대 반복 횟수 (기본값 25)
config: RunnableConfig = {"recursion_limit": 100}
# 스트리밍으로 추론-행동-관찰 과정 확인
async for event in agent.astream_events(prompts, version="v2", config=config):
kind = event["event"]
name = event.get("name", "")
data = event["data"]
if kind == "on_chat_model_start":
print("--- Agent is Thinking ---")
elif kind == "on_tool_start":
print(f"[Tool Call] {name}: {data.get('input', '')}")
elif kind == "on_tool_end":
output = str(data.get("output", ""))[:300]
print(f"[Tool Result] {output}")
elif kind == "on_chain_end" and name == "LangGraph":
messages = data.get("output", {}).get("messages", [])
if messages:
print(f"\n[Final Answer]\n{messages[-1].content}")
asyncio.run(main())2.4 실행 결과 분석
위 코드를 실행하면 Agent가 다음과 같은 과정을 거친다.
--- Agent is Thinking ---
[Tool Call] random_number: {}
[Tool Result] 1
--- Agent is Thinking ---
[Tool Call] random_number: {}
[Tool Result] 10
--- Agent is Thinking ---
[Tool Call] random_number: {}
[Tool Result] 6
--- Agent is Thinking ---
[Tool Call] random_number: {}
[Tool Result] 1
--- Agent is Thinking ---
[Tool Call] random_number: {}
[Tool Result] 7
--- Agent is Thinking ---
[Tool Call] random_number: {}
[Tool Result] 10
--- Agent is Thinking ---
[Final Answer]
순서별 결과:
1번째: 1 (누적: 1)
2번째: 10 (누적: 11)
3번째: 6 (누적: 17)
4번째: 1 (누적: 18)
5번째: 7 (누적: 25)
6번째: 10 (누적: 35) -- 30 초과, 종료
총 6번 만에 30을 넘었고, 최종 합계는 35이다.
주목할 점은 Agent가 매 반복마다 누적 합계를 추론하고, 30 초과 조건을 스스로 판단하여 루프를 종료했다는 것이다. 프롬프트에 종료 조건을 명시하면 Agent가 이를 해석하여 자율적으로 제어한다.
2.5 스트리밍 이벤트 유형
astream_events가 반환하는 주요 이벤트 유형이다.
| 이벤트 | 의미 | 활용 |
|---|---|---|
on_chat_model_start |
Agent가 추론을 시작한다 | “Thinking” 상태 표시 |
on_tool_start |
도구 호출을 시작한다 | 호출할 도구와 입력 확인 |
on_tool_end |
도구 실행이 완료되었다 | 결과 확인 |
on_chain_end + LangGraph |
전체 Agent 실행이 완료되었다 | 최종 답변 추출 |
이 이벤트 스트림을 활용하면 Agent의 추론-행동-관찰 과정을 실시간으로 모니터링할 수 있다. 디버깅뿐 아니라 사용자에게 진행 상황을 보여주는 UI 구현에도 유용하다.
3 recursion_limit과 루프 제어
recursion_limit은 Agent가 추론-행동-관찰 루프를 몇 번까지 반복할 수 있는지 제한하는 파라미터이다.
| 설정값 | 효과 | 적합한 상황 |
|---|---|---|
| 기본값 (25) | 대부분의 단순 태스크에 충분하다 | 1~3회 도구 호출로 완료되는 작업 |
| 50~100 | 다단계 반복이 필요한 태스크에 적합하다 | 조건부 반복, 데이터 수집 |
| 100 이상 | 복잡한 자율 태스크에 사용한다 | 브라우저 자동화, 장기 실행 작업 |
값을 너무 낮게 설정하면 태스크 중간에 Agent가 강제 종료된다. 반대로 너무 높게 설정하면 잘못된 추론이 반복되어 토큰을 낭비할 수 있다. 태스크의 예상 복잡도에 맞춰 적절히 조정한다.
4 system_prompt 활용
create_agent는 system_prompt 파라미터로 Agent의 행동을 제어할 수 있다.
agent = create_agent(
model=llm,
tools=[random_number],
system_prompt="""
당신은 수학 문제를 단계별로 풀어주는 조교이다.
각 단계에서 현재 상태와 다음에 수행할 작업을 명확히 설명한다.
도구 호출 결과를 받으면 반드시 누적 결과를 정리한다.
""",
)효과적인 system_prompt 작성 원칙이다.
| 원칙 | 설명 | 예시 |
|---|---|---|
| 역할 정의 | Agent의 페르소나를 명확히 한다 | “당신은 데이터 분석가이다” |
| 행동 규칙 | 각 단계에서 어떻게 행동할지 지시한다 | “도구 결과를 받으면 반드시 검증한다” |
| 출력 형식 | 최종 답변의 형식을 지정한다 | “결과를 마크다운 표로 정리한다” |
| 제약 조건 | 하지 말아야 할 행동을 명시한다 | “확인되지 않은 정보를 단정하지 않는다” |
5 ReAct 패턴 비교
| 패턴 | 추론 | 도구 사용 | 반복 | 투명성 | 적합한 태스크 |
|---|---|---|---|---|---|
| ReAct | O | O | O | 높음 | 다단계 문제 해결, 정보 수집 + 판단 |
| CoT (Chain-of-Thought) | O | X | X | 높음 | 순수 추론 문제, 수학, 논리 |
| Tool-only | X | O | X | 낮음 | 단순 도구 호출 (날씨 조회, 계산) |
| Plan-and-Execute | O | O | 제한적 | 중간 | 사전에 계획 가능한 구조화된 태스크 |
ReAct의 강점은 추론과 행동의 피드백 루프에 있다. 도구 실행 결과를 관찰한 뒤 추론을 수정할 수 있으므로, 사전에 모든 단계를 계획하기 어려운 탐색적 태스크에 특히 효과적이다. 반면, 매 단계마다 추론이 발생하므로 토큰 소비가 많고 실행 속도가 느리다는 단점이 있다.
6 관련 주제
선행 지식
- Tools – LangChain 도구 정의와 바인딩
- Bind-Tools – LLM에 도구를 연결하는 방법
- Agent – LangChain Agent 기본 개념
후속 주제
- ReAct Agent와 Playwright 브라우저 자동화 – ReAct Agent로 웹 브라우저를 조작하여 검색, 데이터 수집을 자동화하는 방법
- ReAct Agent (LangGraph) – LangGraph
create_react_agent기반 구현. 메모리, PDF 검색, 파일 관리 도구 활용 - Plan-Execute Agent – 사전 계획 기반 Agent 패턴
참고 문헌
- Yao, S., et al. (2023). “ReAct: Synergizing Reasoning and Acting in Language Models.” ICLR 2023.
- LangChain v1 릴리즈 노트
- LangChain v1 마이그레이션 가이드