ReAct Agent

Reasoning and Acting 패턴을 활용한 에이전트

ReAct(Reasoning and Acting) 패턴을 활용하여 추론과 행동을 결합한 에이전트 구현을 다룬다.

AI
RAG
LangChain
저자

Kwangmin Kim

공개

2025년 07월 25일

1 ReAct Agent란?

ReAct(Reasoning and Acting)는 대규모 언어 모델(LLM)이 추론(Reasoning)행동(Acting)을 결합하여 문제를 해결하는 패턴이다.

1.1 ReAct의 핵심 개념

전통적인 방식의 한계: - Chain-of-Thought: 추론만 수행 (외부 정보 접근 불가) - Tool-only: 도구 사용만 수행 (추론 과정 부재)

ReAct의 장점: - 추론 + 행동: 각 단계에서 “왜 이 행동을 하는가?”를 생각하고 실행 - 반복적 개선: 결과를 보고 다음 행동을 결정 - 투명성: 추론 과정이 명시적으로 드러남

1.2 ReAct 작동 원리

1. Thought (생각): "이 문제를 해결하려면 최신 정보가 필요하다"
2. Action (행동): 웹 검색 도구 실행
3. Observation (관찰): 검색 결과 확인
4. Thought (생각): "이 정보로 답변할 수 있다"
5. Final Answer: 최종 답변 생성

1.3 LangGraph의 ReAct 구현

LangGraph의 create_react_agent는 ReAct 패턴을 쉽게 구현할 수 있게 해주는 사전 구축된 에이전트다.

주요 특징: - 자동 루프: Thought → Action → Observation 반복 - 메모리 지원: 이전 대화 기억 - 멀티 도구: 여러 도구를 동시에 사용 가능 - 스트리밍: 실시간 응답 생성

# API 키를 환경변수로 관리하기 위한 설정 파일
from dotenv import load_dotenv

# API 키 정보 로드
load_dotenv()
# LangSmith 추적을 설정한다. https://smith.langchain.com
# !pip install -qU langchain-teddynote
from langchain_teddynote import logging

# 프로젝트 이름을 입력한다.
logging.langsmith("CH15-React-Agent")

2 환경 설정

2.1 기본 구성 요소

ReAct Agent는 세 가지 핵심 요소가 필요하다:

  1. LLM 모델: 추론을 담당
  2. 도구(Tools): 외부 작업 수행
  3. 메모리(Memory): 대화 이력 저장

ReAct Agent 아키텍처
from langchain_openai import ChatOpenAI
from langchain_teddynote.tools.tavily import TavilySearch
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent

# 메모리 설정 (대화 이력 저장)
memory = MemorySaver()

# 모델 설정 (GPT-4o-mini 사용)
model = ChatOpenAI(model_name="gpt-4o-mini")

MemorySaver: - 대화 컨텍스트를 메모리에 저장 - 이전 대화를 기억하여 연속적인 대화 가능 - Thread ID로 세션 관리

3 도구 설정

ReAct Agent는 다양한 도구를 사용하여 복잡한 작업을 수행한다.

3.1 웹 검색 도구

Tavily Search: 실시간 웹 검색을 위한 도구

from langchain_teddynote.tools.tavily import TavilySearch

# 웹 검색 도구 생성
web_search = TavilySearch(
    topic="general",  # 뉴스 주제 (general 또는 news)
    max_results=5,  # 최대 검색 결과 수
    include_answer=False,  # Tavily의 답변 포함 여부
    include_raw_content=False,  # 원본 HTML 포함 여부
    include_images=False,  # 이미지 포함 여부
    format_output=False,  # 결과 포맷팅 여부
)

# 도구 이름 및 설명 설정 (LLM이 도구를 선택할 때 사용)
web_search.name = "web_search"
web_search.description = (
    "Use this tool to search on the web for any topic other than news."
)

웹 검색 테스트:

result = web_search.search("SK AI SUMMIT 2024 관련된 정보를 찾아줘")
print(result)

왜 웹 검색이 필요한가? - LLM의 지식 컷오프 날짜 이후의 최신 정보 획득 - 실시간 데이터 (뉴스, 주가, 날씨 등) 조회 - 팩트 체크 및 검증

3.2 파일 관리 도구

FileManagementToolkit: 로컬 파일 시스템 작업

from langchain_community.agent_toolkits import FileManagementToolkit

# 'tmp'라는 이름의 디렉토리를 작업 디렉토리로 설정
working_directory = "tmp"

# FileManagementToolkit 객체를 생성
file_management_tools = FileManagementToolkit(
    root_dir=str(working_directory),
).get_tools()

