캐퍼빌리티

캐퍼빌리티 (Capabilities)

캐퍼빌리티(capability)는 재사용 가능하고 조립 가능한 에이전트 동작의 단위예요. Agent 생성자에 여러 인자를 흩어 넣는 대신 — 여기 지침, 저기 모델 설정, 또 다른 데 툴셋, 또 다른 파라미터에 히스토리 프로세서 — 관련 동작을 하나의 캐퍼빌리티로 묶어서 capabilities 파라미터로 전달할 수 있어요.

출처: 문서

본문

캐퍼빌리티는 다음 조합을 제공할 수 있어요:

  • 도구 (Tools)툴셋 또는 네이티브 도구를 통해
  • 라이프사이클 훅 (Lifecycle hooks) — 모델 요청, 도구 호출, 전체 런을 가로채고 수정
  • 지침 (Instructions) — 정적 또는 동적 지침 추가
  • 모델 설정 (Model settings) — 정적 또는 스텝별 모델 설정
  • 모델 (Models) — 정적·적응형 모델 선택 및 애플리케이션 특정 모델 ID 해석

이것이 캐퍼빌리티를 Pydantic AI의 기본 확장 지점으로 만듭니다. 메모리 시스템, 가드레일, 비용 추적기, 승인 워크플로 중 무엇을 만들든, 캐퍼빌리티가 올바른 추상화예요.

캐퍼빌리티는 항상 켜짐이거나 모델이 온디맨드로 로드할 수 있어요. 아래 캐퍼빌리티 색인은 Pydantic AI와 Pydantic AI Harness를 다룹니다. 서드파티 패키지가 훨씬 더 많은 캐퍼빌리티를 제공하며, 여러분은 선언적으로 또는 서브클래싱으로 정의할 수 있어요. 실패, 재시작, 긴 대기를 넘어 에이전트를 지속적으로 실행하려면 지속 실행을 보세요.

사용 가능한 캐퍼빌리티 (Available capabilities)

캐퍼빌리티는 두 패키지에서 오며, 서로 그리고 여러분이 정의한 캐퍼빌리티와 조립됩니다. 코어(pydantic-ai)는 모델이나 프레임워크 지원이 필요한 캐퍼빌리티를 동봉합니다: 공급자 네이티브 도구, 공급자 API, 깊은 루프 통합이요. 공식 캐퍼빌리티 라이브러리이자 하니스인 Pydantic AI Harness 는 그 외의 모든 것을, 단일 캐퍼빌리티부터 완전한 에이전트까지 동봉합니다. Package 열이 어느 것인지 알려주고, 모든 항목이 문서로 연결됩니다.

하니스 (Harnesses)

일반 결합 캐퍼빌리티로서의 완전한 에이전트 스택: 한 번의 임포트로 동작하는 에이전트를 얻고, 아래 블록들로 분해할 수 있어요.

하니스 패키지 제공하는 것
Coder Harness 완전한 코딩 에이전트 스택: 파일, 셸, 저장소 컨텍스트, 플래닝, 읽기 전용 탐색 서브에이전트, 컨텍스트 제어
Researcher Harness 완전한 웹 리서치 스택: 검색, 페이지 페치, 위임된 서브 리서처, 제한된 도구 출력

실행 환경 (Execution environments)

에이전트가 동작하는 워크스페이스: 그것이 편집하는 파일과 실행하는 명령, 로컬 또는 격리.

캐퍼빌리티 패키지 하는 일
FileSystem Harness 루트 아래 파일 읽기·쓰기·편집·검색; 경로 횡단·심볼릭 링크 안전, 시크릿 읽기 전용
Shell Harness 허용 목록·차단 목록·타임아웃·자격 증명 제거가 있는 명령 실행
Modal Sandbox Harness 격리된 Modal 클라우드 샌드박스에서 명령·파일

도구와 네이티브 능력 (Tools & native abilities)

에이전트 워크스페이스 밖 시스템으로의 연결, 그리고 공급자가 네이티브로 실행하는 능력.

캐퍼빌리티 패키지 하는 일
MCP Core 어떤 MCP 서버의 도구든 연결; 기본 로컬, 공급자 네이티브 커넥터 옵트인
Image Generation Core 이미지 생성·편집; 지원되면 공급자 네이티브, 그 외 직접 이미지 모델 폴백
Native Tool Core 어떤 공급자 네이티브 도구든 에이전트에 등록
StackOne Harness StackOne으로 연결된 SaaS 계정(HRIS, ATS, CRM...)에서 동작
LocalStack Harness AWS CLI 도구가 있는 에뮬레이트된 AWS 환경
Macroscope Harness 로컬 Macroscope 코드 리뷰 실행 및 결과를 에이전트에 전달

