К содержимому

Ассистенты без MCP

MCP — не единственный способ, которым ассистент вызывает инструменты. Если ваш ассистент работает на GigaChat, YandexGPT или на своей модели в своём контуре, он подключается через слой функций: каталог инструментов тот же, меняется только форма вызова.

Слой функций готовится. Ядро — перевод каталога в формат функций, разбор вызовов, защита от повторов — написано и покрыто тестами. Транспорт для этих клиентов и адаптеры под конкретные платформы пока нет: контракты вызова функций у провайдеров меняются, и писать их по памяти мы не станем. Нужна конкретная платформа — напишите в поддержку, мы уточним контракт и вернёмся с примером.

Как это устроено

Инструмент описан один раз: имя, параметры, область действия, нужно ли подтверждение. Адаптер переводит описание в форму, понятную конкретному клиенту, а проверки, защита от дублей, подтверждения и журнал остаются общими. Иначе поведение начнёт различаться, и документация станет врать одной из половин.

flowchart LR
    core["Каталог инструментов:<br/>проверки, дубли,<br/>подтверждения, журнал"]
    mcp["Нативный MCP<br/>stdio и HTTP"]
    fn["Формат функций<br/>GigaChat, YandexGPT,<br/>свои SDK"]
    tag["Разбор из текста<br/>локальные модели"]
    api["API Инвойсбокса"]
    core --> mcp
    core --> fn
    core --> tag
    core --> api

Что меняется в описании инструмента

  • Схема сводится к общему подмножеству. Плоские объекты, строки, числа, перечисления и массивы объектов — без anyOf и oneOf: часть платформ отбрасывает такое поле вместе со значением. Пара «единица измерения или её код» превращается в два необязательных поля, а согласованность проверяет сервер.
  • Описания короче. У провайдеров есть предел длины описания функции и поля; сервер отдаёт сжатый вариант, сохраняя главное.
  • Подтверждение только двухфазное. Метки «изменяет данные» в формате функций нет вовсе, и попросить человека посреди вызова платформа не умеет. Поэтому первый вызов возвращает сводку и одноразовый токен, а второй исполняет — то же, что в безопасности.
  • Описания по-русски. Русскоязычным моделям описания отдаются на русском: на переводе теряются «счёт-фактура» и «УПД», и модель начинает путать их с инвойсом.

Как выглядит функция

{
  "name": "create_order",
  "description": "Выставляет счёт покупателю и возвращает ссылку на оплату. Покупателем может быть организация, ИП или физлицо. Подтверждение в два шага: первый вызов возвращает сводку и confirmation_token, второй исполняет.",
  "parameters": {
    "type": "object",
    "additionalProperties": false,
    "required": ["description", "customer", "basket_items", "amount", "vat_amount", "currency_id", "expiration_date"],
    "properties": {
      "description": { "type": "string" },
      "amount": { "type": "string", "description": "сумма в копейках строкой: 12200 = 122,00 ₽" },
      "vat_amount": { "type": "string" },
      "currency_id": { "type": "string", "enum": ["RUB", "USD", "EUR", "GBP", "CNY"] },
      "customer": {
        "type": "object",
        "properties": {
          "type": { "type": "string", "enum": ["legal", "private"] },
          "name": { "type": "string" },
          "vat_number": { "type": "string" }
        }
      },
      "confirmation_token": { "type": "string" }
    }
  }
}

Локальные модели, которые вызывают инструмент разметкой

Модели семейства Hermes и родственные вставляют вызов в текст ответа, а сервер вывода разбирает его. Разбор ненадёжен, поэтому сервер отклоняет невалидное вместо того, чтобы дополнять догадками.

<tool_call>
{"name": "get_order", "arguments": {"order_id": "01771534-1a57-f184-dee3-ebeb91dded75"}}
</tool_call>
  • Невалидный JSON, выдуманное имя функции и аргументы массивом отклоняются с причиной.
  • Два одинаковых вызова в одном ответе — реальное поведение части моделей — сводятся в один: иначе получился бы второй счёт.
  • В контуре без авторизации и ролей права по умолчанию — только чтение. Запись включается явно, и ответственность за действия ассистента остаётся на владельце контура: сервер не отличает «модель ошиблась» от «оператор попросил» — кроме подтверждений и потолков.

Чего мы не поддерживаем

  • Прямое подключение модели к API Инвойсбокса без нашего слоя: тогда исчезают защита от дублей, подтверждения, потолки и журнал — то есть всё, что делает канал безопасным.
  • Автоматическое исполнение денежной операции «агентом без человека» — ни в одном протоколе, даже если платформа это позволяет.
  • Обещание совместимости с платформой, на которой мы не прогнали сквозной тест на живом ключе.