Локальный агент для кода на Mac: MTPLX + pi + Qwen3.8–27B. Что реально ускоряет, а что нет. ai-агенты.. ai-агенты. Apple Silicon.. ai-агенты. Apple Silicon. mlx.. ai-агенты. Apple Silicon. mlx. mtp.. ai-агенты. Apple Silicon. mlx. mtp. MTPLX.. ai-агенты. Apple Silicon. mlx. mtp. MTPLX. pi.. ai-агенты. Apple Silicon. mlx. mtp. MTPLX. pi. qwen.. ai-агенты. Apple Silicon. mlx. mtp. MTPLX. pi. qwen. локальные llm.. ai-агенты. Apple Silicon. mlx. mtp. MTPLX. pi. qwen. локальные llm. спекулятивное декодирование.

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

Локальный агент для кода на Mac: MTPLX + pi + Qwen3.8–27B. Что реально ускоряет, а что нет - 1

Я взял 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)

Модель

Youssofal/Qwen3.8-27B-MTPLX-Optimized-Speed, 4 бита, ~20,7 ГБ весов

Контекст

262 144 токена

Профиль MTPLX

turbo, глубина черновика D3

Агент

pi 0.87.1, чистая установка без расширений

Как работает MTPLX

Обычная генерация — один проход модели на один токен. Спекулятивное декодирование сначала дёшево «угадывает» несколько следующих токенов, а потом одним проходом большой модели проверяет их все. Если угадано верно, за один проход получаем несколько токенов.

Обычно для угадывания нужна вторая, маленькая модель. У Qwen3.8 угадыватель встроен: это MTP‑головы, которые учились вместе с моделью. MTPLX использует их напрямую, так что лишней памяти под черновую модель не нужно. Проверка точная: распределение ответа не меняется, меняется только скорость.

Локальный агент для кода на Mac: MTPLX + pi + Qwen3.8–27B. Что реально ускоряет, а что нет - 2

У 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
Локальный агент для кода на Mac: MTPLX + pi + Qwen3.8–27B. Что реально ускоряет, а что нет - 3

Тройное ускорение генерации — это реально. Вопрос в том, сколько от него остаётся в агентной работе.

Подключение к 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 мин

Локальный агент для кода на Mac: MTPLX + pi + Qwen3.8–27B. Что реально ускоряет, а что нет - 4

Ещё два прогона (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 тыс. токенов

—

Локальный агент для кода на Mac: MTPLX + pi + Qwen3.8–27B. Что реально ускоряет, а что нет - 5

Выводы:

  • В агентной работе генерация вдвое медленнее, чем в 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(...). Два прогона чуть не получили незаслуженный минус. Мораль: проверяйте тесты на эталонной реализации и внимательно смотрите на каждый провал, прежде чем списывать его на модель.

Выводы

  1. MTPLX делает то, что обещает: генерация втрое быстрее без потери качества. В агентной работе на длинном контексте остаётся примерно 34 ток/с — всё равно ощутимо больше, чем без MTP.

  2. Сложная задача занимает 10–16 минут, и узкое место — не сервер, а объём размышлений модели. Настройки сервера на это почти не влияют.

  3. Размышления не урезайте. Родной xhigh и medium, который подставляет MTPLX, дали одинаковую точность и время в пределах разброса. xhigh работает чище, с меньшим числом ошибок по ходу. low и off время не экономят.

  4. Меряйте несколько прогонов. Разброс одного режима (10–16 минут) больше разницы между режимами.

  5. Кеши и квантование оставьте на случай, где они нужны: SSD‑кеш — для долгих сессий и перезапусков, q8 — если упираетесь в память на огромном контексте.

В следующей части сравню с другим сервером на тех же задачах, а заодно проверю, как всё это ведёт себя на реальном репозитории, где prefill становится главной проблемой.

Автор: VladimirVP

Источник