웹과 리서치 (Web & research)

열린 웹에서 무언가를 찾고 읽는 것.

캐퍼빌리티 패키지 하는 일
Web Search Core 가능하면 공급자 네이티브 검색, 어디서나 로컬 DuckDuckGo 폴백
Web Fetch Core URL 가져오기·읽기, 네이티브 또는 로컬
X Search Core X 검색; xAI에서 네이티브, 그 외 서브에이전트 폴백
Exa Search Harness Exa로 웹 리서치: 발췌 검색, 전체 페이지 읽기, 옵트인 인용 딥 검색
Exa Agent Harness 개방형 리서치를 Exa Agent API에 위임
You.com Search Harness You.com으로 웹 검색·페이지 읽기: 쿼리 관련 발췌 또는 전체 페이지 마크다운
You.com Research Harness You.com Answer·Research·Finance Research API로 인용 답변과 다단계 리서치
Browser Use Harness 실제 브라우저를 구동하는 자율 browser-use 에이전트에 웹 작업 위임
Playwright Browser Harness 실제 Chromium 페이지를 직접 구동: 탐색, 클릭, 입력, 읽기, 페이지가 한 일 검사

추론, 플래닝과 위임 (Reasoning, planning & delegation)

에이전트가 어떻게 생각하고 일을 나누는지.

캐퍼빌리티 패키지 하는 일
Thinking Core 설정 가능한 노력의 provider-adaptive 확장 추론
Planning Harness 캐시 안전한 실시간 알림이 있는 모델 소유 태스크 계획
Subagents Harness 자족적 작업을 이름 붙은 자식 에이전트에 위임
Dynamic Workflow Harness 모델이 하나의 파이썬 스크립트에서 서브에이전트 오케스트레이션: 단일 도구 호출에서 fan-out·chain·vote, 하드 max_agent_calls 예산 포함
Advisor Harness 실행자가 런 도중 더 강한 모델을 상담하게 함
Background Tools Harness 선택한 도구를 동시에 실행; 결과가 후속 메시지로 도착

컨텍스트 관리 (Context management)

에이전트가 컨텍스트 창을 쓰는 방식: 긴 런에서 열화되는 에이전트와 그렇지 않은 에이전트의 차이, 토큰을 N번 지불하는 것과 한 번 지불하는 것의 차이.

캐퍼빌리티 패키지 하는 일
Code Mode Harness 모델이 Monty 샌드박스 안에서 많은 도구를 호출하는 파이썬 스크립트 하나를 작성: N번 왕복 대신 1번, 중간 결과가 컨텍스트 창에 절대 들어가지 않음
Tool Search Core 매 프롬프트에 수백 개를 싣는 대신 온디맨드로 도구 정의 로드
Compaction Core OpenAI·Anthropic의 공급자 네이티브 압축; 공급자가 서버 측에서 히스토리 요약
Compaction Harness 모델 무관 전략: 도구 결과 지우기, 슬라이딩 윈도우 정리, LLM 요약, 계층적; 모두 윈도우 상대, 실시간 사용량 보고
Tool Output Limits Harness 소스에서 과대 도구 반환을 잘라내거나·쿼리 가능한 파일로 누출시키거나·요약
Warn On Cache Busts Harness 공급자 자체 숫자에서 요청 간 프롬프트 캐시 접두사 붕괴 감지

지식과 메모리 (Knowledge & memory)

에이전트가 알고 기억하는 것, 관련될 때 로드되고 매 프롬프트에 실리진 않음. Storage는 이것들이 대화 히스토리 자체 — 에이전트가 처한 런에 대한 기억 — 옆에 어떻게 놓이는지를 다룹니다.

캐퍼빌리티 패키지 하는 일
Memory Harness 영속적이고 네임스페이스된 노트북: 제한된 프롬프트 주입, 온디맨드 검색; 인메모리/파일/Postgres 저장소
Conversation Search Harness 저장된 히스토리 위 BM25 검색, 압축이 버린 턴 포함
Skills Harness Agent Skill (SKILL.md) 지침 온디맨드 로드
Repo Context Harness AGENTS.md/CLAUDE.md + 저장소 구조로 런 방향 잡기
Pydantic AI Docs Harness 온디맨드 Pydantic AI 문서 조회

제어와 안전 (Control & safety)

에이전트가 할 수 있는 것을 제한하고 지침을 따르게 하는 것.

