Работа на открытых моделях
Как слой модели остаётся отделённым от слоя агентов — опрос возможностей, маршрутизация между несколькими бэкендами и четыре способа, которыми небольшие открытые модели ломаются на вызовах инструментов и которые шлюз берёт на себя.
Агенты здесь не пишутся под конкретную модель. Всё, что нужно слою агентов — чат, векторизация, вызовы инструментов, — идёт через шлюз, который говорит на OpenAI-совместимом HTTP, поэтому за ним может стоять облачный фронтир-API, открытая модель на вашем железе или сразу несколько.
Эта страница — инженерная сторона такого заявления. Она существует потому, что «поддерживаем открытые модели» легко сказать и трудно проверить, а тот, кто выбирает платформу, вправе спросить.
Шлюз
Бэкенд — это строка в таблице, привязанная к арендатору. В ней базовый URL, API-ключ, имя модели, вид
(chat или embedding) и вес. Добавить или заменить бэкенд — изменение конфигурации: без деплоя и
без переписывания агентов.
Ни одного вендорского SDK во всём дереве зависимостей нет. Шлюз делает обычные HTTP-запросы к OpenAI-совместимой поверхности — поэтому всё, что её реализует (vLLM, Ollama, серверы на llama.cpp, большинство коммерческих API), работает без кода-адаптера.
Возможности опрашиваются, а не предполагаются
При старте шлюз вызывает /v1/models на включённом бэкенде и считывает то, что модель действительно
даёт: контекстное окно, размерность векторов, заявленные возможности.
Ничего про модель не зашито в код. Это важнее, чем звучит: платформа, которая рассчитывает на фронтирное контекстное окно, молча переполнит открытую модель с окном в 32K, и сбой проявится не ошибкой, а обрезанным или бессмысленным ответом.
Если бэкенд не отвечает на опрос, шлюз записывает окно как неизвестное и отказывается подрезать контекст вместо того, чтобы подставить наугад маленькое число. Неверная догадка молча удаляет контекст, которым модель могла бы воспользоваться; попытка и явный отказ хотя бы видны.
Четыре способа сломаться у небольших открытых моделей
Это не гипотезы. Каждый случай обрабатывается потому, что встретился на практике.
1. Вызовы инструментов приходят текстом
Многие открытые модели игнорируют структурное поле tool_calls и вписывают вызов прямо в тело
ответа. Распространены две формы:
<tool_call>
<function=search_docs>
<parameter=query>refund policy</parameter>
</function>
</tool_call><tool_call>
{"name": "search_docs", "arguments": {"query": "refund policy"}}
</tool_call>Если это не разобрать, не выполняется ничего, а пользователь видит в чате сырую разметку. Цикл
инструментов разбирает обе формы, вызывает инструмент как обычно и — на всякий случай — вычищает из
текста остатки разметки <tool_call> перед отправкой дальше. Сырой синтаксис вызова инструмента не
должен доходить до конечного пользователя никогда, даже если разбор не удался.
2. Потоковые вызовы приходят по кускам
OpenAI-совместимые серверы расходятся в том, как разрезать вызов инструмента по чанкам потока. Один вызов может прийти несколькими частичными дельтами, иногда вперемешку, если модель запросила больше одного инструмента. Фрагменты накапливаются по индексу и собираются в целые вызовы прежде, чем что-либо будет выполнено.
3. Бюджет контекста считается от настоящего окна
Полезный бюджет входа не настраивается, а выводится:
input_budget =
probed_context_window − output_reservation − safety_margin— с нижним порогом, чтобы чрезмерный резерв никогда не обнулил бюджет. На модели с окном 32K получается совсем другой бюджет, чем на модели с миллионом токенов, и в этом весь смысл: одно и то же описание агента корректно работает и там, и там.
4. Рассуждения съедают ответ
У моделей, которые выдают рассуждения перед финальным ответом, бюджет вывода, рассчитанный только на сам ответ, уходит на рассуждения, и пользователь получает обрезанную реплику. Допуск на вывод поднимается с учётом токенов рассуждений, чтобы ответ уцелел.
Маршрутизация и переключение при отказе
Бэкенды одного вида выбираются по весу, поэтому трафик можно делить — например, большую часть запросов на небольшую модель у себя, остальное на более сильную облачную. Если запрос упал так, что его имеет смысл повторить, шлюз пробует остальные подходящие бэкенды и только потом переходит к экспоненциальной задержке на том, что у него есть.
На практике делят не по сложности, а по цене ошибки: то, что только читает и мало чем рискует, — на модель подешевле, всё, что записывает и подтверждает, — на модель посильнее.
Чего это не делает
Честно сказать о границах полезнее, чем удлинить список возможностей:
- Это не делает маленькую модель такой же способной, как большая. Выбор инструмента — решить, какой именно вызвать и с какими аргументами в запутанной многошаговой ситуации — это как раз то, где качество модели видно сильнее всего. Прогоните собственные задачи, прежде чем переносить на модель поменьше работу с высокой ценой ошибки.
- Это не управляет вашей инфраструктурой инференса. Если вы хостите модель сами, мощности GPU, сервер модели и его доступность остаются вашими. Шлюз обойдёт лежащий бэкенд, но не сделает его быстрее.
- Это не сертифицирует конкретные модели. Работа над совместимостью касается тех форм, которые модели выдают, а не проверенного списка. Ожидается, что заработает любой OpenAI-совместимый эндпоинт; какая модель подходит вашей нагрузке — это оценка, которую стоит провести вам.
Смотрите также
- Своя модель — то же самое, но для того, кто принимает решение, а не интегрирует
- Инструменты и MCP — что агент действительно может вызвать
- Приватность по устройству — изоляция, шифрование и то, что уходит за пределы вашего контура