langchain / / 2025. 11. 8. 21:06

[LangChain v1.0] Agents

출처

https://docs.langchain.com/oss/python/langchain/agents


에이전트는 언어 모델과 도구(tools)를 결합하여 작업에 대해 추론하고, 사용할 도구를 결정하며, 반복적으로 솔루션을 향해 작업할 수 있는 시스템을 만듭니다. create_agent는 프로덕션 준비가 완료된 에이전트 구현을 제공합니다.

에이전트는 중지 조건이 충족될 때까지 실행됩니다 - 즉, 모델이 최종 출력을 내보내거나 반복 제한에 도달할 때까지입니다.

create_agentLangGraph그래프 기반 아키텍처를 사용하여 에이전트 런타임을 구축합니다. 그래프는 노드(단계)와 에지(연결)로 구성되어 에이전트가 정보를 처리하는 방법을 정의합니다. 에이전트는 이 그래프를 이동하며, 모델 노드(모델을 호출), 도구 노드(도구를 실행) 또는 미들웨어와 같은 노드를 실행합니다.

Graph API에 대해 자세히 알아보기.

핵심 구성 요소

Model

모델은 에이전트의 추론 엔진입니다. 정적 및 동적 모델 선택을 모두 지원하는 여러 방식으로 지정할 수 있습니다.

정적 모델(Static model)

정적 모델은 에이전트를 생성할 때 한 번 구성되며 실행 중에는 변경되지 않습니다. 이것이 가장 일반적이고 간단한 접근 방식입니다.

모델 식별자 문자열에서 정적 모델을 초기화하려면:

from langchain.agents import create_agent

agent = create_agent("gpt-5", tools=tools)

모델 식별자 문자열은 자동 추론을 지원합니다(예: "gpt-5""openai:gpt-5"로 추론됩니다). 모델 식별자 문자열 매핑의 전체 목록은 참조 문서를 확인하세요.

모델 구성에 대한 더 많은 제어를 위해 provider 패키지를 사용하여 모델 인스턴스를 직접 초기화합니다. 이 예제에서는 ChatOpenAI를 사용합니다. 사용 가능한 다른 채팅 모델 클래스는 Chat models를 참조하세요.

from langchain.agents import create_agent
from langchain_openai import ChatOpenAI

model = ChatOpenAI(
    model="gpt-5",
    temperature=0.1,
    max_tokens=1000,
    timeout=30
    # ... (기타 파라미터)
)

agent = create_agent(model, tools=tools)

모델 인스턴스는 구성에 대한 완전한 제어를 제공합니다. temperature, max_tokens, timeouts, base_url 및 기타 provider별 설정과 같은 특정 파라미터를 설정해야 할 때 사용하세요. 모델에서 사용 가능한 매개변수와 메서드를 보려면 참조 문서를 확인하세요.

동적 모델(Dynamic model)

동적 모델은 현재 상태(state)와 컨텍스트를 기반으로 런타임에 선택됩니다. 이를 통해 정교한 라우팅 로직과 비용 최적화가 가능합니다.

동적 모델을 사용하려면 @wrap_model_call 데코레이터를 사용하여 요청의 모델을 수정하는 미들웨어를 생성합니다:

from langchain_openai import ChatOpenAI
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse

basic_model = ChatOpenAI(model="gpt-4o-mini")
advanced_model = ChatOpenAI(model="gpt-4o")

@wrap_model_call
def dynamic_model_selection(request: ModelRequest, handler) -> ModelResponse:
    """대화 복잡도에 따라 모델 선택."""
    message_count = len(request.state["messages"])

    if message_count > 10:
        # 긴 대화에는 고급 모델 사용
        model = advanced_model
    else:
        model = basic_model

    request.model = model
    return handler(request)

agent = create_agent(
    model=basic_model,  # 기본 모델
    tools=tools,
    middleware=[dynamic_model_selection]
)

참고: 사전 바인딩된 모델(bind_tools가 이미 호출된 모델)은 구조화된 출력을 사용할 때 지원되지 않습니다. 구조화된 출력과 함께 동적 모델 선택이 필요한 경우 미들웨어에 전달되는 모델이 사전 바인딩되지 않았는지 확인하세요.

모델 구성 세부 정보는 Models를 참조하세요. 동적 모델 선택 패턴은 미들웨어의 동적 모델을 참조하세요.

Tools

