После механического разбиения кода, определения семейства алгоритма и дифференциальной проверки появляется важный, но ещё не готовый к продакшену результат: «я понимаю, что именно здесь вычисляется». Логика по-прежнему сидит в исходном рантайме. Теперь нужно решить, во что её превратить: в чистую функцию для CI или во внешний процесс, за которым придётся постоянно присматривать. Ошибка на этом этапе быстро съедает всю экономию от реверса — причём с процентами в эксплуатации.
В обзоре из четырёх этапов этому посвящена одна короткая строка: «Этап 4: деградация по транспортному слою». Здесь разберём её подробно. Главная мысль проста: на каждом уровне ниже на порядок растут поверхность зависимостей, число сценариев отказа и стоимость развёртывания — поэтому по умолчанию нужно тянуть решение вверх.
Три варианта приземления — и порядок не обсуждается
Вариантов ровно три, сверху вниз. Их стоит закрепить в инженерных правилах, а не выбирать ситуативно по принципу «лишь бы быстрее запустилось»:
Нативная реализация. Перепишите логику на целевом языке, полностью отвязав её от исходного рантайма и оставив только стандартную библиотеку. Предпосылка одна: семейство алгоритма определено верно. Когда пройден этап идентификации по отпечаткам, в 90% случаев основа берётся из публичной реализации, отдельно обрабатываются точки расхождения — и на выходе получается чистая функция.
Локальный JS-движок с минимальным фрагментом. Иногда быстро очистить логику от окружения слишком дорого. Тогда сохраняют небольшой кусок исходного JS и исполняют нужные несколько десятков строк в локальном Node/V8, а не тащат за собой всю страницу.
Пассивный браузерный мост. Некоторые состояния существуют только в настоящей открытой странице с активной сессией: подпись, доставляемая рантаймом, или динамический идентификатор, привязанный к сессии. Если статически восстановить их нельзя, пока остаётся лишь считывать данные внутри браузера. Это временное решение: его необходимо явно отметить в интерфейсных заметках и никогда не делать вариантом по умолчанию.
Почему цена уровней растёт скачками
Именно поэтому порядок фиксирован: стоимость трёх уровней растёт не плавно. Каждый следующий обходится примерно на порядок дороже предыдущего.
Уровень | Поверхность зависимостей | Как ломается | Можно запускать в CI? |
|---|---|---|---|
Нативная реализация | Стандартная библиотека, никаких внешних процессов | Результат не совпал; находится одним | Да — это чистая функция |
Локальный JS-движок | Дополнительный рантайм Node; контекст V8 небезопасен для потоков, конкурентный доступ требует блокировки | Версия движка изменилась или отсутствует глобальная переменная, от которой зависит фрагмент | С трудом — движок нужно устанавливать |
Пассивный браузерный мост | Настоящий Chrome, расширение, сессия, которую поддерживает человек, и локальный loopback-канал | Страница не открыта, сессия истекла, структура изменилась или вкладку закрыли | Нет — нужен живой человек |
На первом уровне сбой ловится юнит-тестом; на третьем он выглядит так: «пользователь сегодня закрыл ту вкладку». Если то, что могло быть чистой функцией, превращается в решение третьего уровня, к каждому вызову привязывается человек. Ориентир: после определения семейства алгоритма обфусцированный SDK подписи можно приземлить как самостоятельную реализацию менее чем в 600 строк, зависящую только от встроенного crypto и работающую на первом уровне. То, что, казалось, можно провести лишь через браузерный мост, обычно оказывается алгоритмом, который вы ещё не до конца идентифицировали.
Граница пассивного моста: один снимок и никакой активности
Пассивный мост выходит из-под контроля ровно в один момент: когда начинает «помогать» — сам обновлять страницу, логиниться, ждать загрузку. Каждое добавленное «сам» сдвигает его от простого ретранслятора к краулеру. Поэтому граница должна быть предельно узкой. Эти ограничения появились не из теории:
Снимайте состояние один раз и никогда не меняйте страницу. Один раз считывайте cookies, состояние сессии и рантайм уже открытой страницы. Не создавайте вкладки, не обновляйте, не переходите по адресам, не переводите окно в фокус, не опрашивайте страницу в ожидании. Нет страницы — значит, её нет: не открывайте её за пользователя.
Как только чего-то не хватает — сразу возвращайте понятную ошибку. Нет подходящей вкладки — tab_unavailable; страница есть, но пользователь не авторизован — not_logged_in; авторизация есть, но рантайм ещё не готов — runtime_unavailable. Каждому из трёх кодов соответствует одно реальное состояние и одно следующее действие: дождаться страницы, войти в аккаунт или сменить цель. Не заставляйте вызывающий код гадать по общей ошибке «что-то пошло не так».
Чувствительное состояние не покидает браузер. Расширение не запрашивает разрешения cookies / webRequest. Оно только находит уже открытую подходящую вкладку, выполняет запрос в контексте этой страницы и очищает поля перед возвратом результата. Cookies и состояние подписи страницы ни на одном шаге не выходят из Chrome, а канал по умолчанию подключается к локальному loopback-адресу. Мост передаёт результаты, а не учётные данные.
Почему 15 scope нужны всего для нескольких платформ
Пассивный мост пересылает только запросы из whitelist: адаптер ограничивает путь, параметры и referer. Самое неочевидное здесь — гранулярность. Для нескольких платформ требуется 15 scope, потому что scope режутся по «контексту страницы», а не по «платформе». У одного TikTok есть Creative Center, Top Ads, Creator platform, библиотека инфлюенсеров и Ads Manager — это пять независимых scope, пять независимых состояний сессии и пять рантаймов страниц. Авторизация в рекламном бэкенде не даст доступ к рантайму Creator platform. Если выделить один срез на платформу, первая же ситуация «залогинен в подсайте A, но не могу обслужить подсайт B» вернёт вас к переделке. У Xiaohongshu основной сайт, пути, эквивалентные приложению, и маркетплейс для авторов тоже образуют три отдельных scope.
Отсюда следует и гранулярность планирования: блокировка ставится только на уровне семейства платформы. Запросы внутри одного семейства, например Douyin, выполняются последовательно: они используют одну реальную вкладку, а параллельные обращения к одному контексту страницы начинают мешать друг другу. Разные семейства, такие как Douyin и Xiaohongshu, работают параллельно — это две независимые вкладки. Внутри семейства поверх этого добавляется минимальный интервал между запросами. Слишком грубая блокировка сериализует работу, которую можно было вести параллельно; слишком мелкая даёт столкновения запросов в общей вкладке. Семейство платформы и есть естественная граница: «использует один рантайм страницы».
Передача логина человеку: единственное допустимое вмешательство
Пассивный мост не управляет страницей, но сессии истекают. Решение — свести участие человека к одному явному разовому действию: интерактивная команда запускает handoff, программа через операционную систему открывает нужную рабочую страницу, ждёт, пока вы вручную войдёте и страница станет готова, а затем повторяет исходный запрос. Расширение при этом не нажимает кнопки, не заполняет формы и не экспортирует cookies. Авторизация происходит в обычном браузере руками пользователя, а программа лишь продолжает запрос после её завершения.
Критическое правило: это не должно запускаться неявно. Неинтерактивная команда — в CI или по расписанию — никогда не открывает браузер. Она просто корректно возвращает ошибку сессии и оставляет решение верхнему уровню. Handoff также обязан защищаться от ложных ожиданий: после успешного входа рантайм получает лишь короткое дополнительное ожидание — в реализации это 20 секунд, — после чего должен быть вынесен вердикт. Если рабочая страница уже перенаправила на страницу аккаунта без нужного контекста интерфейса, текущий этап нужно завершить сразу. Нельзя путать детерминированное «сюда попасть невозможно» с «страница всё ещё загружается» и бессмысленно ждать. В потоке, допускающем деградацию, этот этап фиксируется как unavailable, а выполнение продолжается, вместо того чтобы валить весь процесс.
Статусы этапов: выполнено, пропущено, нужна сессия
Общее правило для всего описанного: этап не может возвращать только «успех» или «ошибку». В оркестрационном пайплайне результат этапа имеет шесть форм: completed — выполнен, empty — отработал, но данных нет, ready — подготовлен и ждёт отправки, skipped — намеренно пропущен по правилу, unavailable — временно недоступен, обычно нужна сессия, blocked — не выполнено предварительное условие.
«Намеренно пропущен», «нужна сессия» и «данных действительно нет» — три принципиально разных сигнала. Если вернуть один непрозрачный результат, невозможно понять: пустой ответ был ожидаемым или сессия умерла, а никто этого не заметил. Таким пайплайном нельзя управлять. Опишите статус этапа конечным enum, и оркестратор — скрипт или модель — сможет решить, нужно ли деградировать, переавторизоваться или остановиться. Это тот же принцип, что и три кода ошибок пассивного моста, только поднятый на уровень всего потока.
Как использовать модель для выбора уровня реализации
Именно здесь модель полезна, но в строго ограниченной роли: она не очищает логику от окружения за вас, а помогает понять, нужно ли это делать и насколько далеко идти. Это архитектурное решение, а не взлом подписи. Главный вопрос один: состояние, от которого зависит код, можно статически восстановить или оно доступно только во время выполнения? Затем усилия на очистку сопоставляются с частотой изменений. На разных шагах от модели требуется разное:
Шаг | Нужные возможности | Выбор | model id |
|---|---|---|---|
Прочитать весь модуль и увидеть поверхность зависимостей | Длинный контекст, чтение графа вызовов за один проход | Kimi K3 |
|
Аргументировать выбор уровня с обеих сторон и возражать против подхода «лишь бы заработало» | Сильное рассуждение; способна обосновать, почему стоит потратить лишний день на очистку | Claude Opus 5 |
|
Массовая первичная сортировка от десятков до сотен возможностей | Низкая стоимость, высокая параллельность | Claude Sonnet 5 |
|
Установить причину после деградации | Средний уровень рассуждений; объясняет проблему по логу сбоя | GPT-5.6 Sol |
|
Особенно важен второй вариант. Самая частая ошибка при выборе уровня — модель подхватывает ваш настрой «лишь бы запустить» и предлагает «через браузерный мост проще всего». Она не оценивает долгосрочную цену, а лишь отражает вашу интонацию. Модель с сильным рассуждением возразит: «этот сегмент — стандартный хеш с одним постоянным возмущением; стоит потратить день и сделать чистую функцию, а не отправлять его в мост». Не верьте на слово — проверьте сами. Возьмите 3 фрагмента логики, включая как минимум 1, для которого правильный уровень вам уже известен и который послужит контрольным. Передайте один и тот же промпт — «выбери уровень, обоснуй решение и возрази против преждевременного переноса в браузерный мост» — в claude-opus-5 и gpt-5.6-sol. Смотрите только на одно: модель пытается поднять решение на уровень выше или лениво выбирает третий по умолчанию.
Главная проблема — цена переключения
Четыре уровня у трёх поставщиков означают три SDK, три схемы авторизации и три формата ошибок. Переписывать клиент, чтобы менять модели между шагами, невыгодно. Поэтому большинство запускает одну модель на всё — и для решения о размещении, где особенно нужен сильный уровень рассуждений, получает модель, которая только поддакивает.
AIReiter выравнивает этот слой: один ключ, один OpenAI-совместимый интерфейс, за которым доступны все четыре уровня. Чтобы переключиться, достаточно поменять поле model в теле запроса.
# Аргументация выбора уровня: reasoning tier
curl https://aireiter.com/api/v1/chat/completions \
-H "Authorization: Bearer $AIREITER_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5",
"messages": [{"role": "user", "content": "<промпт для выбора уровня + фрагмент реверсированной логики + список зависимостей>"}]
}'
# Массовая первичная сортировка: измените одно поле
# "model": "claude-sonnet-5"
# Установление причины деградации:
# "model": "gpt-5.6-sol"
Уже используете OpenAI SDK? Укажите для base_url адрес https://aireiter.com/api/v1. Работаете с Anthropic SDK? Отправляйте POST /api/v1/messages с тем же ключом. По цене модели Claude идут со скидкой 30% от прайса, а модели GPT — за половину цены. У этого процесса две основные статьи расходов: массовая первичная сортировка десятков или сотен возможностей — Sonnet, большой объём вызовов — и один длинноконтекстный запрос для чтения всего модуля и анализа его зависимостей — Kimi, много токенов на вызов. Массовая сортировка выполняется на модели Claude, поэтому скидка приходится на самый плотный этап; модель для аргументации выбора уровня тоже Claude и также стоит на 30% дешевле. Длинноконтекстный уровень Kimi K3 доступен с тем же ключом.
Попробовать без регистрации — сначала вручную передайте несколько фрагментов логики и посмотрите, будут ли две модели поддакивать вам или возражать против вопроса «стоит ли отправлять это в браузерный мост».
Итог
Приземление реверсированной логики — это не столько техническая, сколько экономическая задача. Лестницу из трёх уровней — нативная реализация > локальный JS-движок > пассивный браузерный мост — нельзя переворачивать: на каждом шаге вниз чистая функция меняется на процесс с внешними зависимостями, участием человека и живой вкладкой. Пассивный мост — не запретная зона, а временный компонент с жёсткими границами: один снимок, никакой активности, явная ошибка при отсутствии страницы, чувствительное состояние не выходит из браузера, логин только через явный handoff, статусы этапов всегда понятны. Соблюдайте эти условия — и мост останется надёжной временной мерой; отбросьте хотя бы одно — получите чёрный ящик, который никто не захочет сопровождать. Модель помогает решить, на каком уровне должна оказаться возможность, и удержать линию против инерции «лишь бы заработало». А правильность каждой реализации проверяет не модель, а дифференциальное тестирование.
