Что такое focus-llama и Declarative Attention
focus-llama - это форк llama.cpp, в котором автор завёл поддержку Declarative Attention (DA) из статьи arXiv:2609.02737, подготовленной Google DeepMind и KAIST AI. Логика такая: модель в собственном выводе объявляет тег <focus magic_chunks="N">, указывая, какие фрагменты контекста ей нужны, а движок после этого ограничивает внимание последующих токенов только этими чанками. Отдельный scorer не нужен, дообучение тоже: работают промптинг и сервер, который реагирует на теги. Описание форка автор выложил в посте на r/LocalLLaMA.
Статья называется «Language Models Can Control Their Own Attention», её подали 2 сентября 2026 года. Название описывает суть точнее любого пересказа: решение о том, куда смотреть, принимает сама модель, а не внешний алгоритм отбора токенов.
Проблема, которую решает подход, знакома каждому, кто запускает LLM локально. При длинном контексте декодирование на каждом шаге читает KV-кэш, и время генерации растёт вместе с промптом. Обычный путь: сжать контекст, отбросить лишнее, обучить отдельный scorer. DA предлагает отдать выбор модели. llama.cpp здесь удобен как площадка: проект изначально создавался для запуска моделей на обычном CPU, хорошо изучен и часто служит основой для экспериментов.
Три режима DA: <global>, <focus> и <local>
DA делит генерацию на три режима. <global> даёт внимание по всему контексту, <focus> ограничивает его конкретной областью, <local> оставляет только недавно сгенерированные токены. Переключаются они прямо в цепочке рассуждений модели.
Пример из описания форка: в выводе появляется <focus magic_chunks="3">, и дальше внимание ограничивается третьим чанком разметки, которую заранее передал клиент. Чанк - это блок промпта, размеченный на стороне приложения: системная инструкция, схемы инструментов, фрагменты документов, история диалога.
DA - протокол, а не архитектура модели. Он работает на готовых весах, в статье проверялись Gemma-4-31B и Qwen-3.6-27B. Порог входа низкий, зато качество переключения режимов целиком зависит от того, насколько дисциплинированно модель ставит теги.
Чем это отличается от scorer-based подходов
Методы сжатия контекста обычно работают извне: лёгкие прокси-оценки заранее выбирают релевантные токены, но такой внешний скоринг всё равно требует O(N) на каждом шаге, то есть перебирает весь контекст. Авторы DA идут от другого вопроса: не знает ли модель сама, какие части контекста ей релевантны. Если знает, отбор сводится к одному объявлению в выводе.
Практическая разница ощутимая. Нет scorer'а, который нужно обучать и держать рядом с моделью, нет дообучения под конкретную архитектуру. Взамен возникает зависимость от качества промптинга: неверная разметка чанков или сорвавшийся тег ломают весь эффект.
Как это реализовано в llama-server
Стоковый llama-server не умеет трогать KV-диапазоны в середине генерации, поэтому автору пришлось менять серверную часть. В форке сервер удаляет диапазоны KV-токенов для запроса (da_rm / da_rm_at), причём как во время prefill, так и после него.
Режимы da_rm, da_rm_at и da_b: в чём разница
Три режима закрывают разные сценарии, и путать их не стоит.
| Режим | Что делает | Когда срабатывает | Ограничение |
|---|---|---|---|
| da_rm / da_rm_at | Удаляет диапазоны KV-токенов для запроса | Во время prefill или после него, по команде клиента | Диапазоны задаёт вызывающая сторона |
| tag-driven | Разбирает первый тег <focus magic_chunks="N"> и удаляет остальные чанки | Во время генерации, один раз | Одноразово и в одну сторону, откат невозможен |
| da_b | Работает со второй последовательностью | Требует флага --kv-unified | Исходная последовательность остаётся нетронутой |
Tag-driven режим устроен так: клиент передаёт разметку чанков, сервер слушает вывод модели и, поймав первый тег <focus magic_chunks="N">, в этот же момент удаляет остальные чанки. Одноразовость здесь принципиальна: если модель выбрала область неудачно, вернуть выброшенные токены нельзя, нужен новый запрос.
Режим da_b пригодится там, где исходный контекст терять не хочется. Он работает со второй последовательностью и оставляет первую нетронутой, но требует флага --kv-unified.
Почему без paged block table маскирования недостаточно
Это главное ограничение текущей версии. В vLLM-варианте из статьи paged block table переписывается, и удалённые блоки действительно перестают читаться. В llama.cpp такой структуры нет. Маскирование меняет только то, на какие токены смотрит attention, а ядро всё равно проходит по всему KV-кэшу.
Реальный пропуск чтения требует поддержки на уровне ядра или компакции. Судя по исходникам, одиночный decode на CUDA сегодня замаскированные чанки не пропускает. Значит, удаление диапазонов экономит память и упрощает логику, но не даёт автоматически того же выигрыша по времени, что в статье. Разница между движками на длинных промптах разобрана в отдельном материале про то, почему vLLM часто выигрывает у llama.cpp на prefill: там про PagedAttention, непрерывный батчинг и работу KV-кэша.
Производительность: что известно и чего пока нет
По данным статьи arXiv:2609.02737, общее время декодирования ответа сокращается до 0,71× (Gemma) и 0,77× (Qwen) относительно vanilla. Это числа vLLM-версии. Автор форка бенчмарков не проводил: ни по скорости, ни по точности.
В статье есть и цифры по attended-токенам. На Gemma-4-31B их суммарное число при декодировании падает на 52,0%, на Qwen-3.6-27B - на 31,1%. При zero-shot оценке на 15 задачах с длинным контекстом точность проседает на 1,27 п.п. и 2,75 п.п. соответственно, причём падение уменьшается с ростом масштаба модели. Даже в исходной работе речь не идёт о бесплатной экономии: за скорость отдают часть точности.
Почему цифры из статьи нельзя переносить на llama.cpp напрямую
vLLM и llama.cpp управляют KV-кэшем по-разному. В vLLM есть paged block table и постраничное хранение: таблицу можно переписать и выкинуть лишние блоки из чтения. В llama.cpp такой структуры нет. Поэтому даже при одинаковой логике DA итог зависит от того, получится ли реализовать пропуск KV на уровне ядра. Пока это открытый вопрос, и рассчитывать на такое же ускорение в локальном запуске нельзя.
Что проверялось в smoke-тестах
Проверка форка ограничилась first-token logprobs на небольших smoke-тестах: гибридная/GDN модель Qwen и крошечная dense-модель, о чём автор пишет в треде с анонсом. Логпробы первого токена служат быстрой проверкой того, что сборка ведёт себя предсказуемо, но они не показывают ни скорости декодирования, ни деградации качества на длинных задачах. Автор это признаёт и не выдаёт числа из статьи за результат форка.
Гибридные модели и рекуррентное состояние
В гибридных архитектурах часть слоёв работает как обычный attention, а часть хранит состояние в рекуррентном виде или в виде SSM. В форке DA ограничивает только attention-слои, как и в статье: рекуррентное состояние не затрагивается. Для такой модели эффект будет частичным, поскольку работа с рекуррентной частью остаётся прежней.
Отсюда простое правило для практики: чем выше доля attention-слоёв в модели, тем заметнее потенциальная экономия. В smoke-тестах участвовала гибридная/GDN модель Qwen, то есть проверка на таких архитектурах формально была, но поверхностная.
Кому и зачем это может пригодиться
Ценность форка сейчас исследовательская. Он интересен тем, кто работает с KV-кэшем и рекуррентной памятью в llama.cpp и готов проверять идею управления вниманием на своём стенде. Автор прямо просит фидбэк именно от таких людей, а не от пользователей, которым нужен готовый инструмент.
Репозиторий лежит на GitHub как edwardyoon/focus-llama. Статус ранний: код есть, режимы da_rm и da_rm_at есть, бенчмарков нет.
Стоит ли пробовать сейчас: аргументы за и против
За: открытый код, свежая поддержка статьи, три режима работы с KV-диапазонами, возможность повлиять на развитие проекта через обсуждение.
Против: нет замеров ни по скорости, ни по точности, в llama.cpp отсутствует paged block table, поэтому реального пропуска чтения KV пока нет, tag-driven режим необратим, на гибридных моделях затрагиваются только attention-слои.
Если вы экспериментируете с управлением контекстом и готовы читать исходники, посмотреть стоит. Если нужен предсказуемый прирост скорости генерации на длинном контексте для рабочего проекта, дождитесь замеров.
Что дальше и какие вопросы открыты
Ключевой вопрос: появится ли пропуск чтения KV на уровне ядра или компакция блоков. Без этого DA в llama.cpp остаётся логикой поверх маскирования. Второй вопрос касается замеров: скорости декодирования и качества ответов на длинных контекстах, которых пока нет ни у автора, ни в публичных обсуждениях.
Открыто и поведение на разных архитектурах. Насколько стабильно модели ставят теги, что делать, если тег не появился или появился не там, как схема уживается с батчингом нескольких запросов.
Отдельный сюжет - судьба подхода в самом llama.cpp. Проект развивают сообществом, и новые возможности появляются тогда, когда находится человек, готовый довести их до конца: история с поддержкой Ling-3.0-flash показывает, как это работает на практике. Автор focus-llama ждёт фидбэка от тех, кто занимался KV и рекуррентной памятью, так что участие сообщества прямо влияет на то, что появится в следующих версиях.