Работа на открытых моделях
Как слой модели остаётся отделённым от слоя агентов — опрос возможностей, маршрутизация между несколькими бэкендами и четыре способа, которыми небольшие открытые модели ломаются на вызовах инструментов и которые шлюз берёт на себя.
Агенты здесь не пишутся под конкретную модель. Всё, что нужно слою агентов — чат, векторизация, вызовы инструментов, — идёт через шлюз, который говорит на 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>Остальные семь вообще без ограждения: голый JSON-объект; вызов, записанный одной строкой кода, doc_update_section({"key": …}); префикс call: или call:пространство:инструмент; JSON-объект, обрезанный посередине лимитом токенов; и — самая свежая — целый ответ из семи символов {{ query_knowledge_table }}.
Если это не разобрать, не выполняется ничего, а пользователь видит в чате сырую разметку. Цикл инструментов разбирает эти формы, вызывает инструмент как обычно и — на всякий случай — вычищает из текста остатки синтаксиса вызова перед отправкой. Сырой синтаксис вызова не должен доходить до конечного пользователя, даже если разбор не удался.
Два правила не дают этой подстраховке стать собственной ошибкой, и оба выучены на нарушениях. Сначала разбор, потом вычистка: вызов, записанный одной строкой кода, исполним, а ранняя версия вычищала его как мусор — документ не менялся, а модель бодро сообщала, что всё сделано. Вычистка, опустошившая ответ, обязана откатиться: если после удаления синтаксиса не остаётся ничего, ход отвечается заново, а не отправляется пустым.
2. Потоковые вызовы приходят по кускам
OpenAI-совместимые серверы расходятся в том, как разрезать вызов инструмента по чанкам потока. Один вызов может прийти несколькими частичными дельтами, иногда вперемешку, если модель запросила больше одного инструмента. Фрагменты накапливаются по индексу и собираются в целые вызовы прежде, чем что-либо будет выполнено.
3. Бюджет контекста считается от настоящего окна
Полезный бюджет входа не настраивается, а выводится:
input_budget =
probed_context_window − output_reservation − safety_margin— с нижним порогом, чтобы чрезмерный резерв никогда не обнулил бюджет. На модели с окном 32K получается совсем другой бюджет, чем на модели с миллионом токенов, и в этом весь смысл: одно и то же описание агента корректно работает и там, и там.
4. Рассуждения съедают ответ
У моделей, которые выдают рассуждения перед финальным ответом, бюджет вывода, рассчитанный только на сам ответ, уходит на рассуждения, и пользователь получает обрезанную реплику. Допуск на вывод поднимается с учётом токенов рассуждений, чтобы ответ уцелел.
Маршрутизация и переключение при отказе
Бэкенды одного вида выбираются по весу, поэтому трафик можно делить — например, большую часть запросов на небольшую модель у себя, остальное на более сильную облачную. Если запрос упал так, что его имеет смысл повторить, шлюз пробует остальные подходящие бэкенды и только потом переходит к экспоненциальной задержке на том, что у него есть.
На практике делят не по сложности, а по цене ошибки: то, что только читает и мало чем рискует, — на модель подешевле, всё, что записывает и подтверждает, — на модель посильнее.
Чего это не делает
Честно сказать о границах полезнее, чем удлинить список возможностей:
- Это не делает маленькую модель такой же способной, как большая. Выбор инструмента — решить, какой именно вызвать и с какими аргументами в запутанной многошаговой ситуации — это как раз то, где качество модели видно сильнее всего. Прогоните собственные задачи, прежде чем переносить на модель поменьше работу с высокой ценой ошибки.
- Это не управляет вашей инфраструктурой инференса. Если вы хостите модель сами, мощности GPU, сервер модели и его доступность остаются вашими. Шлюз обойдёт лежащий бэкенд, но не сделает его быстрее.
- Это не сертифицирует конкретные модели. Работа над совместимостью касается тех форм, которые модели выдают, а не проверенного списка. Ожидается, что заработает любой OpenAI-совместимый эндпоинт; какая модель подходит вашей нагрузке — это оценка, которую стоит провести вам.
Смотрите также
- Своя модель — то же самое, но для того, кто принимает решение, а не интегрирует
- Инструменты и MCP — что агент действительно может вызвать
- Приватность по устройству — изоляция, шифрование и то, что уходит за пределы вашего контура