В нашей системе наблюдаемости раз за разом всплывала одна и та же ошибка вызова инструмента.
В одной выборке из продакшена мы насчитали десять таких случаев на быстром агентном пути с qwen3.6-35b-a3b. В каждом из них биндер аргументов .NET отклонял вызов ещё до запуска инструмента.
Трассировки показали отклонённые аргументы:
Схема ожидала:
Модель выбрала правильный инструмент и правильный источник данных. Валидный вызов от ошибки отделяла одна пара квадратных скобок.
Это классическая проблема интеграции с AI-агентами: намерение верное, данные почти верные, а детерминированная граница превращает небольшой промах в исключение. Интерфейс для агента должен подстраиваться под него настолько, насколько позволяет семантика. Остальная система по-прежнему должна получать одну каноническую форму.
Мы подстроились под модель
Напрашивалось объявить data_source_names как string | string[]. Но публичную схему мы оставили строгой: string[]. Остальные части системы не должны знать об особенностях конкретной модели.
Вместо этого мы добавили одну политику каноникализации на границе вызова функции:
- читаем JSON Schema инструмента;
- если схема требует
array<string>, а модель прислала скалярную строку, превращаем её в массив из одного элемента; - остальные значения не трогаем;
- записываем исправление в телеметрию и вызываем инструмент как обычно.
Для нас это и есть Agent Experience (AX). Схема остаётся точной, а граница вызова исправляет безобидное расхождение в форме данных.
Граница намеренно узкая. Мы не разбиваем строки по запятым, не переименовываем параметры и не приводим несвязанные типы. Каноникализация здесь безопасна, потому что "dotnet/eshop" и ["dotnet/eshop"] означают для инструмента одно и то же.
Результат
После добавления middleware мы повторно прогнали записанные вызовы через настоящий биндер фреймворка. Доля сбоев из-за расхождения между скалярной строкой и string[] составила 0%.
Middleware читает схему каждой функции, поэтому правило работает для любого параметра array<string>, а не для жёстко заданного списка имён.
Минимальная реализация для Microsoft Agent Framework
Middleware Microsoft Agent Framework перехватывает вызов функции до биндинга аргументов. Этот минимальный вариант превращает скалярную строку в массив только тогда, когда объявленная схема ожидает массив строк.
Когда каноникализации недостаточно
Каноникализация работает только тогда, когда обе формы означают одно и то же. Пропущенный обязательный параметр или объект с неправильной структурой не исправить без догадок, поэтому такие вызовы нельзя молча переписывать.
Вместо этого нужно вернуть структурированный результат валидации, сказать модели, что именно изменить, и дать ей повторить вызов. Такой repair-цикл станет темой следующей статьи.
Приложение: окружение примера
Код выше проверен с:
Microsoft.Agents.AI1.13Microsoft.Extensions.AI10.7
Документация: function-calling middleware в Agent Framework и FunctionInvokingChatClient.