도구는 에이전트에게 작업을 수행할 수 있는 능력을 부여합니다. 에이전트는 단순한 모델 전용 도구 바인딩을 넘어 다음을 용이하게 합니다:

  • 순차적으로 여러 도구 호출(단일 프롬프트에 의해 트리거됨)
  • 적절한 경우 병렬 도구 호출
  • 이전 결과를 기반으로 한 동적 도구 선택
  • 도구 재시도 로직 및 오류 처리
  • 도구 호출 간 상태 지속성

자세한 내용은 Tools를 참조하세요.

도구 정의하기

에이전트에 도구 목록을 전달합니다.

from langchain.tools import tool
from langchain.agents import create_agent

@tool
def search(query: str) -> str:
    """정보를 검색합니다."""
    return f"Results for: {query}"

@tool
def get_weather(location: str) -> str:
    """위치에 대한 날씨 정보를 가져옵니다."""
    return f"Weather in {location}: Sunny, 72°F"

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

빈 도구 목록이 제공되면 에이전트는 도구 호출 기능 없이 단일 LLM 노드로 구성됩니다.

도구 오류 처리

도구 오류 처리 방법을 사용자 정의하려면 @wrap_tool_call 데코레이터를 사용하여 미들웨어를 생성합니다:

from langchain.agents import create_agent
from langchain.agents.middleware import wrap_tool_call
from langchain_core.messages import ToolMessage

@wrap_tool_call
def handle_tool_errors(request, handler):
    """사용자 정의 메시지로 도구 실행 오류를 처리합니다."""
    try:
        return handler(request)
    except Exception as e:
        # 모델에 사용자 정의 오류 메시지를 반환합니다
        return ToolMessage(
            content=f"Tool error: Please check your input and try again. ({str(e)})",
            tool_call_id=request.tool_call["id"]
        )

agent = create_agent(
    model="gpt-4o",
    tools=[search, get_weather],
    middleware=[handle_tool_errors]
)

도구가 실패하면 에이전트는 사용자 정의 오류 메시지가 포함된 ToolMessage를 반환합니다:

[
    ...
    ToolMessage(
        content="Tool error: Please check your input and try again. (division by zero)",
        tool_call_id="..."
    ),
    ...
]

ReAct 루프에서의 도구 사용

에이전트는 ReAct("Reasoning + Acting") 패턴을 따르며, 간단한 추론 단계와 타겟팅된 도구 호출을 번갈아 가며 최종 답변을 제공할 수 있을 때까지 결과 관찰을 후속 결정에 반영합니다.

ReAct 루프 예시

프롬프트: 현재 가장 인기 있는 무선 헤드폰을 식별하고 재고 여부를 확인하세요.

================================ Human Message =================================
Find the most popular wireless headphones right now and check if they're in stock

추론(Reasoning): "인기는 시간에 민감하므로 제공된 검색 도구를 사용해야 합니다."
행동(Acting): search_products("wireless headphones") 호출

================================== Ai Message ==================================
Tool Calls:
  search_products (call_abc123)
Call ID: call_abc123
  Args:
    query: wireless headphones
================================= Tool Message =================================
Found 5 products matching "wireless headphones". Top 5 results: WH-1000XM5, ...

추론: "답변하기 전에 상위 순위 항목의 재고 여부를 확인해야 합니다."
행동: check_inventory("WH-1000XM5") 호출

================================== Ai Message ==================================
Tool Calls:
  check_inventory (call_def456)
Call ID: call_def456
  Args:
    product_id: WH-1000XM5
================================= Tool Message =================================
Product WH-1000XM5: 10 units in stock

추론: "가장 인기 있는 모델과 재고 상태를 확인했습니다. 이제 사용자의 질문에 답변할 수 있습니다."
행동: 최종 답변 생성

================================== Ai Message ==================================
I found wireless headphones (model WH-1000XM5) with 10 units in stock...

도구에 대해 자세히 알아보려면 Tools를 참조하세요.

System prompt

프롬프트를 제공하여 에이전트가 작업에 접근하는 방식을 형성할 수 있습니다. system_prompt 매개변수는 문자열로 제공할 수 있습니다:

agent = create_agent(
    model,
    tools,
    system_prompt="You are a helpful assistant. Be concise and accurate."
)

system_prompt가 제공되지 않으면 에이전트는 메시지에서 직접 작업을 추론합니다.

동적 시스템 프롬프트

