3. Agent Basic

벼리·2026년 8월 9일

AI

목록 보기
2/2

1. Agent

Agent란?

  • 사용자의 입력(질문, 지시)에 대해 적절한 행동을 취하도록 추론(Reasoning)한 뒤 행동(Act)하는 주체(Excutor)
  • 구성요소
    • Model(LLM)
    • Tools(함수)

Tool 기초

  • LLM 모델은 학습한 시점이 고정적 → Model 버전보다 미래의 날씨는 답변 못함
  • → 이러한 LLM이 가진 한계를 극복할 수 있도록 도와주는 것이 Tool
    • Model이 직접 날씨를 검색하는 Tool을 호출할 수 있음

Tool을 어떻게 랭체인 환경에서 구현할 수 있을까?

Tool이란?

  • 모델(LLM)이 외부 시스템(API, DB, 파일 시스템 등)과 상호작용하기 위해 사용하는 요소
  • 구성 요소
    • 호출 가능한 함수(callable function)
    • 입력 스키마(input schema)
      • 코드 예시
        import tool
        from pydantic import BaseModel, Field
        
        class WeatherInput(BaseModel):
            location: str = Field(description="날씨를 조회할 도시와 국가")
            unit: str = Field(default="celsius", description="온도 단위 (celsius 또는 fahrenheit)")
        
        @tool("get_current_weather", args_schema=WeatherInput)
        def get_current_weather(location: str, unit: str = "celsius") -> str:
            """주어진 도시의 현재 날씨 정보를 조회합니다."""
            # 도구 실행 로직 작성
            return f"{location}의 날씨는 맑음입니다."
        
  • Tool 작성법
    • 함수 작성
    • 타입 힌팅
    • docstring으로 함수(Tool) 설명
    • @tool 데코레이터 입력
      • Agent가 쓸 수 있는 Tool로 자동 전환됨
from langchain.tools import tool

@tool
def get_weather(location : str) -> str :
  "이 도구는 특정 지역의 날씨 정보를 반환함."
  return f"오늘 {location} 날씨는 비가 옵니다."

LLM + Tool ⇒ Agent

Tool - Model 연결 (Agent 객체 생성)

  • langchain.agents의 create_agent를 사용해 모델과 tools을 전달하면 Agent 생성
from langchain.agents import create_agent

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

  • invoke() 메서드로 Agent 실행
  • messages에 담긴 리스트 속 마지막 content(result[”messages”][-1].content)에 최종 응답 확인
agent.invoke(
    {"messages": [{"role": "user", "content": "서울 날씨 어때요?"}]},
)

응답(result)는 어떻게 생성?

tool_calls 호출 → get_weather 형식대로 ToolMessage가 반환됨 → AIMessage 다시 호출

