멀티모달 입력

멀티모달 입력 (Multimodal Input)

LLM과 대화할 때 꼭 텍스트만 보낼 필요는 없어요. Pydantic AI는 모델이 지원하기만 하면 이미지, 오디오, 비디오, 문서 입력을 같이 받아요. 이 문서는 각 입력 유형을 어떻게 넘기는지, URL을 직접 보낼지 다운로드해서 보낼지, 그리고 프로바이더의 파일 스토리지 API에 업로드한 파일을 어떻게 참조하는지 다뤄요.

출처: 공식문서

이미지 입력

Note: 일부 모델은 이미지 입력을 지원하지 않아요. 모델 문서에서 지원 여부를 확인하세요.

이미지의 직접 URL이 있다면 ImageUrl을 써요.

from pydantic_ai import Agent, ImageUrl

agent = Agent(model='openai:gpt-5.2')
result = agent.run_sync(
    [
        'What company is this logo from?',
        ImageUrl(url='https://iili.io/3Hs4FMg.png'),
    ]
)
print(result.output)
#> This is the logo for Pydantic, a data validation and settings management library in Python.

이미지가 로컬에 있다면 BinaryContent를 써서 바이트를 직접 넘겨요.

import httpx

from pydantic_ai import Agent, BinaryContent

image_response = httpx.get('https://iili.io/3Hs4FMg.png')  # Pydantic logo

agent = Agent(model='openai:gpt-5.2')
result = agent.run_sync(
  [
      'What company is this logo from?',
      BinaryContent(data=image_response.content, media_type='image/png'),  # (1)
  ]
)
print(result.output)
#> This is the logo for Pydantic, a data validation and settings management library in Python.

여기선 예제가 실행 가능하도록 웹에서 이미지를 받았지만, 로컬 파일 내용을 읽으려면 Path().read_bytes()를 쓰면 돼요.

오디오·비디오·문서 입력

문서 URL이 있으면 DocumentUrl로 직접 넘겨요.

from pydantic_ai import Agent, DocumentUrl

agent = Agent(model='anthropic:claude-sonnet-4-6')
result = agent.run_sync(
    [
        'What is the main content of this document?',
        DocumentUrl(url='https://storage.googleapis.com/cloud-samples-data/generative-ai/pdf/2403.05530.pdf'),
    ]
)
print(result.output)
#> This document is the technical report introducing Gemini 1.5, Google's latest large language model...

문서 데이터를 직접 넘기려면 BinaryContent를 써요.

from pathlib import Path
from pydantic_ai import Agent, BinaryContent

pdf_path = Path('document.pdf')
agent = Agent(model='anthropic:claude-sonnet-4-6')
result = agent.run_sync(
    [
        'What is the main content of this document?',
        BinaryContent(data=pdf_path.read_bytes(), media_type='application/pdf'),
    ]
)
print(result.output)
#> The document discusses...

DocumentUrlBinaryContent도 안 맞는 경우(예: 모델이 DocumentUrl을 지원하지 않거나 Non-binary 형식으로 문서를 주고 싶다면), 텍스트를 직접 추출해 문자열이나 TextContent로 넘기면 돼요.

텍스트 입력

TextContent를 쓰면 추가 메타데이터와 함께 텍스트 입력을 줄 수 있어요.

from pydantic_ai import Agent, TextContent

agent = Agent(model='openai:gpt-5.2')
result = agent.run_sync([
    'Summarize the key points from this text.',
    TextContent(
        content=(
            'Pydantic AI is a Python agent framework. '
            'It supports text, image, audio, video, and document input.'
        ),
        metadata={'source': 'pydantic_ai_inputs.txt'},
    ),
])

이건 텍스트를 str로 넘기는 것과 동등하지만, 에이전트 로직에서 프로그래밍 방식으로 접근할 수 있는 추가 metadata를 포함할 수 있어요. 참고로 content 필드는 모델의 입력으로 처리되지만, metadata모델에 전송되지 않아요 — 메시지에 보존되어 프로그래밍 방식으로만 접근돼요.

사용자측 다운로드 vs 직접 파일 URL

ImageUrl, AudioUrl, VideoUrl, DocumentUrl 중 하나를 쓰면 Pydantic AI는 기본적으로 URL을 모델 프로바이더에 보내서(파일은 프로바이더 쪽에서 다운로드) 처리해요. 파일 URL 지원은 유형·프로바이더에 따라 달라요.

