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

Я взял 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 tune
AR 21.1 tok/s 1.00x
D1 48.3 tok/s 2.29x
D2 61.7 tok/s 2.93x
D3 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 считаются вызовы инструментов, ошибки и токены. Время — по часам. Скрытые тесты я запускаю после прогона.
Режимы размышлений. У Qwen3.8 три уровня — xhigh, medium, low — и они зашиты в шаблон чата модели. По умолчанию шаблон ставит xhigh. MTPLX для 27B‑модели подменяет умолчание на medium: так решили разработчики MTPLX по своему замеру времени на кодинговой задаче. Поэтому ниже xhigh — это родное поведение модели, а medium — умолчание MTPLX. Ещё есть off: размышления выключены совсем.
Правило обрыва. Если модель думает дольше, чем в эталонных прогонах, и не пишет код, прогон обрывается. Иначе один неудачный запуск съедал бы по часу.
Результаты
|
Режим размышлений |
Время |
Видимые |
Скрытые |
Вызовов / ошибок |
Выход, токенов |
До первого файла |
|---|---|---|---|---|---|---|
|
medium (умолчание MTPLX), прогон 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-й минуте модель всё ещё думала и не написала ни строки.
Что видно из таблицы
-
Качество одинаковое во всех режимах с размышлениями. Все пять полных прогонов прошли 30 из 30 скрытых тестов. Модель сама писала проверочные скрипты на пограничные случаи из спецификации, поэтому и не провалилась на скрытых тестах.
-
Без размышлений модель не справилась. Режим
offначал писать код через минуту — в шесть раз раньше, чем medium. Дальше пошли пробы и ошибки: дважды целиком переписанные парсер и вычислитель, семь неудачных вызовов инструментов (в основном правки, где модель неверно помнила текущий текст файла), падения собственных проверок. На 8,5 минуте код даже не импортировался (SyntaxError). Модель экономит на обдумывании и потом переплачивает на отладке. -
lowне быстрееmedium. Я ждал, чтоlowдаст свои 5–7 минут. Вышло 18 минут, самый медленный прогон: 30 вызовов инструментов, 4 ошибки, и модель долго чинила требование, которого нет в спецификации (что(-8) ** 0.5должно выдавать ошибку). -
xhighдольше думает, зато работает чисто. 9 минут до первого файла, после этого ни одной ошибки инструментов, тесты прошли с первого запуска. По итоговому времени он в том же коридоре, что и medium. -
Разброс важнее режима. Три прогона 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 минут, и узкое место — не сервер, а объём размышлений модели. Настройки сервера на это почти не влияют.
-
Размышления не урезайте. Родной xhigh и medium, который подставляет MTPLX, дали одинаковую точность и время в пределах разброса. xhigh работает чище, с меньшим числом ошибок по ходу. low и off время не экономят.
-
Меряйте несколько прогонов. Разброс одного режима (10–16 минут) больше разницы между режимами.
-
Кеши и квантование оставьте на случай, где они нужны: SSD‑кеш — для долгих сессий и перезапусков, q8 — если упираетесь в память на огромном контексте.
В следующей части сравню с другим сервером на тех же задачах, а заодно проверю, как всё это ведёт себя на реальном репозитории, где prefill становится главной проблемой.
Автор: VladimirVP


