Валентин Авдеев

Внедряю интеллектуальные системы и занимаюсь бизнес-аналитикой

Саммари двухчасовых созвонов на SLM 4B: иерархический reduce и обвязка важнее промпта

Саммари двухчасовых созвонов на SLM 4B: иерархический reduce и обвязка важнее промпта

Совещание длилось два часа. Файл транскрипта — пятьдесят с лишним тысяч токенов. Слушать запись никто не сядет. Читать тоже.

Идея пришла со второго случая. Помимо собственных созвонов — ещё выступления вышестоящих организаций. Отраслевые сборы, директорские брифинги, годовые итоги. Запись лежит, времени нет. Что нужно по итогу: темы, цифры, факты, стратегические заявления. Не пересказ, а выжимка.

Бюджета на чужой ум — нет. Подписки на GPT нет, H100 нет. Есть две Tesla M60 и Qwen 3.5 4B. Тот же стенд, что и в прошлом RAG-кейсе.

Саммари — это пытка для SLM

Задача кажется простой. Не простая.

Контекст у квантованных моделей в реальности меньше заявленного в карточке. Документация декларирует 32k, на стенде с честным num_ctx модель уже теряет начало. VRAM — отдельный потолок: даже декларированный контекст не раскатишь, если на карте не хватает памяти под KV-cache.

Сверху lost in the middle. Длинный вход — модель забывает середину. На двухчасовом созвоне середина — это ровно то место, где обсуждались сроки, цифры и кто за что отвечает.

Если в проде SLM путается в середине одного длинного транскрипта — пользователи не делают сноску, что «эта модель маленькая, ей простительно». Они делают вывод, что локальные модели не работают. Доверие падает не к одной задаче, а ко всему SLM-стэку: к ассистенту по базе знаний, к 1С-MCP, к чему угодно ещё.

Поэтому на слабых SLM обвязка важнее промптинга. Сколько ни шлифуй системный промпт — если на вход приходит шестьдесят тысяч токенов, модель не вытянет.

Железо и роли

Map — Qwen 3.5 4B всегда. Reduce — на стенде Qwen же (4B q4_K_M), на проде поднимаем до GPT-OSS-20B. Иерархический reduce одинаково нужен в обоих режимах: даже 20B-модель не съест шестьдесят тысяч токенов транскрипта одним проходом без потерь.

То есть тезис «обвязка важнее промптинга» не зависит от размера модели на reduce. Размер модели даёт качество финального текста. Структура пайплайна даёт то, чтобы этому финальному тексту вообще было откуда взяться.

Чанки

Резка текста — token-window через tiktoken (cl100k_base), фиксированный размер с overlap. Без эмбеддингов на этом этапе: семантический чанкинг через BGE-M3 в плане есть, в текущем пайплайне — TODO.

Map

Каждый чанк уходит на Qwen 3.5 4B. Параллельно, по семафору. Температура 0.2. Системный промпт говорит простую вещь: ты не пересказываешь красиво, ты вытягиваешь факты.

На выход — короткое summary одного куска. Без markdown, без заголовков, без украшений. Это сырьё для следующей стадии.

Reduce flat

Частичные саммари склеиваются в один блок с XML-разметкой <partial id="...">. Если рендер укладывается в reduce_num_ctx − reduce_num_predict − 500 (последнее — overhead на system и теги), идёт один финальный проход. Готово.

В половине случаев не укладывается.

Reduce hierarchical

Двухчасовой транскрипт даёт 25–30 partials. В контекст reduce-модели они не лезут. Хитрить нечем — нужно дерево.

Partials упаковываются жадным алгоритмом в батчи под бюджет. Каждый батч → intermediate merge. Системный промпт этой стадии отдельный и жёсткий:

  • сохраняй все цифры, имена, даты, цитаты дословно;
  • отвечай на том же языке, что и partials, не переводи;
  • не применяй финальное форматирование, никаких заголовков и секций;
  • группируй родственное, не дублируй, не теряй хронологию.

Промежуточный merge не делает финальный отчёт — он делает менее длинный набор partials. Дальше рекурсия. На новом уровне всё влезло — финальный pass с preset-aware промптом. Не влезло — снова дерево.

Глубина рекурсии ограничена четырьмя уровнями.

Защиты от себя

Если один partial сам не влезает в intermediate budget — RuntimeError с прямым указанием: виноват chunking, не reduce. Без этой проверки пайплайн молча уходил бы в бесконечную рекурсию.

Тот же max_levels — страховка на случай, когда intermediate-промпт перестал сжимать. Лучше упасть с понятным сообщением, чем зависнуть на ML-сервере.

Финал делает Python

Готовый markdown собирает шаблон на Python. Не LLM.

Финальный текст уже готов после reduce. Дальше нужна только формальная обёртка: заголовки секций, разделители, маркеры. Через LLM она была бы недетерминированной, медленной и дороже на каждый запрос. Через format() — это 0 мс и юнит-тест.

Самое ценное в SLM-обвязке — отдавать модели только то, что она реально умеет. Остальное — обычный код.

Пресеты

Пять штук: meeting, briefing, article, technical, tutorial. Это YAML с системным и пользовательским промптами для map и reduce, плюс шаблон финального формата.

Промпты написаны на английском, ответ требуется на русском. Эта пара даёт более устойчивую модель: на русском Qwen чаще соскакивает в разговорный регистр, на английском держит инструкцию плотнее. Парадокс, к которому привыкаешь.

Что осталось

Скоро добавится определение пресета по тексту — пока пользователь выбирает руками. Семантический чанкинг через BGE-M3 — TODO. До Whisper и диаризации очередь тоже дойдёт.

Двухчасовой созвон умещается в страницу. SLM 4B пересказывает мне совещание, на котором я был, чтобы я наконец понял, о чём оно было.

Читайте также