lanchain - agent (tools, Prompt)

HanJu Han·2025년 11월 12일

1. Tools (도구)

Agent의 **'손발'**입니다. LLM(두뇌)이 실제 세계의 정보를 가져오거나 특정 작업을 수행할 수 있게 해주는 **함수(function)**들입니다.

Agent는 도구를 통해 다음과 같은 강력한 작업을 수행할 수 있습니다.

  • 순차적 호출: 하나의 요청에 대해 여러 도구를 차례로 호출 (예: 1. 검색 -> 2. 재고 확인)
  • 병렬 호출: 필요시 여러 도구를 동시에 호출 (예: 1. 서울 날씨, 2. 부산 날씨 동시 확인)
  • 동적 선택: 이전 도구의 결과(Observation)를 보고 다음에 사용할 도구를 결정

"수식" (도구 정의 방법)

@tool 데코레이터를 파이썬 함수 위에 붙여주기만 하면 됩니다.

from langchain.tools import tool

@tool
def function_name(parameter: type) -> return_type:
    """
    [★매우 중요★] 이 도구가 무엇을 하는지 LLM이 알아볼 수 있게 
    자세하고 명확하게 설명해야 합니다.
    """
    # ... 실제 함수 코드 ...
    return result

가장 중요한 것: """ """ 안에 있는 **설명(docstring)**입니다. LLM(두뇌)은 이 설명을 읽고 "아, 이런 작업이 필요할 땐 이 도구를 써야겠다!"라고 판단합니다.

"수식 예시" (실제 코드)

from langchain.tools import tool
from langchain.agents import create_agent
# (model은 이전에 정의되었다고 가정)

@tool
def search(query: str) -> str:
    """
    최신 정보를 검색할 때 사용합니다. 
    (예: "오늘 뉴스", "인기 제품")
    """
    print(f"DEBUG: search('{query}') 실행")
    return f"'{query}'에 대한 검색 결과입니다: [검색된 내용...]"

@tool
def get_weather(location: str) -> str:
    """
    특정 위치(location)의 현재 날씨를 가져옵니다.
    """
    print(f"DEBUG: get_weather('{location}') 실행")
    return f"{location}의 날씨: 맑음, 25°F"

# Agent를 생성할 때, 이 '손발' 목록을 전달합니다.
tools = [search, get_weather]
agent = create_agent(model, tools=tools)

2. ReAct 루프에서의 도구 사용 (복습)

ReAct = Reasoning (추론) + Acting (행동)

Agent는 "생각하고, 도구를 쓰고, 결과를 보고, 다시 생각하는" 과정을 반복합니다.

예시: "가장 인기 있는 무선 헤드폰을 찾고 재고가 있는지 확인해 줘"

  1. [사용자] "가장 인기 있는 무선 헤드폰 찾고 재고 확인해 줘"
  2. [LLM 추론] "인기 상품은 실시간 정보가 필요하니 search 도구를 써야겠어."
  3. [LLM 행동] search(query="무선 헤드폰") 호출
  4. [도구 결과] "1위: WH-1000XM5, 2위: ..."
  5. [LLM 추론] "1위 제품(WH-1000XM5)을 찾았군. 이제 이 제품의 재고를 확인해야지. (어떤 도구를 쓸까... 아, 재고 확인용 check_inventory 도구가 있네. - 예시에는 없지만 있다고 가정)"
  6. [LLM 행동] check_inventory(product_id="WH-1000XM5") 호출
  7. [도구 결과] "WH-1000XM5: 재고 10개 있음"
  8. [LLM 추론] "인기 상품과 재고를 모두 확인했어. 이제 사용자에게 최종 답변을 하자."
  9. [최종 답변] "가장 인기 있는 무선 헤드폰은 WH-1000XM5이며, 현재 10개 재고가 있습니다."

3. 도구 오류 처리 (Tool Error Handling)

만약 도구가 실행되다가 실패(에러)하면 어떻게 될까요? Agent 전체가 멈출 수 있습니다.

