Я хотел, чтобы кодовый агент работал целиком на ноутбуке, без облака: локальная модель, локальный сервер, агент в терминале. Главная боль с самого начала — скорость. Агент постоянно перечитывает длинный контекст и много «думает», и на локальном железе это превращается в минуты ожидания на каждую задачу.

Я взял MTPLX — сервер для Apple Silicon, который обещает двух-трёхкратное ускорение генерации за счёт встроенных в модель голов multi-token prediction (MTP). Посадил на него агента pi и прогнал одну и ту же сложную задачу в разных режимах. Ниже — что получилось, с цифрами, провалами и граблями.
Коротко:
-
MTP действительно ускоряет генерацию в 3 раза (21 → 63 ток/с на коротком промпте), но в реальной работе агента получается около 34 ток/с.
-
На сложной задаче время решения — 10–16 минут, и больше всего оно зависит от того, сколько модель решит думать, а не от настроек сервера.
-
Отключать или урезать размышления не выгодно: без них модель не довела код до рабочего состояния, на
lowвышло даже медленнее. -
SSD-кеш и квантование KV-кеша на этой задаче ничего заметного не дали.
Стенд
|
Железо |
MacBook Pro, M4 Max, 128 ГБ unified memory |
|
Сервер |
MTPLX 2.11.3 (приложение для macOS) |
|
Модель |
|
|
Контекст |
262 144 токена |
|
Профиль MTPLX |
|
|
Агент |
pi 0.87.1, чистая установка без расширений |
Как работает MTPLX
Обычная генерация — один проход модели на один токен. Спекулятивное декодирование сначала дёшево «угадывает» несколько следующих токенов, а потом одним проходом большой модели проверяет их все. Если угадано верно, за один проход получаем несколько токенов.
Обычно для угадывания нужна вторая, маленькая модель. У Qwen3.8 угадыватель встроен: это MTP-головы, которые учились вместе с моделью. MTPLX использует их напрямую, так что лишней памяти под черновую модель не нужно. Проверка точная: распределение ответа не меняется, меняется только скорость.

У MTPLX есть встроенный подбор глубины черновика. Вот что он показал на моём Mac:
$ mtplx tuneAR 21.1 tok/s 1.00xD1 48.3 tok/s 2.29xD2 61.7 tok/s 2.93xD3 63.1 tok/s 2.99x BEST

Тройное ускорение генерации — это реально. Вопрос в том, сколько от него остаётся в агентной работе.
Подключение к pi
MTPLX отдаёт OpenAI- и Anthropic-совместимый API. В pi это обычный провайдер в ~/.pi/agent/models.json:
{ "providers": { "mtplx": { "name": "MTPLX (локально)", "baseUrl": "http://127.0.0.1:8000/v1", "api": "openai-completions", "apiKey": "none", "compat": { "supportsDeveloperRole": false, "supportsReasoningEffort": false }, "models": [{ "id": "mtplx-qwen38-27b-optimized-speed", "name": "Qwen3.8-27B MTPLX", "input": ["text", "image"], "reasoning": true, "contextWindow": 262144, "maxTokens": 32768, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 } }] } }}
Если сервер требует ключ, pi умеет брать его командой, не сохраняя в конфиге: "apiKey": "!команда, которая печатает ключ".
Методика
Нужна была задача, которую нельзя решить «с наскока», но которая проверяется автоматически.
Задача: с нуля написать пакет minicalc — интерпретатор выражений по спецификации на страницу. Разбить на модули (лексер, парсер, вычислитель, ошибки) и учесть хитрые места:
-
приоритеты операций и правую ассоциативность , причём
-2 2 == -4, а2 ** -1 == 0.5; -
цепочки присваиваний
a = b = 4; -
встроенные функции с проверкой числа аргументов и константы, которые нельзя переприсвоить;
-
позиция символа для каждой ошибки, включая «неожиданный конец ввода»;
-
атомарность: упавшая инструкция не меняет состояние, а успешные до неё сохраняются;
-
запрет на
eval,execиast.
Проверка:
-
24 видимых теста лежат рядом с задачей, модель может их запускать.
-
30 скрытых тестов модель не видит. Они проверяют требования спецификации, которых нет в видимых тестах, — чтобы отличить «реализовал спецификацию» от «подогнал под тесты».
-
Все тесты сначала прогнаны на моей эталонной реализации: 54 из 54.
Запуск: pi -p --no-session --mode json с одним и тем же промптом, каждый раз в чистой папке. Из JSON-лога pi считаются вызовы инструментов, ошибки и токены. Время — по часам. Скрытые тесты я запускаю после прогона.
Правило обрыва. Если модель думает дольше, чем в эталонных прогонах, и не пишет код, прогон обрывается. Иначе один неудачный запуск съедал бы по часу.
Результаты
|
Режим размышлений |
Время |
Видимые |
Скрытые |
Вызовов / ошибок |
Выход, токенов |
До первого файла |
|---|---|---|---|---|---|---|
|
medium, прогон 1 |
10,0 мин |
24/24 |
30/30 |
18 / 2 |
17,8 тыс. |
6,2 мин |
|
medium, прогон 2 |
13,9 мин |
24/24 |
30/30 |
19 / 3 |
23,7 тыс. |
6,0 мин |
|
medium + SSD-кеш |
16,2 мин |
24/24 |
30/30 |
22 / 4 |
23,3 тыс. |
— |
|
xhigh |
14,5 мин |
24/24 |
30/30 |
13 / 0 |
27,1 тыс. |
9,1 мин |
|
low |
18,3 мин |
24/24 |
30/30 |
30 / 4 |
30,2 тыс. |
7,7 мин |
|
off |
оборван на 8,5 мин |
0/24 |
— |
33 / 7 |
14,2 тыс. |
1,1 мин |

