/AI & 자동화/[심화 가이드] Tool Calling과 Memory를 결합한 ReAct 패턴으로 '행동하는' LLM 에이전트 구축하기
AI & 자동화LLM 에이전트ReAct

[심화 가이드] Tool Calling과 Memory를 결합한 ReAct 패턴으로 '행동하는' LLM 에이전트 구축하기

단순한 챗봇을 넘어 외부 API 호출과 장기 기억을 갖춘 실제 에이전트를 구축하는 방법을 다룹니다. ReAct 패턴의 작동 원리부터, Tool Calling과 Memory를 결합한 완전 자동화 코드 예제까지 실습 위주로 안내합니다.

[심화 가이드] Tool Calling과 Memory를 결합한 ReAct 패턴으로 '행동하는' LLM 에이전트 구축하기

[심화 가이드] Tool Calling과 Memory를 결합한 ReAct 패턴으로 '행동하는' LLM 에이전트 구축하기

LLM의 발전 속도는 경이롭습니다. 이제 LLM은 단순한 텍스트 생성기를 넘어, 마치 소프트웨어처럼 '추론하고 행동하는' 단계에 진입하고 있습니다. 하지만 대부분의 개발자가 처음 접하는 LLM 호출은 여전히 '프롬프트 입력 $\rightarrow$ 텍스트 출력'이라는 단방향의 대화에 머물러 있습니다.

만약 여러분의 서비스가 "오늘 서울의 날씨를 알려주고, 그 정보를 바탕으로 사용자에게 맞춤형 외출 계획을 짜주는" 기능을 해야 한다면, 단순한 챗봇으로는 불가능합니다. 외부 API를 호출해야 하고, 이전 대화 내용을 기억해야 하기 때문입니다.

이 글은 LLM의 이론적 개념을 넘어, 실제 작동하는 복잡한 로직—외부 도구 사용(Tool Calling)과 장기 기억(Memory)—을 결합하여 '진짜' 지능형 에이전트를 구축하는 실질적인 방법론을 코드를 통해 안내합니다.

챗봇을 넘어, '행동하는' 에이전트가 필요한 이유

우리가 원하는 AI는 단순히 질문에 답하는 것이 아니라, **목표(Goal)**를 부여받고 그 목표를 달성하기 위해 필요한 **일련의 단계(Steps)**를 스스로 계획하고 실행하는 주체여야 합니다. 이것이 바로 '에이전트(Agent)'의 정의입니다.

에이전트가 되기 위해 필요한 핵심 역량은 세 가지입니다.

  1. 추론 능력 (Reasoning): 주어진 목표를 달성하기 위해 어떤 순서로 생각해야 하는가? (→ ReAct 패턴)
  2. 행동 능력 (Action): 추론한 내용을 바탕으로 외부 세계와 상호작용할 수 있는가? (→ Tool Calling)
  3. 기억력 (Memory): 과거의 경험이나 외부 지식을 잊지 않고 활용할 수 있는가? (→ Memory Management)

이 세 가지가 유기적으로 결합될 때, 비로소 서비스 레벨의 복잡한 자동화 로직이 완성됩니다.

외부 세계와 연결하는 힘: Tool Calling의 원리와 심화 구현

Tool Calling(또는 Function Calling)은 LLM이 자신이 가진 지식만으로 답하는 것이 아니라, 외부의 특정 함수나 API를 호출하여 최신 정보나 계산 결과를 얻어낼 수 있게 하는 핵심 메커니즘입니다.

LLM에게 "이런 기능을 쓸 수 있어"라고 명시적으로 알려주고, LLM이 스스로 판단하여 "이 기능을 써야겠다"라고 결정하게 만드는 것이 핵심 원리입니다.

단순 프롬프트 vs. 구조화된 Tool Calling 성능 비교

구분단순 프롬프트 기반 호출구조화된 Tool Calling 기반 호출
작동 방식프롬프트에 API 사용법을 텍스트로 설명해야 함.LLM이 정의된 함수 시그니처(Schema)를 기반으로 호출을 결정.
안정성낮음. 복잡한 로직에서 누락되거나 잘못 해석될 위험 높음.매우 높음. 구조화된 JSON/Schema를 통해 호출 파라미터가 강제됨.
신뢰성낮음. LLM의 '환각'에 의존하는 부분이 많음.높음. 실제 코드 실행(Execution)을 거치므로 결과가 검증됨.
적합한 경우단순 질의응답, 창의적 글쓰기데이터 조회, 계산, 외부 시스템 연동 등 실행이 필요한 모든 경우

실습: 커스텀 툴 정의 및 연결

우리가 만들 에이전트가 '환율 조회'라는 기능을 수행한다고 가정하고, 이를 툴로 정의해 보겠습니다.

Python
import json
from typing import List, Dict, Any
# 실제 환경에서는 LangChain의 Tool 클래스를 사용합니다.

def get_exchange_rate(base_currency: str, target_currency: str) -> str:
    """
    주어진 두 통화 간의 실시간 환율을 조회합니다.
    예시: get_exchange_rate("USD", "KRW") 호출 시, 현재 USD 대비 KRW 환율을 반환합니다.
    """
    print(f"--- [SYSTEM LOG] 환율 API 호출 시도: {base_currency} -> {target_currency} ---")
    # 실제로는 외부 API 호출 로직이 들어갑니다.
    if base_currency == "USD" and target_currency == "KRW":
        return json.dumps({"rate": 1350.5, "source": "Mock_API"})
    return json.dumps({"error": "지원하지 않는 통화 조합입니다."})

