Files

191 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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)*