Ещё два прогона (xhigh + SSD-кеш и medium + q8) я оборвал по правилу: на 13-й и 7-й минуте модель всё ещё думала и не написала ни строки.
Что видно из таблицы
1. Качество одинаковое во всех режимах с размышлениями. Все пять полных прогонов прошли 30 из 30 скрытых тестов. Модель сама писала проверочные скрипты на пограничные случаи из спецификации, поэтому и не провалилась на скрытых тестах.
2. Без размышлений модель не справилась. Режим off начал писать код через минуту — в шесть раз раньше, чем medium. Дальше пошли пробы и ошибки: дважды целиком переписанные парсер и вычислитель, семь неудачных вызовов инструментов (в основном правки, где модель неверно помнила текущий текст файла), падения собственных проверок. На 8,5 минуте код даже не импортировался (SyntaxError). Модель экономит на обдумывании и потом переплачивает на отладке.
3. low не быстрее medium. Я ждал, что low даст свои 5–7 минут. Вышло 18 минут, самый медленный прогон: 30 вызовов инструментов, 4 ошибки, и модель долго чинила требование, которого нет в спецификации (что (-8) ** 0.5 должно выдавать ошибку).
4. xhigh дольше думает, зато работает чисто. 9 минут до первого файла, после этого ни одной ошибки инструментов, тесты прошли с первого запуска. По итоговому времени он в том же коридоре, что и medium.
5. Разброс важнее режима. Три прогона medium дали 10, 14 и 16 минут. Разница между режимами меньше этого разброса, так что по одному прогону на режим ничего нельзя утверждать о скорости. Уверенно можно говорить только о крайних случаях: off не работает, low не экономит.
Куда уходит время
MTPLX отдаёт подробные метрики по каждому запросу (/metrics). Я сложил последние 22 запроса: прогон medium с SSD-кешем и начало следующего.
|
|
Время |
Объём |
Скорость |
|---|---|---|---|
|
Генерация |
678 с (~70%) |
23,4 тыс. токенов |
34,5 ток/с |
|
Prefill новых токенов |
303 с (~30%) |
45,5 тыс. токенов |
~150 ток/с |
|
Взято из кеша в RAM |
~0 с |
340 тыс. токенов |
— |