@wrap_tool_call 미들웨어를 사용하면, 도구 실행을 '가로채서' 에러를 처리할 수 있습니다.

"수식 예시" (에러 처리기)

from langchain.agents import create_agent
from langchain.agents.middleware import wrap_tool_call
from langchain_core.messages import ToolMessage
# (tools와 model은 이전에 정의되었다고 가정)

@wrap_tool_calldef handle_tool_errors(request, handler):
    """도구 실행을 시도하고, 실패하면 LLM에게 '실패했다'고 알려줍니다."""
    try:
        # 1. 일단 도구 실행 (handler(request))
        return handler(request)
    except Exception as e:
        # 2. 만약 에러가 발생하면(except), Agent가 멈추는 대신,
        # LLM에게 'ToolMessage' (도구 결과 메시지)로 에러 내용을 전달
        print(f"DEBUG: 도구 오류 발생! {e}")
        return ToolMessage(
            content=f"도구 사용 중 오류 발생: 입력값을 확인해주세요. (오류: {str(e)})",
            tool_call_id=request.tool_call["id"]
        )

# Agent 생성 시, 이 '에러 처리기'를 미들웨어로 등록
agent = create_agent(
    model=model,
    tools=tools,
    middleware=[handle_tool_errors]
)

핵심: handle_tool_errors 덕분에 Agent는 도구 에러가 나도 멈추지 않습니다. LLM(두뇌)은 ToolMessage를 받고 "아, 도구 쓰다 실패했네. 입력을 바꿔서 다시 시도해 볼까?"라고 **다시 추론(Reasoning)**할 기회를 얻게 됩니다.


4. System Prompt (시스템 프롬프트)

Agent의 '기본 지침서' 또는 **'역할(Personality)'**을 정해줍니다.

① 정적 프롬프트 (Static Prompt)

가장 간단한 방법. Agent를 만들 때 고정된 지침을 문자열로 전달합니다.

agent = create_agent(
    model,
    tools,
    system_prompt="당신은 친절한 AI 조수입니다. 모든 답변은 간결하고 정확하게 하세요."
)

② 동적 프롬프트 (Dynamic Prompt)

고급 기능입니다. Agent가 실행되는 상황에 따라 시스템 프롬프트를 바꿔줍니다. @dynamic_prompt 미들웨어를 사용합니다.

예시: 사용자가 '전문가'인지 '초보자'인지에 따라 답변 스타일 바꾸기

from typing import TypedDict
from langchain.agents.middleware import dynamic_prompt, ModelRequest

class Context(TypedDict):
    user_role: str  # 'expert' 또는 'beginner'

@dynamic_prompt
def user_role_prompt(request: ModelRequest) -> str:
    """사용자 역할(context)에 따라 동적으로 프롬프트를 생성합니다."""
    # 1. 현재 실행의 'context'에서 user_role 값을 가져옴 (기본값 'user')
    user_role = request.runtime.context.get("user_role", "user")
    
    base_prompt = "당신은 친절한 AI 조수입니다."
    if user_role == "expert":
        return f"{base_prompt} 기술 용어를 사용하여 자세하게 설명해주세요."
    elif user_role == "beginner":
        return f"{base_prompt} 전문 용어는 피하고, 쉽게 비유를 들어 설명해주세요."
    return base_prompt

agent = create_agent(
    model=model,
    tools=tools,
    middleware=[user_role_prompt], # 💡 동적 프롬프트 미들웨어 등록
    context_schema=Context         # 💡 context로 이런 걸 받을 거라고 알려줌
)

# 2. 실행(invoke)할 때 'context'를 함께 전달
result = agent.invoke(
    {"messages": [{"role": "user", "content": "머신러닝이 뭐야?"}]},
    context={"user_role": "expert"} # 💡 'expert'로 실행
)
# -> 이 Agent는 이제 '전문가' 모드로 답변합니다.
profile
시리즈를 기반으로 작성하였습니다.

0개의 댓글