Subagents

Subagents

이 문서에서는 SubAgents capability를 소개해요. 에이전트가 자립적인 작업을 이름이 지정된 자식 에이전트에 위임하게 해요. SubAgent 항목들의 시퀀스를 받아 단일 delegate_task(agent_name, task) 도구를 노출해요. 각 위임은 선택된 하위 에이전트를 자체 실행에서 — 자체 메시지 히스토리로, 부모 대화를 절대 보지 않음 — 실행하고 그 출력을 부모에 반환해요.

출처: 문서

본문

SubAgents는 에이전트가 자립적인 작업을 이름이 지정된 자식 에이전트에 위임하게 해요. SubAgent 항목들의 시퀀스를 받아 단일 delegate_task(agent_name, task) 도구를 노출해요. 각 위임은 선택된 하위 에이전트를 자체 실행에서 — 자체 메시지 히스토리로, 부모 대화를 절대 보지 않음 — 실행하고 그 출력을 부모에 반환해요.

소스

Pydantic AI Harness가 0.x 릴리스인 동안에는 minor 릴리스 사이에 API가 바뀔 수 있어요. 그럴 때는 deprecation 경고와 릴리스 노트 마이그레이션 안내가 (당신이나 당신의 에이전트에게) 정확히 업그레이드 방법을 알려줘요. 버전 정책을 참고하세요.

The problem

모든 것을 하는 단일 에이전트는 큰 도구 집합과 긴 컨텍스트를 축적해요. 전문화된 하위 에이전트에 작업을 나누면 각 컨텍스트가 집중된 상태로 유지되지만, 위임을 손으로 연결하는 것은 에이전트당 도구를 쓰고, deps를 전달하고, usage 한도를 실로 연결하고, 모델에 무엇에 위임할 수 있는지 알려주는 것을 의미해요.

The solution

SubAgentsSubAgent 항목들의 시퀀스를 받아 단일 delegate_task(agent_name, task) 도구를 노출해요. 각 위임은 선택된 하위 에이전트를 자체 실행에서 — 자체 메시지 히스토리로, 부모 대화를 절대 보지 않음 — 실행하고 그 출력을 부모에 반환해요. 사용 가능한 하위 에이전트는 정적 지침으로 시스템 프롬프트에 나열되므로, 목록이 캐시된 접두사에 유지돼요.

from pydantic_ai import Agent
from pydantic_ai_harness import SubAgent, SubAgents

researcher = Agent('anthropic:claude-sonnet-4-6', name='researcher', description='Researches a topic and reports findings')
writer = Agent('anthropic:claude-sonnet-4-6', name='writer', description='Turns notes into polished prose')

orchestrator = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[SubAgents(agents=[SubAgent(researcher), SubAgent(writer)])],
)

result = orchestrator.run_sync('Research the history of TLS and write a one-paragraph summary.')
print(result.output)

대표의 이름 — 부모 모델이 그것을 언급하는 방식이자 프롬프트에 나열되는 방식 — 은 에이전트 자신의 name 또는 SubAgent(name=...) 재정의예요. 같은 이름으로 해석되는 두 대표는 오류이고, 이름도 재정의도 없는 에이전트는 거부돼요.

The tool

Tool

Purpose

delegate_task(agent_name, task)

이름이 지정된 하위 에이전트를 자립적인 작업에 실행하고 그 출력을 반환.

  • 하위 에이전트는 자체 메시지 히스토리로 실행되므로 task는 자립적이어야 해요.
  • 알 수 없는 agent_nameModelRetry를 발생시켜 모델이 스스로 고칠 수 있어요.
  • 부모에 반환되는 결과는 str(result.output)이에요.
  • models 메뉴가 구성되면 도구가 추가 model 인자를 받아요(아래 참고).

Deps, usage, tools, and capabilities

  • Deps가 전달돼요. 부모 실행의 deps가 각 하위 에이전트에 전달되므로, 하위 에이전트가 부모의 AgentDepsT를 공유해요(타입 시그니처로 강제 — 모든 하위 에이전트는 AbstractAgent[AgentDepsT, Any]).
  • Usage가 기본적으로 공유돼요. 부모의 usage가 각 하위 에이전트 실행에 전달되므로 토큰 사용량이 집계되고 부모 usage_limits가 전체 에이전트 트리에 적용돼요. 각 하위 에이전트 실행에 자체 회계를 주려면 forward_usage=False를 설정하세요.
  • 도구가 상속될 수 있어요. inherit_tools=True이면 부모 에이전트의 자체 도구(직접 또는 toolsets로 등록)가 하위 에이전트 자체 것 위에 각 하위 에이전트 실행에 추가돼요. 부모의 capability가 기여한 도구는 상속되지 않아요. 그것들은 부모 실행에 등록된 capability 인스턴스에 묶여 있어서, 의존하는 훅과 지침 없이 도착할 거예요. 하위 에이전트에 capability를 주려면 shared_capabilities를 사용하세요. 이것은 대표 도구 자체도 제외하므로 하위 에이전트가 더 깊은 위임으로 재귀할 수 없어요. 기본 꺼짐.
  • Capabilities가 공유될 수 있어요. shared_capabilities는 모든 하위 에이전트 실행에 적용돼요 — 예를 들어 각 Agent를 다시 빌드하지 않고 모든 하위 에이전트에 공통 가드레일, 메모리, planning capability를 주기.
  • 하위 에이전트 이벤트가 스트리밍될 수 있어요. event_stream_handler를 전달하면 각 하위 에이전트 실행에 전달되어, 하위 에이전트의 모델 스트리밍과 도구 이벤트가 호출자에게 표면화돼요(핸들러는 하위 에이전트의 자체 RunContext를 받음).

Per-delegate run controls

