Настоящий компромисс
Локальный инференс обычно представляют как оптимизацию задержки. Это не только она. Когда модель работает на устройстве, входные данные никогда не покидают устройство. Это решение об оттоке данных с последствиями для приватности, соответствия требованиям и работы в офлайн-режиме, и оно попутно сокращает время кругового обмена.
Компромисс здесь структурный, а не инкрементальный.
Локальный инференс даёт:
- данные, остающиеся в границах устройства;
- работу без сети, что важно для полевых инструментов, транспортных средств, клинических условий и всего, что используется в подвале;
- предсказуемую стоимость запроса после оплаты оборудования;
- отсутствие зависимости от доступности удалённой конечной точки или её лимитов.
Локальный инференс стоит:
- возможностей: меньшее число параметров, агрессивная квантизация и ограниченные контекстные окна снижают глубину рассуждений, надёжность использования инструментов и обработку длинных документов;
- памяти и теплового бюджета, что ограничивает, какие модели могут работать одновременно с остальным приложением;
- второго стека инференса, который нужно оценивать, версионировать и поставлять;
- более медленных итераций, потому что обновление модели на устройстве — это задача распространения, а не команда развёртывания.
Удалённый инференс переворачивает всё это. Он даёт возможности — большие модели, более длинный контекст, более сильную оркестрацию инструментов — ценой перемещения данных через границу, которую продукт не контролирует, плюс жёсткую зависимость от связности и от безотказной работы третьей стороны.
Большинству полезных систем нужно и то, и другое. Поэтому инженерный вопрос не «локально или удалённо», а «где проходит граница и что решает, на какую сторону попадёт данный запрос».

Проведение границы
Распространённая ошибка — маршрутизировать по уверенности модели: запускать локально, а если локальная модель не уверена, эскалировать. Это заманчиво, потому что легко инструментируется, и неправильно по двум причинам. Во-первых, оценки уверенности малых квантованных моделей плохо калиброваны, поэтому триггер эскалации срабатывает непредсказуемо. Во-вторых, и это важнее, маршрутизация идёт по неверной переменной. Может ли полезная нагрузка покинуть устройство — это свойство данных, а не того, насколько сложен вопрос.
Вместо этого маршрутизируйте по классу данных. Определите классы явно и назначьте каждому разрешённое место выполнения:
класс примеры место
--------------- -------------------------------- ------------------
regulated-pii идентификаторы, медицинские только локально
заметки
device-private сырые кадры сенсоров, локальные только локально
файлы
user-content черновики, расшифровки, локально по умолчанию,
сообщения удалённо с согласия
derived эмбеддинги, очищенные сводки удалённо разрешено
public документация, справочные удалённо предпочтительно
материалы
Этап классификации выполняется до инференса, на входных данных, и по возможности детерминирован: проверки схемы, теги на уровне полей, происхождение источника. Там, где для классификации нужна модель, используйте небольшой локальный классификатор, единственная задача которого — выдать метку класса, а не ответить пользователю.
Полезный паттерн — держать рассуждение локально, а вызов инструментов удалённо, когда полезная нагрузка приватна. Локальная модель решает, что делать и с какими аргументами; удалённый вызов несёт только минимальный набор аргументов, никогда полный контекст. Для поиска в календаре нужен диапазон дат, а не разговор, который к нему привёл. Для вызова retrieval нужна строка запроса и пространство имён, а не черновик пользователя. Это сохраняет возможности удалённых сервисов для тех частей, которым они полезны — свежие данные, большие индексы, тяжёлые вычисления — в то время как приватный материал остаётся на устройстве.
Здесь и живёт честный компромисс. Разделение рассуждения и вызова инструментов лишает удалённую модель способности видеть полный контекст, а именно это делает удалённые модели хорошими в неоднозначных многошаговых задачах. Вы обмениваете часть качества ответа на меньшую поверхность оттока. Этот обмен следует делать осознанно, по классам, и фиксировать.
Проектируйте деградированную работу как полноценное состояние, а не как путь ошибки.
ent:

- какие запросы обслуживаются локально с пониженным качеством;
- какие ставятся в очередь и воспроизводятся при возвращении связности, и как воспроизведение взаимодействует с идемпотентностью;
- какие отклоняются сразу, с сообщением, объясняющим почему;
- что сообщает интерфейс, чтобы пользователь мог отличить деградированный вывод от полного.
Система, которая молча выдаёт худшие ответы в офлайне, не сигнализируя об этом, вызывает меньше доверия, чем та, которая говорит, что не может сделать что-то прямо сейчас.
Измерение разделения
Когда граница существует, её нужно инструментировать. Три измерения несут основную нагрузку.
Во-первых, доля локального обслуживания: доля запросов, полностью обслуженных на устройстве, в разбивке по классам данных. Единое агрегированное число скрывает то, что вас интересует, потому что высокая локальная доля, обусловленная тривиальными запросами, ничего не говорит о том, действительно ли чувствительные классы остаются локальными. Отслеживайте её по классам и по версиям модели и приложения, поскольку изменение промпта может сместить маршрутизацию.
Во-вторых, дельта качества по классам. Для каждого класса держите небольшой набор для оценки и прогоняйте его как по локальному, так и по удалённому пути. Дельта показывает, чего стоит граница в качестве ответа. Если дельта по классу незначительна, локальный путь можно расширить. Если она велика, у вас есть документированная причина текущего разделения — и кандидат для целевого улучшения, а не для тотальной эскалации.
В-третьих, задержка кругового обмена, измеренная от начала до конца, включая сериализацию контекста. Именно здесь сравнения локального и удалённого чаще всего идут не так. Удалённый вызов — это не только сетевое время; это сборка промпта, токенизация, сериализация, транспорт, очередь на конечной точке, prefill, декодирование и десериализация. У локального вызова свои накладные расходы: загрузка модели, давление на память и конкуренция с остальным приложением. Измеряйте оба от одной и той же начальной точки — момента, когда входные данные доступны — иначе сравнение бессмысленно.
Затем следите за молчаливой эскалацией. Это режим отказа, когда локальный фолбэк при некотором условии пересылает полный промпт на удалённую конечную точку. Обычно он проявляется через один из нескольких механизмов: обёртка повторных попыток, которая пересылает исходную полезную нагрузку вместо сокращённой; путь логирования или трассировки, который отправляет сырые входные данные в бэкенд наблюдаемости; адаптер вызова инструментов, который передаёт весь разговор как контекст, потому что удалённый API ожидает список сообщений; или фича-флаг, который переключает класс с локального на удалённый без соответствующего изменения политики оттока.
Защита — сделать отток явным и поддающимся аудиту. Каждая исходящая полезная нагрузка должна конструироваться одним компонентом, который знает класс данных и может выдавать только поля, разрешённые для этого класса. Проверяйте это в тестах: для входных данных класса regulated-pii исходящая полезная нагрузка не должна содержать ни одного поля из запрещённого набора. Логируйте класс и имена полей, которые ушли, а не значения. Если политика применяется в одной точке контроля, а не в каждом месте вызова, молчаливая эскалация становится провалом теста, а не производственным инцидентом.
Практическое резюме: классифицируйте входные данные по чувствительности, маршрутизируйте по этой классификации, держите приватные полезные нагрузки на устройстве, заимствуя удалённые возможности для тех частей, которым они нужны, определяйте офлайн-поведение по классам и инструментируйте разделение так, чтобы локальная доля, дельта качества и поверхность оттока были видимы. Задержка — это выгода, которую замечают пользователи. Отток данных — та, что решает, развёртываема ли система вообще.
