LangChain Agent와 Middleware 실습 가이드

CHZEE93·2026년 2월 9일

AI개발

목록 보기
5/11

Agent란 무엇인가?

Agent(에이전트)는 LLM이 스스로 판단하여 적절한 도구(Tool)를 선택하고 실행한 후, 그 결과를 바탕으로 최종 답변을 생성하는 시스템입니다.

쉽게 말해, LLM에게 "자율성"을 부여하는 것입니다. 사용자의 요청을 분석하고, 필요한 도구를 선택해 작업을 수행한 뒤, 그 결과를 종합하여 답변합니다.

Agent의 동작 흐름

사용자 질문 → Agent가 Tool 선택 → Tool 실행 → 결과 해석 → 최종 답변

Agent에서 Tool(도구)이란?

Tool은 Agent가 사용할 수 있는 "기능" 또는 "함수"입니다. LLM은 학습된 지식만으로는 한계가 있기 때문에, 외부 기능을 도구로 제공하여 실시간 정보 조회, 계산, API 호출 등을 수행할 수 있게 합니다.

Tool의 특징

  • @tool 데코레이터로 일반 함수를 Tool로 변환
  • docstring을 통해 LLM에게 도구의 용도를 설명
  • 타입 힌트로 입력 파라미터를 명확히 정의

1. 환경 설정

!pip install -q -U google-genai
!pip install -U langchain
!pip install -U langchain-google-genai

from langchain.chat_models import init_chat_model
import os

os.environ["GOOGLE_API_KEY"] = "YOUR_API_KEY_HERE"

# 모델 초기화
model = init_chat_model(
    "gemini-2.5-flash",
    model_provider="google_genai"
)

# 기본 테스트
response = model.invoke("현재 한국 날씨 어때?")
print(response.content)

포인트: 일반 LLM은 실시간 정보를 알 수 없습니다. 여기서 Agent와 Tool이 필요해집니다.


2. 첫 번째 Tool 만들기 - 날씨 조회

from langchain.tools import tool

@tool
def get_weather(location: str) -> str:
    """특정 지역의 날씨를 제공하는 Tool"""
    return f"{location}의 날씨는 맑고 영하2도 입니다."

포인트: @tool 데코레이터와 docstring으로 LLM이 이해할 수 있는 도구를 만듭니다.


3. Agent 생성 및 실행

from langchain.agents import create_agent

agent = create_agent(model, tools=[get_weather])

result = agent.invoke(
    {"messages": [{"role": "user", "content": "서울 날씨 어때요?"}]},
)

print(result["messages"][-1].content)

동작 흐름:

Human Message → AI Tool_calls → Tool Message → AI Message

Agent가 자동으로 get_weather 도구를 호출하고 결과를 해석하여 답변합니다.


4. 다중 Tool 사용 - 계산기 Agent

from langchain.tools import tool

@tool
def add(a: int, b: int) -> int:
    """'a'와 'b' 덧셈.
    
    Args:
        a: First number
        b: Second number
    """
    return a + b

@tool
def multiply(a: int, b: int) -> int:
    """'a'와 'b' 곱셈.
    
    Args:
        a: First number
        b: Second number
    """
    return a * b

@tool
def divide(a: int, b: int) -> int:
    """'a'와 'b' 나눗셈.
    
    Args:
        a: First number
        b: Second number
    """
    return a / b

tools = [add, multiply, divide]

agent = create_agent(
    model, 
    tools,
    system_prompt="당신은 유능한 수학 선생님입니다. 사칙 연산 요청 시 모든 단계에서 Tool을 이용하세요"
)

result = agent.invoke(
    {"messages": [{"role": "user", "content": "42 + 3 * 23은 무엇인가요?"}]},
)

print(result["messages"][-1].content)

포인트: system_prompt를 통해 Agent의 행동 방식을 제어할 수 있습니다. 프롬프트가 없으면 Tool을 사용하지 않을 수도 있습니다.


5. 대화 기억하기 - Checkpointer

from langgraph.checkpoint.memory import InMemorySaver

tools = []

agent = create_agent(model, tools, checkpointer=InMemorySaver())

# 첫 번째 대화 (thread_id: t1)
response = agent.invoke(
    {"messages": [{"role": "user", "content": "안녕하세요 저는 치즈입니다"}]},
    {"configurable": {"thread_id": "t1"}}
)
print(response["messages"][-1].content)

# 같은 스레드에서 이어지는 대화
response = agent.invoke(
    {"messages": [{"role": "user", "content": "제 이름이 뭐라고 했죠?"}]},
    {"configurable": {"thread_id": "t1"}}
)
print(response["messages"][-1].content)  # "치즈" 기억함

# 다른 스레드 (thread_id: t2)
response = agent.invoke(
    {"messages": [{"role": "user", "content": "제 이름이 뭐라고 했죠?"}]},
    {"configurable": {"thread_id": "t2"}}
)
print(response["messages"][-1].content)  # 기억 못함

포인트: checkpointer를 사용하면 대화 이력을 저장하고, thread_id로 대화 세션을 구분할 수 있습니다.


6. Middleware - Agent의 강력한 확장 기능

Middleware는 Agent의 동작 과정에 끼어들어 추가 기능을 제공하는 레이어입니다.

