Мой блог
Универсальные инструменты для LLM: как избавиться от привязки к фреймворкам
Разработчикам, интегрирующим большие языковые модели в свои проекты, постоянно приходится переписывать функции-инструменты под разные экосистемы вроде AI SDK, Genkit или MCP. В этой статье Сергей Багров рассматривает концепцию независимых инструментов, использующих открытые спецификации Standard Schema и Standard Tool, которые позволяют написать логику один раз и запускать её с любой моделью или фреймворком.
Чтобы предоставить нейросети доступ к коду, создаются специальные инструменты (написанию инструментов). Базово такой инструмент представляет собой функцию вместе с набором метаданных: уникальным именем, текстовым описанием и схемой принимаемых аргументов. Однако по мере развития проекта или смены технологического стека ту же самую логику приходится реализовывать повторно.
Проблема привязки к фреймворкам
Первоначальная версия функции может быть написана через функцию tool() из экосистемы AI SDK. Когда в архитектуре появляется MCP-сервер, те же самые процедуры переписывают с использованием метода registerTool. Если соседняя команда разработчиков предпочитает Genkit, те же задачи оборачивают уже в третий раз через метод экземпляра defineTool.
Проблема заключается в том, что каждая подобная обёртка жестко привязана к конкретной программной платформе. Метод tool() импортируется из пакета ai, defineTool принадлежит экземпляру Genkit, а registerTool является частью MCP SDK. Сторонней библиотеке, которая хочет поставлять готовые инструменты вместе со своим кодом, приходится выбирать единственный целевой фреймворк, что заставляет каждого конечного пользователя устанавливать именно его.
Если отказаться от навязываемых фреймворками ограничений, инструмент превращается в самодостаточную функцию, которая описывает собственную структуру: исполняемый код, наименование, текстовое пояснение, а также схемы входных и выходных параметров. Модели вполне достаточно этих сведений, чтобы самостоятельно определить целесообразность и способ вызова. Аналогичной информации хватает для автоматической генерации документации, построения пользовательских интерфейсов или добавления команд в интерфейс командной строки.
Роль Standard Schema и Standard JSON Schema
Наиболее сложной частью в описании любого инструмента всегда выступают схемы данных, но сегодня они уже имеют стандартизированные спецификации. Создание единого интерфейса валидации позволило унифицировать работу с различными библиотеками без написания громоздких адаптеров.
Что такое Standard Schema
Интерфейс Standard Schema был спроектирован совместными усилиями авторов таких популярных библиотек валидации, как Zod, Valibot и ArkType. Код, завязанный на этот спецификационный стандарт, успешно взаимодействует со схемами из любой поддерживающей его библиотеки.
Вся спецификация сводится к единственному специальному свойству ~standard. Процесс проверки данных выглядит идентично для любого инструмента независимо от выбранной библиотеки валидации, а результирующий тип автоматически извлекается из выходной схемы.
Данную спецификацию на текущий момент реализуют более тридцати различных библиотек, включая Zod, Valibot, ArkType, yup и joi. Принимают её свыше шестидесяти проектов, среди которых выделяются tRPC, TanStack Form, Hono, Elysia, oRPC и React Hook Form. Поскольку спецификация состоит исключительно из TypeScript-типов и не содержит рантайм-кода, сторонние разработчики могут просто скопировать интерфейс в свой проект без добавления лишних внешних зависимостей.
Что такое Standard JSON Schema
Валидация входящих параметров — лишь одна из задач, решаемых схемами. Вторая важнейшая функция заключается в генерации JSON Schema, поскольку перед вызовом инструмента языковая модель должна получить его схему в понятном текстовом формате.
Спецификация Standard JSON Schema добавляет в то же самое свойство ~standard специальный конвертер. Параметр target позволяет гибко выбирать нужный диалект JSON Schema под требования конкретных потребителей. Например, OpenAI, Anthropic и протокол MCP рассчитаны на стандарт JSON Schema draft 2020-12, тогда как модель Gemini в поле parameters ожидает формат OpenAPI 3.0.
Разделение схем ввода (input) и вывода (output) обусловлено тем, что схема способна преобразовывать данные «на лету». Если входящее строковое значение вроде «42» трансформируется в числовое 42, то у входа и выхода будут совершенно разные JSON Schema.
Архитектура Standard Tool
Когда схемы берут на себя валидацию данных и генерацию JSON Schema, от самого инструмента остаются лишь имя, подробное описание и исполняемая функция. Поскольку для этой оставшейся логики долгое время не существовало единого стандарта, каждый фреймворк изобретал собственные структуры.
Описание интерфейса StandardToolV0
Предложение StandardToolV0 задает универсальный формат для описания подобных объектов. Поле name выполняет роль идентификатора, по которому языковая модель обращается к инструменту. Поле description подробно объясняет нейросети назначение функции и условия её применения, а необязательное поле title служит человекочитаемым названием для интерфейсов клиентов.
Свойства inputSchema и outputSchema обязаны реализовывать обе рассмотренные ранее спецификации, выполняя одновременно проверку данных и генерацию схем. Поле meta хранит статические параметры вроде флага деструктивного действия { destructive: true }, которые читаются внешними потребителями, но не передаются в исполняемую функцию.
Сама функция execute отвечает за запуск логики. Необязательный аргумент контекста передает специфичные данные текущего вызова, такие как пользовательский токен авторизации или языковая локаль. В пакете также предусмотрена необязательная эталонная реализация, которая автоматически проверяет входящие и выходящие данные.
Сравнение с фреймворками
Аналогичные объекты тултекита присутствуют практически в каждой современной экосистеме, однако они отличаются названиями полей и позициями аргументов. Серьезные расхождения наблюдаются в том, какие именно форматы схем принимают те или иные библиотеки.
Например, AI SDK поддерживает Standard Schema, Zod и чистые JSON Schema, в то время как MCP SDK работает исключительно со схемами Zod. Из-за этих барьеров объекты из разных экосистем остаются не взаимозаменяемыми, и для переноса инструмента между фреймворками приходится переписывать обёртки.
Практическое использование универсальных инструментов
Созданный по единому стандарту инструмент имеет множество потенциальных потребителей помимо самой языковой модали. Исполняемую функцию можно напрямую вызвать из обычного программного скрипта или автоматического теста.
Поля объекта содержат достаточно информации для автоматического построения справочной документации, формирования динамических списков инструментов в системном промпте или генерации интерактивных CLI-команд. Сами инструменты легко экспортируются из библиотек в виде обычных значений, позволяя пользователям запускать их без установки тяжелых AI-фреймворков.
Аналогичным образом интегрируются готовые RPC-процедуры из tRPC или oRPC. Если их внутренние схемы поддерживают Standard JSON Schema, они автоматически превращаются в полноценные инструменты для языковых моделей.
Интеграция с моделями и итоги
Любая интеграция с внешними провайдерами сводится к двум базовым шагам: формированию описания инструмента на основе метаданных и последующему запуску функции execute при получении запроса от модели. Написав универсальный адаптер под конкретного провайдера один раз, разработчик избавляется от необходимости вносить правки в сами инструменты при смене или обновлении моделей.
Инструмент, спроектированный как самоописывающаяся функция, остается надежной частью кодовой базы. Предложенный подход избавляет разработчиков от жесткой привязки к конкретным экосистемам и делает код гибким и переносимым.