캐퍼빌리티 패키지 하는 일
Guardrails Harness 사용자 입력, 도구 호출, 도구 결과, 출력 검증/차단/삭제, 시크릿 마스킹과 병렬 async 가드 포함
Prompt Injection Defender Harness 간접 프롬프트 주입을 위해 로컬 도구 결과를 분류하고 선택적으로 고위험 결과 보류
Spend Limits Harness 크로스 윈도우 USD/토큰 예산과 응답별 비용 추적, 모델별·테넌트별
Ask User Harness 모델이 런 도중 사용자에게 객관식 질문; 여러분이 답변자를 공급 (터미널, 웹, 테스트)
Tool approval Core 실행 전 사람 승인이 필요한 도구 호출 표시
Handle Deferred Tool Calls Core 승인-지연 도구 호출을 프로그래매틱하게 해결
System Reminders Harness 지침 퇴색을 막기 위해 런 중간에 안내를 캐시 안전하게 재주입
Trajectory Judge Harness 슬라이딩 토큰 윈도우로 매 N 요청마다 두 번째 모델이 실시간 런을 검토하고 런 도중 방향 제시

자가 확장 (Self-extension)

캐퍼빌리티 패키지 하는 일
Capability Creation Harness 에이전트가 런 중 새 캐퍼빌리티 를 작성·검증·영속화, 다음 런에서 로드: 임의 코드 대신 타입 있고 검사 가능한 단위로 자가 확장

실행 런타임 (Execution runtime)

루프 밖: 런이 영속화되고, 실패를 견디고, 프로덕션에서 관측·구성되는 방식.

캐퍼빌리티 패키지 하는 일
Durable execution Core Temporal, DBOS, Prefect에서 재시작·실패를 견디는 런, Restate, Kitaru, Airflow 통합 포함
AWS Lambda durability Harness 모델 요청·도구 호출을 AWS Lambda 내구성 함수 스텝으로 체크포인트
Step Persistence Harness 런 저장·복원·재개(continue_run)·포크(fork_run); 파일/SQLite/Mongo 백엔드
Instrumentation Core 모든 모델·도구 호출에 OpenTelemetry GenAI 스팬; Logfire 트레이스의 원료
Managed Prompt Harness 지침을 Logfire 관리 프롬프트로 뒷받침; 재배포 없이 버전 관리·롤아웃
Repair Tool Arguments Harness 스키마 검증 전 잘못된 JSON 도구 인수 복구
Thread Executor Core 공유 스레드 풀에서 동기 도구 실행

루프 커스터마이즈 (Loop customization)

코어도 에이전트 루프 자체를 커스터마이즈하는 캐퍼빌리티를 동봉하는데, 대부분 프로덕션 서버용이에요:

캐퍼빌리티 패키지 하는 일
Hooks Core 데코레이터 기반 라이프사이클 훅 등록
Select Model Core callable로 정적 또는 스텝별 모델 선택
Resolve Model ID Core callable로 커스텀 애플리케이션 특정 모델 ID 해석
Prepare Tools / Prepare Output Tools Core 스텝별로 함수·출력 도구 정의 필터링·수정
Prefix Tools Core 캐퍼빌리티 감싸기 및 도구 이름 접두사 붙이기
Include Tool Return Schemas Core 모델에 보내는 도구 정의에 반환 타입 스키마 포함
Set Tool Metadata Core 메타데이터 키-값 쌍을 선택한 도구에 병합
Raise Content Filter Error Core 모델 응답이 finish_reason='content_filter'일 때마다 ContentFilterError 발생
Reinject System Prompt Core 들어오는 메시지 히스토리에 시스템 프롬프트가 없으면 설정된 것을 재주입
Process History Core 히스토리 프로세서 감싸기
Process Event Stream Core 에이전트 스트림 이벤트를 핸들러 함수로 전달

작성 원시 요소, 서브클래싱 없이 동작 묶기를 위한 CapabilityAbstractToolset 감싸기를 위한 Toolset은 아래에서 다룹니다. ACP (실험, Harness)Agent Client Protocol로 어떤 에이전트든 Zed 같은 편집기에 제공합니다. YAML/JSON 에이전트 스펙으로 선언할 수 있는 캐퍼빌리티는 거기 나열됩니다.

from pydantic_ai import Agent
from pydantic_ai.capabilities import Thinking, WebSearch

agent = Agent(
    'anthropic:claude-fable-5',
    instructions='You are a research assistant. Be thorough and cite sources.',
    capabilities=[
        Thinking(effort='high'),
        WebSearch(local='duckduckgo'),
    ],
)

