Для чистой 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 | Путь | Поддерживается | Примечания |
|---|---|---|---|
| vLLM | Rust frontend (default) | ✅ | Использует lightseek llm-multimodal для подсчёта image-token и расширения placeholder. Поддерживаемые модели перечислены ниже. |
| vLLM | Python 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)
- Rust frontend вычисляет
mm_hashдля каждого изображения:xxh3_64от декодированных байтов дляdata:URI (и дляhttp(s)://, когда на модели включёнmedia_decoder), иначеxxh3_64от всей строки URL. Два caller'а будут использовать один и тот жеmm_hashтолько если отправляют byte-identical URL. - Идентификатор 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'ы. Поддержка появится в одном из следующих релизов.
WиHдля каждого изображения считываются из header fetch, ограниченного 64 KBRange(или из bytes в памяти дляdata:URI); crate lightseekllm-multimodalвычисляет расширенное количество token для каждого изображения.- Единичный placeholder token расширяется до N копий в
routing_token_ids(это представление только для router); worker по-прежнему видит по одному placeholder на изображение вtoken_ids. - Per-block MM metadata (
block_mm_infos) строится по расширенному представлению; KV router оценивает пересечение между worker'ами, включая блоки с изображениями. - 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.
Ключевые переменные окружения:
| Переменная | Значение по умолчанию | Описание |
|---|---|---|
MODEL | Qwen/Qwen3-VL-2B-Instruct | Модель для обслуживания |
NUM_WORKERS | 2 | Количество backend worker'ов |
BLOCK_SIZE | 16 | Размер блока KV cache (должен совпадать с backend) |
GPU_MEMORY_UTILIZATION | 0.20 | Доля GPU memory на worker |
SINGLE_GPU | false | Разместить все worker'ы на GPU 0 (только для тестов; передайте --single-gpu или задайте SINGLE_GPU=true для функциональных тестов на машине с одной GPU) |
KV_EVENTS_PORT_BASE | 5557 | Worker i публикует ZMQ KV events на BASE + i - 1 |
DYN_LOG | info,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.
| Переменная | Значение по умолчанию | Описание |
|---|---|---|
MODEL | Qwen/Qwen3-VL-2B-Instruct | Модель для обслуживания |
NUM_WORKERS | 2 | Количество backend worker'ов |
BLOCK_SIZE | 16 | Размер блока KV cache (должен совпадать с backend) |
GPU_MEMORY_UTILIZATION | 0.40 | Доля GPU memory на worker |
SINGLE_GPU | false | Разместить все worker'ы на GPU 0 (только для тестов; передайте --single-gpu или задайте SINGLE_GPU=true для функциональных тестов на машине с одной GPU) |
DYNAMO_MM_TRANSFER | shm | Режим передачи предварительно обработанных mm_kwargs: shm (shared memory, same-node), nixl (RDMA, cross-node) |
DYNAMO_DISABLE_NIXL_MM | unset | Задайте 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 передачи выше, чем стоимость повторной обработки.