Оговорка: это продолжение метафоры из моего раннего поста «Лошади и всадники» (Athanor Domus, май 2026). Там речь шла о всаднике и его роли — здесь мне интереснее не всадник, а упряжь, которую он строит вокруг лошади.

Мне захотелось потренироваться в одном конкретном навыке — строить жёсткие пайплайны и харнесс вокруг модели. Не «подключить ЛЛМ» — это как раз просто, а собрать вокруг неё слой, который делает поведение предсказуемым: поток, протокол, тайминги, структура, состояние. Это ремесло, и ему нужен тренажёр.

С тренажёром вышла незадача. Я перебрал почти все варианты, прежде чем понял, какой из них настоящий.

Тренажёра не оказалось там, где я искал

Сначала — локальные модели, очевидный путь. И тут же две беды, с двух сторон сразу.

Хорошая локальная tool-модель уже обучена протоколу: она сама умеет вызывать функции, держать JSON, слушаться system-промпта. Харнесс вокруг неё получается тривиальным — пара вызовов, и всё работает. Тренироваться не на чем: всю работу за тебя уже сделал тот, кто обучал модель.

Слабая локальная модель — беда с другого конца. Она не тянет задачу, и никакая обвязка этого не исправит. Ты начинаешь отлаживать не харнесс, а саму модель, — а это уже другая профессия и другой эксперимент.

Та же логика — у API-модели. Её «объездил» вендор: tool-calling, сессии, структурированный ответ, system-промпт — всё выдано готовым. Жёсткость пайплайна обеспечивает не ты, а чужой инженерный отдел. Удобно — и совершенно неинтересно.

Дикая лошадь

Нужна была золотая середина: модель, которая достаточно сильна, чтобы задача имела смысл, и при этом не знает ни одного протокола. Тогда всё, чего ей не хватает, придётся построить руками — и именно это и есть тренировка.

Такая модель есть, и она у всех на виду. Это сильная модель в веб-чате — Claude, ChatGPT, Qwen, DeepSeek. Силы у неё хватает с запасом, а про инструменты, сессии и tool_calls она не знает ровным счётом ничего. Это дикая лошадь: сильная, своевольная и совершенно необъезженная. Упряжь — моя.

Разница между ней и объезженной API-моделью — как раз в том, чего у неё нет. Я выписал это себе в столбик, прежде чем что-то строить:

Чего у лошади нетЧто должна дать упряжь
Вызова функций (tools)Впрыснуть каталог функций в сам промпт
Прямого system-промптаЗадать роль инъекцией в пользовательский текст
Структурированного ответаВыпарить JSON из прозы и проверить его
Сессии на нашей сторонеВести «хвост» самому, историю оставить чату
Бинарных артефактовЗабрать картинку сразу, по подписанному URL
Сигнала «я закончила»Определить готовность эвристикой по DOM
ПараллелизмаОдин браузерный поток, запросы в очередь

Каждый пункт — отдельный недостающий орган. Каждый пришлось отрастить.

Как я строил упряжь

Тренировка шла в моей песочнице bagilov.tech, а объектом я взял Qwen (chat.qwen.ai) — сильную модель, доступную только через браузер. Слоёв получилось семь, и ни один не дался с первого раза.

Уздечка — управление. Чтобы модель вообще «увидела» инструменты, их каталог впрыскивается прямо в промпт. Здесь я и споткнулся первый раз: полный набор (40+ инструментов) её перегружает — она начинает путать имена и вызывать несуществующие функции. Помогло сокращение до минимума: семь инструментов под задачу и явное отключение всего тяжёлого.

Вожжи — tool-calling. Инструменты описываются инструкцией в тексте, а ответ модели я парсю обратно в tool_calls. Самое хрупкое место всей упряжи.

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

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

Вьюк — артефакты. Картинка приходит по подписанному URL с коротким сроком жизни. Если забрать её «потом», прилетит 404. Правильно — схватить немедленно, пока подпись жива.

Сбруя — интерфейс. Поверх всего — OpenAI-совместимый прокси, чтобы агент подключился к лошади как к обычному провайдеру, не зная, что за ней браузер. Но сознательно не в авто-фолбэк: переключение на дикую лошадь — ручной выбор, а не невидимая подмена.

Поводья сверху. Отдельный режим, где сообщения идут прямо в лошадь, минуя агентский цикл, — проверка, что упряжь автономна и переживает недоступность основного провайдера.

На чём ломается приручение

Это не баги упряжи, а природа дикой лошади. Часть ограничений — навсегда.

  • Хрупкость. Всё держится на DOM чужой страницы: редизайн — и упряжь рвётся.
  • Очередь. Один браузерный поток — это пропускная способность одного человека.
  • Протухание сессий. Логин время от времени приходится обновлять руками.
  • Ресурсы. Браузер и виртуальный дисплей едят память, которой на слабом сервере не хватает самому сайту.
  • Воля вендора. Режимы и движки включаются и отваливаются по чужому расписанию: сегодня работает, завтра нет.

Правдивый эпилог: хозяин против

Лошадь объезжена, но у неё есть хозяин — и он против. Условия Qwen прямо это запрещают: Service Agreement (§II) — «using bots to access the Service» и «outputting content through automated or programmatic means»; Use Policy (§III.6(3)) — сторонние инструменты без разрешения; плюс оговорка, что сервис «for private and non-commercial use only». Санкция — блокировка аккаунта без предупреждения.

Поэтому для меня это тренировка, а не прод. Для боевого пайплайна берётся объезженная лошадь: официальный API, локальные веса или агрегатор с правом реселла. Дикая — только чтобы отточить навык.

Куда смотреть дальше

  • Бюджет внимания. Сколько инструментов модель выдерживает, пока не начнёт путать имена? Порог я нащупал на семи, но не измерил.
  • Надёжность вожжей. Хочу свою грамматику и валидатор для разбора вызовов — особенно на случай обрезанного JSON.
  • Тайминг. Ждать «тишину» грубо; интересно оценивать длину генерации динамически.
  • Переносимость упряжи. Что здесь универсально, а что приклеено к Qwen — селекторы, логин, названия режимов.

Что понял

Главное здесь — навык, а не модель. Слои упряжи переносимы: протокол, структура, сессия, тайминг, артефакты, интерфейс понадобятся вокруг любой слабоструктурированной модели. Дикая лошадь просто делает тренировку честной — она не прощает самонадеянности и не прячет то, что ты не построил.

Локальные модели для этой тренировки не годятся: одни уже обучены протоколу, другие слишком слабы. Нужна сильная и необъезженная — тогда вся жёсткость пайплайна оказывается твоей заслугой, а не подарком вендора. И когда возвращаешься к обычному API, ты строишь обвязку осознанно, а не по привычке.