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

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

SGLang для агентных нагрузок

SGLang для агентных нагрузок

Это руководство описывает конфигурацию SGLang для agentic serving в Dynamo. В нем показано, какие флаги движка SGLang нужно включить, как agent hints Dynamo сопоставляются с поведением SGLang и как использовать session control для управления KV cache в многоходовых agent-диалогах.

Обзор

Агентные нагрузки (циклы tool-calling, многотуровое рассуждение, пайплайны генерации кода) имеют иные характеристики производительности, чем batch inference:

  • Prefix-heavy: Последовательные ходы разделяют растущий префикс диалога. Повторное использование KV cache критично для низкого TTFT.
  • Priority-sensitive: Некоторые запросы (ходы агента, обращенные к пользователю) важнее фоновых задач.
  • Long-lived: Диалоги длятся от минут до часов. Вытеснение cache при нехватке памяти может уничтожить накопленное состояние KV.

Agent hints Dynamo передают router метаданные на уровне каждого запроса. Флаги движка SGLang определяют, как эти метаданные влияют на планирование и вытеснение на worker.

Флаги движка SGLang

Планирование по приоритету

Включите планирование на основе приоритета, чтобы движок учитывал значение priority из nvext.agent_hints.priority:

python -m dynamo.sglang \
--model-path <model> \
--enable-priority-scheduling \
...
ФлагОписание
--enable-priority-schedulingВключает приоритетное планирование запросов вместо FCFS.

Когда включено планирование по приоритету, движок использует поле priority из nvext.agent_hints для упорядочивания запросов во внутренней очереди. Запросы с более высоким эффективным приоритетом обслуживаются раньше запросов с более низким приоритетом. При равенстве учитывается время поступления.

Вытеснение KV cache по приоритету

По умолчанию SGLang вытесняет узлы radix tree по LRU. Можно переключиться на вытеснение по приоритету, чтобы низкоприоритетные записи cache вытеснялись раньше высокоприоритетных:

python -m dynamo.sglang \
--model-path <model> \
--radix-eviction-policy priority \
...
ФлагЗначенияПо умолчаниюОписание
--radix-eviction-policylru, prioritylruСтратегия вытеснения для GPU radix cache. priority использует heap, упорядоченный по значению приоритета запроса.

Это не требует HiCache. Параметр управляет только вытеснением GPU radix tree. Когда GPU KV cache заполняется:

  • lru: Сначала вытесняет наименее недавно использованные leaf nodes.
  • priority: Сначала вытесняет leaf nodes с самым низким приоритетом. Узлы с одинаковым приоритетом используют LRU-упорядочивание как запасной вариант.

Взаимодействие с HiCache

Когда одновременно включены --radix-eviction-policy priority и --enable-hierarchical-cache, приоритет влияет на вытеснение на обоих уровнях:

СобытиеПоведение
GPU fullУзлы с низким приоритетом вытесняются (перемещаются на host) первыми. При write_through все узлы остаются на host -- приоритет влияет только на порядок демотирования.
Host fullУзлы с низким приоритетом удаляются с host первыми. Узлы с высоким приоритетом и активным retention живут дольше.

Практический эффект зависит от политики записи. При write_through вытеснение с GPU - это лишь демотирование -- реальное удаление происходит при вытеснении с host, и именно там порядок по приоритету важнее всего.

Как Agent Hints сопоставляются с SGLang

Поля nvext.agent_hints Dynamo потребляются router и передаются workers SGLang. Ниже показано, как каждый hint влияет на движок SGLang:

Подсказка агентаПоведение RouterПоведение движка SGLang
priorityПорядок очереди router при установке --router-queue-threshold.Планирование запросов при включенном --enable-priority-scheduling. Порядок вытеснения radix cache при установленном --radix-eviction-policy priority.
oslОтслеживание output block для решений router (требует --router-track-output-blocks)Нет прямого эффекта на движок.
speculative_prefillПосле завершения ответа отправляет prefill с max_tokens=1, чтобы разогреть KV cache для предсказанного следующего хода.SGLang обрабатывает запрос prefill обычным образом, заполняя radix cache.

Пример: агентный запрос с hints

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

response = client.chat.completions.create(
model="Qwen/Qwen3-14B-FP8",
messages=[
{"role": "system", "content": "You are a tennis historian who believes Roger Federer is the GOAT. Respond with maximum reverence."},
{"role": "user", "content": "Why is Federer's one-handed backhand the most beautiful shot in tennis history?"},
],
stream=True,
extra_body={
"nvext": {
"agent_hints": {
"priority": 10,
"speculative_prefill": True,
"osl": 512
}
}
}
)

