Перейти к содержимому
29 дек. 2025 г.·7 мин чтения

Частично верные аргументы в tool calling: как чинить

Разберем, как исправлять частично верные аргументы в tool calling: repair loop, проверку по полям и безопасную передачу сложных случаев человеку.

Частично верные аргументы в tool calling: как чинить

Что ломается при частично верных аргументах

Самый неприятный сбой в tool calling выглядит почти безобидно. Модель выбирает правильный инструмент, отдает нормальный JSON, большая часть полей заполнена верно. Ломается одно поле, иногда два, и этого уже достаточно, чтобы вызов стал опасным или бесполезным.

Такое происходит постоянно. ID заявки модель берет из диалога верно, а приоритет додумывает сама. Или находит правильный город доставки, но подставляет не ту дату, потому что в переписке было несколько вариантов. Снаружи это похоже на мелкую неточность. Для системы это уже неверная команда.

Проблема в том, что ошибка редко бывает грубой. Модель не пишет явную ерунду. Она смешивает факты из сообщения пользователя со своей догадкой. Поэтому обычные проверки вроде "JSON валиден" или "tool выбран правильно" не ловят сбой. В логах все почти нормально, а бизнес-операция уходит не туда.

Чаще всего ломаются самые чувствительные места: статус обновляется не у той сущности, заявка создается с неверной категорией, в CRM уходит чужой email или телефон, сумма, дата или тариф берутся не из текста, а из предположения модели.

Из-за этого частично верные аргументы опаснее, чем полный отказ. Когда модель совсем не поняла задачу, система хотя бы останавливается. Когда она ошиблась на 10 процентов, вызов часто проходит дальше и портит данные, историю действий и отчеты.

Обычный retry тут почти не помогает. Если отправить тот же диалог и тот же schema prompt еще раз, модель нередко повторит ту же ошибку. Иногда она меняет формулировку, но сохраняет неверное поле, потому что источник проблемы никуда не делся. В контексте все так же есть двусмысленность, а модель все так же пытается достроить пропуск.

Хуже всего, что такие сбои плохо видны в верхнеуровневых метриках. Вы увидите высокий процент правильного выбора tool и нормальный формат ответа, но пользователи все равно будут жаловаться. Причина обычно одна: система проверяет сам факт вызова, а не смысл каждого аргумента внутри него.

Откуда берутся такие ошибки

Частично верные аргументы обычно появляются не из-за одной большой поломки, а из-за нескольких мелких причин сразу. Модель видит неполный текст, старается собрать корректный JSON и угадывает там, где ей не хватило данных.

Одна из частых причин - похожие поля. Если в схеме рядом стоят city, branch_id, office_name и delivery_point, модель легко смешивает их между собой. Пользователь пишет: "Нужен филиал в Самаре, кажется 42", а в ответе получается внешне аккуратный JSON с правильным городом и случайным идентификатором филиала.

Ошибки часто тянутся из прошлого шага. Это особенно заметно в длинных диалогах, где пользователь сначала назвал один адрес, потом передумал, а затем уточнил только часть данных. Модель может подставить новый city, но оставить старый branch_id, потому что он уже был в черновике ответа. Формально структура проходит, по смыслу - нет.

Есть и более неприятный случай: формат верный, а смысл сломан. Поле выглядит валидным, регулярное выражение прошло, тип данных тоже. Но номер филиала может относиться к другому городу, дата может не совпадать с часовым поясом, а код продукта может быть устаревшим. Такая ошибка опаснее явной, потому что базовая валидация ее не замечает.

Иногда проблема во входных данных. Пользователь пишет: "Хочу доставку в офис на Ленина, это вроде филиал 17". Если в системе два офиса на Ленина, а филиал 17 вообще в другом городе, модель все равно попытается собрать ответ до конца. Она не любит оставлять поле пустым, если ее к этому явно не приучили.

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

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

Как проверять поля по отдельности

Когда модель выдает частично верные аргументы, не проверяйте объект целиком по принципу "прошел или не прошел". Такой подход прячет источник ошибки. Гораздо полезнее разбирать каждый аргумент отдельно и сразу понимать, что именно сломано: тип, формат, диапазон или смысл значения.

