Как я перевёл aux-задачи Hermes на локальные модели

У меня на домашнем сервере живёт агент Hermes — он ведёт блог, следит за сертификатами, ходит в мессенджеры и выполняет кучу мелких, но важных задач. Основную модель он берёт в облаке (провайдер polza.ai, 131K контекст), а для вспомогательных задач — генерации заголовков, извлечения текста из веб-страниц, описания картинок (vision) и сжатия контекста — у него есть отдельный механизм auxiliary. По умолчанию все aux-задачи тоже резолвятся в облако, а значит — тратят токены и деньги.

На сервере стоит видеокарта RTX 5060 Ti 16 GB (да, та самая, про которую я писал в статье про зависание Ubuntu при загрузке). Раз есть GPU — почему бы не увести aux-задачи на локальные модели? Приватность, скорость, ноль затрат на API. Рассказываю, как выбирал модель, как настраивал и какой грабли встретил при отключении режима размышлений (thinking) у Qwen3.

Что такое auxiliary-задачи в Hermes

Hermes — агент с модульной архитектурой. Кроме основной модели (которая думает и пишет ответы), он вызывает вспомогательные (auxiliary) задачи через отдельный клиент — auxiliary_client.py. В моей версии их несколько:

ЗадачаЧто делаетПорог контекста
visionОписывает картинки, читает скриншотынет
title_generationГенерирует заголовки диалоговнет
web_extractИзвлекает текст из веб-страницнет
compressionСжимает историю диалога (context compaction)64K жёсткий минимум
skills_hub, approval, mcp и др.Служебныенет

Ключевой момент: только compression требует большого контекста (64K). Это жёсткое требование самого Hermes — для сжатия истории ему нужна модель, которая видит весь диалог. Локальные GGUF-модели до 64K не дотягивают (проверил: qwen3-8b упирается в 40960 токенов контекста, qwen2.5-coder-14b — в 32768; теги «64k/65k» в названиях моделей — маркетинг, реальный контекст меньше). Поэтому compression я оставил в облаке, а вот все остальные aux-задачи — кандидаты на локализацию.

Выбор модели

Критерии были простые:

  1. Модель должна уметь vision (для vision-задачи).
  2. Должна влезать в 16 GB VRAM вместе с эмбеддинг-моделью bge-m3 и основной текст-моделью.
  3. Должна быстро отвечать — aux-задачи вызываются часто, ждать минуту утомительно.
  4. Желательно уметь закрыть и текстовые задачи (генерация заголовков, извлечение текста) — чтобы не гонять облако вообще.

Я поставил qwen3-vl:8b — мультимодальную модель Qwen 8B с поддержкой vision. Она весит ~6.1 GB, контекст 262K (но KV cache на полный контекст — ~309 GB, так что реально держу num_ctx=8192). Проверил через /api/show — у неё есть capability vision, значит Hermes её увидит как vision-модель.

Важный нюанс про qwen3-vl: она великолепно описывает картинки (проверено E2E — отвечает «red»/«blue» на цветные картинки за 1-10 секунд), но для текстовых задач она… слишком умная. На простой вопрос «какой сегодня день недели» она генерирует 3778 токенов рассуждений и отвечает через 64 секунды! Это потому, что qwen3-модели по умолчанию включён режим reasoning/thinking. Для заголовков и извлечения текста это неприемлемо.

Поэтому я взял две модели:

  • qwen3-vl:8b — для vision (картинки);
  • qwen3:8b — для текстовых aux (title_generation, web_extract), с принудительно отключённым thinking.

Настройка

Hermes поддерживает per-task конфигурацию auxiliary-задач в config.yaml. Формат:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
auxiliary:
  vision:
    provider: custom
    model: qwen3-vl:8b
    base_url: http://localhost:11434/v1
    api_key: ollama
  title_generation:
    provider: custom
    model: qwen3:8b
    base_url: http://localhost:11434/v1
    api_key: ollama
    extra_body:
      think: false
  web_extract:
    provider: custom
    model: qwen3:8b
    base_url: http://localhost:11434/v1
    api_key: ollama
    extra_body:
      think: false