지침모델 설정Agent(또는 AgentSpec)의 instructionsmodel_settings 파라미터로 직접 구성합니다. 캐퍼빌리티는 단순 구성 — 도구, 라이프사이클 훅, 커스텀 확장 — 을 넘어서는 동작을 위한 거예요. 특히 여러 에이전트에서 같은 설정을 재사용하거나 스펙 파일에서 로드하고 싶을 때 잘 조립됩니다.

Capability로 동작 묶기 (Bundling behavior with Capability)

자신의 캐퍼빌리티를 정의하려고 서브클래스가 필요하지 않아요. Capability는 지침, 함수 도구, 툴셋을 선언적으로 묶습니다 — 스킬을 정의한다고 생각하면 돼요:

from pydantic_ai import Agent
from pydantic_ai.capabilities import Capability

refunds = Capability(
    id='refunds',
    description='Use for refund eligibility and refund status.',
    instructions='Always confirm the order ID before issuing a refund.',
)


@refunds.tool_plain
def refund_status(order_id: str) -> str:
    """Look up the refund status for an order."""
    return f'Order {order_id}: refund issued on 2026-05-01.'


agent = Agent('openai:gpt-5.6-sol', capabilities=[refunds])

defer_loading=True를 추가하면 묶음이 모델이 로드할 때까지 한 줄 카탈로그 항목으로 접힌 온디맨드 캐퍼빌리티가 됩니다 — Capability로 직접 감쌀 수 있는 Agent Skills처럼요. 전체 API는 The Capability convenience class를 보세요. 지침, 도구, 툴셋을 넘어서는 동작 — 라이프사이클 훅, 모델 설정, 네이티브 도구 — 은 Building Custom Capabilities에서 다루듯 AbstractCapability를 서브클래스하세요.

캐퍼빌리티 이벤트 (Capability events)

재사용 가능한 캐퍼빌리티는 조정(코디네이션)과 관측성을 위해 타입 있는 CapabilityEvent를 발행할 수 있어요. 이벤트 패밀리에 안정적인 네임스페이스를 주고, 각 페이로드를 dataclass로 정의하고, async 캐퍼빌리티 훅이나 캐퍼빌리티 기여 도구에서 ctx.emit()을 await해서 발행하세요:

from dataclasses import dataclass
from typing import Any

from pydantic_ai import CapabilityEvent, RunContext
from pydantic_ai.capabilities import AbstractCapability
from pydantic_ai.toolsets import AgentToolset, FunctionToolset

WORKSPACE = 'workspace'


@dataclass(kw_only=True)
class FileWriteEvent(CapabilityEvent, namespace=WORKSPACE):
    path: str
    bytes_written: int


workspace = FunctionToolset()


@workspace.tool
async def write_file(ctx: RunContext[Any], path: str, content: str) -> str:
    await ctx.emit(FileWriteEvent(path=path, bytes_written=len(content)))
    return f'Wrote {path}'


@dataclass
class Workspace(AbstractCapability[Any]):
    def get_toolset(self) -> AgentToolset[Any] | None:
        return workspace

(이 예제는 완전하며, "그대로" 실행할 수 있어요)

네임스페이스를 모듈 수준 상수에 두고 캐퍼빌리티의 이름이나 id를 주세요. 패밀리의 모든 이벤트가 그것을 반복하며, 구독자가 매칭하는 접두사가 됩니다.

캐퍼빌리티는 특히 캐퍼빌리티 이벤트를 발행하고, 거기서 애플리케이션 CustomEvent를 발행하면 UserError가 발생해요. 구분은 어떤 이벤트 타입을 쓰나요?를 보세요. 페이로드는 봉투가 스스로 필요로 하는 필드 이름을 쓸 수 없습니다: data, capability_id, tool_call_id, tool_name, event_kind는 클래스가 정의될 때 거부됩니다.

Pydantic AI는 발행하는 캐퍼빌리티의 런 id를 capability_id로 찍습니다. id를 준 캐퍼빌리티라면 그것이 그 id예요. 주지 않았다면 프레임워크가 그 런을 위해 만드는 핸들 — '<file_reader:4f3a9c>' — 인데, 매 런마다 달라서 매칭하면 안 됩니다. 구독자가 인식해야 한다면 명시적 id를 주세요. 그 도구가 발행한 이벤트도 tool_call_idtool_name을 받습니다. 그것들은 에이전트 런 이벤트 스트림에 표시되지만 내부 조정 신호라서 UI 어댑터는 기본적으로 전달하지 않아요. 프로토콜 어댑터는 handle_capability_event()을 오버라이드해서 자기 프로토콜에 매핑할 수 있는데, CapabilityEventto_payload()가 없으므로 페이로드를 스스로 만듭니다. 프런트엔드에 노출하려는 애플리케이션은 대신 @agent.on_event로 수신하고 공개 페이로드를 싣는 애플리케이션 CustomEvent를 발행할 수 있어요:

from dataclasses import dataclass

from pydantic_ai import Agent, CapabilityEvent, CustomEvent, RunContext

SEARCH_INDEX = 'search_index'


@dataclass(kw_only=True)
class IndexRebuiltEvent(CapabilityEvent, namespace=SEARCH_INDEX):
    documents: int


@dataclass(kw_only=True)
class SearchReadyEvent(CustomEvent):
    documents: int


agent = Agent('test')


@agent.on_event(IndexRebuiltEvent)
async def republish(ctx: RunContext, event: IndexRebuiltEvent) -> None:
    await ctx.emit(SearchReadyEvent(documents=event.documents))

(이 예제는 완전하며, "그대로" 실행할 수 있어요)

리스너는 캐퍼빌리티가 아니라 애플리케이션에 속하므로, 애플리케이션 CustomEvent를 발행할 수 있는 장소 중 하나예요. Hooks.on.event는 어차피 Hooks 캐퍼빌리티를 원할 때 — 다른 훅 패밀리 때문이거나, 다른 캐퍼빌리티들 사이의 위치를 고르려고 — 같은 일을 합니다.

네임스페이스와 이벤트 이름이 직렬화된 kind를 이룹니다 (예: workspace.file_read), 그리고 명시적 name=을 전달하지 않으면 이벤트 이름이 클래스 이름에서 유도됩니다. 네임스페이스는 필수예요: 네임스페이스 없이 CapabilityEvent 서브클래스를 정의하면 네임스페이스 없는 이벤트가 스트림에 닿게 놔두는 대신 그 자리에서 TypeError를 냅니다. 다만 패밀리당 한 번만 주면 됩니다 — 다른 캐퍼빌리티 이벤트를 서브클래스하는 이벤트는 그 네임스페이스를 상속하고 자기 이름만 기여하므로, 공유 베이스가 패밀리를 정의하는 가장 깔끔한 방법이에요 — 그리고 서브클래스는 자체 namespace=를 전달해 상속받은 네임스페이스에서 벗어날 수 있습니다. 네임스페이스와 패밀리 공통 필드만 담는 베이스를 abstract=True로 표시하면, 레지스트리에서 빠지고 스스로 발행될 수 없으며, 서브클래스들은 평소처럼 등록됩니다. 다른 이벤트처럼 @dataclass로 장식하세요. 장식되지 않은 베이스는 필드가 전혀 기여하지 않는데, 페이로드가 조용히 빠진 채 표면화되도록 놔두는 대신 거부됩니다. kind는 이벤트의 통신 식별자이므로 클래스 이름을 바꾸면 태그도 함께 바뀌어, 이벤트가 발행 프로세스를 오래 살아남는 곳 — 지속 실행 히스토리와 캐시, 영속 이벤트 로그, kind로 매칭하는 구독자 — 에서 호환성을 깨뜨립니다. 라이브러리로 배포하는 캐퍼빌리티는 각 이벤트에 name=을 고정해야 해요. Kind는 클래스가 정의될 때 등록되고 프로세스 내에서 유일해야 합니다. 같은 클래스 정의를 재실행하면(노트북 셀을 다시 실행할 때처럼) 등록을 교체해요. 이벤트를 역직렬화하는 어댑터를 만들기 전에 그 이벤트를 정의하는 모듈을 임포트하세요. 각 pydantic TypeAdapter가 생성될 때 등록된 kind를 포착하니까요. 그렇지 않으면 이벤트가 페이로드 필드를 잃지 않으면서 UnknownCapabilityEvent가 되고 UserWarning이 발행됩니다.

이벤트에 반응하기 (Reacting to events)

async 캐퍼빌리티 메서드에서 @on_event을 사용해 선택된 이벤트 클래스에 반응하세요. 예를 들어 저장소 컨텍스트 캐퍼빌리티는 파일시스템 캐퍼빌리티가 저장소 안내 파일 읽기를 보고한 직후 지침을 큐에 넣을 수 있어요:

from dataclasses import dataclass
from typing import Any

from pydantic_ai import CapabilityEvent, RunContext
from pydantic_ai.capabilities import AbstractCapability, on_event

REPO_CONTEXT = 'repo_context'


@dataclass(kw_only=True)
class FileReadEvent(CapabilityEvent, namespace=REPO_CONTEXT):
    path: str


@dataclass(kw_only=True)
class DirectoryListedEvent(CapabilityEvent, namespace=REPO_CONTEXT):
    path: str