SubAgent는 자체 예산을 지니므로, 한 대표의 제어가 다른 것들을 건드리지 않아요. 제어가 설정되지 않은 SubAgentSubAgents 기본값으로 실행돼요.

from pydantic_ai import Agent
from pydantic_ai.usage import UsageLimits
from pydantic_ai_harness import SubAgent, SubAgents

reproducer = Agent('anthropic:claude-sonnet-4-6', instructions='Reproduce the reported bug from a minimal script.')
librarian = Agent('anthropic:claude-sonnet-4-6', instructions='Find relevant docs, issues, and prior art.')

orchestrator = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[
        SubAgents(
            agents=[
                SubAgent(reproducer, usage_limits=UsageLimits(request_limit=35), timeout_seconds=600, max_calls=1),
                SubAgent(librarian, usage_limits=UsageLimits(request_limit=18), timeout_seconds=300, max_calls=2),
            ]
        )
    ],
)

Field

Effect

models

이 대표가 실행할 수 있는 SubAgents 모델 메뉴의 키들과, 기본적으로 실행되는 키: 나열된 첫 키. 아래 "Per-delegation model selection" 참고.

usage_limits

한 위임에 대한 요청/토큰 예산. 자식은 자체 usage 회계로 실행되므로, 예산은 forward_usage=True에서도 그 자식의 요청과 토큰만 세어요(부모나 형제 것은 아님). 트레이드오프: 그 자식의 토큰은 더 이상 부모의 usage에 집계되지 않아요. 예산 도달은 실행을 멈추는 UsageLimitExceeded가 아니라 부드러운 결과예요(아래 참고).

timeout_seconds

한 위임에 대한 벽시계 예산. 자식이 그것을 초과하면 실행이 취소되고 부모가 자식에 매달리는 대신 부드러운 스티어링 메시지를 받아요. 취소된 자식의 event_stream_handler(있다면)는 종료 이벤트 없이 이벤트 받기를 멈춰요.

max_calls

부모 실행당 이 하위 에이전트에 대한 최대 위임 수. 도달하면 추가 위임이 자식을 실행하지 않고 부드러운 예산 소진 메시지를 반환해요. 횟수는 하나의 Agent.run(run_id)으로 제한되고 그것이 끝나면 지워지므로, 각 부모 실행과 중첩 트리의 각 수준이 독립적으로 예산을 정해요.

on_failure

내장 기본 대신, 이 대표의 어떤 부드러운 저하에 대해 부모에 반환되는 스티어링 메시지. 그것을 설정하면 자식 실패도 부드럽게 해요(아래 참고).

contain_errors

이 대표의 예기치 않은 충돌이 잡혀서 부모 실행을 중단하는 대신 제한된 ModelRetry로 부모에 반환되는지 여부(아래 참고). 설정하지 않으면 SubAgents(contain_errors=...) 기본값(꺼짐)을 상속해요.

Per-delegation model selection

오케스트레이터는 브리프를 쓸 때 작업이 얼마나 어려운지 알므로, 어떤 모델이 그것을 실행할지 결정하기에 올바른 곳이에요. models 메뉴를 구성하면 대표 도구가 그 키 중 하나를 이름짓는 model 인자를 얻어요.

from pydantic_ai import Agent
from pydantic_ai.settings import ModelSettings
from pydantic_ai_harness import SubAgent, SubAgents
from pydantic_ai_harness.subagents import ModelOption

reviewer = Agent('anthropic:claude-sonnet-4-6', name='reviewer', description='Reviews a diff')
linter = Agent('anthropic:claude-sonnet-4-6', name='linter', description='Runs the linter and reports failures')

orchestrator = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[
        SubAgents(
            agents=[SubAgent(reviewer), SubAgent(linter, models=['fast'])],
            models={
                'fast': 'anthropic:claude-haiku-4-5',
                'standard': 'anthropic:claude-sonnet-4-6',
                'deep': ModelOption(
                    'anthropic:claude-opus-4-7',
                    description='hard reasoning, multi-file changes',
                    settings=ModelSettings(thinking='xhigh'),
                ),
            },
        )
    ],
)
  • 기본 꺼짐. models 메뉴가 없으면 model 인자가 도구 스키마에 전혀 없고, 모든 위임이 전에 했던 것처럼 정확히 실행돼요.
  • 키가 인터페이스예요. 그것들은 각 항목의 모델과 설명과 함께 시스템 프롬프트에 나열되고, 도구 스키마가 그것을 enum으로 제안하므로, 모델이 모델 이름을 발명하는 대신 메뉴에서 고른다. 직무에 맞게('fast', 'deep') 이름짓고 벤더 이름으로 짓지 마세요.
  • 항목은 모델 또는 ModelOption이에요. ModelOption(model, description=..., settings=...)은 라우팅 힌트와 옵션별 ModelSettings를 추가하므로, 한 키가 "같은 모델, 더 많은 thinking"을 의미할 수 있어요. 그 설정은 하위 에이전트의 자체 model_settings 위에 병합되며, 옵션이 설정하지 않는 부분을 유지해요.
  • 해석 순서. 부모가 전달한 키, 그 다음 대표의 첫 허용 키(아래 참고), 그 다음 대표의 자체 모델, 그 다음 부모 실행의 모델.
  • 대표가 제한될 수 있어요. SubAgent(linter, models=['fast'])는 그 대표를 fast에 고정해요. 부모가 model을 전달하지 않을 때 나열된 첫 키가 실행되는 것이고, 나머지는 ModelRetry로 거부돼요. 제한은 프롬프트 목록에 렌더링돼요(- linter: Runs the linter (models: fast)). 메뉴가 정의하지 않는 키로 제한하는 것은 구성 시 ValueError예요.
  • 거부된 키는 비용이 없어요. 알 수 없거나 사용할 수 없는 키는 대표의 max_calls 예산이 청구되기 전에 유효 옵션을 나열하는 ModelRetry로 돌아와요.