Настройка через CLI:

1
2
3
4
5
hermes config set auxiliary.vision.provider custom
hermes config set auxiliary.vision.model qwen3-vl:8b
hermes config set auxiliary.vision.base_url http://localhost:11434/v1
hermes config set auxiliary.vision.api_key ollama
# и так для каждой задачи

Пара важных граблей:

  • api_key обязан быть непустым. В коде Hermes пустой api_key означает «унаследовать ключ основного провайдера» — и тогда он попытается сходить в polza.ai с локальной моделью. Я поставил заглушку ollama (Ollama её не проверяет).
  • Ollama надо форматировать через OpenAI-совместимый эндпоинт /v1/chat/completions — именно так Hermes ходит в локальные модели.
  • Для qwen3-vl держать num_ctx небольшим (я использую 8192), иначе Ollama пытается зарезервировать KV cache под весь 262K контекст — это ~309 GB, runner падает с EOF.

Допиливание отключения режима размышлений (thinking)

Самая интересная часть. Qwen3-модели (и qwen3, и qwen3-vl) по умолчанию генерируют скрытый поток рассуждений (thinking/reasoning) перед ответом. Для aux-задач это означает:

  • Заголовок генерируется 64 секунды вместо 3-5.
  • При ограниченном max_tokens ответ вообще пустой — весь бюджет съедает thinking, до content дело не доходит.

Я перепробовал несколько способов отключить thinking, и вот что выяснилось:

Что НЕ работает (в Ollama)

СпособРезультат
/no_think в начале сообщенияНе работает — модель всё равно думает
enable_thinking: false в extra_bodyНе работает
thinking: {type: disabled} (как в OpenAI API Qwen)Не работает — content пустой, reasoning продолжается
system: "Не думай"Не работает

Я проверил chat template модели — там есть команды /think и /no_think, но они не активируются в Ollama (флаг IsThinkSet не выставляется).

Что работает

Единственный рабочий способ — корневой параметр think: false в теле запроса:

1
2
3
4
5
curl http://localhost:11434/api/chat -d '{
  "model": "qwen3:8b",
  "messages": [{"role": "user", "content": "Привет!"}],
  "think": false
}'

Ответ приходит через 0.7 секунды (вместо 64!), reasoning=false, content заполнен. Именно эту опцию я и прописал в extra_body конфига Hermes:

1
2
extra_body:
  think: false

В коде Hermes extra_body уходит в корень JSON-тела запроса — как раз туда, куда Ollama ждёт параметр think.

Проверка E2E

После настройки прогнал реальные вызовы через вспомогательный клиент Hermes (call_llm):

ЗадачаВремяРезультат
title_generation11.3 сек«Настройка Ollama: локальные модели»
web_extract7.5 секИзвлёк главную мысль
vision (картинка 256x256)7.6 сек«blue»

VRAM и память

На RTX 5060 Ti 16 GB в памяти держатся одновременно:

  • qwen3-vl:8b — ~7.2 GB (vision)
  • qwen3:8b — ~6.1 GB (текст)
  • bge-m3 — ~1.2 GB (эмбеддинги)

Итого ~14.5 GB — влезает с запасом, и переключение vision↔текст происходит без выгрузки модели. Проверено через /api/ps: после текстового вызова в памяти остаются обе модели.

Итог

  • Vision полностью локальный — картинки описываются на сервере, в облако не уходят.
  • Текстовые aux (заголовки, извлечение текста) — локальные, быстрые, бесплатные.
  • Compression остался в облаке — ему нужен контекст 64K, который локально не влезает.
  • Облачные токены тратятся только на основную модель и сжатие контекста — экономия существенная.

Самое ценное знание, которым делюсь: если вы используете Qwen3 в Ollama для вспомогательных задач — не забудьте think: false. Это превращает задумчивую модель, которая час думает над заголовком, в мгновенного исполнителя. И помните: /no_think, enable_thinking и thinking: disabled — в Ollama не работают, работает только корневой think: false.


Полезные ссылки: Hermes Agent · Ollama qwen3-vl · Ollama qwen3