class RepoContext(AbstractCapability[Any]):
    @on_event(FileReadEvent, DirectoryListedEvent)
    async def _follow_discovered_instructions(
        self,
        ctx: RunContext[Any],
        event: FileReadEvent | DirectoryListedEvent,
    ) -> None:
        if event.path.endswith('AGENTS.md'):
            ctx.enqueue(f'Follow the instructions in {event.path}.')

필터링은 명시적입니다. 데코레이터는 전달된 클래스에 대해 isinstance를 사용해요. 베어 @on_event는 모델 응답 델타, 도구 호출·결과 이벤트, 지연·큐 삽입 메시지 이벤트, 커스텀 이벤트, 캐퍼빌리티 이벤트를 포함한 전체 AgentStreamEvent 유니온을 받습니다.

가능하면 클래스 이름을 지정하세요. 타입 체커를 위해 event 인수를 좁히는 것 외에, 클래스들은 디스패치가 캐퍼빌리티에 들어가지 않고 건너뛰게 합니다. 캐퍼빌리티는 그 리스너 중 하나가 수용하는 이벤트에만 깨어나거든요. 베어 @on_event — 또는 디스패치를 미리 알 수 없는 오버라이드된 on_event() — 은 그 캐퍼빌리티를 모든 이벤트에 옵트인시키고, 다른 것을 결합하는 캐퍼빌리티는 자식들의 유니온을 보고합니다. on_event()를 오버라이드하고 무엇을 디스패치하는지 설명할 수 있다면, 그것과 함께 listens_to()도 오버라이드해서 그렇게 말하세요.

리스너는 캐퍼빌리티 순서대로 순차 실행되고, 한 캐퍼빌리티 내 표시된 메서드는 정의 순서대로 실행됩니다. 발행 캐퍼빌리티도 자기 이벤트를 받아요. 기본적으로 리스너는 이벤트가 스트림에서 그 위치에 도달할 때 실행되므로, 리스너 순서가 항상 스트림 순서와 일치하고 리스너 작업이 발행자의 지연에 더해지지 않아요. 도구 실행 중 발행된 이벤트의 리스너는 다음 모델 요청 전에 실행됩니다. before_model_request 중에 발행된 이벤트는 그 요청이 시작된 후에만 리스너에 닿을 수 있는데, ctx.enqueue()과 같은 as-soon-as-possible 타이밍이에요.

발행자가 계속하기 전에 리스너 변경이 필요한 결정 이벤트는 이벤트 클래스에 dispatch='immediate'를 선언합니다. 위 워크스페이스 캐퍼빌리티에 기반해, 쓰기가 일어나기 전에 스스로 알리고 리스너가 거부권을 행사하게 할 수 있어요:

from dataclasses import dataclass
from typing import Any

from pydantic_ai import CapabilityEvent, RunContext
from pydantic_ai.capabilities import AbstractCapability, on_event
from pydantic_ai.toolsets import FunctionToolset

WORKSPACE = 'workspace'


@dataclass(kw_only=True)
class FileWriteStartEvent(
    CapabilityEvent, namespace=WORKSPACE, dispatch='immediate'
):
    path: str
    cancelled: bool = False
    cancel_reason: str | None = None

    def cancel(self, reason: str | None = None) -> None:
        self.cancelled = True
        self.cancel_reason = reason


workspace = FunctionToolset()


@workspace.tool
async def write_file(ctx: RunContext[Any], path: str, content: str) -> str:
    event = await ctx.emit(FileWriteStartEvent(path=path))  # (1)
    if event.cancelled:  # (2)
        return f'Refused to write {path}: {event.cancel_reason}'
    return f'Wrote {path}'


class ProtectGitDirectory(AbstractCapability[Any]):
    @on_event(FileWriteStartEvent)
    async def _veto_writes_to_git(
        self, ctx: RunContext[Any], event: FileWriteStartEvent
    ) -> None:
        if event.path.startswith('.git/'):
            event.cancel('.git/ is managed by the repository, not the agent')
  • (1) emit은 모든 리스너가 실행된 후에만 반환하며, 주어진 것과 같은 인스턴스를 반환합니다.
  • (2) 따라서 결정은 반환된 이벤트든 발행자 자신의 참조든 즉시 읽을 수 있어요.

(이 예제는 완전하며, "그대로" 실행할 수 있어요)