Failure handling

부드러운 결과 는 정상 도구 결과로 부모에 스티어링 메시지를 반환하므로, 그 모델이 메시지를 읽고 (즉시 재위임을 유도하는 ModelRetry 대신) 다음에 무엇을 할지 결정해요. 타임아웃, 도달한 usage_limits 예산, 소진된 max_calls 예산은 항상 부드러워요. on_failure가 설정되면 그것이 지니는 메시지가 이러한 결과에 대한 내장 기본값을 대체해요.

부드러운 모델 오류(ModelRetry, UnexpectedModelBehavior, 예: 자체 재시도를 소진)로 실패하는 하위 에이전트 실행은 기본적으로 부모에 대한 ModelRetry로 변환돼요. 그래서 부모의 모델이 Sub-agent '<name>' failed: ...를 보고 재위임으로 반응할 수 있어요. 대표 도구는 기본적으로 tool_retries=2이므로, 부모는 그만큼 연속된 대표 실패 후에만 중단해요. 카운터는 성공적인 위임 후 리셋돼요. 좀 더 변덕스러운 하위 에이전트를 견디려면 tool_retries를 높이거나, 부모 에이전트의 기본 도구 재시도를 상속하려면 None을 설정하세요. 대표에 on_failure를 설정하면 그 실패를 대신 부드럽게 해요. 자식 오류가 정상 도구 결과로 on_failure 메시지를 반환해요.

하드 오류는 전체 실행을 멈추도록 전파돼요. 자체 대표별 usage_limits가 없는(그래서 부모의 회계를 공유하는) 자식의 UsageLimitExceeded는 전체 트리가 예산 밖임을 의미하고 전파돼요. 자체 usage_limits에 도달하는 자식은 위처럼 부드러워요.

예기치 않은 충돌 — 자식이 발생시키는 다른 어떤 예외, 예: 프로바이더 ModelAPIError/FallbackExceptionGroup 또는 잘못된 도구 인자의 평범한 ValueError — 은 기본적으로 전파되고 부모 실행을 중단해요. contain_errors=True(대표별 또는 SubAgents 기본값으로)를 설정하면 그것을 잡아 대신 제한된 ModelRetry로 부모에 반환하므로, 한 대표 충돌이 전체 실행을 죽일 수 없어요. Containment는 시끄럽게 유지돼요. 예외가 재시도 메시지(Sub-agent '<name>' crashed: ...)를 타고, 표준 logging 모듈로 기록되며, tool_retries가 여전히 연속 충돌을 중단으로 제한해요. 이것은 on_failure와 직교해요. contained 충돌은 항상 시끄러운 재시도를 발생시키고 부드러운 on_failure 반환을 절대 하지 않으므로, 진짜 버그가 성공으로 위장되지 않아요. 취소, 공유 UsageLimitExceeded, pydantic-ai 제어 흐름 신호(CallDeferred, ApprovalRequired, Skip* 신호), UserErrorcontain_errors와 무관하게 containment를 우회해요. 취소는 두 종류를 다뤄요. 외부 취소(asyncio.CancelledError)는 그대로 전파되고, 자식의 자체 일방 당사자 취소(자식 안의 RunContext.cancel(), RunCancelled 발생)는 대표 도구를 uncontained로 두며, 그 후 pydantic-ai가 그것을 재위임을 초대하는 충돌 재시도가 아니라 부모 모델이 반응할 수 있는 실패한 delegate_task 반환으로 격리해요.

Events

SubAgentssub_agents 네임스페이스에서 타입이 지정된 capability 이벤트를 방출하므로, 호스트가 대표 도구의 인자와 결과를 파싱하지 않고 위임이 실행되고 어떻게 끝났는지 보여줄 수 있어요:

Event

Dispatch

When

Payload

DelegationStartEvent

stream

위임이 모든 검사를 통과했고 자식 실행이 시작되려 할 때

agent_name, task, truncated, model(메뉴 키, 또는 None), inherits_tools

DelegationEndEvent

stream

위임이 부모가 받는 것으로 정착했을 때

agent_name, outcome, output, truncated, usage, duration_seconds

둘 다 알림이에요. 하나의 위임은 하나의 delegate_task 호출이므로, 시작과 그 끝이 core가 모든 이벤트에 찍는 tool_call_id를 공유해요. 모델이 병렬로 위임할 때 구독자가 그것들을 짝짓는 방법이에요.

outcome은 위의 실패 처리를 따라요. ok(자식의 출력이 부모로 돌아감), timeout, budget(자식의 자체 usage_limits), failed(부드러운 모델 오류, on_failure로 반환되거나 ModelRetry로 발생), contained(충돌을 contain_errors가 잡음). output은 각 경우에 대표 도구가 부모에 돌려주는 것이에요. 자식의 출력, 스티어링 메시지, 또는 재시도 텍스트. 그것은 도구 안에서, 어떤 ToolGuardrail 결과 가드가 모델용으로 그 텍스트를 선별하기 전에 방출돼요. 선별된 버전이 필요한 호스트는 core의 FunctionToolResultEvent에서 ToolReturnPart를 읽어요. usage는 별도 회계(usage_limits 설정 또는 forward_usage=False)가 있을 때 자식의 자체 RunUsage이고, 부모의 usage로 발생할 때는 None인데, 그 경우 그것의 몫은 분리할 수 없어요.

자식이 실행되기 전에 거부된 위임(알 수 없는 하위 에이전트, 메뉴 밖의 모델 키, 소진된 max_calls 예산)은 아무것도 방출하지 않아요. 도구 결과가 이유를 말해요. 대표 도구 밖으로 전파되는 예외(공유 사용 한도, uncontained 충돌, 취소)는 끝 이벤트 없이 끝나요. Agent(toolsets=[...])에 직접 등록된 SubAgentToolset은 소유 capability가 없고 아무것도 방출하지 않아요. 어떤 capability 이벤트와 마찬가지로, 발생시키는 리스너는 부모 실행을 중단해요.

