После механического разделения кода, определения семейства алгоритма и дифференциальной проверки вы приходите к выводу: «Я понимаю, что здесь вычисляется». Но сам по себе этот вывод в прод не отправишь. Логика всё ещё живёт в исходном окружении, и нужно решить, во что её превратить: в чистую функцию для CI или во внешний процесс, за которым придётся постоянно присматривать. Ошибётесь на этом этапе — и сэкономленное раньше время вернётся в эксплуатации с процентами.
Обзор из четырёх этапов описывает этот шаг одной строкой: «Этап 4: понижаем уровень через транспортный слой». Здесь разберём его подробнее. Главный принцип прост: с каждым переходом вниз на уровень поверхность зависимостей, число сценариев отказа и стоимость внедрения вырастают на порядок — поэтому по умолчанию нужно двигаться вверх.
Три варианта реализации — и порядок не обсуждается
Форматов всего три, сверху вниз. Этот порядок стоит закрепить в инженерных правилах, а не выбирать ситуативно по принципу «лишь бы быстрее заработало»:
Нативная реализация. Алгоритм переписывается на целевом языке, отвязывается от исходного рантайма и использует только стандартную библиотеку. Предпосылка одна: семейство алгоритма определено верно. Когда этап снятия отпечатка пройден, 90% кода берётся из публичной реализации, оставшиеся точки расхождения обрабатываются отдельно — и результат становится чистой функцией.
Локальный JS-движок с минимальным фрагментом. Иногда быстро очистить логику от окружения слишком дорого. Тогда сохраняют небольшой кусок исходного JS и запускают нужные несколько десятков строк в локальном Node/V8, а не поднимают всю страницу.
Пассивный браузерный мост. Бывает состояние, доступное только в настоящей открытой странице с авторизованной сессией: например, подпись, получаемая во время работы рантайма, или динамический идентификатор, привязанный к сессии. Статически восстановить его нельзя, и пока что остаётся лишь считывать его из браузера. Это временный вариант: он должен быть явно отмечен в заметках к интерфейсу и никогда не должен становиться решением по умолчанию.
Во сколько на самом деле обходится каждый уровень
Именно поэтому порядок фиксирован. Стоимость этих трёх уровней растёт не плавно: каждый следующий примерно на порядок тяжелее предыдущего.
Уровень | Поверхность зависимостей | Как выглядит сбой | Подходит для CI? |
|---|---|---|---|
Нативная реализация | Стандартная библиотека, никаких внешних процессов | Результат не совпал — находится одним | Да: это чистая функция |
Локальный JS-движок | Ещё один рантайм Node; контекст V8 не потокобезопасен, для параллельности нужен lock | Версия движка или отсутствующая глобальная переменная, от которой зависит фрагмент | С натяжкой: движок нужно устанавливать |
Пассивный браузерный мост | Настоящий Chrome + расширение + поддерживаемая человеком сессия + локальный loopback-канал | Страница не открыта, сессия истекла, структура изменилась, вкладка закрыта | Нет: нужен живой человек |
На первом уровне отказ ловится юнит-тестом, а на третьем причиной может стать то, что пользователь сегодня закрыл нужную вкладку. Если то, что могло быть чистой функцией, превратить в решение третьего уровня, человек окажется привязан к каждому вызову. Ориентир такой: после определения семейства алгоритма обфусцированный SDK подписи можно оформить в самостоятельную реализацию менее чем в 600 строк, зависящую лишь от встроенного crypto и работающую на первом уровне. То, что кажется возможным только через браузерный мост, обычно оказывается алгоритмом, который вы ещё не до конца распознали.
Граница пассивного моста: один снимок и никакой активности
Пассивный мост выходит из-под контроля ровно в тот момент, когда начинает «помогать»: сам обновляет страницу, сам авторизуется, сам ждёт загрузку. Каждое такое «сам» сдвигает его от простого прокси в сторону краулера. Поэтому границу нужно провести предельно жёстко. Эти ограничения выработаны на практике:
Делайте только один снимок состояния, страницу не меняйте. Один раз считайте cookies, состояние сессии и рантайм уже открытой страницы — и на этом всё. Не создавайте вкладки, не обновляйте и не перенаправляйте страницу, не переводите на неё фокус, не опрашивайте её в ожидании готовности. Нет страницы — значит, страницы нет; не открывайте её за пользователя.
Как только чего-то не хватает, сразу возвращайте явную ошибку. Нет подходящей вкладки — возвращайте tab_unavailable; страница открыта, но пользователь не авторизован — not_logged_in; авторизация есть, но рантайм ещё не готов — runtime_unavailable. Каждый из трёх кодов соответствует реальному состоянию и следующему действию: дождаться страницы, войти в аккаунт или сменить цель. Не заставляйте вызывающую сторону угадывать причину по единственному сообщению «не сработало».
Чувствительные данные не покидают браузер. Расширение не запрашивает разрешения cookies / webRequest. Оно только находит уже открытую подходящую вкладку, выполняет запрос в контексте этой страницы и очищает поля перед возвратом. Cookies и состояние подписи страницы ни на одном шаге не выходят из Chrome, а канал по умолчанию подключается к локальному loopback-адресу. Мост передаёт результат, а не учётные данные.
Почему 15 scope нужны даже для нескольких платформ
Пассивный мост пропускает только запросы из белого списка: путь, параметры и referer ограничены адаптером. Самое неочевидное здесь — детализация. Всего scope 15, хотя платформ лишь несколько, потому что scope выделяются по «контексту страницы», а не по «платформе». У одного TikTok есть Creative Center, Top Ads, Creator platform, библиотека инфлюенсеров и Ads Manager — это пять независимых scope, пять независимых состояний сессии и пять рантаймов страниц. Вход в рекламный бэкенд не даст вам рантайм Creator platform. Разрежете платформу одним общим слоем — и при первом же случае «в под-сайте A авторизация есть, а под-сайт B обслужить нельзя» придётся переделывать схему. У Xiaohongshu аналогично три scope: основной сайт, пути, эквивалентные приложению, и маркетплейс для авторов.
Та же логика определяет планирование: lock ставится только на уровне семейства платформы. Запросы внутри одного семейства, например Douyin, идут последовательно: они используют одну настоящую вкладку, а параллельные обращения в один контекст страницы начинают мешать друг другу. Разные семейства, например Douyin и Xiaohongshu, можно обрабатывать параллельно — это две несвязанные вкладки. Внутри семейства дополнительно задаётся минимальный интервал между запросами. Возьмёте границу слишком широко — последовательно пойдёт работа, которую можно было выполнять параллельно. Возьмёте слишком узко — столкнутся запросы, разделяющие вкладку. Семейство платформы и есть естественная граница для понятия «использует один рантайм страницы».
Явная передача авторизации: единственное допустимое участие человека
Пассивный мост не управляет страницей, но сессии всё равно истекают. Решение — свести участие человека к одному явному одноразовому действию. Интерактивная команда запускает передачу управления: программа открывает нужную бизнес-страницу средствами операционной системы, ждёт, пока вы вручную авторизуетесь и страница подготовится, а затем повторяет исходный запрос. Расширение при этом не нажимает кнопки, не заполняет формы и не экспортирует cookies. Авторизация происходит в обычном браузере руками пользователя, а программа лишь продолжает запрос после завершения этого действия.
Критически важно, что это не должно запускаться неявно. Неинтерактивная команда — в CI или задаче по расписанию — никогда не открывает браузер. Она просто корректно возвращает ошибку сессии и оставляет решение верхнему уровню. Передача управления должна защищать и от ложных ожиданий: после успешного входа рантайму даётся лишь короткая дополнительная пауза — в реализации 20 секунд, — после которой необходим однозначный результат. Если бизнес-страница уже перенаправила на страницу аккаунта без нужного контекста интерфейса, текущий этап надо завершать сразу. Не стоит принимать детерминированное «сюда попасть нельзя» за «страница ещё грузится» и бессмысленно ждать. В сценарии, допускающем деградацию, такой этап записывается как unavailable, а поток продолжается, а не падает целиком.
Статусы этапов: выполнен, намеренно пропущен, нужна сессия
Общее правило для всего описанного выше: ни один этап не должен возвращать только «успех» или «ошибку». В orchestration-пайплайне результат этапа может принимать шесть значений: completed (выполнен), empty (запущен, но данных нет), ready (подготовлен и ждёт отправки), skipped (намеренно пропущен по правилу), unavailable (сейчас недоступен, обычно требуется сессия), blocked (не выполнено предварительное условие).
«Намеренно пропущен», «нужна сессия» и «данных действительно нет» — это три принципиально разных сигнала. Если возвращать один непрозрачный результат, невозможно понять, пустой ответ должен быть пустым или сессия умерла незаметно. Такой пайплайн нельзя нормально эксплуатировать. Опишите статус этапа конечным enum, и orchestration-слой — скрипт или модель — сможет решать, что делать дальше: деградировать, повторно авторизоваться или прервать выполнение. Это тот же принцип, что и три кода ошибок пассивного моста, только поднятый на уровень всего потока.
Как использовать модель для выбора уровня реализации
Только на этом этапе в дело вступает модель — и в очень ограниченной роли: она не очищает логику за вас, а помогает оценить, нужно ли и насколько её очищать. Это архитектурное решение, а не взлом подписи. Центральный вопрос один: зависит ли нужное состояние от того, что можно статически восстановить, или оно доступно только во время работы рантайма? Затем нужно сопоставить усилия на очистку с частотой изменений. На разных шагах модели требуются разные качества:
Шаг | Что требуется от модели | Выбор | 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 в теле запроса.
# Аргументация выбора уровня: модель для рассуждений
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-движок > пассивный браузерный мост — нельзя переворачивать: каждый шаг вниз меняет чистую функцию на процесс с внешними зависимостями, участием человека и живой вкладкой браузера. Пассивный мост не запрещён, но это временный компонент с жёсткими границами: один снимок, никакой активности, явная ошибка при отсутствии страницы, чувствительные данные не покидают браузер, вход возможен только через явную передачу управления, а статус этапа всегда читаем. Соблюдайте эти правила — и мост останется надёжной временной мерой; нарушьте хотя бы одно — получите чёрный ящик, который никто не захочет сопровождать. Модель помогает определить уровень для конкретной возможности и противостоять инерции «лишь бы заработало», но корректность каждой реализации проверяет не она, а дифференциальное тестирование.