핸드오프
핸드오프를 사용하면 에이전트가 다른 에이전트에 작업을 위임할 수 있습니다. 이는 서로 다른 에이전트가 각기 다른 영역을 전문적으로 처리하는 시나리오에서 특히 유용합니다. 예를 들어 고객 지원 앱에는 주문 상태, 환불, FAQ 등의 작업을 각각 전문적으로 처리하는 에이전트가 있을 수 있습니다.
핸드오프는 LLM에 도구로 표시됩니다. 따라서 Refund Agent이라는 에이전트로 핸드오프하는 경우 도구 이름은 transfer_to_refund_agent이 됩니다.
핸드오프 생성
모든 에이전트에는 handoffs 매개변수가 있으며, 이 매개변수에는 Agent를 직접 전달하거나 핸드오프를 맞춤 설정하는 Handoff 객체를 전달할 수 있습니다.
일반 Agent 인스턴스를 전달하면 해당 인스턴스의 handoff_description가 설정된 경우 기본 도구 설명에 추가됩니다. 전체 handoff() 객체를 작성하지 않고 모델이 해당 핸드오프를 선택해야 하는 시점을 알려주는 용도로 사용할 수 있습니다.
Agents SDK에서 제공하는 handoff() 함수를 사용하여 핸드오프를 생성할 수 있습니다. 이 함수를 사용하면 핸드오프할 에이전트와 선택적 재정의 및 입력 필터를 지정할 수 있습니다.
기본 사용법
다음과 같이 간단한 핸드오프를 생성할 수 있습니다.
from agents import Agent, handoff
billing_agent = Agent(name="Billing agent")
refund_agent = Agent(name="Refund agent")
# (1)!
triage_agent = Agent(name="Triage agent", handoffs=[billing_agent, handoff(refund_agent)])
- 에이전트를 직접 사용하거나(
billing_agent에서처럼)handoff()함수를 사용할 수 있습니다.
handoff() 함수를 통한 핸드오프 맞춤 설정
handoff() 함수를 사용하면 여러 항목을 맞춤 설정할 수 있습니다.
agent: 작업을 핸드오프할 대상 에이전트입니다.tool_name_override: 기본적으로Handoff.default_tool_name()함수가 사용되며, 이 함수는transfer_to_<agent_name>으로 해석됩니다. 이를 재정의할 수 있습니다.tool_description_override:Handoff.default_tool_description()의 기본 도구 설명을 재정의합니다.on_handoff: 핸드오프가 호출될 때 실행되는 콜백 함수입니다. 핸드오프가 호출된다는 사실을 확인하는 즉시 데이터 가져오기를 시작하는 등의 작업에 유용합니다. 이 함수는 에이전트 컨텍스트를 받고, 선택적으로 LLM이 생성한 입력도 받을 수 있습니다. 입력 데이터는input_type매개변수로 제어합니다.input_type: 핸드오프 도구 호출 인수의 스키마입니다. 설정하면 파싱된 페이로드가on_handoff에 전달됩니다.input_filter: 다음 에이전트가 받는 입력을 필터링할 수 있습니다. 자세한 내용은 아래를 참조하세요.is_enabled: 핸드오프 활성화 여부입니다. 불리언 값 또는 불리언 값을 반환하는 함수로 지정할 수 있으므로 런타임에 핸드오프를 동적으로 활성화하거나 비활성화할 수 있습니다.nest_handoff_history: RunConfig 수준의nest_handoff_history설정을 핸드오프별로 재정의하는 선택적 값입니다.None이면 활성 실행 구성에 정의된 값이 대신 사용됩니다.
handoff() 헬퍼는 항상 전달된 특정 agent로 제어권을 이전합니다. 가능한 대상이 여러 개인 경우 대상마다 하나의 핸드오프를 등록하고 모델이 그중에서 선택하도록 하세요. 자체 핸드오프 코드가 호출 시점에 반환할 에이전트를 결정해야 하는 경우에만 맞춤 Handoff을 사용하세요.
from agents import Agent, handoff, RunContextWrapper
def on_handoff(ctx: RunContextWrapper[None]):
print("Handoff called")
agent = Agent(name="My agent")
handoff_obj = handoff(
agent=agent,
on_handoff=on_handoff,
tool_name_override="custom_handoff_tool",
tool_description_override="Custom description",
)
핸드오프 입력
특정 상황에서는 핸드오프를 호출할 때 LLM이 일부 데이터를 제공하도록 해야 할 수 있습니다. 예를 들어 "에스컬레이션 에이전트"로 핸드오프한다고 가정해 보겠습니다. 기록을 남길 수 있도록 모델이 사유를 제공하게 할 수 있습니다.
from pydantic import BaseModel
from agents import Agent, handoff, RunContextWrapper
class EscalationData(BaseModel):
reason: str
async def on_handoff(ctx: RunContextWrapper[None], input_data: EscalationData):
print(f"Escalation agent called with reason: {input_data.reason}")
agent = Agent(name="Escalation agent")
handoff_obj = handoff(
agent=agent,
on_handoff=on_handoff,
input_type=EscalationData,
)
input_type은 핸드오프 도구 호출 자체의 인수를 설명합니다. SDK는 해당 스키마를 핸드오프 도구의 parameters로 모델에 노출하고, 반환된 JSON을 로컬에서 검증한 후 파싱된 값을 on_handoff에 전달합니다.
is_enabled는 모델이 핸드오프 인수를 반환하기 전, SDK가 사용 가능한 핸드오프를 준비하는 동안 평가되므로 인수가 있는 핸드오프 내부의 값을 승인하는 데 사용할 수 없습니다. 승인이 파싱된 필드에 따라 달라지는 경우 애플리케이션 측 부수 효과가 발생하기 전에 on_handoff의 시작 부분에서 확인하세요. 승인에 실패하면 값을 반환하는 대신 예외를 발생시키세요. on_handoff이 성공적으로 반환되면 SDK는 이전을 계속 진행합니다. 도구 입력 가드레일은 핸드오프가 아닌 함수 도구에 적용됩니다.
이는 다음 에이전트의 기본 입력을 대체하지 않으며 다른 대상을 선택하지도 않습니다. handoff() 헬퍼는 여전히 래핑한 특정 에이전트로 이전하며, input_filter 또는 중첩 핸드오프 기록 설정으로 변경하지 않는 한 수신 에이전트는 여전히 대화 기록을 확인할 수 있습니다.
input_type은 RunContextWrapper.context와도 별개입니다. 이미 로컬에 있는 애플리케이션 상태나 종속성이 아니라 핸드오프 시점에 모델이 결정하는 메타데이터에는 input_type을 사용하세요.
input_type 사용 시점
핸드오프에 reason, language, priority, summary과 같이 모델이 생성한 소량의 메타데이터가 필요한 경우 input_type을 사용하세요. 예를 들어 분류 에이전트는 { "reason": "duplicate_charge", "priority": "high" }과 함께 환불 에이전트로 핸드오프할 수 있으며, 환불 에이전트가 작업을 이어받기 전에 on_handoff가 해당 메타데이터를 기록하거나 저장할 수 있습니다.
목적이 다르면 다른 메커니즘을 선택하세요.
- 기존 애플리케이션 상태와 종속성은
RunContextWrapper.context에 넣으세요. 컨텍스트 가이드를 참조하세요. - 수신 에이전트가 확인하는 기록을 변경하려면
input_filter,RunConfig.nest_handoff_history또는RunConfig.handoff_history_mapper를 사용하세요. - 가능한 전문 에이전트가 여러 개인 경우 대상마다 하나의 핸드오프를 등록하세요.
input_type는 선택된 핸드오프에 메타데이터를 추가할 수 있지만 대상 간의 디스패치를 수행하지는 않습니다. - 대화를 이전하지 않고 중첩된 전문 에이전트에 구조화된 입력을 제공하려면
Agent.as_tool(parameters=...)을 사용하는 것이 좋습니다. 도구를 참조하세요.
입력 필터
핸드오프가 발생하면 새 에이전트가 대화를 이어받아 이전의 전체 대화 기록을 확인하는 것과 같습니다. 이를 변경하려면 input_filter를 설정할 수 있습니다. 입력 필터는 HandoffInputData를 통해 기존 입력을 받고 새 HandoffInputData을 반환해야 하는 함수입니다.
HandoffInputData에는 다음이 포함됩니다.
input_history:Runner.run(...)이 시작되기 전의 입력 기록pre_handoff_items: 핸드오프가 호출된 에이전트 턴 전에 생성된 항목new_items: 핸드오프 호출 및 핸드오프 출력 항목을 포함하여 현재 턴에 생성된 항목input_items:new_items대신 다음 에이전트에 전달할 선택적 항목으로, 세션 기록을 위해new_items을 그대로 유지하면서 모델 입력을 필터링할 수 있습니다.run_context: 핸드오프가 호출된 시점에 활성 상태였던RunContextWrapper
중첩 핸드오프 기록은 선택적으로 사용할 수 있는 베타 기능이며, 안정화가 진행되는 동안 기본적으로 비활성화되어 있습니다. RunConfig.nest_handoff_history를 활성화하면 러너는 요약 가능한 기록을 순서가 지정된 어시스턴트 요약 세그먼트로 압축하면서 무손실 메시지 항목은 원래 위치에 보존합니다. 생성된 각 요약 세그먼트는 <CONVERSATION HISTORY> 래퍼를 사용하며, 이후 핸드오프는 순서가 지정된 대화 기록을 다시 구성하기 전에 이전에 생성된 세그먼트를 평탄화합니다. 세션, RunState, RunResult.to_input_list()는 SDK 기본 기록으로 이동된 메시지의 정확한 발생 항목을 추적하여 해당 항목이 두 번 추가되지 않도록 하며, 내용이 동일하더라도 별개의 메시지는 계속 보존됩니다. 기본 제공 세그먼트화 대신 RunConfig.handoff_history_mapper를 통해 자체 매핑 함수를 제공하여 다음 에이전트에 전달할 정확한 입력 항목 목록을 반환할 수 있습니다. 이 선택적 기능은 핸드오프의 input_filter와 활성 실행의 RunConfig.handoff_input_filter가 모두 설정되지 않은 경우에만 적용되므로, 이미 페이로드를 맞춤 설정하는 기존 코드(이 저장소의 코드 예제 포함)는 변경 없이 현재 동작을 유지합니다. handoff(...)에 nest_handoff_history=True 또는 False을 전달하여 단일 핸드오프의 중첩 동작을 재정의할 수 있으며, 이 경우 Handoff.nest_handoff_history가 설정됩니다. 생성된 요약 세그먼트의 래퍼 텍스트만 변경하려면 에이전트를 실행하기 전에 set_conversation_history_wrappers을 호출하세요. 이후 실행에서 기본 래퍼를 복원해야 하는 경우 실행 전에 reset_conversation_history_wrappers을 호출하세요.
중첩 핸드오프 기록은 대화 기록의 표현 방식을 변경할 뿐 민감한 데이터를 삭제하지는 않습니다. 해당 구조화된 도구 항목이 더 이상 별도로 전달되지 않더라도 도구 호출 인수와 도구 출력이 생성된 어시스턴트 요약에 남아 있을 수 있습니다. 수신 에이전트와 해당 모델 제공자를 전달된 기록의 수신자로 간주하세요.
클라이언트가 관리하는 기록의 경우 명시적인 input_filter 또는 RunConfig.handoff_input_filter를 사용하여 수신 에이전트가 확인할 수 있는 콘텐츠를 선택하거나 삭제하세요. 맞춤 필터가 nest_handoff_history도 호출하는 경우 해당 호출 전에 input_history, pre_handoff_items, new_items을 정제하세요. 헬퍼는 이 세 필드에서 중첩 기록을 구성하며 기존 input_items 재정의를 무시합니다. 따라서 input_items만 필터링하면 제외한 도구 콘텐츠가 생성된 요약에 남을 수 있습니다.
필터가 세션 기록을 위해 원래 new_items을 보존해야 하는 경우, 대신 nest_handoff_history을 호출하고 중첩 결과를 반환하기 전에 반환된 input_history을 정제할 수 있습니다. 중첩 후 input_items만 지우거나 교체해도 이미 input_history에 포함된 콘텐츠는 제거되지 않습니다.
서버에서 관리하는 대화(conversation_id, previous_response_id 또는 auto_previous_response_id)는 핸드오프 입력 필터를 지원하지 않습니다. 수신 에이전트가 해당 서버 관리 기록을 상속하지 않아야 하는 경우 명시적으로 선택한 입력으로 별도의 실행을 사용하세요. 이 별도 실행에서 원래 conversation_id 또는 previous_response_id을 재사용하지 마세요.
핸드오프와 활성 RunConfig.handoff_input_filter가 모두 필터를 정의하면 핸드오프별 input_filter가 해당 핸드오프에 우선 적용됩니다.
Note
핸드오프는 단일 실행 내에서 유지됩니다. 입력 가드레일은 계속해서 체인의 첫 번째 에이전트에만 적용되며, 출력 가드레일은 최종 출력을 생성하는 에이전트에만 적용됩니다. 워크플로 내의 각 맞춤 함수 도구 호출 전후에 검사가 필요한 경우 도구 가드레일을 사용하세요.
기록에서 모든 도구 호출을 제거하는 것과 같은 몇 가지 일반적인 패턴은 agents.extensions.handoff_filters에 구현되어 있습니다.
from agents import Agent, handoff
from agents.extensions import handoff_filters
agent = Agent(name="FAQ agent")
handoff_obj = handoff(
agent=agent,
input_filter=handoff_filters.remove_all_tools, # (1)!
)
FAQ agent이 호출되면 기록에서 모든 도구 관련 항목이 자동으로 제거됩니다.
remove_all_tools는 구조화된 도구 항목을 제거합니다. 일반 메시지나 중첩 기록 요약에 이미 복사된 도구 인수 또는 결과는 삭제하지 않습니다. 필요한 경우 맞춤 입력 필터를 사용하여 해당 메시지 콘텐츠를 제거하거나 삭제하세요.
권장 프롬프트
LLM이 핸드오프를 올바르게 이해하도록 하려면 에이전트에 핸드오프 관련 정보를 포함하는 것이 좋습니다. agents.extensions.handoff_prompt.RECOMMENDED_PROMPT_PREFIX에 권장 접두사가 있으며, agents.extensions.handoff_prompt.prompt_with_handoff_instructions을 호출하여 권장 데이터를 프롬프트에 자동으로 추가할 수도 있습니다.