즉시 디스패치의 경우 Pydantic AI는 리스너를 호출하기 전에 이벤트를 버퍼링하지만, 스트림 소비자는 모든 리스너가 실행된 후에만 그것을 받으므로 반쯤 만들어진 결정을 결코 관찰하지 못해요. 귀속(attribution)은 이벤트에 제자리에서 찍히므로 await ctx.emit(event) 후 발행자는 event.cancelled를 자기 참조(emit이 반환하는 같은 인스턴스)에서 읽을 수 있고, 리스너가 발행한 이벤트는 결정 이벤트 뒤에 스트림에 나타납니다. 인라인 이벤트는 같은 이벤트 인스턴스가 재발행될 때를 포함해 여전히 정확히 한 번 전달됩니다. 스트림 디스패치 리스너는 사용자 정의 wrap_run_event_stream() 래퍼 안에서 실행됩니다.

참고

어떤 on_event 리스너든 그 외에는 비-스트리밍인 agent.run()에 스트리밍을 자동으로 활성화합니다. 이벤트가 있어야 들을 수 있으니까 모델 요청이 공급자의 스트리밍 API로 만들어지기 때문이에요. 공급자는 거의 모든 측면에서 스트리밍·비-스트리밍 요청을 똑같이 취급하지만, 비-스트리밍 요청을 보장해야 한다면 리스너를 붙이지 마세요.

Provider-adaptive 도구

WebSearch, WebFetch, ImageGeneration, XSearch, MCP는 각각 두 구현에 걸쳐 하나의 캐퍼빌리티(웹 검색, URL 페치, 이미지 생성, X 검색, MCP)를 다룹니다:

  • 네이티브 (Native) — 모델이 지원할 때 모델 공급자가 호출. 작업이 공급자 측에서 일어나요 (예: Anthropic의 웹 검색은 서버 측에서 실행되어 결과를 인라인으로 반환).
  • 로컬 (Local) — 여러분의 파이썬 프로세스에서 실행. 모델이 네이티브 도구를 지원하지 않을 때 사용하며, 여러분의 코드가 작업을 해요 (예: DuckDuckGo 직접 호출).
캐퍼빌리티 로컬 폴백 참고
WebSearch local='duckduckgo' 또는 local=True (DuckDuckGo) duckduckgo 옵션 그룹 필요
WebFetch local=True (markdownify 기반 페치) web-fetch 옵션 그룹 필요
ImageGeneration local=ImageGenerator(...) 또는 fallback_image_model='provider:image-model'로 직접 모델, 또는 fallback_subagent_model=로 서브에이전트 직접 경로는 서브에이전트 없이 직접 이미지 생성 API 사용
XSearch fallback_subagent_model=로 서브에이전트 기본 비-xAI 폴백 없음; XSearchTool을 지원하는 xAI 모델로 fallback_subagent_model 설정
MCP MCP 서버로 직접 연결 (기본값) 어떤 MCPToolset 입력도 허용; 트랜스포트는 URL에서 자동 감지

이 캐퍼빌리티들은 모델 직면 도구를 기여하므로 id, description, defer_loading 필드가 의미 있어요. 해당 도구가 모델이 load_capability 도구로 일치하는 워크플로를 로드할 때까지 숨어 있어야 한다면 descriptiondefer_loading을 설정하세요. 이 각각은 단일 고정 관심사를 다루므로 id는 이미 안정적인 값('web_search', 'web_fetch', 'image_generation', 'x_search'; MCP는 서버 URL에서 유도)으로 기본 설정됩니다 — 그것이 지속 실행이 그것들이 기여하는 툴셋을 식별하는 것이므로, 설정 없이 동작해요. 이름을 바꾸려고만 id를 설정하고, 두 개가 하나를 공유하면 어떻게 되는지는 커스텀 캐퍼빌리티 만들기를 보세요. 이미지 생성이 이미지 특정 워크플로에만 제공되어야 할 때의 ImageGeneration도 여기에 포함됩니다 — 네이티브 이미지 도구, 직접 이미지 모델 폴백, 서브에이전트 폴백 중 무엇으로 해석되든요.

여러 툴셋을 기여하는 Capability는 각각을 '{capability_id}_{index}'로 이름 짓습니다 — 지속 실행이 그것들을 등록하는 id예요.