6.1 LLMToolEmulator - Tool 실행 시뮬레이션

from typing import List, Dict

@tool
def send_email_tool(to: str, subject: str, body: str) -> str:
    """지정한 이메일 주소로 메일을 보내는 도구입니다."""
    return f"이메일 전송 성공. \n수신자: {to}\n제목: {subject}\n내용: {body[:50]}..."

@tool
def read_email_tool(limit: int = 3) -> List[Dict[str, str]]:
    """최근 받은 이메일 3개를 읽는 도구입니다."""
    return f"이메일 조회 성공"

from langchain.agents.middleware import LLMToolEmulator
from langchain_google_genai import ChatGoogleGenerativeAI

emulator_model = ChatGoogleGenerativeAI(model="gemini-2.5-flash")

tools = [send_email_tool, read_email_tool]

agent = create_agent(
    model,
    tools,
    middleware=[LLMToolEmulator(model=emulator_model)],
)

response = agent.invoke(
    {"messages": [{"role": "user", "content": "최근 온 메일 확인해줘"}]}
)

print(response["messages"][-1].content)

포인트: Tool을 실제로 실행하지 않고 LLM이 예상 결과를 시뮬레이션합니다. 테스트나 비용 절감에 유용합니다.


6.2 TodoListMiddleware - 작업 계획 수립

from langchain.agents.middleware import TodoListMiddleware

agent = create_agent(
    model,
    tools,
    middleware=[
        LLMToolEmulator(model=emulator_model),
        TodoListMiddleware()
    ],
)

response = agent.invoke(
    {"messages": [{"role": "user", "content": "온 메일들 확인후 나한테 요약해줘"}]}
)

print(response["messages"][-1].content)

포인트: 복잡한 요청을 여러 단계로 나누어 계획을 세우고 순차적으로 실행합니다.


6.3 HumanInTheLoopMiddleware - 사람의 개입

from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver

checkPointer = InMemorySaver()

agent = create_agent(
    model,
    tools,
    checkpointer=checkPointer,
    middleware=[
        LLMToolEmulator(model=emulator_model),
        HumanInTheLoopMiddleware(
            interrupt_on={
                "send_email_tool": {
                    "allowed_decisions": ["approve", "edit", "reject"]
                },
                "read_email_tool": False
            }
        )
    ]
)

response = agent.invoke(
    {"messages": [{"role": "user", "content": "무슨 메일 왔는지 확인해줘"}]},
    {"configurable": {"thread_id": "HIL-a"}}
)

print(response["messages"][-1].content)

포인트: 특정 Tool 실행 전에 사람의 승인을 받습니다. 중요한 작업(이메일 발송 등)에 필수적입니다.


6.4 기타 유용한 Middleware

LangChain은 다양한 Middleware를 제공합니다:

  • PIIMiddleware: 개인정보(이메일, 주민번호, 카드번호 등)를 자동으로 감지하고 마스킹
  • RetryMiddleware: Tool 실행 실패 시 자동 재시도
  • LoggingMiddleware: Agent의 모든 동작을 로깅
  • CachingMiddleware: Tool 실행 결과를 캐싱하여 성능 향상
  • RateLimitMiddleware: API 호출 속도 제한

이러한 Middleware들을 조합하여 안전하고 효율적인 Agent를 구축할 수 있습니다.


6.5 Middleware 체이닝 - 다중 보안 레이어

from langchain.agents.middleware import PIIMiddleware

agent = create_agent(
    model,
    tools,
    checkpointer=checkPointer,
    middleware=[
        # 1단계: PII 감지 (개인정보 필터링)
        PIIMiddleware("email", strategy="redact", apply_to_input=True),
        PIIMiddleware("credit_card", strategy="mask", apply_to_input=True),
        
        # 2단계: Tool 에뮬레이션
        LLMToolEmulator(model=emulator_model),
        
        # 3단계: 사람의 최종 승인
        HumanInTheLoopMiddleware(
            interrupt_on={
                "send_email_tool": {
                    "allowed_decisions": ["approve", "edit", "reject"]
                },
                "read_email_tool": False
            }
        )
    ]
)

response = agent.invoke(
    {"messages": [{"role": "user", "content": "무슨 메일 왔는지 확인해줘"}]},
    {"configurable": {"thread_id": "HIL-a"}}
)

print(response["messages"][-1].content)

포인트: 여러 Middleware를 체인으로 연결하여 보안, 시뮬레이션, 승인 프로세스를 순차적으로 적용할 수 있습니다.


마무리

이 실습을 통해 배운 내용:

  • Agent의 개념: LLM에 자율성을 부여하는 시스템
  • Tool의 역할: Agent가 사용하는 외부 기능/함수
  • Checkpointer: 대화 이력 관리 및 세션 분리
  • Middleware 활용: LLMToolEmulator, TodoListMiddleware, HumanInTheLoopMiddleware
  • 기타 Middleware: PIIMiddleware, RetryMiddleware, LoggingMiddleware 등
  • Middleware 체이닝: 다중 보안 레이어 구성

LangChain Agent와 Middleware를 조합하면 안전하고 강력한 AI 자동화 시스템을 구축할 수 있습니다

profile
치즈 키우는 사람

0개의 댓글