taskoutputMAX_EVENT_TEXT_CHARS(4096)에서 truncated 플래그와 함께 잘리므로, 영속되거나 전달된 이벤트 스트림이 하나의 장황한 위임에 넘칠 수 없어요.

from pydantic_ai import Agent
from pydantic_ai.capabilities import AbstractCapability, on_event
from pydantic_ai_harness.subagents import DelegationEndEvent, SubAgent, SubAgents


class ReportDelegations(AbstractCapability):
    @on_event(DelegationEndEvent)
    async def on_delegation_end(self, ctx, event: DelegationEndEvent) -> None:
        print(f'{event.agent_name}: {event.outcome} in {event.duration_seconds:.1f}s')


researcher = Agent('anthropic:claude-sonnet-4-6', name='researcher')
agent = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[SubAgents(agents=[SubAgent(researcher)]), ReportDelegations()],
)

자식 실행의 중첩 모델 스트리밍은 이벤트 관심사가 아니에요. 그것은 event_stream_handler를 전달하세요.

@on_event가 어떻게 동작하는지는 capability events를 참고하세요.

SubAgents는 자체 OpenTelemetry 스팬을 방출하지 않아요. 자식 실행은 부모의 도구 호출 스팬 아래 중첩된 자체 스팬을 가진 core 에이전트 실행이고, 위의 이벤트들은 트레이스가 예외나 도구 결과로만 보여줄 outcome을 지녀요.

Discovery

하위 에이전트는 get_instructions를 통해 각 에이전트의 description(또는 SubAgent(description=...) 재정의)로 시스템 프롬프트에 나열돼요. 설명이 없는 하위 에이전트는 이름만으로 나열돼요.

Loading sub-agents from disk

저장소의 markdown 에이전트 정의가 Agent 코드를 쓰지 않고 대표가 돼요. 기본적으로 관례 폴더 아래의 모든 *.md 파일이 명시적으로 전달된 agents와 함께 하위 에이전트로 로드돼요.

from pydantic_ai import Agent
from pydantic_ai_harness import SubAgents

orchestrator = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[SubAgents(inherit_tools=True)],  # auto-loads ./.agents/agents/ and ~/.agents/agents/
)

agent_folders는 정의가 오는 곳을 제어해요. 기본값은 'agents', 관례 레이아웃:

  • 폴더 이름 str(기본 'agents'): 프로젝트 루트(cwd) 다음 홈 루트에 대해, <root>/.agents/<name>/에서 로드하고, <root>/.agents/가 없으면 <root>/.claude/<name>/로 폴백.
  • 경로 시퀀스는 정확히 그 폴더들에서 순서대로 로드.
  • None은 디스크 로드를 비활성화하고 명시적으로 전달된 agents만 노출.

Definition format

정의는 선택적 frontmatter가 있는 markdown 파일이에요:

---
name: researcher
description: Researches a topic and reports findings
tools: Read, Grep
---
You research topics. Report your findings, each with a source.
  • name은 대표 이름(부모가 그것을 언급하는 방식이자 나열되는 방식). 없으면 파일 이름 스템으로 폴백.
  • description은 프롬프트 목록을 구동.
  • markdown 본문이 에이전트의 지침이 됨.
  • tools(또는 allowed-tools)는 쉼표 구분 문자열 또는 YAML 블록 목록. 아래 "Tools" 참고.
  • modelcolor는 무시돼요. 모델은 부모에서 상속(아래 참고)되고, color는 pyai 동등물이 없어요.

Frontmatter는 그 키들로 제한된 작은 의존성 없는 파서가 읽어요(pyyaml은 harness 의존성이 아님). 전체 YAML frontmatter는 지원되지 않아요.

Models and effort

디스크 에이전트는 기본적으로 부모 실행의 모델을 상속해요. 에이전트별로 호출자가 agent_overrides로, 에이전트의 이름으로 키하여 모델을 재정의하고 thinking/effort 수준을 설정할 수 있어요:

from pydantic_ai_harness import SubAgents
from pydantic_ai_harness.subagents import AgentOverride

SubAgents(
    agent_folders='agents',
    agent_overrides={'researcher': AgentOverride(model='anthropic:claude-sonnet-4-6', effort='high')},
)

capability가 만드는 모든 에이전트는 최소 thinking-effort 바닥에서 실행돼요. MINIMUM_EFFORT_FLOORclamp_effort(level, floor=...) 헬퍼가 export되므로 오케스트레이터가 자체 에이전트에 같은 바닥을 적용할 수 있어요(그 오케스트레이터 측 적용은 호출자의 책임). clamp_effortNone/False를 바닥으로 매핑하고, True(프로바이더 기본 effort)는 변경 없이 두며, 바닥 아래의 구체적 수준은 바닥까지 올려요. Effort는 pyai의 ModelSettings.thinking을 통해 적용돼요.

Tools

디스크 에이전트는 기본적으로 도구가 없어요(inherit_toolsFalse). 부모의 도구를 inherit_tools 메커니즘을 통해 그것에 노출하려면 inherit_tools=True를 설정하세요. 그러면 그것의 tools frontmatter는 무시돼요. 대신 frontmatter 도구 이름을 특정 툴셋에 매핑하려면 tool_resolver를 전달하세요. 각 도구 이름을 받아(Bash(git:*) 같은 항목을 존중할 수 있음) 그것을 제공하는 툴셋을 반환하거나, 알 수 없는 이름에는 None을 반환해 경고와 함께 건너뛰어요.

