Когда строгость - это всего лишь иллюзия
Вы настраиваете агента с вызовом инструментов. Пишете красивую JSON-схему. Указываете strict=True в надежде, что модель не будет генерировать мусор. А она генерирует. Потому что этот параметр - фикция в большинстве inference-движков на февраль 2026 года.
Факт: VLLM версии 0.5.6 (актуальная на февраль 2026) декларирует поддержку strict, но на практике он влияет только на валидацию входящего промпта, а не на генерацию.
Что обещают и что получаем
В документации всё выглядит логично. Параметр strict должен гарантировать, что модель будет строго следовать JSON-схеме. В реальности - это маркетинговая уловка для привлечения разработчиков, уставших от хаоса в tool calling.
Как работает (точнее, не работает) strict в VLLM
Откройте исходники VLLM. Найдите обработку structured outputs. Вы увидите примерно такую картину:
# Примерное поведение VLLM 0.5.6
def validate_schema(schema, strict=False):
if strict:
# Проверяет, что схема валидна
# НЕ гарантирует, что модель её соблюдает
pass
Параметр проверяет схему на корректность. Не более того. Модель всё равно может сгенерировать {"name": "get_weather", "arguments": "city: Москва"} вместо {"name": "get_weather", "arguments": {"city": "Москва"}}.
llama.cpp: история с продолжением
В llama.cpp ситуация ещё интереснее. Там вообще нет параметра strict в классическом понимании. Вместо этого есть система грамматик, которая должна обеспечивать корректный синтаксис.
Но грамматики в llama.cpp работают на уровне токенов. Они гарантируют, что следующий токен будет синтаксически корректным. Не семантически. Модель может сгенерировать:
{
"tool": "calculate",
"args": {
"operation": "add",
"numbers": [1, 2, "три"] # Ошибка типа, но синтаксис верный
}
}
Почему так происходит?
- Разработчики inference-движков фокусируются на скорости, а не на корректности
- Реализация настоящего strict требует сложной валидации на лету, что замедляет генерацию
- Модели сами по себе плохо обучены для точного следования схемам
- Нет стандарта: каждый движок интерпретирует strict по-своему
Практические последствия для разработчиков
Вы создаёте агента для обработки заказов. Модель должна вызывать функцию create_order с определёнными полями. В 30% случаев она генерирует некорректный JSON, даже с strict=True. Ваш продакшен падает.
Типичные ошибки:
| Ошибка | Частота | Влияние |
|---|---|---|
| Неправильные типы данных | 25% | Критические ошибки парсинга |
| Лишние поля | 15% | Нарушение контрактов API |
| Неполные объекты | 20% | Частичная работа функций |
| Строки вместо объектов | 10% | Полный отказ системы |
Что делать, если strict не работает?
Не ждите, что движки исправятся. Берите ситуацию в свои руки.
1 Валидируйте выход модели самостоятельно
Добавьте слой пост-обработки. После каждого tool call проверяйте результат через JSON Schema валидатор. Если не проходит - пробуйте исправить или запрашивайте повторную генерацию.
import json
import jsonschema
from typing import Dict, Any
def validate_tool_call(response: str, schema: Dict) -> Dict[str, Any]:
try:
data = json.loads(response)
jsonschema.validate(data, schema)
return {"valid": True, "data": data}
except (json.JSONDecodeError, jsonschema.ValidationError) as e:
# Здесь можно попробовать исправить JSON
# Или запросить регенерацию
return {"valid": False, "error": str(e)}
2 Используйте модели с лучшей поддержкой tool calling
Не все модели одинаковы. Некоторые специально дообучаются для работы с инструментами. В нашем обзоре лучших LLM с поддержкой Tool Calling мы сравнивали, какие модели действительно умеют работать со схемами.
На февраль 2026 года лучшие результаты показывают:
- Claude 3.7 Sonnet Thinking (но он не локальный)
- GPT-4o Mini с дообучением
- Llama 3.2 90B Instruct с fine-tuning под tool calling
- Qwen 2.5 72B с расширенным контекстом
3 Настройте грамматики в llama.cpp правильно
Грамматики - мощный инструмент, если понимать их ограничения. Они не гарантируют семантическую корректность, но могут предотвратить синтаксические ошибки.
# Пример запуска с грамматикой для tool calling
./llama-cli -m model.gguf \
--grammar-file tool_call.gbnf \
--prompt "Какая погода в Москве?"
Грамматика должна описывать ВСЕ возможные инструменты и их параметры. Это громоздко, но эффективно против синтаксического мусора.
4 Добавьте ретраи и fallback-логику
Ожидайте, что первая попытка может провалиться. Спроектируйте систему так, чтобы она могла перезапросить модель или использовать запасной путь.
Практический совет: ограничьте количество ретраев тремя. Больше - трата ресурсов. Если после трёх попыток модель не генерирует валидный JSON, переходите к fallback-действию.
Почему разработчики движков не фиксят проблему?
Спросите любого разработчика VLLM или llama.cpp про strict. Они пожмут плечами. Проблема в фундаментальном несоответствии ожиданий и реальности.
Разработчики думают: "Мы добавили параметр strict, теперь всё будет строго". Пользователи думают: "Теперь модель точно будет следовать схеме". Ни те, ни другие не правы.
Настоящая реализация strict потребовала бы:
- Валидации на лету во время генерации
- Отката некорректных токенов
- Перегенерации с учётом ошибок
- Сложной логики обработки типов данных
Всё это убивает производительность. А inference-движки заточены на скорость. Вот и получаем компромисс: быстрая генерация с возможными ошибками вместо медленной, но точной.
Будущее tool calling: что нас ждёт?
К концу 2026 года ситуация может измениться. Появляются специализированные модели для tool calling, которые из коробки понимают JSON-схемы. Движки начинают добавлять настоящую валидацию через внешние валидаторы.
Но пока - рассчитывайте только на себя. Стройте системы с предположением, что модель будет ошибаться. Добавляйте слои защиты. Тестируйте на разнообразных сценариях. Как правильно тестировать такие системы, мы разбирали в статье про тестирование недетерминированных LLM.
Чеклист для продакшена
- Не доверяйте параметру strict - проверяйте всё сами
- Добавьте JSON Schema валидацию после каждой генерации
- Используйте модели, специально обученные для tool calling
- Настройте грамматики для синтаксической защиты
- Добавьте логирование всех ошибок валидации
- Реализуйте ретраи с экспоненциальной задержкой
- Создайте fallback-стратегии на случай повторных ошибок
- Мониторьте процент успешных tool calls (должен быть >95%)
И помните: проблема не в вашем коде. Она системная. Все разработчики агентов сталкиваются с тем же. Разница лишь в том, как быстро они это осознают и начинают строить защитные механизмы.
Последний совет: если ваша система критически зависит от точности tool calling, рассмотрите использование специализированных сервисов вроде OpenAI's structured outputs (если они доступны в вашем регионе) или локальных моделей с доказанной точностью выше 98%.
Что почитать дальше
Если вы только начинаете работать с локальными LLM, посмотрите практический гайд по избежанию основных ошибок. Для тонкой настройки llama.cpp есть материал про сборку и оптимизацию под ваше железо.
А если столкнулись с тем, что модель постоянно отказывается выполнять запросы, изучите техники удаления "отказов" через Refusal Steering.
Tool calling - это будущее LLM-приложений. Но настоящее этого будущего пока сырое. Придётся дорабатывать напильником.