각 측은 native=local= kwargs로 구성합니다. native=True(캐퍼빌리티의 기본 네이티브 도구 인스턴스 사용), False(네이티브 비활성화), 곱게 구성하기 위한 명시적 인스턴스(WebSearchTool(...) 같은), 또는 네이티브 도구나 None을 반환하는 RunContext를 받는 callable을 받아요 (참고: Dynamic Configuration). None을 반환하는 팩토리는 그 요청에 대해 네이티브 도구를 생략합니다. local=True(있는 캐퍼빌리티 — WebSearch, WebFetch — 의 번들 로컬 폴백), False(로컬 비활성화), 지원되는 곳의 이름 붙은 전략 문자열, 또는 callable, Tool, AbstractToolset — 그리고 ImageGeneration에서는 ImageGenerator — 를 받습니다. 로컬 폴백에 필요한 선택 설치물은 옵트인이에요. 엑스트라가 설치되지 않은 로컬 전략을 요청하면 캐퍼빌리티는 생성 시(설치 힌트 포함) UserError를 발생시킵니다.

fallback_subagent_model이 있는 캐퍼빌리티의 None

XSearchImageGeneration은 미지원 모델을 로컬 도구 대신 서브에이전트로 라우팅합니다. fallback_subagent_model이 설정되면, None을 반환하는 팩토리는 더 이상 아무것도 생략하지 않아요. 서브에이전트 도구는 네이티브를 지원하는 모델에도 여전히 제안되고, 그것을 호출하면 UserError가 발생합니다 — 참고: X SearchImage Generation.

from pydantic_ai import Agent
from pydantic_ai.capabilities import MCP, ImageGeneration, WebFetch, WebSearch, XSearch

agent = Agent(
    'anthropic:claude-fable-5',
    capabilities=[
        # Native when supported; DuckDuckGo fallback on unsupported models
        WebSearch(local='duckduckgo'),
        # Native when supported; markdownify-based fallback on unsupported models
        WebFetch(local=True),
        # Native when supported; direct image-model fallback otherwise
        ImageGeneration(fallback_image_model='openai:gpt-image-1.5'),
        # Native on xAI; on other models, explicitly delegate to an xAI model
        XSearch(fallback_subagent_model='xai:grok-4.3'),
        # Runs the MCP server locally by default; pass `native=True` to also advertise native MCP
        MCP('https://mcp.example.com/api'),
    ],
)

MCP는 다른 것과 반대로 기본 설정됩니다. MCP가 자격 증명을 지니므로 기본적으로 로컬에서 실행되고, native=True로 네이티브 MCP에 옵트인합니다. 나머지는 기본적으로 네이티브이고 local=로 로컬에 옵트인합니다.

XSearchWebSearchWebFetch와 조금 달라요. 기본 비-xAI 폴백이 없습니다. 에이전트가 xAI 모델에서 실행되지 않는다면 XSearchTool을 지원하는 xAI 모델로 fallback_subagent_model을 명시적으로 설정하세요.

fallback_model은 더 이상 사용되지 않아요

XSearchImageGeneration 둘 다에서 fallback_modelfallback_subagent_model의 이전 이름입니다. 파이썬과 에이전트 스펙에서 계속 동작하며 경고를 냅니다. 두 이름을 함께 전달하면 거부됩니다: 캐퍼빌리티를 만들 때는 UserError, 스펙 로더는 그것을 ValueError로 감쌉니다. 새 이름은 어떤 폴백을 구성하는지 말해 줍니다: local= 도구 — 또는 ImageGenerationfallback_image_model — 가 여러분의 프로세스에서 실행되는 것에 반해, 추가 에이전트 런을요.

일부 제약 필드는 네이티브 도구가 필요해요 (번들 로컬 폴백은 그것을 강제할 수 없음) — 그것들을 전달하면 캐퍼빌리티를 네이티브 경로에 고정합니다. 모델이 네이티브 도구를 지원하지 않으면 캐퍼빌리티는 UserError를 발생시킵니다.

# Limit to 5 searches per run -- requires native (the local fallback can't track call count)
WebSearch(max_uses=5)

# Only fetch example.com -- enforced locally when native is unavailable
WebFetch(allowed_domains=['example.com'], local=True)

자신의 것 만들기 (Building your own)

다섯 캐퍼빌리티 모두 직접 사용하거나 서브클래스화해 자신의 provider-adaptive 도구를 만들 수 있는 NativeOrLocalTool의 서브클래스예요. 예를 들어 CodeExecutionTool을 로컬 폴백과 짝지으려면:

from pydantic_ai.native_tools import CodeExecutionTool
from pydantic_ai.capabilities import NativeOrLocalTool

cap = NativeOrLocalTool(native=CodeExecutionTool(), local=my_local_executor)

서드파티 캐퍼빌리티 (Third-party capabilities)

서드파티 패키지가 자신의 캐퍼빌리티를 배포해요 — 생태계는 Third-Party Capabilities를, 여러분의 캐퍼빌리티를 다른 사람이 쓰게 만드는 방법은 Publishing capabilities를 보세요.

더 알아보기 (Learn more)