출력

출력 (Output)

"출력(Output)"은 에이전트 실행이 끝나고 돌아오는 최종 값을 말해요. plain text일 수도, 구조화 데이터일 수도, 이미지일 수도, 모델이 제공한 인자로 호출되는 함수의 결과일 수도 있어요. 이 문서는 Pydantic AI가 출력을 어떻게 정의·검증·스트리밍하는지 다뤄요.

출처: 공식문서

출력은 AgentRunResultStreamedRunResult로 감싸져서, 실행의 usage메시지 히스토리 같은 다른 데이터에도 접근할 수 있어요. 둘 다 감싸는 데이터에 대해 제네릭이라, 에이전트가 반환하는 데이터의 타입 정보가 보존돼요.

실행은 모델이 출력 타입 중 하나로 응답할 때 끝나요. 출력 타입이 없거나 str이 허용 옵션 중 하나면 plain text 응답을 받았을 때도 끝나요. 사용량 제한을 초과하면 실행이 취소될 수도 있어요.

output_type으로 Pydantic 모델을 쓰는 예시를 볼게요.

from pydantic import BaseModel

from pydantic_ai import Agent


class CityLocation(BaseModel):
    city: str
    country: str


agent = Agent('google:gemini-3-flash-preview', output_type=CityLocation)
result = agent.run_sync('Where were the olympics held in 2012?')
print(result.output)
#> city='London' country='United Kingdom'
print(result.usage)
#> RunUsage(cost=Decimal('0.0000525'), input_tokens=57, output_tokens=8, requests=1)

구조화 출력 데이터

Agent 생성자의 output_type 인자는 하나 이상의 타입이나 출력 함수를 받아요. 스칼라 타입, 리스트·dict 타입(TypedDict, StructuredDict 포함), dataclass, Pydantic 모델, 타입 유니온 — 일반적으로 Pydantic 모델의 타입 힌트로 지원되는 모든 것을 지원해요. 여러 선택지의 리스트도 넘길 수 있어요.

기본적으로 Pydantic AI는 모델의 도구 호출 능력을 활용해 구조화 데이터를 반환하게 해요. 여러 출력 타입(유니온이나 리스트)을 지정하면, 스키마의 복잡성을 줄이고 모델이 올바르게 응답할 확률을 높이기 위해 각 멤버를 별도의 출력 도구로 모델에 등록해요. 이 방식은 광범위한 모델에서 잘 동작하는 것으로 입증됐어요. 출력 도구 이름을 바꾸거나 모델의 네이티브 구조화 출력 기능을 쓰거나 출력 스키마를 지시에 넣고 싶다면 출력 모드 마커 클래스를 쓰면 돼요.

출력 타입이 지정되지 않았거나 str이 출력 타입 중 하나면, 모델의 plain text 응답을 출력 데이터로 써요. str이 출력 타입에 없으면 모델은 구조화 데이터를 반환하거나 출력 함수를 호출하도록 강제돼요.

구조화 출력은 도구처럼 Pydantic으로 JSON 스키마를 만들고 모델이 반환한 데이터를 검증해요.

타입 체크 고려사항

Agent 클래스는 출력 타입에 대해 제네릭이고, 이 타입이 AgentRunResult.output·StreamedRunResult.output까지 이어져서 IDE·정적 타입 체커가 코드가 그 출력의 가능한 값 전체를 고려하지 않으면 경고해줘요. pyright·mypy 같은 정적 타입 체커는 output_type에서 출력 타입을 추론하려 하지만, 함수나 유니온·리스트의 여러 타입을 주면 항상 정확히 추론하지는 못해요(Pydantic AI 자체는 올바르게 동작해도요). 그럴 땐 Agent 생성자에 제네릭 파라미터를 명시적으로 지정해서 도와줘야 해요.