포함된 도구들:

# 파일 관리 도구 출력
file_management_tools

주요 기능: - write_file: 파일 쓰기 - read_file: 파일 읽기 - list_directory: 디렉토리 목록 조회 - copy_file: 파일 복사 - move_file: 파일 이동 - delete_file: 파일 삭제

사용 예시: 1. 웹에서 정보 수집 2. 보고서 작성 3. 파일로 저장

4 Retriever 도구

PDF 문서 검색: RAG 시스템과 통합

4.1 PDF 문서 로드 및 벡터화

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain.document_loaders import PDFPlumberLoader

# PDF 파일 로드. 파일의 경로 입력
loader = PDFPlumberLoader("data/SPRI_AI_Brief_2023년12월호_F.pdf")

# 텍스트 분할기를 사용하여 문서를 분할
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)

# 문서를 로드하고 분할
split_docs = loader.load_and_split(text_splitter)

# VectorStore를 생성 (FAISS 사용)
vector = FAISS.from_documents(split_docs, OpenAIEmbeddings())

# Retriever를 생성
pdf_retriever = vector.as_retriever()

처리 과정: 1. PDF 로드: PDFPlumberLoader로 텍스트 추출 2. 청크 분할: 1000자 단위로 분할 (100자 오버랩) 3. 임베딩: OpenAI Embeddings로 벡터 변환 4. 벡터 저장: FAISS에 저장 5. Retriever 생성: 유사도 검색 가능한 객체

4.2 Retriever를 도구로 변환

from langchain_core.tools.retriever import create_retriever_tool
from langchain_core.prompts import PromptTemplate

# PDF 문서를 기반으로 검색 도구 생성
retriever_tool = create_retriever_tool(
    pdf_retriever,
    "pdf_retriever",
    "Search and return information about SPRI AI Brief PDF file. It contains useful information on recent AI trends. The document is published on Dec 2023.",
    document_prompt=PromptTemplate.from_template(
        "<document><context>{page_content}</context><metadata><source>{source}</source><page>{page}</page></metadata></document>"
    ),
)

도구 설명 작성 팁: - LLM이 언제 이 도구를 사용할지 명확히 기술 - 문서 내용 및 출판 날짜 명시 - 검색 가능한 주제 힌트 제공

4.3 전체 도구 목록

# 도구 목록을 정의
tools = [web_search, *file_management_tools, retriever_tool]
tools

도구 조합의 장점: - 웹 검색: 최신 정보 - 파일 관리: 결과 저장 및 관리 - PDF 검색: 내부 문서 지식

5 에이전트 생성

5.1 create_react_agent

LangGraph의 사전 구축된 ReAct Agent를 생성한다.

from langgraph.prebuilt import create_react_agent

agent_executor = create_react_agent(model, tools, checkpointer=memory)

파라미터: - model: 사용할 LLM - tools: 도구 목록 - checkpointer: 메모리 저장소 (대화 이력)

내부 동작: 1. 사용자 입력 받기 2. LLM이 추론 (어떤 도구를 사용할지) 3. 도구 실행 4. 결과 관찰 5. 다음 행동 결정 (반복) 6. 최종 답변 생성

5.2 에이전트 시각화

from langchain_teddynote.graphs import visualize_graph

visualize_graph(agent_executor)

그래프 구조: - agent 노드: LLM이 추론 및 도구 선택 - tools 노드: 선택된 도구 실행 - 순환 구조: agent ↔︎ tools 반복

6 실행 함수 정의

6.1 스트리밍 출력

실시간으로 에이전트의 사고 과정을 확인할 수 있다.

from langchain_teddynote.messages import stream_graph

stream_graph 함수: - 에이전트 실행을 스트리밍으로 출력 - 각 노드의 출력을 실시간 확인 - 디버깅 및 모니터링에 유용

7 사용 예시

7.1 예시 1: 기본 대화 (메모리 테스트)

# Config 설정 (Thread ID로 세션 관리)
config = {"configurable": {"thread_id": "abc123"}}
inputs = {"messages": [("human", "안녕? 내 이름은 테디야")]}

# 그래프 스트림
stream_graph(agent_executor, inputs, config, node_names=["agent"])

메모리 확인:

config = {"configurable": {"thread_id": "abc123"}}
inputs = {"messages": [("human", "내 이름이 뭐라고?")]}

# 그래프 스트림
stream_graph(agent_executor, inputs, config, node_names=["agent"])

기대 결과: - “테디”라고 기억하고 답변 - 이전 대화를 참조하여 연속적인 대화