Model URL 직접 전송 다운로드 후 바이트 전송 지원 안 함
OpenAIChatModel ImageUrl AudioUrl, DocumentUrl VideoUrl. DocumentUrlAzureProvider·AlibabaProvider에서 미지원
OpenAIResponsesModel ImageUrl, AudioUrl, DocumentUrl -- VideoUrl
AnthropicModel ImageUrl, DocumentUrl(PDF) DocumentUrl(text/plain) AudioUrl, VideoUrl
GoogleModel (Google Cloud) 모든 URL 유형 -- --
GoogleModel (Gemini API) YouTube, Files API 그 외 모든 URL --
XaiModel ImageUrl DocumentUrl AudioUrl, VideoUrl
MistralModel ImageUrl, DocumentUrl(PDF) DocumentUrl(text/plain) AudioUrl, VideoUrl, DocumentUrl(비PDF·비텍스트)
BedrockConverseModel S3 URL(s3://) ImageUrl, DocumentUrl, VideoUrl AudioUrl
OpenRouterModel ImageUrl, DocumentUrl, VideoUrl AudioUrl --

모델 API가 파일 URL을 지원해도 크롤링·접근 제한 때문에 파일을 다운로드하지 못할 수 있어요(예: Google Cloud의 GoogleModel은 YouTube 비디오 URL을 요청당 하나로 제한). 이런 경우 URL 객체에 force_download를 설정하면 Pydantic AI가 파일 내용을 로컬로 다운로드해서 URL 대신 보내요.

from pydantic_ai import ImageUrl, AudioUrl, VideoUrl, DocumentUrl

ImageUrl(url='https://example.com/image.png', force_download=True)
AudioUrl(url='https://example.com/audio.mp3', force_download=True)
VideoUrl(url='https://example.com/video.mp4', force_download=True)
DocumentUrl(url='https://example.com/doc.pdf', force_download=True)

파일 URL 신뢰 관련 보안 주의

URL을 프로바이더에 전달하면 프로바이더가 자신의 자격 증명으로 그걸 가져와요. s3://(Bedrock), gs://(Google Cloud) 같은 클라우드 스토리지 스킴에서는 그 자격 증명이 서버의 IAM 역할·서비스 계정이라서, URL을 제어하는 쪽이 사실상 프로바이더가 내 대신 읽을 수 있는 범위를 제어하게 돼요.

신뢰할 수 없는 사용자 입력으로 ImageUrl·AudioUrl·VideoUrl·DocumentUrl을 만들지 말고, 스킴과 범위를 검증하세요. 프론트엔드 시작 업로드라면 s3://bucket/key 같은 참조를 서버측에서 pre-signed https:// URL로 변환한 뒤 파일 URL 파트를 구성하세요. force_download=Truehttp(s):// URL에서만 동작하고(라이브러리 HTTP 클라이언트 경유 + SSRF 보호 적용), s3://, gs:// 같은 클라우드 스토리지 스킴은 로컬 다운로드 경로에서 지원되지 않고 프로바이더에 원본 그대로 전달돼요. force_download='allow-local'은 로컬 네트워크 접근을 허용하므로 서버가 작성한 URL에만 사용하세요.

업로드된 파일 (Uploaded Files)

일부 모델 프로바이더는 자체 파일 스토리지 API가 있어서 파일을 업로드하고 ID·URL로 참조할 수 있어요. 프로바이더의 파일 스토리지 API에 업로드된 파일은 UploadedFile로 참조해요.

지원 모델: AnthropicModel(✅ Anthropic Files API), OpenAIChatModel(✅ OpenAI Files API), OpenAIResponsesModel(✅), GoogleModel(✅ Google Files API), BedrockConverseModel(✅ S3 URLs), XaiModel(✅ xAI Files API). 다른 모델은 ❌ 미지원.

UploadedFile를 쓸 때는 반드시 provider_name을 설정해야 해요. 업로드된 파일은 업로드된 시스템에 특화돼 있어서 프로바이더 간에 이전할 수 없어요. 다른 프로바이더로 UploadedFile이 담긴 메시지를 쓰려 하면 오류가 나요. 올바른 프로바이더 이름을 동적으로 얻으려면 model.system을 쓰세요 — 프로바이더 이름이 바뀌어도 코드가 올바르게 동작해요.

media_type 파라미터는 UploadedFile에서 선택사항이에요. 지정하지 않으면 Pydantic AI가 file_id에서 추론하려 해요: ① file_id가 인식 가능한 확장자(.pdf, .png 등)를 가진 URL·경로면 자동 추론, ② 불투명한 파일 ID('file-abc123')면 기본값 'application/octet-stream'. 알고 있을 땐 명시적으로 설정하는 걸 권장해요.

Anthropic 예시 — Anthropic Files API는 현재 베타예요. AnthropicModel은 요청에 Anthropic UploadedFile이 포함되면 필요한 anthropic-beta: files-api-2025-04-14 헤더를 자동으로 추가해요.

import asyncio

from pydantic_ai import Agent, UploadedFile
from pydantic_ai.models.anthropic import AnthropicModel
from pydantic_ai.providers.anthropic import AnthropicProvider


async def main():
    provider = AnthropicProvider()
    model = AnthropicModel('claude-sonnet-4-5', provider=provider)

    # Upload a file using the provider's client (Anthropic client)
    with open('document.pdf', 'rb') as f:
        uploaded_file = await provider.client.beta.files.upload(file=f)

    # Reference the uploaded file; the beta header is added automatically
    agent = Agent(model)
    result = await agent.run(
        [
            'Summarize this document',
            UploadedFile(file_id=uploaded_file.id, provider_name=model.system),
        ]
    )
    print(result.output)
    #> The document discusses the main topics and key findings...


asyncio.run(main())

OpenAI 참고OpenAIChatModel은 업로드된 문서만 file_id로 참조할 수 있어요. 업로드된 이미지(image/* 미디어 타입)를 참조하면 UserError가 나요. Chat Completions API가 이미지 파트에 file_id를 받지 않기 때문이에요. 이미지는 ImageUrl이나 BinaryContent를 쓰거나, 업로드된 이미지를 지원하는 OpenAIResponsesModel을 쓰세요.

더 알아보기 (Learn more)