Выводы:
-
В агентной работе генерация вдвое медленнее, чем в
tune: 34,5 ток/с вместо 63. Тюнинг меряет на коротких промптах, а у агента длинный контекст, где и проход модели дороже, и черновик угадывается хуже. MTP всё равно помогает, но не втрое. -
Основное время уходит на генерацию, а внутри неё — на размышления. В прогоне medium до первой строки кода проходит около 6 минут. При 34 ток/с это порядка 11–12 тысяч токенов размышлений. Скорость токенов стабильна, от запуска к запуску меняется лишь то, сколько модель решит подумать.
-
Prefill медленный, около 150 ток/с. Каждый прочитанный файл или вывод тестов стоит примерно 7 секунд на тысячу токенов. В этой задаче его сглаживает кеш в оперативной памяти: 340 тысяч токенов истории не пришлось обрабатывать заново. На большом репозитории prefill станет заметнее.
Что не помогло
-
SSD-кеш сессий (
ssd_session_cache). 0 попаданий. Внутри сессии историю и так держит RAM-кеш. SSD нужен, когда RAM-кеш потерян: после перезапуска сервера или при возврате к старой сессии. Выключать не стоит, но и ускорения задачи от него ждать не надо. -
Квантование KV-кеша q8. Prefill 185 ток/с, генерация 30,8 ток/с — в пределах шума относительно режима без квантования. Полный прогон с q8 не завершился: модель надумалась дольше обычного, и я его оборвал.
-
Подбор глубины черновика.
tuneподтвердил, что D3 уже оптимальна.
Для сравнения: oMLX
Ту же модель (обычный 4-битный MLX-чекпоинт Qwen3.8-27B, без MTP) я запускал в oMLX с тем же окном контекста. По объёму размышлений за время прогона генерация шла примерно на 13 ток/с, за 25 минут модель так и не записала ни одного файла. Прогон оборвался посередине: на сервере запустили встроенный бенчмарк, и тот выгрузил модель. Так что это оценка, а не чистый замер. Отдельно сравню серверы в следующей статье.
Грабли
Запуск из образа DMG. Приложение MTPLX, запущенное прямо из смонтированного .dmg, создаёт среду выполнения со ссылками внутрь /Volumes/MTPLX …. Образ отмонтировался — сервер умер. Приложение нужно перетащить в «Программы».
«Ограниченный режим — MTPLX уже запущен». Я сменил порт в настройках, приложение перезапустило сервер, но старый процесс не отпустил порт. Новый сервер упал, приложение потеряло связь со старым и бесконечно «переподключало поток». Лечится полным выходом из приложения (Cmd+Q) и проверкой, что порт свободен.
Режим размышлений задаёт сервер, а не клиент. MTPLX по умолчанию игнорирует reasoning_effort из запроса анонимного клиента. Переключать режим надо на сервере — на лету, без перезапуска:
mtplx settings set --port 8000 reasoning=auto reasoning_effort=xhigh
А вот ssd_session_cache и paged_kv_quantization на лету не меняются: только через настройки приложения и перезапуск.
Вентиляторы на максимуме. MTPLX умеет сам управлять вентиляторами (режим smart и встроенная утилита thermalforge). Во время генерации он раскручивает их заранее, чтобы чип не сбрасывал частоты. Если шум мешает, режим вентиляторов можно отдать macOS, а профиль сменить с turbo на sustained. Попутно заметил, что после каждой сессии остаётся процесс thermal_sidecar: мелкая утечка, на работу не влияет.
Plan-mode в агенте. У меня в pi было расширение, которое переводит любую задачу в режим «сначала план, потом подтверждение». В неинтерактивном pi -p подтверждать некому, и агент просто писал план и выходил. Для тестов такие расширения надо выключать.
Модель читает всё, что лежит в папке. В первых прогонах я складывал логи pi рядом с задачей, и модель тратила ходы, изучая собственный лог. Логи надо писать за пределы рабочей папки.
Ошибки в собственном тесте. Скрытый тест «в коде нет eval» искал подстроку eval( и помечал как нарушение обычный метод self._eval(...). Два прогона чуть не получили незаслуженный минус. Мораль: проверяйте тесты на эталонной реализации и внимательно смотрите на каждый провал, прежде чем списывать его на модель.
Выводы
-
MTPLX делает то, что обещает: генерация втрое быстрее без потери качества. В агентной работе на длинном контексте остаётся примерно 34 ток/с — всё равно ощутимо больше, чем без MTP.
-
Сложная задача занимает 10–16 минут, и узкое место — не сервер, а объём размышлений модели. Настройки сервера на это почти не влияют.
-
Размышления не трогайте. Режим по умолчанию (medium) — лучший компромисс.
xhighдаёт ту же точность с меньшим числом ошибок по ходу, но думает дольше.lowиoffне экономят время. -
Меряйте несколько прогонов. Разброс одного режима (10–16 минут) больше разницы между режимами.
-
Кеши и квантование оставьте на случай, где они нужны: SSD-кеш — для долгих сессий и перезапусков, q8 — если упираетесь в память на огромном контексте.
В следующей части сравню с другим сервером на тех же задачах, а заодно проверю, как всё это ведёт себя на реальном репозитории, где prefill становится главной проблемой.
ссылка на оригинал статьи https://habr.com/ru/articles/1085928/