7.2 예시 2: PDF 문서 검색

config = {"configurable": {"thread_id": "abc123"}}
inputs = {
    "messages": [
        ("human", "AI Brief 보고서에서 Anthropic 투자 관련된 정보를 요약해줘.")
    ]
}
stream_graph(agent_executor, inputs, config, node_names=["agent", "tools"])

실행 흐름: 1. Thought: “PDF 문서를 검색해야겠다” 2. Action: pdf_retriever 실행 3. Observation: Anthropic 투자 관련 문서 청크 반환 4. Thought: “이 정보로 요약을 작성할 수 있다” 5. Final Answer: 요약 생성

7.3 예시 3: 웹 검색

config = {"configurable": {"thread_id": "abc123"}}
inputs = {
    "messages": [
        (
            "human",
            "한강 작가의 노벨상 수상 관련된 뉴스를 검색하고 보고서 형식에 맞게 작성해줘",
        )
    ]
}
stream_graph(agent_executor, inputs, config, node_names=["agent", "tools"])

실행 흐름: 1. Thought: “최신 뉴스를 검색해야 한다” 2. Action: web_search 실행 3. Observation: 검색 결과 확인 4. Thought: “보고서 형식으로 정리해야 한다” 5. Final Answer: 보고서 작성

7.4 예시 4: 복합 작업 (검색 + 작성 + 저장)

instruction = """
당신의 임무는 `보도자료`를 작성하는 것이다.
----
다음의 내용을 순서대로 처리해 주세요.
1. `한강 작가의 노벨상 수상` 관련된 뉴스를 검색해 주세요.
2. 노벨상 수상 관련 뉴스를 바탕으로 보고서 / 보드자료 작성해 주세요.
3. 단, 중간에 요점 정리를 위한 markdown 테이블 형식 요약을 적극 활용해 주세요.
4. 출력 결과를 파일로 저장해 주세요. (파일 이름은 "agent_press_release.md")
"""
config = {"configurable": {"thread_id": "abc123"}}
inputs = {"messages": [("human", instruction)]}
stream_graph(agent_executor, inputs, config, node_names=["agent", "tools"])

실행 흐름: 1. 웹 검색: web_search 도구로 최신 뉴스 검색 2. 보고서 작성: 검색 결과를 바탕으로 markdown 보고서 작성 3. 파일 저장: write_file 도구로 파일 저장

멀티 도구 사용: - 하나의 작업에서 여러 도구를 순차적으로 사용 - 각 도구의 결과를 다음 단계의 입력으로 활용 - 복잡한 워크플로우를 자동화

8 ReAct Agent 활용 팁

8.1 도구 선택 최적화

명확한 도구 설명 작성:

tool.name = "specific_tool_name"
tool.description = "Use this tool when [specific condition]. It returns [output format]."

도구 우선순위 힌트: - 시스템 프롬프트에 도구 사용 가이드 추가 - 도구 설명에 사용 시기 명시

8.2 에러 처리

일반적인 문제: 1. 무한 루프: 최대 반복 횟수 설정 2. 잘못된 도구 선택: 도구 설명 개선 3. 메모리 부족: Thread ID 정리

해결 방법:

agent_executor = create_react_agent(
    model, 
    tools, 
    checkpointer=memory,
    # 최대 반복 횟수 제한
    max_iterations=10,
    # 에러 발생 시 계속 진행
    handle_parsing_errors=True
)

8.3 성능 최적화

모델 선택: - 빠른 응답: GPT-4o-mini - 복잡한 추론: GPT-4o

도구 개수: - 너무 많은 도구 → 선택 혼란 - 권장: 3-7개 도구

메모리 관리: - 긴 대화 → 토큰 소비 증가 - 주기적으로 Thread 초기화

9 ReAct vs 다른 패턴

패턴 추론 도구 사용 반복 투명성
ReAct 높음
Chain-of-Thought 높음
Tool-only 낮음
Plan-and-Execute 제한적 중간

ReAct의 장점: - 각 단계에서 “왜”를 설명 - 실패 시 재시도 가능 - 디버깅이 쉬움

단점: - 토큰 소비가 많음 - 느린 실행 속도

10 참고 자료

11 다음 단계

ReAct Agent의 기본을 익혔다면, 다음 주제들을 살펴보자:

  • 커스텀 도구 개발: 특화된 도구 만들기
  • Plan-and-Execute Agent: 더 복잡한 작업 계획
  • Multi-Agent 시스템: 여러 에이전트 협업

Subscribe

Enjoy this blog? Get notified of new posts by email: