Детерминированный tool-routing: почему LLM — плохой диспетчер инструментов
Статистический tool-calling ненадёжен: модель пропускает очевидные вызовы или выдумывает несуществующие инструменты. Skilly PGP предлагает детерминированный слой маршрутизации между намерением агента и вызовом инструмента.
Проблема, которую все видят, но мало кто называет
Вы подключили к агенту десяток инструментов через MCP. Описали каждый в системном промпте. Протестировали — вроде работает. Выкатили в прод — и начался хаос.
Модель забывает вызвать калькулятор, когда пользователь просит посчитать. Вызывает API погоды вместо поиска по базе. Выдумывает инструмент validate_email, которого нет в реестре. Уверенно возвращает результат «вызова», который никогда не происходил.
Это не баг конкретной модели. Это архитектурная проблема: мы отдали статистическому процессу решение, которое должно быть детерминированным.
Как работает tool-calling сегодня
Стандартная схема: LLM получает список доступных инструментов (function definitions), промпт пользователя и должна решить — вызывать инструмент или отвечать текстом. Если вызывать — какой именно, с какими параметрами.
Это решение принимается тем же вероятностным процессом, что генерирует текст. Модель не «понимает», что калькулятор точнее ментальной арифметики. Она предсказывает следующий токен, и если в обучающих данных люди чаще считали «в уме», чем вызывали API — модель сделает так же.
Три типовых сбоя
Пропуск вызова (false negative). Пользователь спрашивает: «Сколько будет 1847 × 293?» Модель уверенно отвечает «541,171» вместо вызова калькулятора. Правильный ответ — 541,371. Разница в одну цифру, и поймать её невозможно без проверки.
Фантомный вызов (hallucinated tool). Модель генерирует вызов инструмента, которого нет в реестре. Если фреймворк не валидирует имя — получаем ошибку на уровне runtime. Если валидирует — модель пытается «объяснить» пользователю, почему инструмент недоступен, хотя его никогда не существовало.
Неправильный выбор (wrong dispatch). Из пяти инструментов поиска модель выбирает search_web, когда контекст однозначно указывает на search_internal_docs. Решение зависит от того, как описаны инструменты, от длины контекстного окна и от фазы луны.
Почему prompt engineering не решает проблему
Первая реакция — дописать в системный промпт: «ВСЕГДА используй калькулятор для математических вычислений». Это помогает в конкретном случае, но создаёт хрупкую систему правил.
При 20 инструментах нужно 20 правил. Правила конфликтуют. Модель интерпретирует их по-разному в зависимости от контекста. При обновлении модели правила перестают работать. Это как писать бизнес-логику комментариями к коду, надеясь, что компилятор их прочитает.
Skilly PGP: детерминированный слой маршрутизации
Идея Skilly (Pattern-Guided Parsing) — вынести решение о маршрутизации из LLM в отдельный детерминированный слой. Модель по-прежнему общается с пользователем, понимает контекст и формулирует намерение. Но решение «какой инструмент вызвать» принимает не она.
Слой 1: Извлечение намерения
LLM анализирует запрос и возвращает структурированное описание того, что пользователь хочет сделать. Не название инструмента — а семантику задачи: «вычислить арифметическое выражение», «найти документ по ключевым словам», «получить текущую погоду для города X».
Слой 2: Детерминированный маршрутизатор
Набор правил (pattern matching, decision tree, конечный автомат), который по структурированному намерению определяет инструмент и валидирует параметры. Этот слой — обычный код. Его можно тестировать, версионировать, покрывать unit-тестами.
Слой 3: Исполнение и интеграция
Вызов инструмента, получение результата, передача обратно в LLM для формулировки ответа.
Что это даёт
- Детерминизм на критическом участке. Если правило говорит «арифметика → калькулятор», вызов произойдёт всегда — неважно, какая модель за слоем 1.
- Тестируемость. Маршрутизатор — это код. К нему пишут тесты: «при намерении X вызывается инструмент Y с параметрами Z».
- Модель-агностичность. Можно менять LLM-провайдера, не трогая маршрутизацию.
- Аудитируемость. Каждое решение — запись в логе с чётким обоснованием.
Когда детерминированный routing оправдан
Не везде. Для чатбота-игрушки статистический tool-calling приемлем. Но как только появляется хотя бы одно из условий — routing нужно выносить:
- Финансовые операции. Неправильный вызов API = потеря денег.
- Медицина и здоровье. Пропуск вызова диагностического инструмента — не «забавный баг».
- Продуктовые агенты. Пользователь платит за результат.
- Мультимодельные системы. Нужен единый маршрутизатор.
- Больше 10 инструментов. Точность статистического выбора падает нелинейно.
Реальные цифры
Исследование «Reducing Tool Hallucination via Reliability Alignment» (2025) показало, что даже frontier-модели при работе с 20+ инструментами допускают: 3–7% фантомных вызовов, 8–15% неверного выбора, 5–12% пропуска вызова.
Для агента, обрабатывающего 1000 запросов в день, это 50–300 ошибок. Каждый день.
Как это вписывается в экосистему MCP
Model Context Protocol стандартизировал обнаружение, описание и вызов инструментов. Но MCP не решает вопрос «кто принимает решение о вызове». По умолчанию — LLM.
Skilly-подход не противоречит MCP, а дополняет его. MCP описывает инструменты, маршрутизатор использует эти описания для построения правил. Это похоже на то, как в микросервисной архитектуре API Gateway не заменяет сервисы — он решает, какому сервису направить запрос. LLM — это сервис понимания языка. Маршрутизатор — это gateway.
Практические шаги
- Шаг 1. Аудит ошибок. Логируйте каждый tool-call: запрос → решение модели → результат. Через неделю посчитайте процент ошибок по трём категориям.
- Шаг 2. Классификация намерений. Составьте таблицу: «тип задачи → инструмент».
- Шаг 3. Промежуточный вариант — валидация. Модель решает, какой инструмент вызвать, а детерминированный код проверяет, разрешён ли этот вызов в данном контексте. Это уже устраняет фантомные вызовы.
- Шаг 4. Полный routing. Когда валидация покажет регулярные ошибки в выборе — переносите решение в код.
Заключение
LLM — инструмент понимания языка, а не диспетчер. Мы не просим базу данных решать, какой запрос выполнить. Маршрутизация — это инженерная задача, и решать её должен инженерный инструмент.
Skilly PGP — один из первых проектов, явно формализующих этот принцип. Архитектурный паттерн «детерминированный слой между намерением и вызовом» — это направление, в котором двинется вся индустрия агентных систем. Вопрос не в том, перейдём ли мы на детерминированный routing, а в том, сколько ошибок допустим, прежде чем перейдём.