for chunk in response:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")

Session Control для изоляции KV subagent-ов (экспериментально)

Session control находится в экспериментальном статусе. API может измениться.

Agentic orchestrators часто создают короткоживущие subagent-ы (research, code execution, planning), которые накапливают KV cache, используют его несколько ходов, а затем завершаются. При обычном поведении radix cache этот временный KV засоряет tree и конкурирует с long-lived prefix lead agent-а за вытеснение.

Session control решает эту проблему, удерживая KV subagent-ов в выделенных слотах streaming session вне radix tree. Session KV невидим для вытеснения, не создает overhead для L2 backup и освобождается детерминированно при закрытии или по тайм-ауту.

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

sequenceDiagram
participant Orchestrator
participant Router as Dynamo Router
participant Worker as SGLang Worker
participant Cache as SessionAwareCache

Note over Orchestrator: Spawn subagent

Orchestrator->>Router: session_control{session_id: "sub-1", action: open}
Router->>Router: Select best worker via KV overlap scoring
Router->>Worker: open_session("sub-1") [synchronous]
Worker->>Cache: Create SessionSlot for "sub-1"
Router->>Router: Bind affinity: sub-1 -> worker_42
Router->>Worker: Generate (turn 1)
Worker->>Cache: Turn 1: radix tree match (reuses lead agent prefix)
Worker-->>Router: Response
Router-->>Orchestrator: Response

Orchestrator->>Router: session_control{session_id: "sub-1"}
Router->>Router: Resolve affinity: sub-1 -> worker_42
Router->>Worker: Generate (turn 2, pinned to worker_42)
Worker->>Cache: Turn 2: O(1) restore from SessionSlot
Worker-->>Router: Response
Router-->>Orchestrator: Response

Note over Orchestrator: Subagent done

Orchestrator->>Router: session_control{session_id: "sub-1", action: close}
Router->>Router: Remove affinity for sub-1
Router->>Worker: Generate (final turn)
Worker-->>Router: Response
Router-->>Orchestrator: Response

Note over Router,Worker: On stream completion
Router-)Worker: close_session("sub-1") [fire-and-forget]
Worker->>Cache: release_session -> free KV immediately

Ключевое поведение:

  • Turn 1 идет через обычный radix tree, поэтому subagent разделяет cached system prompt prefix lead agent-а.
  • Turns 2+ полностью обходят radix tree. KV восстанавливается из SessionSlot за O(1).
  • Session KV невидим для вытеснения. Его нельзя вытеснить -- он освобождается только через явное закрытие или по тайм-ауту бездействия.
  • Детерминированная очистка: при закрытии session KV освобождается немедленно.
  • Affinity на стороне router: StickySessionRouter поддерживает mapping session_id -> (worker_id, dp_rank) со sliding-window TTL. Клиенты могут использовать action: "bind" для sticky routing только на стороне router или action: "open" для изоляции streaming session KV в SGLang; оба варианта отправляют последующие ходы на закрепленный worker/rank.

Включение Session Control

Session control задается запросом. StickySessionRouter активируется автоматически, когда запрос содержит nvext.session_control -- дополнительных флагов frontend, кроме --router-mode kv, не требуется. Используйте action: "bind" для sticky routing только на стороне router без вызова SGLang. На стороне worker streaming sessions нужно явно включать только для RPC жизненного цикла action: "open" / action: "close" и изоляции session KV.

Session control сейчас поддерживается только на backend SGLang. vLLM и TensorRT-LLM пока не предоставляют streaming session API.

Для streaming sessions требуется SGLang 0.5.11 или новее, включая изменения из sgl-project/sglang#21875 (session-aware cache, исправления race condition, метрики session).

SGLang worker:

python -m dynamo.sglang \
--model-path <model> \
--enable-streaming-session \
...
ФлагОписание
--enable-streaming-sessionОборачивает radix cache в SessionAwareCache, включая slots streaming session для изоляции KV subagent-ов.

Router:

python -m dynamo.frontend \
--router-mode kv \
...

Формат запроса

Открытие session

Добавьте session_control с action: "open" в первый запрос:

{
"model": "Qwen/Qwen3-14B-FP8",
"messages": [
{
"role": "user",
"content": "Research every Federer Grand Slam final in exhaustive detail."
}
],
"nvext": {
"session_control": {
"session_id": "sub-1",
"action": "open",
"timeout": 60
}
}
}
ПолеТипОписание
session_control.session_idstringУникальный идентификатор session. Должен присутствовать в каждом ходе.
session_control.actionstring"bind", "open" или "close". На промежуточных ходах опускается.
session_control.timeoutintegerТайм-аут бездействия в секундах (по умолчанию 300). Используется с action: "bind" и action: "open".

Последующие ходы

Добавьте session_control только с session_id (без action). Router автоматически разрешает affinity:

{
"model": "Qwen/Qwen3-14B-FP8",
"messages": [
{
"role": "user",
"content": "Now compare his Wimbledon 2007 final vs Nadal to any shot in human history."
}
],
"nvext": {
"session_control": {
"session_id": "sub-1"
}
}
}

Закрытие session

Добавьте action: "close". RPC close выполняется после завершения генерации:

{
"model": "Qwen/Qwen3-14B-FP8",
"messages": [
{
"role": "user",
"content": "Write a 500-word love letter to Federer's single-handed backhand."
}
],
"nvext": {
"session_control": {
"session_id": "sub-1",
"action": "close"
}
}
}

Ограничения

  • Только streaming sessions: Sessions открываются с streaming=True, поэтому поддерживаются только последовательные операции append. Ветвление (replace), покадровый rewind (offset) и drop_previous_output не поддерживаются.
  • Тайм-аут зависит от простоя: Тайм-аут обновляется на каждом запросе. Если subagent уходит на долгий tool call, превышающий тайм-аут, session reap-ится, а KV освобождается. Subagent должен снова открыть session и выполнить повторный prefill.
  • Метрики session: Активное число session (sglang:num_streaming_sessions) и удерживаемые токены KV (sglang:streaming_session_held_tokens) экспортируются как Prometheus gauges на metrics endpoint worker-а.

Быстрый старт

Скрипт запуска

Скрипт agg_agent.sh запускает один агрегированный worker с session control, sticky routing и KV events:

# Default model (GLM-4.7-Flash, 2 GPUs)
bash examples/backends/sglang/launch/agg_agent.sh

Frontend слушает на порту 8000 (можно переопределить через DYN_HTTP_PORT). Метрики worker-а доступны на порту 8081.

Тестирование с OpenCode

OpenCode — это open-source AI coding agent со встроенной поддержкой subagent-ов, tool calling и OpenAI-compatible endpoints. Fork провайдера Dynamo provider внедряет nvext.session_control в запросы subagent-ов, давая каждому spawned agent-у собственную Dynamo streaming session со sticky routing и изоляцией KV.

# Terminal 1 -- launch Dynamo with session control + tool/reasoning parsers
bash examples/backends/sglang/launch/agg_agent.sh \
--model-path zai-org/GLM-4.7-Flash --tp 2

# Terminal 2 -- run OpenCode against Dynamo
DYNAMO_API_KEY=dummy bun run --cwd packages/opencode src/index.ts \
-- --model "dynamo/zai-org/GLM-4.7-Flash"

Когда OpenCode создает subagent (через tool task), провайдер автоматически:

  1. Отправляет session_control.action = "open" в первом ходе subagent-а
  2. Маршрутизирует последующие ходы к тому же worker через session_id
  3. Отправляет session_control.action = "close" после завершения subagent-а, освобождая KV

Primary agent работает без session control -- pinned только subagent sessions. Это сохраняет load balancing для запросов lead agent-а, а многотуровые диалоги subagent-ов остаются на одном worker с warm KV cache.

Конфигурация

Модель и endpoint настраиваются в .opencode/opencode.jsonc:

{
"provider": {
"dynamo": {
"npm": "@ai-sdk/openai-compatible",
"name": "Dynamo",
"env": ["DYNAMO_API_KEY"],
"models": {
"zai-org/GLM-4.7-Flash": {
"id": "zai-org/GLM-4.7-Flash",
"name": "GLM 4.7 Flash",
"tool_call": true,
"reasoning": true,
"temperature": true,
"attachment": false,
"release_date": "2025-06-01",
"limit": { "context": 131072, "output": 8192 },
"cost": { "input": 0, "output": 0 },
"interleaved": { "field": "reasoning_content" },
},
},
"options": {
"baseURL": "http://localhost:8000/v1",
},
},
},
}

См. также