Перейти к основному содержимому

Для чистой Markdown-версии этой страницы добавьте .md к этому URL. Полный индекс документации см. в https://docs.nvidia.com/dynamo/llms.txt. Полное содержимое, включая API reference и примеры SDK, см. в https://docs.nvidia.com/dynamo/llms-full.txt.

Мультимодальная KV-маршрутизация

Обзор

Multimodal KV routing расширяет KV-aware router в Dynamo так, чтобы при вычислении коэффициентов пересечения кэша учитывалось и содержимое изображений. Хэш изображения (mm_hash) вычисляется для каждого запроса - в Rust frontend по умолчанию для backend'ов vLLM, самим vLLM processor, когда включён вариант chat-processor, или отдельным MM router worker для backend'ов TRT-LLM - и включается в routing metadata для каждого блока. Затем KV router выбирает backend worker с наибольшим пересечением кэша, включая пересечение по блокам image embedding.

Повторные запросы с одним и тем же изображением направляются к worker'у, у которого уже есть соответствующие блоки KV cache, что максимизирует повторное использование prefix cache.

Примечание: KV cache отделён от embedding cache, также называемого encoder cache, который повторно использует результаты vision encoder (image→embeddings), чтобы не запускать encoder заново. Для повторного использования на стороне encoder см. Embedding Cache.

Когда использовать

Используйте multimodal KV routing, когда:

  • У вас несколько backend worker'ов, обслуживающих multimodal-запросы
  • В вашей нагрузке встречаются повторяющиеся изображения между запросами (например, одно и то же фото товара или общие reference images)
  • Вы хотите максимизировать hit rate KV cache для multimodal-контента

Без MM-aware routing стандартный router рассматривает блоки image token как непрозрачные и не может определить, у какого worker'а закэшированы KV-блоки конкретного изображения.

Матрица поддержки

BackendПутьПоддерживаетсяПримечания
vLLMRust frontend (default)Использует lightseek llm-multimodal для подсчёта image-token и расширения placeholder. Поддерживаемые модели перечислены ниже.
vLLMPython chat-processor (--dyn-chat-processor vllm --router-mode kv)Использует собственный multimodal processor vLLM - поддерживает любой VLM, который поддерживает vLLM.
TRT-LLMИспользует выделенный MM Router Worker. Требует --publish-events-and-metrics на worker'ах TRT-LLM.
SGLangПока не поддерживается.

Поддерживаемые семейства моделей (путь Rust frontend)

Путь MM-aware routing в Rust frontend поддерживает те семейства VLM, которые регистрирует crate lightseek llm-multimodal; см. ImageProcessorRegistry::with_defaults() для актуального списка. Если модель не распознаётся этим crate, происходит fallback на KV routing только по text-prefix (запрос всё равно завершается; просто между изображениями не будет выгоды от prefix-cache).

Вариант Python chat-processor этого ограничения не имеет - он делегирует работу собственному multimodal processor vLLM и работает с любым VLM, который поддерживает vLLM.

Как это работает

vLLM (по умолчанию — Rust frontend)

Frontend (Rust + lightseek llm-multimodal + KV router) → Backend Workers