Для каждого поля задайте простое правило. У поля должны быть тип, допустимый диапазон, список разрешенных значений или шаблон формата. Например, priority принимает только low, medium, high, amount должен быть числом больше 0, email должен быть строкой нужного формата. Без этой базы repair loop быстро превращается в угадайку.

Потом разделите поля на обязательные и необязательные. Если модель не заполнила customer_id, вызов лучше не пускать дальше. Если она пропустила comment, это не всегда повод отклонять весь запрос. Такая граница заметно снижает число лишних отказов и помогает включать fallback на человека только там, где без данных нельзя действовать безопасно.

Проверка по полям не заканчивается на JSON-схеме. Корректный JSON еще не значит, что значение годится. delivery_date: "2035-01-01" может быть синтаксически верным, но бессмысленным для срочной заявки. region: "Москва" может пройти как строка, хотя в вашей системе нужен код msk. Смысловую проверку лучше писать рядом с формальной, а не откладывать на потом.

Полезно сохранять причину отказа по каждому полю. Достаточно короткого кода и понятного текста:

  • missing_required
  • wrong_type
  • out_of_range
  • invalid_enum
  • semantic_mismatch

Это нужно не только для отладки. Когда вы видите, что ломается ровно одно поле, не просите модель переписать весь объект. Верните ей только проблемный аргумент, его текущее значение, правило проверки и ожидаемый формат. Так вы не потеряете уже верные данные и снизите шанс, что модель испортит то, что до этого было в порядке.

В поддержке это выглядит просто. Модель собрала заявку, верно заполнила тему, канал и текст обращения, но ошиблась в account_type. Вместо полного перегенерирования вы просите исправить только это поле по списку допустимых значений. Если вы ведете аудит через RU LLM, удобно сохранять такие отказы и исправления в одном журнале запроса. Потом команда быстро видит, какие поля ломаются чаще всего и где надо менять промпт, схему или сам инструмент.

Как собрать repair loop по шагам

Рабочий repair loop не просит модель собирать весь объект заново. Он берет первый ответ как черновик, проверяет его по полям и сохраняет то, что уже прошло валидацию. Такой подход особенно полезен, когда в объекте много правильных значений и всего пара ошибок.

Сначала сервис принимает первый вызов инструмента, парсит аргументы и прогоняет каждый атрибут через один и тот же набор правил. Если поле прошло проверку, система его замораживает. Модель больше не должна его менять. Иначе вы сами стираете правильные данные на каждом новом шаге.

Представьте заявку в поддержку: ticket_type и priority верны, phone пришел без кода страны, а visit_date указывает вчерашний день. Нет смысла просить модель пересобрать всю заявку. Ей нужно вернуть только два проблемных поля и точное объяснение, что не так.

Базовый цикл

Сообщение на исправление должно быть коротким и жестким. Укажите, какие поля надо поправить, почему текущие значения не подходят и что остальные поля менять нельзя. Лучше просить не новый объект, а маленький патч.

Исправь только поля: phone, visit_date.
Не меняй остальные поля.
Причины ошибок:
- phone: нужен формат +7XXXXXXXXXX
- visit_date: дата не может быть в прошлом
Верни только JSON с этими двумя полями.

Это снижает шанс, что модель снова перепишет уже верные части. Repair loop работает лучше, когда задача маленькая: одна ошибка формата, один неверный enum, одно пустое поле. Чем меньше свободы у модели, тем ровнее цикл.

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

Лимит попыток нужен с самого начала. На практике часто хватает двух итераций, иногда трех. Полезно ограничить и общее время цикла, например 5-10 секунд. Если модель не починила поля в этих рамках, случай лучше отдать человеку, а не крутить бесконечный диалог.

Хороший repair loop чинит только сломанное, не трогает остальное и каждый раз проверяет результат по тем же правилам. За счет этого ошибка не расползается по всему объекту, а остается локальной и управляемой.

Когда передавать случай человеку

Оставьте SDK без правок
Смените base_url и продолжайте работать с тем же кодом и промптами.

Если частично верные аргументы повторяются, автоматику лучше остановить раньше, чем позже. Обычная граница - две или три неудачные правки подряд. После этого модель чаще не исправляет ошибку, а просто меняет ее форму: подставляет похожее значение, теряет одно поле или начинает додумывать то, чего в запросе не было.

Есть ситуации, где ждать вообще не стоит. Если спорное поле влияет на деньги, доступ или персональные данные, оператор нужен сразу. Сумма возврата, смена тарифа, выдача прав, номер телефона, паспортные данные, адрес доставки - не те места, где модель может угадывать.

Когда автоматика должна остановиться

  • Модель уже 2-3 раза правила аргументы, но проверка снова находит ошибку.
  • Два источника данных противоречат друг другу, и система не может выбрать один без риска.
  • Пользователь не дал явного подтверждения, а без него нельзя продолжать.
  • Поле затрагивает оплату, права доступа или персональные данные.

Конфликт между источниками - частая причина передачи человеку. Например, CRM хранит один номер договора, а текст обращения указывает другой. Если система выберет любой из них сама, она может открыть не тот аккаунт или изменить не ту заявку. В такой точке лучше спросить человека, чем еще раз пытаться починить вызов.

Еще одно правило простое: не давайте модели угадывать там, где нужен четкий ответ пользователя. Если в заявке нет даты, суммы или номера заказа, repair loop должен запросить подтверждение или передать случай оператору. Правдоподобные домыслы потом дают самые дорогие ошибки.

Что должен увидеть оператор

Человеку нужен не только финальный отказ. Дайте ему короткий, но полный контекст:

  • исходный запрос пользователя
  • аргументы первого вызова и все последующие правки
  • результат проверки по полям
  • причину, по которой автоматика остановилась
  • источник данных, где возник конфликт

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

Пример на заявке в поддержку

Клиент пишет в чат: "Нужно перевыпустить карту. Заберу ее в Новосибирске". Модель делает почти все правильно: выбирает tool для перевыпуска карты, подставляет номер клиента и причину обращения, но берет branch_id из старого профиля. Этот офис относится к Казани, а не к Новосибирску.

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

Как ловится ошибка

Проверка по полям не должна ограничиваться форматом. Мало убедиться, что branch_id вообще существует. Нужно проверить связку полей: город есть в справочнике, офис с таким branch_id тоже есть, и этот офис относится к указанному городу.

Первые два теста пройдут. Третий сразу покажет конфликт: город в тексте клиента один, а офис привязан к другому. Значит, чинить надо не всю заявку, а только одно поле.

Repair loop в таком случае работает точечно. Система не просит модель сгенерировать аргументы заново, потому что так легко испортить уже верные значения. Она отправляет короткое уточнение: текущий branch_id не подходит для города Новосибирск, выбери офис только из списка филиалов этого города. Остальные поля менять нельзя.

Обычно этого хватает. Модель возвращает новый branch_id, и вторая проверка подтверждает, что город и офис теперь совпадают. После этого tool можно вызывать без лишнего риска.

Когда нужен оператор

Если модель второй раз присылает старый офис, пустое значение или branch_id, которого нет в справочнике, автоматизацию лучше остановить. Оператору стоит передать исходный текст клиента, первую версию аргументов, результат проверки и исправленную попытку, если она была.

В проде такой сценарий лучше логировать отдельно. Если вызовы идут через RU LLM, удобно сохранять исходные и исправленные аргументы в аудит-трейле запроса. Потом проще разбирать спорные случаи и править промпт или правила проверки.

Ошибки, которые часто допускают команды

Проверьте российский контур
Используйте data residency, маскирование PII и аудит для запросов с персональными данными.

Первая частая ошибка - чинить весь объект целиком, когда сломано одно поле. Модель заново собирает JSON и вместе с неверной датой вдруг меняет город, тариф или номер заказа, который раньше был правильным. Так команда сама расширяет сбой.

Надежнее замораживать валидные поля и отправлять в repair loop только то, что не прошло проверку. Если сомнение есть в delivery_date, не трогайте customer_name и order_id. Это заметно снижает шум и упрощает разбор инцидентов.