런타임 컨텍스트나 에이전트 상태를 기반으로 시스템 프롬프트를 수정해야 하는 고급 사용 사례의 경우 미들웨어를 사용할 수 있습니다.

@dynamic_prompt 데코레이터는 모델 요청을 기반으로 시스템 프롬프트를 동적으로 생성하는 미들웨어를 만듭니다:

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

class Context(TypedDict):
    user_role: str

@dynamic_prompt
def user_role_prompt(request: ModelRequest) -> str:
    """사용자 역할에 따라 시스템 프롬프트를 생성합니다."""
    user_role = request.runtime.context.get("user_role", "user")
    base_prompt = "You are a helpful assistant."

    if user_role == "expert":
        return f"{base_prompt} Provide detailed technical responses."
    elif user_role == "beginner":
        return f"{base_prompt} Explain concepts simply and avoid jargon."

    return base_prompt

agent = create_agent(
    model="gpt-4o",
    tools=[web_search],
    middleware=[user_role_prompt],
    context_schema=Context
)

# 시스템 프롬프트는 컨텍스트를 기반으로 동적으로 설정됩니다
result = agent.invoke(
    {"messages": [{"role": "user", "content": "Explain machine learning"}]},
    context={"user_role": "expert"}
)

메시지 유형 및 형식에 대한 자세한 내용은 Messages를 참조하세요. 포괄적인 미들웨어 문서는 Middleware를 참조하세요.

Invocation

상태(State)에 업데이트를 전달하여 에이전트를 호출할 수 있습니다. 모든 에이전트는 상태에 메시지 시퀀스를 포함합니다. 에이전트를 호출하려면 새 메시지를 전달합니다:

result = agent.invoke(
    {"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]}
)

에이전트에서 단계 및/또는 토큰을 스트리밍하려면 streaming 가이드를 참조하세요.

그 외에 에이전트는 LangGraph Graph API를 따르며 관련된 모든 메서드를 지원합니다.

고급 개념

Structured output

일부 상황에서는 에이전트가 특정 형식의 출력을 반환하도록 하고 싶을 수 있습니다. LangChain은 response_format 매개변수를 통해 구조화된 출력을 위한 전략을 제공합니다.

ToolStrategy

ToolStrategy는 가상의 도구 호출을 사용하여 구조화된 출력을 생성합니다. 이것은 도구 호출을 지원하는 모든 모델에서 작동합니다:

from pydantic import BaseModel
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

class ContactInfo(BaseModel):
    name: str
    email: str
    phone: str

agent = create_agent(
    model="gpt-4o-mini",
    tools=[search_tool],
    response_format=ToolStrategy(ContactInfo)
)

result = agent.invoke({
    "messages": [{
        "role": "user",
        "content": "Extract contact info from: John Doe, john@example.com, (555) 123-4567"
    }]
})

result["structured_response"]
# ContactInfo(name='John Doe', email='john@example.com', phone='(555) 123-4567')

ProviderStrategy

ProviderStrategy는 모델 공급자의 네이티브 구조화된 출력 생성을 사용합니다. 이것은 더 신뢰할 수 있지만 네이티브 구조화된 출력을 지원하는 공급자에서만 작동합니다(예: OpenAI):

from langchain.agents.structured_output import ProviderStrategy

agent = create_agent(
    model="gpt-4o",
    response_format=ProviderStrategy(ContactInfo)
)

참고: langchain 1.0부터 단순히 스키마를 전달하는 것(예: response_format=ContactInfo)은 더 이상 지원되지 않습니다. ToolStrategy 또는 ProviderStrategy를 명시적으로 사용해야 합니다.

구조화된 출력에 대해 자세히 알아보려면 Structured output을 참조하세요.

Memory

에이전트는 메시지 상태를 통해 대화 기록을 자동으로 유지합니다. 또한 대화 중에 추가 정보를 기억하기 위해 사용자 정의 상태 스키마를 사용하도록 에이전트를 구성할 수도 있습니다.

상태에 저장된 정보는 에이전트의 단기 메모리(short-term memory)로 생각할 수 있습니다:

사용자 정의 상태 스키마는 TypedDictAgentState를 확장해야 합니다.

사용자 정의 상태를 정의하는 두 가지 방법이 있습니다:

  1. 미들웨어를 통해(권장)
  2. create_agentstate_schema를 통해

미들웨어를 통해 사용자 정의 상태를 정의하는 것이 create_agentstate_schema를 통해 정의하는 것보다 선호됩니다. 이를 통해 상태 확장을 관련 미들웨어 및 도구에 개념적으로 범위를 지정할 수 있기 때문입니다.

state_schemacreate_agent의 하위 호환성을 위해 여전히 지원됩니다.

미들웨어를 통한 상태 정의

사용자 정의 상태가 특정 미들웨어 훅 및 해당 미들웨어에 연결된 도구에서 액세스해야 할 때 미들웨어를 사용하여 사용자 정의 상태를 정의합니다.

from langchain.agents import AgentState
from langchain.agents.middleware import AgentMiddleware

class CustomState(AgentState):
    user_preferences: dict

class CustomMiddleware(AgentMiddleware):
    state_schema = CustomState
    tools = [tool1, tool2]

    def before_model(self, state: CustomState, runtime) -> dict[str, Any] | None:
        ...

agent = create_agent(
    model,
    tools=tools,
    middleware=[CustomMiddleware()]
)

# 이제 에이전트는 메시지 이상의 추가 상태를 추적할 수 있습니다
result = agent.invoke({
    "messages": [{"role": "user", "content": "I prefer technical explanations"}],
    "user_preferences": {"style": "technical", "verbosity": "detailed"},
})

state_schema를 통한 상태 정의

도구에서만 사용되는 사용자 정의 상태를 정의하는 단축키로 state_schema 매개변수를 사용합니다.

from langchain.agents import AgentState

class CustomState(AgentState):
    user_preferences: dict

agent = create_agent(
    model,
    tools=[tool1, tool2],
    state_schema=CustomState
)

# 이제 에이전트는 메시지 이상의 추가 상태를 추적할 수 있습니다
result = agent.invoke({
    "messages": [{"role": "user", "content": "I prefer technical explanations"}],
    "user_preferences": {"style": "technical", "verbosity": "detailed"},
})

참고: langchain 1.0부터 사용자 정의 상태 스키마는 반드시 TypedDict 유형이어야 합니다. Pydantic 모델과 데이터클래스는 더 이상 지원되지 않습니다. 자세한 내용은 v1 마이그레이션 가이드를 참조하세요.

메모리에 대해 자세히 알아보려면 Memory를 참조하세요. 세션 간에 지속되는 장기 메모리 구현에 대한 정보는 Long-term memory를 참조하세요.

Streaming

invoke로 에이전트를 호출하여 최종 응답을 얻는 방법을 살펴보았습니다. 에이전트가 여러 단계를 실행하는 경우 시간이 오래 걸릴 수 있습니다. 중간 진행 상황을 표시하려면 발생하는 메시지를 스트리밍할 수 있습니다.

for chunk in agent.stream({
    "messages": [{
        "role": "user",
        "content": "Search for AI news and summarize the findings"
    }]
}, stream_mode="values"):
    # 각 청크에는 해당 시점의 전체 상태가 포함됩니다
    latest_message = chunk["messages"][-1]
    if latest_message.content:
        print(f"Agent: {latest_message.content}")
    elif latest_message.tool_calls:
        print(f"Calling tools: {[tc['name'] for tc in latest_message.tool_calls]}")

스트리밍에 대한 자세한 내용은 Streaming을 참조하세요.

Middleware

미들웨어는 실행의 다양한 단계에서 에이전트 동작을 사용자 정의하기 위한 강력한 확장성을 제공합니다. 미들웨어를 사용하여 다음을 수행할 수 있습니다:

  • 모델이 호출되기 전에 상태를 처리합니다(예: 메시지 트리밍, 컨텍스트 주입)
  • 모델의 응답을 수정하거나 검증합니다(예: 가드레일, 콘텐츠 필터링)
  • 사용자 정의 로직으로 도구 실행 오류를 처리합니다
  • 상태 또는 컨텍스트를 기반으로 동적 모델 선택을 구현합니다
  • 사용자 정의 로깅, 모니터링 또는 분석을 추가합니다

미들웨어는 에이전트의 실행 그래프에 원활하게 통합되어 핵심 에이전트 로직을 변경하지 않고 주요 지점에서 데이터 흐름을 가로채고 수정할 수 있습니다.

@before_model, @after_model, @wrap_tool_call과 같은 데코레이터를 포함한 포괄적인 미들웨어 문서는 Middleware를 참조하세요.


Langchain v1.0

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유