런타임 Capability 생성

런타임 Capability 생성 (Runtime Capability Creation)

런타임 capability 생성은 에이전트가 한 실행 동안 Pydantic AI capability를 작성·검증·영속해 다음 실행에서 활성화하게 해요. CapabilityCreation은 모델이 AbstractCapability 서브클래스를 파이썬 소스로 디스크에 쓰고 즉시 검증하게 하는 도구를 노출해요. 오케스트레이터는 활성 작성된 capability를 다음 agent.run(...)에 로드합니다.

CapabilityCreation은 에이전트가 파이썬 소스로 작성하는 capability용이에요. 애플리케이션 코드가 작성하거나 선택하는 capability는 커스텀 Capability 구축을 참고하세요.

Source

Pydantic AI Harness가 0.x 릴리스인 동안 API는 마이너 릴리스 사이에 바뀔 수 있어요. 바뀔 때는 폐기 경고와 릴리스 노트 마이그레이션 안내가 정확히 어떻게 업그레이드할지 알려줘요. 버전 정책 참고.

출처: 문서

본문

문제 (The problem)

코딩 에이전트는 종종 작업 중간에 호스트가 아직 갖지 않은 동작을 원하는 걸 발견해요. 가드레일, 추가 지시문, 도구, 요청 훅 같은 거요. 그것을 표현할 capability 표면은 이미 존재하지만, 보통 개발자만 capability 클래스를 쓰고, 에이전트에 끼우고, 재시작할 수 있어요. 런타임 capability 생성이 없으면, 에이전트는 실행 중 그 확장을 작성해 다음 실행에 사용 가능하게 만들 수 없어요.

해결책 (The solution)

CapabilityCreation은 세 도구를 노출해요:

  • author_capability(name, code)code<directory>/<name>.py에 쓰고, 임포트하고, 검증해요. 검증은 정확히 하나의, 인자 없이 구성되는 pydantic_ai.capabilities.AbstractCapability 서브클래스를 요구하고, 부수 효과 없는 정적 게터(get_instructions, get_toolset, get_native_tools, get_model_settings, get_serialization_name)를 실행해요. 비동기 라이프사이클 훅은 실행하지 않아요. 라이브 RunContext가 필요하니까요.
  • list_authored_capabilities() — 작성된 capability를 그 상태와 검증 오류와 함께 나열해요.
  • disable_authored_capability(name) — 다음 실행에 capability가 주입되지 않게 막아요.

"훅"은 pydantic-ai의 독립 객체가 아니에요. capability의 메서드죠. 그래서 훅 작성은 하나의 라이프사이클 메서드를 오버라이드하는 capability를 작성하는 것을 뜻해요. 하나의 오버라이드된 훅은 유효한 capability예요.

사용법 (Usage)

작성된 파일의 directoryCapabilityCreation을 구성하고, 에이전트의 capabilities에 추가하세요:

from pathlib import Path

from pydantic_ai import Agent
from pydantic_ai_harness import CapabilityCreation

creation = CapabilityCreation(directory=Path('.authored'))
agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[creation])

에이전트는 이제 author_capability, list_authored_capabilities, disable_authored_capability를 호출할 수 있어요. CapabilityCreation은 또한 이 도구들을 설명하는 정적·캐시 안정 시스템 프롬프트 안내를 기여해요. 기본 텍스트에 guidance=None을 두거나, 고유 문자열을 넘기세요. 완전히 생략하려면 guidance='' 설정.

활성화 경계 (Activation boundary)

쓰기와 검증은 현재 실행에서 일어나고, 활성화는 다음 실행에서 일어나요. capability는 라이브·이미 실행 중인 실행에 추가될 수 없어요. pydantic-ai는 각 실행 시작에 유효 capability 집합을 한 번 해석해요(실행의 루트 capability는 고정, 세터 없음). 그래서 작성된 capability는 그것을 작성한 실행이 아니라 다음 agent.run(...)에서 라이브가 돼요. 작성은 capability를 즉시 쓰고 검증하지만, 그 도구·훅은 다음 실행의 toolset·capability 체인이 실행 시작에 조립될 때만 존재해요.