from pydantic_ai_harness import SubAgents

def resolve(tool_name: str):
    return TOOLSETS.get(tool_name)  # -> Sequence[AgentToolset[object]] | None

SubAgents(agent_folders='agents', tool_resolver=resolve)

Precedence

같은 이름이 둘 이상의 소스에 나타날 때 더 높은 우선순위의 것이 이기고 나머지는 경고와 함께 건너뛰어져요. 명시적으로 전달된 agents 먼저, 그 다음 프로젝트 폴더, 그 다음 홈 폴더(그리고 명시적 경로 시퀀스의 경우 앞선 경로가 뒷경로보다 먼저). 명시적으로 전달된 agents 목록 안의 중복 이름은 여전히 오류예요.

Configuration

SubAgents(
    agents=(),             # Sequence[SubAgent[AgentDepsT]] -- each pairs an agent with its run controls
    models={},             # Mapping[str, Model | str | ModelOption] -- per-delegation model menu (off when empty)
    agent_folders='agents',# folder-name str (convention) | Sequence[Path] | None (disable)
    agent_overrides={},    # Mapping[str, AgentOverride] -- per-disk-agent model/effort override
    tool_resolver=None,    # Callable[[str], Sequence[AgentToolset[object]] | None] -- disk-agent tool mapping
    forward_usage=True,    # share the parent's usage with sub-agent runs
    inherit_tools=False,   # expose the parent's own tools to sub-agents (capability tools excluded)
    shared_capabilities=(),# capabilities applied to every sub-agent run
    event_stream_handler=None,  # forwarded to each sub-agent run to stream its events
    tool_name='delegate_task',
    tool_retries=2,        # extra delegate-tool attempts after a sub-agent error before aborting (None inherits the agent default)
    contain_errors=False,  # default for SubAgent.contain_errors: contain an unexpected crash as a bounded retry
)
SubAgent(
    agent,                 # AbstractAgent[AgentDepsT, Any] -- the child agent to run
    name=None,             # delegate name; defaults to the agent's own `name`
    description=None,      # prompt-listing description; defaults to the agent's own `description`
    models=None,           # Sequence[str] -- menu keys this delegate may run on; the first is its default
    usage_limits=None,     # per-delegation request/token budget (isolated accounting)
    timeout_seconds=None,  # per-delegation wall-clock budget
    max_calls=None,        # max delegations to this sub-agent per parent run
    on_failure=None,       # steering message for soft degradations of this delegate
    contain_errors=None,   # contain an unexpected crash as a bounded retry; None inherits the SubAgents default
)

SubAgentsagent spec으로 직렬화할 수 없어요(라이브 Agent 인스턴스를 보유), 그래서 get_serialization_name()None을 반환해요.

Notes

  • 하위 에이전트는 자체 SubAgents를 가질 수 있어 트리를 형성해요. usage를 공유(기본)하고 최상위 실행에 usage_limits를 설정해 전체 트리를 제한하세요.
  • 모델이 병렬로 발행한 위임은 독립적인 하위 에이전트 실행으로 실행돼요.

Further reading

API reference

SubAgents

Bases: AbstractCapability[AgentDepsT]

에이전트가 자립적인 작업을 이름이 지정된 하위 에이전트에 위임하게 해요.

단일 delegate_task(agent_name, task) 도구를 노출해요. 각 위임은 선택된 하위 에이전트를 새롭고 고립된 실행에서(부모 대화를 절대 보지 않음) 실행하고, 사용 가능한 하위 에이전트는 정적이고 캐시 안정적인 지침으로 시스템 프롬프트에 나열돼요.

하위 에이전트는 SubAgent 항목들의 시퀀스로 전달되며, 각각 에이전트와 그 대표별 실행 제어(foreground: usage_limits 예산, 벽시계 timeout_seconds, 실행당 max_calls 예산, on_failure 스티어링 메시지, 선택적 name/description 재정의)를 짝짓습니다. 대표의 이름은 SubAgent.name 또는 설정되지 않으면 에이전트 자신의 name이에요. 명시적으로 전달된 두 대표가 같은 이름으로 해석되는 것은 오류예요.

models 메뉴가 구성되지 않으면 위임은 하위 에이전트의 자체 모델로 실행돼요. 구성되면 delegate_task도 메뉴의 키 중 하나를 이름짓는 model 인자를 받아, 부모가 각 작업을 그것에 맞는 모델로 라우팅해요. SubAgent는 받아들이는 키를 제한할 수 있어요(SubAgent.models).

하위 에이전트는 또한 기본적으로 디스크에서 로드돼요. ./.agents/agents/~/.agents/agents/(또는 .claude/ 동등물) 아래의 각 markdown 에이전트 정의가 부모의 모델로 빌드된 대표가 돼요. 디스크 대표는 기본적으로 도구가 없어요(inherit_toolsFalse). 부모의 도구를 노출하려면 inherit_tools=True를 설정하거나, frontmatter 도구 이름을 매핑하려면 tool_resolver를 전달하세요. 디스크 대표는 명시적으로 전달된 것들과 공존해요. 명시적으로 전달된 에이전트가 우선하고, 그 다음 프로젝트 폴더, 그 다음 홈 폴더. 이름이 이미 점유된 디스크 대표는 경고와 함께 건너뛰어져요. agent_folders로 구성하거나 비활성화하세요. agent_overridestool_resolver도 참고.

부모의 deps는 각 하위 에이전트에 전달돼요(하위 에이전트는 따라서 부모의 AgentDepsT를 공유), 그리고 기본적으로 부모의 usage가 공유되어 사용 한도가 전체 에이전트 트리에 적용돼요. 선택적으로, 부모의 도구가 상속될 수 있고(inherit_tools), 추가 capabilities가 모든 하위 에이전트 실행에 적용될 수 있으며(shared_capabilities), 하위 에이전트 이벤트가 핸들러로 스트리밍될 수 있어요(event_stream_handler).