{
	"messages": [
		HumanMessage(content='서울 날씨는 어때요?'),
		AIMessage(content='', tool_calls[///]),
		ToolMessage(content='서울 날씨는...'),
		AIMessage(content='서울 현재 날씨는...')
}
  • Model이 content를 생성하지 않고, tool_calls로 get_weather 도구 호출
    • AI 모델이 스스로 추론해서 행동한것
    • 사용자의 응답을 통해 입력 파라미터를 직접 작성
  • 도구는 응답을 ToolMessage에 담아 응답
  • Model은 도구의 응답을 참조해 답변 생성

Tool 응용

Agent가 도구를 호출하여 사용할 수 있도록 SystemMessage를 부여해보자!

  • SystemMessage 부여 이전
    • 언어 모델은 확률 기반으로 작동함
    • 하나씩 단어를 선택해서 최종적으로 답변을 만들어줌
    • 확률에 따라서 직접 content를 작성할 때도 있고, tool_calling을 할 때도 있음
    • 추론 결과, 도구 호출 없이도 답변을 작성해도 되겠다고 판단한거임
      • agent가 반드시 tool을 활용하면 좋겠다 → 시스템 메시지 주입하면됨
tools = [add, multiply, divide]
agent = create_agent(
    model=model,
    tools = tools
)
result = agent.invoke(
    {"messages": [{"role": "user", "content": "42 + 3 * 23은 뭔가요?"}]},
)
# HumanMessage -> AIMessage 답변 바로 출력
  • SystemMessage 부여
agent = create_agent(
    model,
    tools,
    system_prompt="너는 사칙연산할 때 무조건 도구를 호출해라"
)
result = agent.invoke(
    {"messages": [{"role": "user", "content": "42 + 3 * 23은 뭔가요?"}]},
)

# HumanMessage -> AIMessage(tool_calls: 곱하기) -> ToolMessage(결과) -> AIMessage(tool_calls: 더하기) -> ToolMessage -> AIMessage(결과)

API를 사용한 Tool을 만들어 더 강력한 Agent 만들어보기

→ 알라딘 베스트셀러 Top10 파이썬 함수를 Tool로 넣을 수 있음

Memory

  • 다중 턴 대화시 대화 맥락을 고려한 Tool 호출을 위해 메모리 관리 필요

  • 메모리 종류

    • Short-term memory: Checkpointer를 통해 대화스레드(세션) 저장
      • Checkpointer: 스레드(대화 상태)를 저장할 저장소

        • thread_id를 바꾸면 새로운 대화 스레드 생성
      • checkpointer=InMemoryServer() → 기억용 수첩과 펜을 Agent에게 쥐여주기

      • response = agent.invoke(
            {"messages": [{"role": "user", "content": "안녕하세요, 저는 벼리입니다."}]},
            {"configurable": {"thread_id": "1"}} # thread id에 해당하는 저장공간에 대화를 저장함
        )

    • Long-term memory: Store를 통해 장기 메모리 저장

Middleware

: Agent 내부에서 일어나는 일을 섬세하게 컨트롤하는 방법

Middleware 기능

  • Agent 내부 활동 모니터링컨트롤
  • 프롬프트, tool 선택, 출력 포맷 수정
  • 가드레일(개인정보 인식, 프롬프트 인젝션 방지)
  • 종류
    • Built-in middleware
    • Custom middleware

Built-in middleware

랭체인에서 제공하는 middleware: https://docs.langchain.com/oss/python/langchain/middleware/built-in

  • 예시
    • 대화 요약
    • 사용자 개입
    • 개인정보 탐지
    • 할 일 목록
    • 도구 재시도
    • 기타 등등
from langchain.tools import 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"✅ 이메일이 성공적으로 조회되었습니다."
    
    
agent = create_agent(
    model="gpt-5-nano",
    tools=[send_email_tool, read_email_tool],
    middleware=[
        LLMToolEmulator(
            model="gpt-5-nano",
        ),
    ],
)

response = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "최근 온 메일 확인하고 알아서 답장해줘."}
        ]
    }
)

작동 순서

  1. HumanMessage: 최근 온 메일 …
  2. AIMessage: tool_calls
    • LLMToolEmulator가 read_email_tool 을 흉내내서 답변을 가지고옴
    • 스스로 리턴값을 가져옴

LLM tool emulator(LLM기반 tool 흉내내기)

  • tool 응답을 LLM이 대신 생성하는 미들웨어
  • → Tool 호출이 불가능한 경우, 또는 프로토타입의 경우 활용

Todo list(할 일 목록)

  • 복잡한 요청을 처리할 때 작업 단계를 스스로 계획하고 추적하는 미들웨어
agent = create_agent(
    model="gpt-5-nano",
    tools=[send_email_tool, read_email_tool],
    middleware=[
        LLMToolEmulator(model="gpt-5-nano"),
        TodoListMiddleware(),
    ],
)

response = agent.invoke(
    {
        "messages": [
            {"role": "user", "content": "최근 온 메일 확인하고 나한테 요약해서 보고해줘. 그 다음 답장을 작성해서 회신해줘. 마지막으로 어떻게 보냈는지 보고하고"}
        ]
    }
)

작동 순서

  1. HumanMessage: 최근 온 메일 …
  2. AIMessage: tool_calls
    • write_todos = 우리가 입력한 요청값을 변환해서 메서드 파라미터로 넘김
  3. ToolMessage
    • 해야할 일 목록이 반환/갱신
    • step1 - 최신 메일 3개 확인 status: Pending
    • stpe2 - 요약
    • step3 - 주요 포인트 요약 작성
    • ….
  4. AIMessage: tool_calls
    • LLMToolEmulator가 read_email_tool 을 흉내내서 답변을 가지고옴
    • todo list의 업무 하나가 끝남
  5. step이 끝날때까지 2~4를 반복
    • 끝나면 각 목록의 상태를 갱신함
      • pending → Complete

