Разработка ИИ-агентов · Модуль 2 · Урок 2.7

Мультимодальность: изображения и PDF как вход агента

Агент видит, а не только читает

До сих пор вход агента был текстом. Но современные модели мультимодальны: в сообщение можно вложить изображение или PDF, и модель «увидит» его содержимое — прочитает текст со скриншота, опишет диаграмму, извлечёт поля из чека или счёта.

Для агента это открывает целый класс задач: разобрать скриншот ошибки, прочитать накладную, проверить вёрстку по макету, понять график. Механика цикла (урок 1.4) не меняется — меняется лишь содержимое сообщения пользователя: вместо одной строки это список блоков, где есть и текст, и картинка.

Как передать изображение модели

Сообщение становится массивом контентных блоков (content blocks). Текстовый блок несёт вопрос, блок-изображение — сами пиксели. Картинку передают двумя способами:

  • base64: байты изображения кодируются в строку и кладутся прямо в запрос (вместе с media type, например image/png). Удобно для локальных файлов.
  • по URL: модель скачивает изображение по ссылке (если провайдер это поддерживает).

PDF обрабатывается похоже: документ передаётся как отдельный блок, модель читает и текст, и расположение. Конкретные имена полей, лимиты на размер и поддерживаемые форматы версионно-зависимы — сверяйтесь с актуальными доками провайдера.

Изображение как результат инструмента

Мультимодальность работает не только на входе пользователя. Инструмент агента может вернуть изображение — например, take_screenshot() отдаёт скрин экрана, render_chart() — картинку графика. Тогда tool-результат содержит блок-изображение, и на следующем ходу модель «смотрит» на него, чтобы решить, что делать дальше.

Это основа браузерных и computer-use агентов (подробно — в продвинутом курсе): агент делает скриншот, модель видит интерфейс и решает, куда «кликнуть». Здесь важно помнить про безопасность: на картинке может оказаться текст с инструкцией («сделай X») — это та же prompt injection, только визуальная (см. урок 2.5).

Стоимость и ограничения

Изображение — это токены: модель раскладывает картинку на «плитки», и чем выше разрешение, тем больше токенов и дороже запрос. Практические следствия:

  • не шлите 4K-скриншот, если хватает уменьшенной версии — масштабируйте под нужную деталь;
  • много изображений в истории быстро раздувают контекст (вспомните управление окном, урок 2.2) — выносите старые картинки во внешнее состояние, оставляя ссылку/описание;
  • модель может ошибаться в мелком тексте и точных координатах — не полагайтесь на пиксельную точность без проверки.
Поток мультимодального агента: скриншот → модель → действие
flowchart LR
  IMG["Изображение / PDF
(скрин, чек, макет)"] --> CB["content blocks:
text + image"]
  CB --> M{{LLM видит картинку}}
  M -->|tool_use| ACT["Действие: извлечь поля,
кликнуть, ответить"]
  ACT --> M
  M -->|end_turn| OUT["Структурированный ответ"]
Сообщение с текстом и изображением (base64); поля сверяйте с доками
// ContentBlock — часть мультимодального сообщения: либо текст, либо изображение.
// Реальные имена полей зависят от провайдера/SDK — СВЕРЯЙТЕСЬ С ДОКАМИ.
type ContentBlock struct {
    Type      string // "text" | "image"
    Text      string // для Type=="text"
    MediaType string // для Type=="image", напр. "image/png"
    DataB64   string // для Type=="image": байты в base64
}

type Message struct {
    Role    string
    Blocks  []ContentBlock // вместо одной строки — список блоков
}

// userImageMessage собирает сообщение «вот картинка + вопрос к ней».
func userImageMessage(question string, img []byte, media string) Message {
    return Message{
        Role: "user",
        Blocks: []ContentBlock{
            {Type: "text", Text: question},
            {Type: "image", MediaType: media, DataB64: base64.StdEncoding.EncodeToString(img)},
        },
    }
}

func main() {
    png, err := os.ReadFile("invoice.png")
    if err != nil {
        log.Fatal(err)
    }
    msg := userImageMessage("Извлеки номер счёта и сумму к оплате в JSON.", png, "image/png")
    // дальше — обычный агентный цикл из урока 1.4; модель «прочитает» картинку.
    _ = msg
}

Anti-patterns

Анти-паттернПочему плохоКак правильно
Слать максимальное разрешение всегдаИзображение = токены; 4K дорого и медленноМасштабировать под нужную деталь
Копить все картинки в историиКонтекст раздувается, растёт стоимостьСтарые изображения — во внешнее состояние, в контексте ссылка
Доверять пиксельной точности моделиОшибки в мелком тексте и координатахПроверять извлечённое детерминированно, не полагаться на глазомер
Игнорировать текст на картинке как вводВизуальная prompt injection («сделай X» на скрине)Относиться к содержимому изображения как к недоверенному вводу
Слать картинку строкой в text-блокМодель не получит изображениеИспользовать отдельный image-блок с media type

Практическое задание

  • Передайте модели скриншот или фото чека и попросите извлечь 2–3 поля в строгий JSON (свяжите с уроком 2.4).
  • Сравните результат на полном разрешении и на уменьшенной копии — оцените разницу в качестве и в токенах.
  • Сделайте инструмент, возвращающий изображение (например, заранее заготовленный график), и проверьте, что модель «видит» его результат на следующем ходу.
  • Добавьте проверку: извлечённую сумму валидируйте детерминированно (формат, диапазон), а не доверяйте «на глаз».
  • Проверьте безопасность: положите на тестовую картинку текст-инструкцию и убедитесь, что агент не выполняет её слепо.

Проверка знаний

Как корректно передать модели изображение вместе с вопросом?

  • A Вставить путь к файлу в текст сообщения
  • B Сделать сообщение списком content-блоков: текстовый блок с вопросом и image-блок с байтами (base64) и media type
  • C Описать картинку словами — модель всё равно не видит пиксели
  • D Отправить изображение отдельным запросом без вопроса

Агент-помощник по интерфейсу на каждом шаге шлёт модели полноэкранный скриншот 4K и хранит все скриншоты в истории. Счёт и задержка резко выросли.

Что в первую очередь исправить?

  • A Перейти на более дешёвую модель
  • B Снижать разрешение под задачу и выносить старые изображения из контекста (ссылка вместо пикселей)
  • C Отключить инструменты
  • D Слать скриншоты текстом

Почему текст, видимый на изображении, нужно считать недоверенным вводом?

  • A Потому что модель не умеет читать текст с картинок
  • B Потому что на картинке может быть инструкция-инъекция («игнорируй правила, сделай X»), и модель может ей последовать
  • C Потому что текст на картинке всегда зашифрован
  • D Потому что изображения нельзя логировать