from pydantic_ai import Agent
from pydantic_ai_harness.subagents import SubAgent, SubAgents

researcher = Agent('anthropic:claude-sonnet-4-6', name='researcher', description='Researches topics')
writer = Agent('anthropic:claude-sonnet-4-6', name='writer', description='Writes prose')

orchestrator = Agent(
    'anthropic:claude-opus-4-7',
    capabilities=[SubAgents(agents=[SubAgent(researcher), SubAgent(writer)])],
)

Attributes

agents

노출할 하위 에이전트. 각각 에이전트를 대표별 실행 제어와 짝짓는 SubAgent. SubAgent 참고. 이것들은 같은 이름의 디스크 로드 에이전트보다 우선해요.

Type: Sequence[SubAgent[AgentDepsT]] Default: ()

models

부모가 개별 위임을 라우팅할 수 있는 모델 메뉴. 부모가 하나를 고르는 데 사용하는 이름으로 키. 기본 꺼짐: 메뉴가 없으면 대표 도구에 model 인자가 없고 모든 위임이 항상 그랬던 대로 실행돼요.

각 값은 모델 참조 또는 라우팅 힌트와 자체 ModelSettings를 지니는 ModelOption이에요(그래서 한 키가 "같은 모델, 더 많은 thinking"을 의미할 수 있음). 키와 설명은 시스템 프롬프트에 나열되므로, 벤더가 아니라 직무에 맞게 이름짓기 — 'fast', 'deep'. SubAgent.models가 주어진 대표가 받아들이는 것을 제한해요.

from pydantic_ai_harness.subagents import SubAgents

SubAgents(models={'fast': 'anthropic:claude-haiku-4-5', 'deep': 'anthropic:claude-opus-4-7'})

Type: Mapping[str, Model | KnownModelName | str | ModelOption] Default: field(default_factory=(dict[str, 'Model | KnownModelName | str | ModelOption']))

agent_folders

agents 외에 markdown 에이전트 정의를 로드할 곳. 기본값은 관례 레이아웃이므로, capability를 구성하면 추가 구성 없이 저장소의 에이전트 파일을 자동 로드해요.

  • 폴더 이름 str(기본 'agents'가 관례 레이아웃): 프로젝트 루트(cwd) 다음 홈 루트에 대해, <root>/.agents/<name>/에서 로드하고, <root>/.agents/가 없으면 <root>/.claude/<name>/로 폴백.
  • 경로 시퀀스: 정확히 그 폴더들에서 순서대로 로드.
  • None: 디스크 로드를 완전히 비활성화(agents만 노출).

누락된 폴더는 건너뛰어져요. 폴더 안에서 모든 *.md 파일이 후보다.

Type: str | Sequence[Path] | None Default: 'agents'

agent_overrides

에이전트의 이름으로 키된 디스크 에이전트별 재정의. 항목은 에이전트의 model(그 외 부모의 모델이 상속)과 effort(그 외 최소 바닥)를 설정할 수 있어요. 명시적으로 전달된 agents에는 효과가 없어요.

Type: Mapping[str, AgentOverride] Default: field(default_factory=(dict[str, AgentOverride]))

tool_resolver

디스크 에이전트가 도구를 얻는 방법의 선택적 재정의. 설정되면 정의의 tools/allowed-tools frontmatter의 각 도구 이름이 이 리졸버에 전달되고 반환된 툴셋이 그 에이전트에 붙어요. 알 수 없는 이름(리졸버가 None 반환)은 경고와 함께 건너뛰어져요. 설정하지 않으면 frontmatter 도구 목록이 무시되고 디스크 에이전트는 inherit_tools로 부모의 도구를 상속해요(노출하려면 inherit_tools=True 설정).

Type: ToolResolver | None Default: None

forward_usage

True이면 부모 실행의 usage가 각 하위 에이전트 실행과 공유되어 토큰 사용량이 집계되고 사용 한도가 전체 에이전트 트리에 적용돼요.

Type: bool Default: True

inherit_tools

True이면 부모 에이전트의 도구가 각 하위 에이전트 실행에 노출돼요(대표 도구 자체는 걸러져 하위 에이전트가 더 깊은 위임으로 재귀할 수 없음). 하위 에이전트 접근을 조용히 넓히지 않도록 기본 꺼짐.

Type: bool Default: False

shared_capabilities

각 하위 에이전트가 이미 가진 것에 더해 모든 하위 에이전트 실행에 적용되는 capabilities.

Type: Sequence[AgentCapability[AgentDepsT]] Default: ()

event_stream_handler

설정되면 이 핸들러가 각 하위 에이전트 실행에 전달되어, 하위 에이전트의 모델 스트리밍과 도구 이벤트가 호출자에게 표면화돼요. 핸들러는 하위 에이전트의 자체 RunContext와 이벤트 스트림을 받아요.

Type: EventStreamHandler[AgentDepsT] | None Default: None

tool_name

모델에 노출된 대표 도구의 이름.

Type: str Default: 'delegate_task'

id

One-off: 에이전트는 단일 대표 도구를 노출하므로 id는 고정이에요.

tool_name은 하나의 이름이므로 두 SubAgents capability가 같은 도구를 등록하고 충돌해요. 여기서 id를 선언하는 것이 두 개가 대신 병합되게 하며, 그 명단을 합칩니다 — 이것은 위임하는 패키지형 harness가 같은 일을 하는 다른 하나와 구성되게 해요.

필드에서 KW_ONLY 마커가 아니라 키워드 전용: 마커는 그 뒤의 모든 필드에 적용되며, tool_retriescontain_errors를 이미 가진 위치 계약에서 빼게 돼요.