Вторая слабая точка - проверка только по форме. Строка может выглядеть нормально, но по смыслу быть неверной. Дата проходит regex, хотя день уже в прошлом. ИНН имеет нужную длину, но не сходится по контрольным цифрам. Город написан без ошибок, но не относится к выбранному региону.

Поле стоит проверять в два слоя: сначала формат, потом смысл. Смысловая проверка обычно опирается на справочники, бизнес-правила и контекст самой заявки. Иначе tool calling выглядит аккуратно только на бумаге.

Еще одна проблема - система прячет причину отказа. Модель получает сухое validation failed, а оператор видит "ошибка обработки". В таком режиме никто не понимает, что именно сломалось: пустое поле, конфликт значений или рискованное действие.

Полезнее возвращать короткую и точную причину по каждому спорному полю: какое поле не прошло проверку, что именно ожидалось, что пришло фактически, можно ли исправить автоматически и нужен ли человек. Такой ответ помогает и модели на следующем шаге, и оператору в очереди.

Еще команды часто оставляют retry без жесткого лимита. В итоге модель три, пять, десять раз повторяет почти один и тот же вызов, а логи забиваются мусором. Цена растет, причина неясна, пользователь ждет.

Обычно хватает 2-3 попыток. После этого сценарий должен остановиться: запросить уточнение у пользователя или передать случай человеку.

И последнее: не все поля одинаково опасны. Опечатку в названии города можно поправить автоматически. Сумму платежа, реквизиты, паспортные данные или юридически значимый статус лучше вынести в отдельную группу и не чинить без подтверждения. Когда команды смешивают безопасные и рискованные поля, мелкая ошибка быстро становится дорогой.

Короткий чек-лист перед запуском

Проверьте repair loop в проде
Подключите RU LLM и ведите tool calling через один OpenAI-совместимый эндпоинт.

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

Если поле можно заполнить, для него должно быть правило и источник истины. Имя клиента можно сверять с CRM, email - с шаблоном и карточкой аккаунта, дату - с парсером и часовым поясом. Поле без такого правила быстро превращается в догадку модели.

Перед запуском достаточно пройтись по пяти пунктам:

  • Для каждого обязательного поля есть явная проверка: формат, диапазон, список допустимых значений или связь с другим полем.
  • Лог пишет, какое поле не прошло проверку, какое значение пришло и почему система его отклонила.
  • Repair loop ограничен по числу попыток. На практике обычно хватает 2-3 циклов.
  • Есть порог для передачи человеку: если не прошло обязательное поле, конфликтуют два источника или цена ошибки слишком высока.
  • Оператор видит исходный аргумент, предложенную правку и короткую причину, чтобы быстро принять или отклонить исправление.

Без этих правил repair loop быстро начинает ходить по кругу. Модель меняет одно поле, ломает другое, а в логах остается только факт повторной попытки. Потом команда чинит уже не саму ошибку, а последствия.

Полезно прогнать короткий сценарий руками. Допустим, модель создает заявку в поддержку и верно вытаскивает номер договора, но путает приоритет обращения. Валидатор должен отклонить только поле priority, вернуть понятную причину и отправить модель на точечную правку, а не заставлять ее собирать весь JSON заново.

Если вы работаете в среде с жесткими требованиями к аудиту, сохраняйте цепочку решений целиком: исходный вызов, ошибки по полям, каждую попытку исправления и момент передачи оператору. Такой журнал потом экономит часы при разборе инцидента. Для команд, которым важны логи и хранение следов обработки внутри РФ, RU LLM может быть удобной базой: у сервиса уже есть встроенные аудит-трейлы по каждому запросу и OpenAI-совместимый эндпоинт без смены SDK и кода.

Хороший признак готовности простой: любой спорный кейс можно открыть и за минуту понять, что именно пошло не так, кто это исправил и почему система приняла такое решение.

Что делать дальше в проде

В проде не стоит сразу покрывать repair loop всеми инструментами. Начните с одного tool, где ошибка дорого стоит, но поток уже понятен команде. Так вы быстро увидите, какие поля модель путает чаще всего: ID клиента, дату, тип запроса, приоритет или канал связи.