Human-in-the-loop

  • 작업 전 사용자의 의사를 묻는 미들웨어
    → 고도화된 업무에 적합
  • 반드시 checkpointer와 함께 사용
    • 이전 대화를 기억해야 결과를 만들 수 있기 때문
  • allow_decisions 옵션
    • approve
    • edit
    • reject
from langchain.agents.middleware import LLMToolEmulator, HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver

checkpointer = InMemorySaver()

agent = create_agent(
    model,
    tools=[send_email_tool, read_email_tool],
    # 체크포인터 연결
    checkpointer=checkpointer,
    middleware=[
        LLMToolEmulator(model="gpt-5-nano"),
        HumanInTheLoopMiddleware(
            interrupt_on= {
                "send_email_tool": {
                    "allowed_decisions": ["approve", "edit", "reject"]
                },
                "read_email_tool" : False
            }
        )
    ],
)

prompt = "무슨 메일 왔는지 확인해줘"

response = agent.invoke(
    {"messages": [{"role": "user", "content": prompt}]},
    {"configurable": {"thread_id": "HIL-a"}}
)

PII detection(개인정보 탐지)

  • PII: Personally Indentifiable Information
    → 의료, 헬스케어, 금융 등 민감 정보를 다루는 Agent 구축 시 활용
  • pii_type
    • built-in: email, credit_card, ip, mac_address, url…
    • custom type
  • 블록 방식
    • block: 에러 발생
    • redact: 완전 마스킹
    • mask: 부분 마스킹
    • hash: 해싱
  • 적용 시점
    • model 호출 전
    • model 호출 이후
    • tool의 응답 메시지에 대한 블록

!image.png

apply_to_input을 설정하면, before_agent 단에서 마스킹 처리됨

agent = create_agent(
    model,
    tools=[save_customer_feedback],
    middleware=[
        LLMToolEmulator(model="gpt-5-nano"),
        # 이메일 주소는 전부 마스킹 처리
        PIIMiddleware("email", strategy="redact",apply_to_input=True),

        # 카드번호는 마지막 4자리만 남기고 나머지 마스킹 처리
        PIIMiddleware("credit_card", strategy="mask",apply_to_input=True) 

    ],
)

Summarization(대화 요약)

  • model: 요약 작업을 수행할 대상 LLM 모델 지정(필수)
  • trigger: 메시지 개수나 토큰 수 등 요약 기능이 실행될 ‘발동 조건’ 설정
  • keep: 요약 실행 후에도 문맥 유지를 위해 원본 그대로 남겨둘 ‘최신 대화’ 범위
  • summary_prompt: 기본 템플릿 대신 구체적인 요약 스타일을 지시할 ‘커스텀 프롬프트’
  • trim_tokens_to_summarize: 요약 모델에 입력할 대화 내용이 너무 길지 않도록 제한하는 ‘최대 토큰 수’
  • 쌓인 대화가 너무 많으면 오히려 AI 성능이 떨어질 수 있음 → 대화 요약 필요

Custom middleware

  • 개념
    • Agent가 작동하는 특정 순간 개입(hook)하도록 직접 미들웨어 개발 가능
  • 방식
    • 데코레이터 방식: 단일 훅을 간단하게 구현할 때 적합
    • 클래스 방식: 여러 훅을 포함한 복잡한 미들웨어 구현 시 적합

Structured Outputs

  • 다음 작업을 위해 답변을 특정 형태로 파싱할 때 사용
    • 스키마(class) 생성
    • model에 with_structured_output을 사용해 새로운 모델 객체 생성
  • Agent의 실행 결과를 자연어가 아닌, 특정 포멧의 구조화된 데이터로 파싱 가능
    • Agent가 의도, 감정 상태, 요약, 다음 업무 수행 등을 분석 후 구조에 맞춰서 데이터 생성 가능
    • 자연어일 때보다 데이터 저장/관리 용이
    • 후작업이 필요한 경우 사용
profile
코딩일기

0개의 댓글