# 툴 목록 정의 (LLM에게 제공할 도구들의 메타데이터)
available_tools = [
    {
        "name": "get_exchange_rate",
        "description": "두 통화 간의 현재 환율을 조회하는 함수입니다.",
        "parameters": {
            "type": "object",
            "properties": {
                "base_currency": {"type": "string", "description": "기준 통화 (예: USD)"},
                "target_currency": {"type": "string", "description": "목표 통화 (예: KRW)"}
            },
            "required": ["base_currency", "target_currency"]
        }
    }
]

시간의 흐름을 기억하는 에이전트: Memory Management 전략

에이전트가 대화의 맥락을 잃는 순간, 그 지능은 급격히 하락합니다. 기억력 관리는 에이전트의 지속성을 결정합니다.

단기 기억 vs. 장기 기억

  • 단기 기억 (Context Window): 현재 대화창에 남아있는 최근 대화 기록입니다. LLM이 한 번에 처리할 수 있는 토큰 수(Context Window)에 의해 한계가 명확합니다. (예: 최근 10턴의 대화)
  • 장기 기억 (Vector DB): 과거의 모든 대화, 사용자가 업로드한 문서, 외부 지식 베이스를 임베딩하여 벡터 데이터베이스에 저장합니다. 에이전트가 필요할 때 '검색'하여 관련 정보를 가져와 컨텍스트에 주입합니다. (RAG의 핵심)

실제 구현에서는 이 두 가지를 결합합니다. 대화가 길어지면, 가장 오래된 대화는 요약하여 컨텍스트에 넣고, 중요한 정보는 벡터 DB에 저장하여 필요할 때마다 검색(Retrieval)하는 방식이 표준입니다.

🚀 통합 구현: ReAct 패턴과 에러 핸들링

가장 강력한 에이전트들은 ReAct (Reasoning + Acting) 패턴을 따릅니다. 이는 "생각(Thought) $\rightarrow$ 행동(Action) $\rightarrow$ 관찰(Observation) $\rightarrow$ 다음 생각"의 순환 구조를 통해 복잡한 문제를 해결하는 방식입니다.

아래는 이 패턴을 기반으로, 도구 사용(Tool Use)과 에러 처리를 통합한 개념적 흐름입니다.

Python
# --- 가상의 에이전트 프레임워크 ---

def run_agent_cycle(initial_query: str, tools: dict):
    """
    ReAct 패턴을 사용하여 에이전트의 추론 및 행동 사이클을 실행합니다.
    """
    current_state = initial_query
    max_iterations = 5
    
    print(f"--- [START] 초기 쿼리: {initial_query} ---")

    for i in range(max_iterations):
        print(f"\n===== [Iteration {i+1}] =====")
        
        # 1. Thought (추론): 에이전트가 스스로 생각하는 과정
        thought = f"현재 상태 '{current_state}'를 바탕으로, 어떤 도구를 사용해야 할지 추론합니다."
        print(f"[Thought]: {thought}")
        
        # 2. Action (행동): 가장 적절한 도구와 입력값을 결정
        # (실제로는 LLM이 이 부분을 결정함)
        action_name = "search_weather" # 예시로 결정
        action_input = {"city": "서울", "date": "오늘"}
        print(f"[Action]: {action_name} 실행, 입력: {action_input}")

        try:
            # 3. Tool Execution (도구 실행): 실제 함수 호출
            if action_name in tools:
                tool_function = tools[action_name]
                observation = tool_function(action_input)
                
                # 4. Observation (관찰): 도구 실행 결과
                print(f"[Observation]: {observation}")
                
                # 5. 다음 상태 업데이트: 관찰 결과를 다음 추론의 기반으로 사용
                current_state = f"도구 실행 결과: {observation}. 이제 이 정보를 바탕으로 최종 답변을 구성해야 합니다."
            else:
                current_state = "오류: 정의되지 않은 도구입니다."
                break
                
        except Exception as e:
            # 에러 핸들링: 도구 사용 실패 시, 에러 메시지를 다음 추론에 포함
            error_message = f"도구 실행 중 예외 발생: {str(e)}"
            print(f"[Observation]: {error_message}")
            current_state = error_message
            break
            
    print("\n--- [END] 최종 추론 완료 ---")
    return current_state

# --- 도구 정의 (Tool Definition) ---
def search_weather(params: dict) -> str:
    """도시와 날짜를 받아 현재 날씨 정보를 검색합니다."""
    city = params.get("city")
    date = params.get("date")
    if not city:
        raise ValueError("도시 이름은 필수입니다.")
    return f"'{city}'의 '{date}' 날씨는 맑고 기온은 25도입니다. (API 호출 성공)"

def get_user_profile(user_id: str) -> str:
    """사용자 ID를 받아 프로필 정보를 검색합니다."""
    return f"User ID {user_id}의 프로필은 '프리미엄 회원'입니다."

available_tools = {
    "search_weather": search_weather,
    "get_user_profile": get_user_profile
}

# --- 실행 ---
final_result = run_agent_cycle("이번 주말 서울 날씨와 내 프로필을 알려줘.", available_tools)
print(f"\n[최종 결과 요약]: {final_result}")
✦ ✦ ✦
편집 검토 · Editorial Review

이 글은 AI 에이전트가 1차 초안을 작성한 뒤, 사람 편집자가 사실관계·출처·톤과 맥락을 검토하여 발행했습니다. 오류나 부정확한 내용이 확인되면 24시간 이내에 정정합니다.

작성 · Content Reviewer·검토 · 사람 편집자·발행 · 2026년 6월 8일

댓글

불러오는 중...