통합 계약 (Integration contract)

오케스트레이터가 루프를 조종하므로, 한 줄 계약을 소유해요. 스토어의 활성 capability를 agent.run(..., capabilities=...)로 각 실행에 실어 넣는 것. 그것이 있으면 작성된 capability는 매우 다음 루프 반복에서 라이브가 돼요. 프로세스 재시작이 필요 없습니다:

from pathlib import Path

from pydantic_ai import Agent
from pydantic_ai_harness import CapabilityCreation

creation = CapabilityCreation(directory=Path('.authored'))
agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[creation])

history = None
done = False
next_prompt = 'Start the task.'
while not done:
    extra = creation.store.load_active()
    result = await agent.run(next_prompt, message_history=history, capabilities=extra)
    history = result.all_messages()
    # ... decide `next_prompt` and `done` from `result` ...

creation.store는 같은 directory 위의 디스크 백드 CapabilityStore예요. store.load_active()는 다음 실행에 주입할 모든 활성 작성 capability를 다시 임포트·재구성해요. 로드에 실패하는 항목(손상된 소스, 구성 오류)은 발생시키지 않고 건너뛰므로, 하나의 나쁜 capability가 나머지를 막지 않아요.

영속성 (Persistence)

작성된 capability는 디스크에 영속돼요. 각각 <directory>/<name>.py 파일 하나이고, 형제 manifest.json으로 인덱스됩니다. 새 프로세스는 같은 directory 위에 새 CapabilityCreation을 구성하고 store.load_active()를 호출해 그것들을 집어요.

manifest.json은 각 capability의 이름·모듈 파일·클래스 이름·상태(active 또는 disabled)·마지막 검증 오류를 기록해요. 그것이 UI가 읽어 에이전트가 뭘 작성했는지 보여줄 수 있는 표면이에요. 매니페스트는 원자적으로 쓰여져요(temp 파일 + os.replace). 쓰기 중 크래시가 "capability 없음"으로 읽히는 부분 파일을 남기지 않게요.

capability 이름은 소문자·숫자·밑줄이어야 하고 문자로 시작해요. 이름 재사용은 그 이름의 이전 capability를 교체해요. 임포트되지만 검증에 실패하는 코드는 여전히 디스크에 쓰여(검사 가능하도록) last_error를 설정해 기록되며, load_active()는 그것을 건너뛰어요.

신뢰 경계 (Trust boundary)

CapabilityCreation은 임포트·구성·실행 시점에 프로세스 안에서 임의의 파이썬을 실행해요. 이미 셸 명령을 실행하고 파일을 편집하는 에이전트가 동작하는 것과 같은 신뢰 경계이고, 여기서 의도적인 선택이에요. 직접 실행하지 않을 디렉터리를 가리키지 말고, 작성된 capability를 에이전트가 호스트에서 실행하는 코드로 취급하세요.

작성된 capability는 라이브 코드를 담으므로 spec 직렬화가 되지 않고(get_serialization_name()None 반환), agent spec이 아니라 소스로 영속돼요.

타입 (Typing)

임포트된 작성 코드는 동적이지만, Any로 타입된 어떤 것도 하네스로 교차해 들어오지 않아요. 작성된 모듈에서 끌어온 모든 값은 사용 전에 isinstance/issubclass로 좁혀지고, 로드된 인스턴스는 AbstractCapability[object]로 타입돼요. AgentDepsT가 반공변(contravariant)이라, AbstractCapability[object]는 어떤 에이전트의 capabilities= 파라미터에도 받아들여져요.

API 참고 (API reference)

CapabilityCreation

Bases: AbstractCapability[AgentDepsT]

한 실행 동안 Pydantic AI capability를 만들어 다음 실행에서 활성화.

