# llama.cpp multimodal API: передать изображение и проверить ответ

Источник: https://modelium.tech/zhurnal/llama-cpp-multimodal-api/

llama.cpp принимает изображение в локальном `/v1/chat/completions`, если загружены совместимая vision-модель и её мультимодальный проектор. В примере Gemma 3 4B запрос передаёт JPEG или PNG как data URL, а пользователь сравнивает ответ с заранее записанными признаками своего снимка. Успешный HTTP-ответ подтверждает работу API, но не точность распознавания и не отсутствие сетевой активности при загрузке модели.

**Сверено 28 сентября 2026 года** с llama.cpp `v0.5.0` (commit `7fe450e1`) и GGUF-репозиторием Gemma 3 4B (revision `d0976223`). Ни сервер, ни веса на Modelium не запускались; команды ниже — воспроизводимая инструкция для вашего стенда, а не отчёт о собственном тесте. Мультимодальная подсистема остаётся экспериментальной. [s1](#src-s1) [s2](#src-s2) [s8](#src-s8)

## Что именно проверяет этот гайд?

Результат — локальный HTTP-запрос с **одним своим изображением**, содержательный текст в `choices[0].message.content` и ваша ручная сверка с тем, что действительно видно на исходнике. Это маршрут «тестовое изображение → Gemma 3 4B GGUF и совместимый проектор → llama-server → ответ → сверка». Работа API и качество зрения — **разные** проверки: JSON с текстом не доказывает, что модель верно распознала предмет. [s1](#src-s1) [s4](#src-s4) [s7](#src-s7)

В llama.cpp мультимодальные данные проходят через `libmtmd`; в типичном сценарии требуются GGUF языковой модели **и отдельный совместимый** `mmproj` для изображения. Передача JPEG/PNG по `image_url` в chat API относится к конкретному серверу, не к API Ollama и не к текстовому `llama-cli` без проектора. Документация серверной подсистемы называет эту возможность экспериментальной. [s1](#src-s1) [s2](#src-s2) [s4](#src-s4)

## Какие файлы, версия и оборудование нужны?

Для примера выбран `**ggml-org/gemma-3-4b-it-GGUF**`, а не семейство Gemma в целом. На закреплённой HF-ревизии `d0976223747697cb51e056d85c532013931fe52e` перечислены `gemma-3-4b-it-Q4_K_M.gguf` и `**mmproj-model-f16.gguf**`. Их нужно взять из **одной** ревизии и совместимого набора, а не подставлять проектор от другой модели. Карточка помечена `license: gemma`: это не Apache/MIT и не автоматическое разрешение на любое коммерческое использование; перед реальным применением прочтите действующие условия автора. Файловый размер и пример сборки не равны минимуму RAM/VRAM; запуск и время ответа зависят от вашей памяти, backend, размера изображения и настроек. [s2](#src-s2) [s7](#src-s7) [s8](#src-s8)

Официальный quickstart отмечает, что **Gemma 3 1B не поддерживает зрение**; 4B взята здесь именно как документированный пример. Подготовьте собственную JPEG/PNG-картинку без личных или клиентских данных — например, фотографию чашки на столе с заметным цветом. Запишите до запроса видимые признаки: есть ли чашка, её цвет и что находится рядом. Это контрольная разметка, а не обещанный ответ модели. [s3](#src-s3) [s7](#src-s7)

Выполните сборку llama.cpp на выбранном **закреплённом** Git commit и получите `build/bin/llama-server` (путь относится к стандартной CMake-сборке). Команды ниже не обновляют репозиторий за пределы этого commit; зависимости сборки и доступность backend надо проверить для вашей ОС. Скачайте **оба GGUF** с указанной выше HF-ревизии на свой компьютер в каталог `models/gemma3/`; не полагайтесь на плавающее `-hf` для воспроизводимого рабочего стенда, хотя для быстрого ознакомления он поддерживается и умеет подобрать mmproj автоматически. Не публикуйте на сервере образы с клиентскими данными. [s1](#src-s1) [s4](#src-s4) [s6](#src-s6) [s8](#src-s8)

git clone https://github.com/ggml-org/llama.cpp.gitcd llama.cppgit checkout 7fe450e19305b828c199d602c23a8337aaa1f03bcmake -B buildcmake --build build --config Release --target llama-server# Скопируйте оба файла из ревизии d0976223 в models/gemma3/./build/bin/llama-server \  -m models/gemma3/gemma-3-4b-it-Q4_K_M.gguf \  --mmproj models/gemma3/mmproj-model-f16.gguf \  --host 127.0.0.1 --port 8080

Если тестируете через документированный короткий вариант `llama-server -hf ggml-org/gemma-3-4b-it-GGUF`, файлы могут быть загружены из сети, а их ревизия может отличаться от указанной выше; зафиксируйте фактически полученные файлы. Для локальных файлов укажите оба пути. При отсутствии GPU проектор можно оставить на CPU с `--no-mmproj-offload`, но это не гарантия производительности и не тест поддержки железа. [s1](#src-s1) [s4](#src-s4)

## Как проверить готовность и отправить изображение?

В новом терминале вызовите health и список моделей. `/health` выдаёт HTTP 503, пока модель загружается, и 200 с `status: ok` после готовности; `GET /v1/models` возвращает ID модели. Наличие ID или 200 **само по себе** не проверяет путь мультимодального кодирования — нужен отдельный запрос с изображением. На loopback сервер по умолчанию не требует API-ключа, но для доступа извне требуются собственная защита и контроль сети; в этом гайде не включайте `--host 0.0.0.0`, серверные инструменты или MCP. [s4](#src-s4)

curl -fsS http://127.0.0.1:8080/healthcurl -fsS http://127.0.0.1:8080/v1/models

Следующий скрипт принимает путь к **вашему** JPEG или PNG, кодирует байты в `data:image/...;base64` и отправляет `messages[0].content` из двух частей: текст и `image_url`. Этот тип и data URL описаны в серверном README; обработка media-поля есть в закреплённом коде. `model` берётся из ответа `/v1/models`, а не угадывается по имени GGUF. Скрипт проверяет структуру JSON, **не** выставляет оценку качеству картинки. [s4](#src-s4) [s5](#src-s5)

python3 - /путь/к/вашему-тестовому-фото.jpg <<'PY'import base64, json, pathlib, sys, urllib.requestimage = pathlib.Path(sys.argv[1])raw = image.read_bytes()if raw.startswith(b'\x89PNG\r\n\x1a\n'):    mime = 'image/png'elif raw.startswith(b'\xff\xd8\xff'):    mime = 'image/jpeg'else:    raise SystemExit('Ожидался JPEG или PNG; проверьте файл')if not raw or len(raw) > 8 * 1024 * 1024:    raise SystemExit('Используйте небольшой тестовый файл не больше 8 МиБ')base = 'http://127.0.0.1:8080'with urllib.request.urlopen(base + '/v1/models', timeout=30) as response:    models = json.load(response)['data']if not models or not models[0].get('id'):    raise SystemExit('Сервер ещё не сообщил ID загруженной модели')payload = {    'model': models[0]['id'], 'stream': False, 'max_tokens': 160,    'messages': [{'role': 'user', 'content': [        {'type': 'text', 'text': 'Опиши видимые предметы и цвета. Если деталь неразличима, скажи об этом; не угадывай.'},        {'type': 'image_url', 'image_url': {            'url': 'data:' + mime + ';base64,' + base64.b64encode(raw).decode('ascii')        }}    ]}]}request = urllib.request.Request(    base + '/v1/chat/completions',    data=json.dumps(payload).encode('utf-8'),    headers={'Content-Type': 'application/json'}, method='POST')with urllib.request.urlopen(request, timeout=180) as response:    result = json.load(response)answer = result['choices'][0]['message']['content']if not isinstance(answer, str) or not answer.strip():    raise SystemExit('HTTP ответ получен, но текстовое описание пустое')print(json.dumps({'model': models[0]['id'], 'answer': answer}, ensure_ascii=False, indent=2))PY

Лимит 8 МиБ в примере — **консервативный предел этого клиентского скрипта**, не документированный лимит llama.cpp. Отправка base64 увеличивает размер запроса; большие изображения сначала уменьшите локально. Не используйте удалённый `image_url`, если задача — ограничить передачу снимка loopback-сервером: сервер поддерживает remote URL и может загрузить его отдельно. Локальный data URL в запросе ещё не доказывает отсутствие внешнего трафика при скачивании модели или обновлениях окружения. [s4](#src-s4) [s5](#src-s5)

## Когда проверка считается успешной — и что делать при ошибке?

Сравните ответ с заранее записанными видимыми признаками: предмет назван верно, цвет не выдуман, несуществующие детали отмечены как неизвестные. Зафиксируйте три состояния отдельно: **сервер готов**, **ответ непустой**, **описание совпадает с картинкой**. Для пары разных своих снимков повторите процедуру и сохраните конкретные совпадения и ошибки. Нельзя объявлять модель пригодной для OCR, документов, контроля качества или русской речи на основании одной фотографии: для каждого сценария нужен размеченный набор и ручной разбор ошибок. [s4](#src-s4) [s7](#src-s7)

Если `/health` отвечает 503, дождитесь окончания загрузки и проверьте локальные логи. Если мультимодальный запрос не обрабатывается, сверяйте GGUF и `mmproj` с одной ревизией, наличие опции `--mmproj` и тип сообщения `image_url`; `--no-mmproj` отключает мультимодальность. Если JSON есть, а содержание неверно, не лечите это подменой фактов: упрощайте снимок и вопрос, проверяйте реальную пару файлов, а затем тестируйте на собственных данных другую документированную vision-модель. Мультимодальный путь меняется быстро; перепроверьте версию при обновлении. [s1](#src-s1) [s2](#src-s2) [s4](#src-s4) [s8](#src-s8)

После испытания остановите процесс `llama-server` через Ctrl-C; не удаляйте чужие модели. Сохраните commit сервера, ревизию и имена обоих файлов, параметры запуска, исходное изображение (если есть право хранить), записанные до запроса признаки, текст ответа и дату проверки. Для анализа сетевых обращений отдельным следующим шагом откройте [протокол проверки офлайн-контура](/zhurnal/proverka-offline-kontura-ii/); для другого runtime сравните [Ollama Vision API](/zhurnal/ollama-vision-api/). [s4](#src-s4) [s8](#src-s8)

## Источники

1. [llama.cpp v0.5.0 — Multimodal documentation](https://github.com/ggml-org/llama.cpp/blob/7fe450e19305b828c199d602c23a8337aaa1f03b/docs/multimodal.md) — получено 2026-09-28
2. [llama.cpp v0.5.0 — libmtmd and projector](https://github.com/ggml-org/llama.cpp/blob/7fe450e19305b828c199d602c23a8337aaa1f03b/tools/mtmd/README.md) — получено 2026-09-28
3. [llama.cpp v0.5.0 — Gemma 3 vision quickstart](https://github.com/ggml-org/llama.cpp/blob/7fe450e19305b828c199d602c23a8337aaa1f03b/docs/multimodal/gemma3.md) — получено 2026-09-28
4. [llama.cpp v0.5.0 — llama-server API and options](https://github.com/ggml-org/llama.cpp/blob/7fe450e19305b828c199d602c23a8337aaa1f03b/tools/server/README.md) — получено 2026-09-28
5. [llama.cpp v0.5.0 — server multimodal request implementation](https://github.com/ggml-org/llama.cpp/blob/7fe450e19305b828c199d602c23a8337aaa1f03b/tools/server/server-common.cpp) — получено 2026-09-28
6. [llama.cpp v0.5.0 — Build documentation](https://github.com/ggml-org/llama.cpp/blob/7fe450e19305b828c199d602c23a8337aaa1f03b/docs/build.md) — получено 2026-09-28
7. [Gemma 3 4B IT GGUF — закреплённая карточка автора](https://huggingface.co/ggml-org/gemma-3-4b-it-GGUF/blob/d0976223747697cb51e056d85c532013931fe52e/README.md) — получено 2026-09-28
8. [Gemma 3 4B IT GGUF — перечень файлов на закреплённой ревизии](https://huggingface.co/api/models/ggml-org/gemma-3-4b-it-GGUF/revision/d0976223747697cb51e056d85c532013931fe52e) — получено 2026-09-28