Если у вас уже есть частично верные аргументы в tool calling, не спорьте с моделью на уровне "вызов хороший или плохой". Разберите сбои по полям. Часто 80 процентов пользы дает не новый промпт, а простая статистика: какое поле ломается, после какой подсказки, в каком сценарии и сколько правок нужно до успеха.

Что мерить

Не смотрите только на общий процент успешных вызовов. Он почти всегда скрывает проблему. Гораздо полезнее считать отдельно:

  • долю вызовов, где repair loop исправил аргументы без человека
  • долю повторных ошибок после первой правки
  • долю случаев, которые система отдала на ручной разбор
  • среднее число правок на один успешный вызов
  • поля, которые чаще других доходят до fallback

Эти цифры быстро показывают, где вы теряете время и деньги. Если модель почти всегда чинит формат даты со второй попытки, это нормальный кандидат для автоматического ремонта. А если после двух правок она все еще путает номер договора и номер заявки, лучше не упорствовать и сразу звать оператора.

Отдельно храните аудит по каждому вызову и по каждой правке. Сохраняйте исходные аргументы, результат проверки по полям, текст repair-подсказки, новую версию аргументов и финальное решение системы. Без такого следа команда спорит по ощущениям. С ним можно разбирать реальные сбои, обновлять правила и видеть, стало лучше или нет.

Даже если промпты заметно улучшились, fallback на человека нужно оставить. Прод - это не про красивое демо, а про контроль ошибок. Простое правило работает лучше всего: после N неудачных правок, при конфликте критичных полей или при низкой уверенности валидатора система сразу передает случай человеку.

Хороший запуск выглядит скучно: один tool, понятные метрики, полный аудит и живой человек на подхвате. Обычно именно такая схема переживает реальную нагрузку.

Часто задаваемые вопросы

Почему частично верные аргументы опаснее полного отказа?

Потому что система видит не "почти верно", а конкретную команду. Если модель угадала branch_id, дату, сумму или email, вызов уходит дальше и портит данные. Полный отказ обычно безопаснее: вы сразу останавливаете действие и не меняете запись зря.

Поможет ли обычный retry, если одно поле неверное?

Обычно нет. Если в диалоге осталась двусмысленность или схема подталкивает модель додумать поле, повторный запрос часто возвращает ту же ошибку. Лучше проверить спорное поле отдельно и отправить модели точную правку, а не весь объект заново.

С чего начать проверку аргументов по полям?

Начните с простых правил для каждого поля: тип, формат, диапазон, enum или связь с другим полем. Потом разделите поля на обязательные и необязательные. Если обязательное поле не прошло проверку, не запускайте tool.

Хватит ли одной JSON-схемы для защиты?

Нет. JSON-схема ловит форму, но не смысл. delivery_date может иметь верный формат и при этом указывать прошлый день, а branch_id может существовать, но относиться к другому городу. Поэтому держите рядом и формальную, и смысловую проверку.

Как должен работать repair loop?

Не просите модель пересобирать весь объект. Возьмите первый ответ как черновик, заморозьте валидные поля и верните только спорные. В сообщении на исправление укажите, что именно сломано, почему это не подходит и что остальные поля менять нельзя.

Зачем замораживать поля, которые уже прошли валидацию?

Потому что модель легко ломает то, что раньше заполнила верно. Если вы снова откроете весь JSON для правки, она может исправить дату и одновременно заменить город, тариф или ID. Заморозка держит уже проверенные значения на месте.

Сколько попыток давать модели на исправление?

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

Когда лучше сразу звать оператора?

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

Что писать в логах и аудите по таким сбоям?

Сохраняйте исходный запрос, первую версию аргументов, каждую правку, результат проверки по полям и причину остановки. Тогда команда быстро видит, где модель путает смысл, а оператор понимает, что исправлять без разбора с нуля. Если вы ведете вызовы через RU LLM, такой след удобно держать в одном аудит-трейле.

Что мерить в проде, чтобы видеть реальную проблему?

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