Type: str | None Default: field(default='sub_agents', kw_only=True)

tool_retries

대표 도구의 재시도 — 하위 에이전트 오류 후 부모 실행이 중단되기 전에 얻는 추가 시도 수. 하위 에이전트 실패(예: 자체 출력 재시도 소진)는 부모에 도구 재시도로 표면화되어 그것이 수정된 작업으로 재위임해 반응할 수 있어요. 재시도 카운터는 성공적인 위임 후 리셋되므로, 이것은 총 실패가 아니라 연속 실패를 제한해요. 기본 2(pydantic-ai의 도구별 기본은 1)라 반복적으로 변덕스러운 하위 에이전트가 첫 반복에서 부모 실행을 중단하지 않게 해요. 대신 부모 에이전트의 기본 도구 재시도를 상속하려면 None을 설정하세요.

Type: int | None Default: 2

contain_errors

SubAgent.contain_errors의 기본값: 예기치 않은 하위 에이전트 충돌이 부모 실행을 중단하는 대신 잡혀 제한된 ModelRetry로 부모에 반환되는지. 기본 꺼짐이므로 충돌이 전파돼요. 어떤 SubAgent든 이 것을 대표별로 재정의할 수 있어요. containment 계약과 무관하게 항상 전파되는 것은 SubAgent.contain_errors 참고.

Type: bool Default: False

Methods

wrap_run

@async

def wrap_run(
    ctx: RunContext[AgentDepsT],
    *,
    handler: WrapRunHandler,
) -> AgentRunResult[Any]

부모 에이전트를 실행한 다음 이 실행의 위임 횟수를 버려 누적되지 않게 해요.

Returns

AgentRunResult[Any]

get_instructions
def get_instructions() -> AgentInstructions[AgentDepsT] | None

사용 가능한 하위 에이전트와 모델의 정적이고 캐시 안정적인 목록.

Returns

AgentInstructions[AgentDepsT] | None

get_toolset
def get_toolset() -> AgentToolset[AgentDepsT] | None

대표 도구를 제공하는 툴셋, 또는 하위 에이전트가 구성되지 않으면 None.

Returns

AgentToolset[AgentDepsT] | None

get_serialization_name

@classmethod

def get_serialization_name(cls) -> str | None

spec 직렬화 불가 — capability가 라이브 Agent 인스턴스를 보유.

Returns

str | None

combine

@classmethod

def combine(
    cls,
    capabilities: Sequence[AbstractCapability[AgentDepsT]],
) -> AbstractCapability[AgentDepsT]

명단을 구성하고 나머지 모든 것이 이미 동의하도록 요구해요.

한 에이전트의 두 패키지형 harness가 각각 자기 대표를 가져오고, 그것을 구성하는 것이 공유 id의 용도예요. agentsmodels만 구성돼요. 다른 모든 필드는 대표가 어떻게 실행되는지 결정해요 — 어떤 capabilities를 받는지, 부모의 도구를 보는지, 대표 도구가 무엇이라 불리는지, 대표가 어디서 로드되는지 — 그래서 그것을 병합하면 한 harness의 정책을 다른 것의 하위 에이전트에 적용하게 되며, 어느 저작자도 원하지 않았어요. 그것들은 동의해야 하고, 그렇지 않을 때 그렇게 말해요.

명단은 __post_init__을 다시 실행하는 대신 입력이 이미 구체화한 대표들에서 재구축돼요. 그것은 현재 작업 디렉터리에 상대적인 agent_folders를 다시 로드하고 tool_resolver를 다시 호출하므로, 병합이 어느 입력과도 다르게 답할 수 있어요.

Returns

AbstractCapability[AgentDepsT]

SubAgent

Bases: Generic[AgentDepsT]

대표 하나: 자식 에이전트와 그 대표별 실행 제어.

이것들의 시퀀스를 SubAgents(agents=[...])로 전달하세요. 대표의 이름 — 부모 모델이 그것을 언급하는 방식이자 시스템 프롬프트에 나열되는 방식 — 은 설정되면 name, 그 외 에이전트 자신의 name이에요. 둘 다 없는 에이전트는 SubAgents가 거부해요.

아래 모든 제어는 선택적이며, 설정되지 않은 필드는 대응 동작을 SubAgents 기본값에 둬요.

Attributes

agent

이 대표가 호출될 때 실행되는 에이전트.

Type: AbstractAgent[AgentDepsT, Any]

name

부모 모델이 이 에이전트에 위임하는 데 사용하는 이름. 설정되지 않으면 에이전트 자신의 name으로 기본값.

Type: str | None Default: None

description

시스템 프롬프트 목록용 설명. 설정되지 않으면 에이전트 자신의 description으로 기본값. 둘 다 없는 대표는 이름만으로 나열돼요.

Type: str | None Default: None

models

이 대표가 SubAgents.models 중 메뉴 키로 실행할 수 있는 것과, 기본적으로 실행되는 것: 나열된 첫 키. 설정하지 않으면 부모가 어떤 구성 옵션이든 고르게 하고, 고르지 않으면 대표의 자체 모델로 폴백하게 해요. 설정하면 대표를 한 옵션(models=['fast'])에 고정하거나, 비싼 대표를 하위 집합으로 제한해요. 메뉴가 정의하지 않는 키를 이름짓는 것은 오류예요.

Type: Sequence[str] | None Default: None

usage_limits

한 위임에 대한 요청/토큰 예산. 설정되면 자식은 자체 usage 회계로 실행되어 예산이 forward_usage=True에서도 자식 자신의 요청과 토큰만 세어요(부모나 형제 것은 아님). 트레이드오프: 그 자식의 토큰은 더 이상 부모의 usage에 집계되지 않아요. 이 예산에 부딪히는 것은 부드러운 결과(스티어링 메시지)이지 실행을 멈추는 UsageLimitExceeded가 아니에요.