author_capability(name, code), list_authored_capabilities, disable_authored_capability를 노출해요. 작성은 파이썬 소스를 directory에 쓰고, 임포트하고, 검증해요(인자 없이 구성되고 정적 게터가 실행되는 정확히 하나의 AbstractCapability 서브클래스). 작성된 capability는 라이브 코드를 담으므로 spec 직렬화가 되지 않고, spec이 아니라 소스로 영속돼요.

활성화 경계: capability는 라이브·이미 실행 중인 실행에 추가될 수 없어요. pydantic-ai는 각 실행 시작에 capability 집합을 한 번 해석해요. 작성된 capability는 다음 agent.run(...)에서 사용 가능해져요. 통합 계약은 오케스트레이터 쪽 한 줄이에요. 스토어의 활성 capability를 다음 실행에 실어 넣는 것.

from pathlib import Path

from pydantic_ai import Agent
from pydantic_ai_harness.capability_creation import CapabilityCreation

creation = CapabilityCreation(directory=Path('.authored'))
agent = Agent('anthropic:claude-sonnet-4-6', capabilities=[creation])

# Loop: each iteration injects whatever the agent has authored so far.
result = await agent.run('build a logging capability', capabilities=creation.store.load_active())

이것은 프로세스 안에서 작성된 파이썬을 실행해요. 이미 셸 명령을 실행하고 파일을 편집하는 에이전트가 동작하는 것과 같은 신뢰 경계예요.

속성 (Attributes)
directory

작성된 <name>.py 파일들과 manifest.json 인덱스를 담는 디렉터리.

타입: Path

guidance

작성에 대한 정적 시스템 프롬프트 안내. 캐시 안정. 기본값에 None을 두거나, 안내를 완전히 생략하려면 '' 설정.

타입: str | None 기본: None

store

디스크 백드 스토어. store.load_active()를 호출해 작성된 capability를 다음 실행에 주입.

타입: CapabilityStore

메서드 (Methods)
get_instructions
def get_instructions() -> AgentInstructions[AgentDepsT] | None

작성 도구에 대한 정적·캐시 안정 안내.

반환

AgentInstructions[AgentDepsT] | None

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

이 capability의 스토어 위에 작성 도구를 제공하는 toolset.

반환

AgentToolset[AgentDepsT] | None

get_serialization_name

@classmethod

def get_serialization_name(cls) -> str | None

Spec 직렬화 불가: capability가 라이브·디스크 백드 스토어를 보유.

반환

str | None

CapabilityStore

directory 아래 작성된 capability .py 파일들의 읽기/쓰기 인덱스.

메서드 (Methods)
write
def write(name: str, code: str) -> AuthoredCapability

code<name>.py에 쓰고, 검증하고, 매니페스트 항목을 upsert.

잘못된 이름은 ValueError를 발생시켜요(아무것도 쓰기 전에). 임포트되지만 검증에 실패하는 코드는 여전히 쓰여(검사 가능하도록) last_error를 설정해 기록되고, load_active는 그것을 건너뛰어요.

반환

AuthoredCapability

disable
def disable(name: str) -> bool

이름 붙은 capability를 비활성으로 표시해 load_active가 그것을 반환하지 않게 해요. 존재했는지 여부를 반환.

반환

bool

list_all
def list_all() -> list[AuthoredCapability]

모든 매니페스트 항목을 삽입 순서로 반환.

반환

list[AuthoredCapability]

load_active
def load_active() -> list[AbstractCapability[object]]

실행별 주입을 위해 모든 활성 작성 capability를 구성.

각 활성 항목을 다시 임포트·재구성해요. 로드에 실패하는 항목(손상된 소스, 구성 오류)은 발생시키지 않고 건너뛰므로 하나의 나쁜 capability가 나머지를 막지 않아요. 기록의 last_error와 다른 로드 결과는 매니페스트에 다시 영속돼요. 새로 깨진 항목은 오류를 기록하고, 다시 고쳐진 항목은 지워서, 매니페스트가 실제로 어떤 capability가 활성인지에 대해 진실하게 유지돼요.

반환

list[AbstractCapability[object]]

더 알아보기 (Learn more)