From f47f82fda1bf1efec22dbd04acced1589ca92a2b Mon Sep 17 00:00:00 2001 From: Storozhenko Evgeny Date: Sun, 30 Aug 2026 12:08:10 +0000 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=BE=D0=B2=D0=B0=D1=8F=20=D1=81=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=D1=8F=20=D0=BF=D1=80=D0=BE=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=B2=D0=BE=D0=B4=20aux-=D0=B7=D0=B0=D0=B4=D0=B0?= =?UTF-8?q?=D1=87=20Hermes=20=D0=BD=D0=B0=20=D0=BB=D0=BE=D0=BA=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B5=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D0=B8?= =?UTF-8?q?=20=D0=B8=20=D0=BE=D1=82=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D0=B5=20thinking=20=D1=83=20Qwen3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hero.svg | 33 +++ .../index.md | 191 ++++++++++++++++++ 2 files changed, 224 insertions(+) create mode 100644 content/posts/20260830 - local aux models hermes/hero.svg create mode 100644 content/posts/20260830 - local aux models hermes/index.md diff --git a/content/posts/20260830 - local aux models hermes/hero.svg b/content/posts/20260830 - local aux models hermes/hero.svg new file mode 100644 index 0000000..8886816 --- /dev/null +++ b/content/posts/20260830 - local aux models hermes/hero.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Локальные aux-модели Hermes + + + qwen3-vl · qwen3 · think:false · Ollama + + + + + \ No newline at end of file diff --git a/content/posts/20260830 - local aux models hermes/index.md b/content/posts/20260830 - local aux models hermes/index.md new file mode 100644 index 0000000..f0b2bda --- /dev/null +++ b/content/posts/20260830 - local aux models hermes/index.md @@ -0,0 +1,191 @@ +--- +date: '2026-08-30T12:00:00+03:00' +lastmod: '2026-08-30T12:00:00+03:00' +draft: false +title: 'Как я перевёл aux-задачи Hermes на локальные модели: экономия токенов и отключение режима размышлений у Qwen3' +slug: 'local-aux-models-hermes-qwen3' +description: 'Рассказываю, как выбрал модель для вспомогательных (auxiliary) задач агента Hermes, перевёл vision и текстовые задачи на локальную Ollama, сэкономил токены облачного провайдера и допилил отключение режима размышлений (thinking) у Qwen3.' + +categories: + - 'AI' + - 'DevOps' + +tags: + - 'hermes' + - 'ollama' + - 'qwen3' + - 'llm' + - 'aux' + - 'vision' + - 'локальные-модели' + - 'экономия-токенов' + +keywords: + - 'hermes' + - 'ollama' + - 'qwen3' + - 'qwen3-vl' + - 'auxiliary' + - 'thinking' + - 'reasoning' + - 'локальная модель' + - 'экономия токенов' + +cover: + image: "hero.svg" + alt: "Локальные aux-модели Hermes: qwen3-vl и qwen3" +--- + +# Как я перевёл aux-задачи Hermes на локальные модели + +У меня на домашнем сервере живёт агент [Hermes](https://hermes-agent.nousresearch.com) — он ведёт блог, следит за сертификатами, ходит в мессенджеры и выполняет кучу мелких, но важных задач. Основную модель он берёт в облаке (провайдер 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](https://ollama.com/library/qwen3-vl) — мультимодальную модель 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`. Формат: + +```yaml +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: + +```bash +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` в теле запроса:** + +```bash +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: + +```yaml +extra_body: + think: false +``` + +В коде Hermes `extra_body` уходит в корень JSON-тела запроса — как раз туда, куда Ollama ждёт параметр `think`. + +### Проверка E2E + +После настройки прогнал реальные вызовы через вспомогательный клиент Hermes (`call_llm`): + +| Задача | Время | Результат | +|---|---|---| +| `title_generation` | 11.3 сек | «Настройка Ollama: локальные модели» | +| `web_extract` | 7.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](https://hermes-agent.nousresearch.com) · [Ollama qwen3-vl](https://ollama.com/library/qwen3-vl) · [Ollama qwen3](https://ollama.com/library/qwen3)* \ No newline at end of file