├─ Hash image (xxh3_64 of the raw URL — full-URL identity; use --frontend-decoding for content-addressed hashing)
├─ Resolve image-token id via lightseek's per-model ModelProcessorSpec
├─ Read (W, H) from a Range: 0-65535 header fetch (or in-memory data: bytes)
├─ lightseek::count_tokens(W, H) → expanded image-token count N
├─ Expand placeholder × N in routing_token_ids (worker token_ids unchanged)
├─ Build per-block MM metadata (block_mm_infos)
├─ KV router selects best worker
└─ Forward mm_hash to worker via extra_args["mm_hashes"] →
vLLM's multi_modal_uuids (cache key match)
  1. Rust frontend вычисляет mm_hash для каждого изображения: xxh3_64 от декодированных байтов для data: URI (и для http(s)://, когда на модели включён media_decoder), иначе xxh3_64 от всей строки URL. Два caller'а будут использовать один и тот же mm_hash только если отправляют byte-identical URL.
  2. Идентификатор image-placeholder token определяется через делегирование к per-model ModelProcessorSpec в lightseek (одна спецификация на поддерживаемое семейство VLM — Qwen3-VL, Qwen2.5-VL, Qwen2-VL, LLaVA-NeXT, LLaVA-1.5, Phi-3-vision, Llama-4, Kimi-K2.5). Каждая спецификация читает соответствующее поле config.json для своего семейства моделей (image_token_id, image_token_index или media_placeholder_token_id) и при необходимости переходит к проверке vocab tokenizer'а, если зарегистрирована только строка placeholder. Для моделей, которые registry не распознаёт, выполняется fallback на routing только по text-prefix.

Примечание: расширение image token для Qwen3.5 / Qwen3.6 пока не поддерживается в Rust frontend для MM routing, поэтому KV routing будет учитывать только text inputs и нерасширенные image token placeholder'ы. Поддержка появится в одном из следующих релизов.

  1. W и H для каждого изображения считываются из header fetch, ограниченного 64 KB Range (или из bytes в памяти для data: URI); crate lightseek llm-multimodal вычисляет расширенное количество token для каждого изображения.
  2. Единичный placeholder token расширяется до N копий в routing_token_ids (это представление только для router); worker по-прежнему видит по одному placeholder на изображение в token_ids.
  3. Per-block MM metadata (block_mm_infos) строится по расширенному представлению; KV router оценивает пересечение между worker'ами, включая блоки с изображениями.
  4. Frontend передаёт mm_hash каждого изображения (16-символьный hex-prefix, padded) через extra_args["mm_hashes"]; backend handler подставляет их как multi_modal_uuids в vLLM, поэтому собственный KV-cache key vLLM совпадает с хэшем, который использовал router.

vLLM (альтернативный вариант — Python chat-processor)

Frontend (vLLM processor + KV router) → Backend Workers

├─ Download image (via DynamoMediaConnector, LRU cached)
├─ Run vLLM's process_inputs() (HF processor, model-agnostic)
├─ Extract mm_hash from mm_features
├─ Build per-block MM metadata (block_mm_infos)
├─ KV router selects best worker
└─ Transfer pre-processed mm_kwargs via SHM or NIXL
→ Backend skips HF processor

Используйте этот вариант (--dyn-chat-processor=vllm), когда хотите, чтобы frontend запускал HF image processor vLLM в процессе и отправлял предварительно обработанные mm_kwargs выбранному worker'у через shared memory или NIXL RDMA, чтобы backend полностью пропускал HF processor. См. ниже раздел Transfer Mode Details для флагов DYNAMO_MM_TRANSFER.

TRT-LLM

Frontend (round-robin) → MM Router Worker → Backend Workers

├─ Download image
├─ Compute mm_hash
├─ Build per-block MM metadata
└─ KvRouter selects best worker

Для TRT-LLM между frontend и backend worker'ами находится выделенный MM Router Worker. Инструкции по настройке см. в TRT-LLM MM Router README.

Запуск

vLLM (по умолчанию — Rust frontend)

cd $DYNAMO_HOME
bash examples/backends/vllm/launch/agg_multimodal_router.sh

Rust frontend использует crate lightseek llm-multimodal (source) для подсчёта token count на изображение и расширения placeholder. llm-multimodal предоставляет полностью Rust-реализацию calculate_num_tokens(W, H, PreProcessorConfig) для каждого семейства VLM (Qwen2/2.5/3-VL, LLaVA, Pixtral, …), проверенную по golden tests против transformers, поэтому router может сопоставлять расширенное количество image-token vLLM без запуска HF image processor. Затем frontend передаёт каждый mm_hash worker'у как multi_modal_uuids, чтобы KV events vLLM публиковали тот же ключ, который вычисляет router.

Ключевые переменные окружения:

ПеременнаяЗначение по умолчаниюОписание
MODELQwen/Qwen3-VL-2B-InstructМодель для обслуживания
NUM_WORKERS2Количество backend worker'ов
BLOCK_SIZE16Размер блока KV cache (должен совпадать с backend)
GPU_MEMORY_UTILIZATION0.20Доля GPU memory на worker
SINGLE_GPUfalseРазместить все worker'ы на GPU 0 (только для тестов; передайте --single-gpu или задайте SINGLE_GPU=true для функциональных тестов на машине с одной GPU)
KV_EVENTS_PORT_BASE5557Worker i публикует ZMQ KV events на BASE + i - 1
DYN_LOGinfo,lightseek_mm=debug,...Фильтр логов frontend
VLLM_EXTRA_ARGS(unset)Аргументы, передаваемые в python -m dynamo.vllm. Задайте --frontend-decoding, чтобы включить content-addressed mm_hash (повторное использование KV cache между разными URL).

Чтобы включить декодирование изображений на frontend (так чтобы frontend скачивал и декодировал изображение один раз, а mm_hash стал content-addressed вместо URL-addressed):

VLLM_EXTRA_ARGS="--frontend-decoding" \
bash examples/backends/vllm/launch/agg_multimodal_router.sh

Затем worker регистрирует media_decoder в своей model card; MediaLoader во frontend работает в процессе и хэширует декодированные RGB bytes через xxh3. Два разных (signed) URL с одинаковыми bytes изображения будут совпадать по одному routing key.

vLLM (альтернативный вариант — Python chat-processor)

bash examples/backends/vllm/launch/agg_multimodal_router_chat_processor.sh

Использует --dyn-chat-processor=vllm, чтобы frontend запускал HF processor vLLM в процессе. Добавляет между frontend и worker канал доставки заранее подготовленных mm_kwargs через DYNAMO_MM_TRANSFER shm/NIXL.

ПеременнаяЗначение по умолчаниюОписание
MODELQwen/Qwen3-VL-2B-InstructМодель для обслуживания
NUM_WORKERS2Количество backend worker'ов
BLOCK_SIZE16Размер блока KV cache (должен совпадать с backend)
GPU_MEMORY_UTILIZATION0.40Доля GPU memory на worker
SINGLE_GPUfalseРазместить все worker'ы на GPU 0 (только для тестов; передайте --single-gpu или задайте SINGLE_GPU=true для функциональных тестов на машине с одной GPU)
DYNAMO_MM_TRANSFERshmРежим передачи предварительно обработанных mm_kwargs: shm (shared memory, same-node), nixl (RDMA, cross-node)
DYNAMO_DISABLE_NIXL_MMunsetЗадайте 1, чтобы полностью отключить передачу mm_kwargs (backend повторно обрабатывает изображения по URL)

TRT-LLM

cd $DYNAMO_HOME/examples/backends/trtllm/mm_router_worker
./launch.sh

Полные инструкции по настройке и варианты конфигурации см. в TRT-LLM MM Router README.

Подробности режима передачи (только для варианта vLLM chat-processor)

Применяется к запуску --dyn-chat-processor=vllm (agg_multimodal_router_chat_processor.sh), но не к стандартному пути Rust frontend. В варианте chat-processor frontend запускает HF image processor в процессе и отправляет предварительно обработанные mm_kwargs выбранному backend worker'у, чтобы backend мог пропустить повторную обработку; переменная окружения DYNAMO_MM_TRANSFER управляет тем, как передаётся этот payload.

Стандартный путь Rust frontend не запускает HF processor и не готовит mm_kwargs заранее - он передаёт только mm_hashes, а каждый worker сам повторно обрабатывает изображение. Backend'и TRT-LLM аналогично повторно выполняют собственную preprocessing и не учитывают DYNAMO_MM_TRANSFER.

  • shm (по умолчанию): POSIX shared memory через сегмент /dev/shm. Предназначен для развёртываний на одном node, где frontend и backend используют общий файловый system хоста. Если backend не может получить доступ к сегменту (например, запущен на другом node), происходит fallback на повторную обработку изображения по URL.
  • nixl: NIXL RDMA transfer. Требуется для развёртываний между node'ами, где /dev/shm не общий между frontend и backend. Работает между node'ами через InfiniBand или TCP, в зависимости от того, что выберет UCX.
  • DYNAMO_DISABLE_NIXL_MM=1: Полностью отключает передачу предварительно обработанных mm_kwargs. Backend сам скачивает и обрабатывает изображения по исходным URL. Полезно для отладки или когда overhead передачи выше, чем стоимость повторной обработки.