특히 다음 세 경우에 output_type을 쓸 땐 도움이 필요해요: ① 타입 유니온 사용 시(output_type=Foo | Bar, PEP-747이 Python 3.15에 도착하기 전까지는 # type: ignore도 필요), ② mypy + 리스트 사용 시, ③ mypy + async 출력 함수 사용 시. (pyright는 이 둘을 올바르게 처리해요.)

출력 함수

plain text나 구조화 데이터 대신, 모델이 제공한 인자로 호출되는 함수의 결과를 출력으로 원할 수도 있어요 — 예를 들어 인자를 통해 들어온 데이터를 추가 처리·검증하고 싶거나(모델에 재시도하라고 알리는 옵션 포함), 다른 에이전트로 넘기고 싶을 때요.

출력 함수는 함수 도구와 비슷하지만, 모델이 그중 하나를 반드시 호출해야 하고, 그 호출이 에이전트 실행을 끝내며, 결과가 모델에 다시 전달되지 않아요. 도구 함수처럼 모델이 제공한 출력 함수 인자는 Pydantic(선택적 검증 컨텍스트)으로 검증되고, 첫 인자로 RunContext를 받을 수 있으며, 수정된 인자(또는 다른 출력 타입)로 재시도하라고 ModelRetry를 raise할 수 있어요. 출력 함수는 ToolFailed를 지원하지 않아요 — 여기선 보통 예외처럼 취급돼요.

출력 함수를 지정하려면 output_type을 단일 함수(또는 바인딩된 인스턴스 메서드)나 함수 리스트로 설정해요. 리스트에는 스칼라나 Pydantic 모델 같은 다른 출력 타입도 넣을 수 있어요. 출력 함수를 @agent.tool 데코레이터·tools 인자로 또한 도구로 등록하지 않는 게 좋아요 — 모델이 어느 것을 호출해야 할지 헷갈릴 수 있거든요.

출력 모드

Pydantic AI는 모델이 구조화 데이터를 출력하게 하는 세 가지 방법을 구현해요.

  1. Tool Output — 도구 호출로 출력을 생산.
  2. Native Output — 모델이 제공된 JSON 스키마에 맞는 텍스트 콘텐츠만 출력하도록 강제(모델의 네이티브 "Structured Outputs"/"JSON Schema response format"). 모든 모델이 지원하진 않고 때로 제한이 있어요(예: Gemini 3는 함수·네이티브 도구와 함께 지원하지만, 이전 Gemini 모델은 함수 도구와 함께 쓸 수 없음).
  3. Prompted Output — 원하는 JSON 스키마를 포함한 프롬프트를 모델 지시에 주입하고, 모델의 plain text 응답을 그에 맞게 파싱.

기본 Tool Output 모드에선 각 출력 타입(또는 함수)의 JSON 스키마가 특별한 출력 도구의 파라미터 스키마로 모델에 제공돼요. 거의 모든 모델이 지원하고 매우 잘 동작하는 게 입증돼서 기본값이에요.

출력 도구 이름을 바꾸거나 모델을 돕는 커스텀 description을 주거나 strict mode를 켜고 끄려면 타입들을 ToolOutput 마커 클래스로 감싸고 적절한 인자를 주면 돼요. 기본적으로 description은 Pydantic 모델·출력 함수에 지정된 docstring에서 가져오므로, 마커 클래스로 지정할 필요가 보통 없어요.

from pydantic import BaseModel

from pydantic_ai import Agent, ToolOutput


class Fruit(BaseModel):
  name: str
  color: str


class Vehicle(BaseModel):
  name: str
  wheels: int


agent = Agent(
  'openai:gpt-5.2',
  output_type=[ # (1)
      ToolOutput(Fruit, name='return_fruit'),
      ToolOutput(Vehicle, name='return_vehicle'),
  ],
)
result = agent.run_sync('What is a banana?')
print(repr(result.output))
#> Fruit(name='banana', color='yellow')

출력 도구를 쓸 때 각 도구는 자체 재시도 카운터를 가져요. 에이전트 재시도 예산의 출력 쪽(Agent(retries={'output': N}) 또는 실행별 agent.run(retries={'output': N}))이 도구당 기본 한도예요. 개별 출력 도구의 한도를 재정의하려면 ToolOutput(Fruit, max_retries=2)처럼 넘기세요.

커스텀 JSON 스키마와 검증 컨텍스트

Pydantic AI는 JSON 스키마를 자동 생성하지만, 커스텀 JSON 스키마를 쓰고 싶을 수도 있어요. 이때는 도구·출력 함수에 json_schema 인자를 넘겨 커스텀 스키마를 지정하면 돼요. 또한 Pydantic 모델의 model_configJsonSchemaExtra를 써서 스키마에 추가 키를 주입할 수 있어요.

검증 컨텍스트(ValidationContext)는 스키마 검증 시 모델·도구·에이전트 상태에 접근하는 방법이에요. 출력 함수·검증기가 검증 컨텍스트를 사용하려면 validation_context 인자에 ValidationContext를 명시적으로 설정하고, 검증 함수의 첫 인자로 ValidationContext를 받도록 해요.

출력 검증기

Pydantic 검증기로는 불편하거나 불가능한 검증이 있어요 — 특히 검증이 I/O를 요구하고 비동기일 때요. Pydantic AI는 agent.output_validator 데코레이터로 검증 함수를 추가할 수 있게 해요.

여기서 raise된 각 ModelRetry는 실행의 출력 재시도 예산 한 단위를 소모해요. 예산 기본값은 1이고, Agent(retries={'output': N}), 실행별 agent.run(retries={'output': N}), 또는 출력 도구별 ToolOutput(max_retries=N)로 설정할 수 있어요. 검증기 안에서 ctx.max_retries는 실제로 막을 한도를, ctx.retry는 전역 재시도 카운터를 반영해요.

출력 검증기는 ToolFailed를 지원하지 않아요. ModelRetry로 모델에 다른 출력을 요청하세요. 출력 타입별로 검증 로직을 분리하고 싶다면 출력 검증기 안에서 isinstance 체크를 하느니 출력 함수를 쓰는 걸 권장해요.

@agent.output_validator
async def validate_sql(ctx: RunContext[DatabaseConn], output: Output) -> Output:
    if isinstance(output, InvalidRequest):
        return output
    try:
        await ctx.deps.execute(f'EXPLAIN {output.sql_query}')
    except QueryError as e:
        raise ModelRetry(f'Invalid query: {e}') from e
    else:
        return output

부분 출력 처리

run_stream()·run_stream_sync()으로 스트리밍할 때 출력 검증기는 여러 번 호출돼요 — 모델에서 받은 각 부분 출력마다, 그리고 최종 완전 출력에 한 번. 중간 부분 값이 아니라 완전한 결과만 검증하고 싶다면 RunContext.partial_output 플래그를 확인하세요. 스트리밍 시 각 부분 출력엔 True, 최종 완전 출력엔 False예요. 다른 실행 메서드에선 검증기가 완전 출력과 함께 한 번만 호출되므로 항상 False예요.

@agent.output_validator
def validate_output(ctx: RunContext, output: str) -> str:
    if ctx.partial_output:
        return output

    if len(output) < 50:
        raise ModelRetry('Output is too short.')
    return output

이미지 출력

일부 모델은 응답의 일부로 이미지를 생성할 수 있어요(Image Generation 네이티브 도구를 지원하는 모델, 차트 생성하라고 하면 Code Execution 네이티브 도구를 쓰는 OpenAI 모델 등). 생성된 이미지를 에이전트 실행의 출력으로 쓰려면 output_typeBinaryImage로 설정해요. 이미지 생성 네이티브 도구를 명시하지 않으면 ImageGenerationTool이 자동으로 활성화돼요.

from pydantic_ai import Agent, BinaryImage

agent = Agent('openai-responses:gpt-5.2', output_type=BinaryImage)

result = agent.run_sync('Generate an image of an axolotl.')
assert isinstance(result.output, BinaryImage)

에이전트가 항상 이미지를 생성할 필요가 없다면 BinaryImagestr의 유니온을 쓰면 돼요. 모델이 둘 다 생성하면 이미지가 출력으로 우선하고 텍스트는 ModelResponse.text에서 볼 수 있어요.

선택적 출력 (None 허용)

output_typestr | None이면 에이전트는 plain text 응답 대신 None을 반환할 수 있어요. 이 패턴은 문서 확인 예시처럼 "결과가 있을 수도 없을 수도 있다"는 상황에서 유용해요.

class Answer(BaseModel):
    answer: str
    confidence: float

output_type: type[Answer] | None = Answer | None
agent = Agent('openai:gpt-5.2', output_type=output_type)

여기서 에이전트는 구조화된 Answer를 반환하거나 아무 답이 없다고 판단하면 None을 반환해요.

스트리밍 결과

텍스트 스트리밍

run_stream()으로 텍스트 응답을 스트리밍할 수 있어요. response.stream_text()가 텍스트 조각을 async iterable로 yield해요. response.stream()은 완전한 응답 객체(예: Pydantic 모델)를 방출하는 데 쓰여요.

async with agent.run_stream('What is the capital of the UK?') as response:
    async for text in response.stream_text():
        print(text)

구조화 출력 스트리밍

response.stream_json()로 구조화 출력이 JSON으로 생성되는 동안 부분 검증된 JSON을 스트리밍하고, response.stream()/response.output()로 완전히 검증된 최종 구조화 데이터를 받을 수 있어요.

async with agent.run_stream('What is the capital of the UK?') as response:
    async for json_chunk in response.stream_json():
        print(json_chunk, end='')
    print()
    final = await response.output()
    print(final)

스트림 취소

run_stream()에서 진행 중인 스트림은 컨텍스트 매니저를 나가거나 response.cancel()을 호출해 취소할 수 있어요.

예제

더 알아보기 (Learn more)