Type: UsageLimits | None Default: None

timeout_seconds

한 위임에 대한 벽시계 예산. 자식이 그것을 초과하면 실행이 취소되고 부모가 자식에 매달리는 대신 부드러운 스티어링 메시지를 받아요.

Type: float | None Default: None

max_calls

부모 실행당 이 하위 에이전트에 대한 최대 위임 수. 도달하면 추가 위임이 자식을 실행하지 않고 부드러운 예산 소진 메시지를 반환해요.

Type: int | None Default: None

on_failure

이 대표의 어떤 부드러운 저하(타임아웃, 자식 실패, 사용 예산 도달, 호출 예산 소진)에 대해 내장 기본 대신 부모에 반환되는 스티어링 메시지. 그것을 설정하면 자식 실패도 부드럽게 해요. 자식 오류가 부모 ModelRetry를 발생시키는 대신 정상 도구 결과로 이 메시지를 반환해요.

Type: str | None Default: None

contain_errors

예기치 않은 하위 에이전트 충돌이 부모 실행을 중단하는 대신 contained되는지. True이면 자식이 발생시키는, 기대된 부드러운 저하가 아닌 예외(프로바이더 ModelAPIError/FallbackExceptionGroup, 잘못된 도구 인자의 평범한 ValueError 등)가 잡혀 제한된 ModelRetry로 부모에 반환되므로, 한 대표 충돌이 전체 실행을 죽일 수 없어요. 시끄럽게 유지돼요: 예외가 재시도 메시지를 타고 기록되며, tool_retries가 여전히 연속 충돌을 중단으로 제한해요. 취소, 공유 사용 한도, pydantic-ai 제어 흐름 신호, UserError는 무관하게 항상 전파돼요. 설정하지 않으면 SubAgents.contain_errors(기본 꺼짐)를 상속해요. 기대된 부드러운 저하에 대한 메시지만 설정하는 on_failure와 직교해요. contained 충돌은 항상 시끄러운 ModelRetry를 발생시켜요.

Type: bool | None Default: None

resolved_name

대표의 이름: 설정되면 name, 그 외 에이전트 자신의 name.

Type: str | None

ModelOption

모델 메뉴의 항목 하나. 모델과 어떻게 실행되어야 하는지.

메뉴 키가 스스로 말할 때는 순수 모델 참조를 전달하고, 항목이 라우팅 힌트나 자체 설정이 필요할 때는 ModelOption을 전달하세요:

from pydantic_ai.settings import ModelSettings
from pydantic_ai_harness.subagents import ModelOption, SubAgents

SubAgents(
    models={
        'fast': 'anthropic:claude-haiku-4-5',
        'deep': ModelOption(
            'anthropic:claude-opus-4-7',
            description='hard reasoning, multi-file changes',
            settings=ModelSettings(thinking='xhigh'),
        ),
    },
)

Attributes

model

이 옵션으로 라우팅된 위임이 실행되는 모델.

Type: Model | KnownModelName | str

description

이 옵션이 무엇을 위한 것인지, 키 옆에 프롬프트에 나열되어 부모가 모델 이름만이 아니라 작업 난이도로 라우팅할 수 있게 함.

Type: str | None Default: None

settings

이 옵션으로 라우팅된 위임을 위한 설정 — thinking effort, temperature 등. 하위 에이전트의 자체 model_settings 위에 병합되며, 여기서 재정의하지 않는 하위 에이전트가 설정한 것을 유지해요.

Type: ModelSettings | None Default: None

AgentOverride

에이전트의 이름으로 키된 디스크 로드 하위 에이전트의 에이전트별 재정의.

두 필드 모두 선택적. 설정되지 않은 model은 부모 실행의 모델을 상속하고, 설정되지 않은 effort는 capability의 최소 effort 바닥에서 실행돼요(clamp_effort 참고).

Attributes

model

부모의 것을 상속하는 대신 이 디스크 에이전트를 실행할 모델.

Type: Model | KnownModelName | str | None Default: None

effort

이 디스크 에이전트의 thinking/추론 수준. 최소 바닥까지 올라감.

Type: ThinkingLevel | None Default: None

DelegationStartEvent

Bases: CapabilityEvent

한 위임에 대해 하위 에이전트 실행이 시작되려 합니다.

위임이 거부할 수 있는 모든 검사를(알 수 없는 하위 에이전트, 메뉴 밖의 모델 키, 소진된 max_calls 예산) 통과한 직후, 자식 실행이 시작되기 바로 전에 방출돼요.

Attributes

model

위임이 실행되는 메뉴 키, 또는 옵션이 선택되지 않았을 때 None: 메뉴가 없거나, 대표가 전체 메뉴를 허용하고 부모가 키를 이름지지 않음.

Type: str | None

inherits_tools

부모의 자체 도구가 자식 실행에 전달됐는지(SubAgents.inherit_tools).

Type: bool

DelegationEndEvent

Bases: CapabilityEvent

위임이 부모가 받는 것으로 정착했어요.

output은 대표 도구가 부모에 돌려주는 것이에요. ok에서는 자식의 출력, 그 외에는 반환하는 스티어링 메시지나 발생시키는 ModelRetry. 대표 도구 밖으로 전파되는 예외(공유 사용 한도, uncontained 충돌, 취소)는 이 이벤트 없이 끝나요.

Attributes

usage

자식이 별도 회계(SubAgent.usage_limits 설정 또는 forward_usage 꺼짐)가 있을 때 자식의 자체 usage. 부모의 usage로 발생할 때는 None.

Type: RunUsage | None

duration_seconds

시작 이벤트가 방출된 직후부터 위임이 정착할 때까지의 벽시계 초.

Type: float

더 알아보기 (Learn more)