Блог • AI / App Intents

Как на самом деле течёт JSON App Action, когда iOS 27 открывает больше действий Siri? От фразы к вызову API

Команда Siri выполняется, потому что сначала становится параметрами App Intent, а AppIntent в perform() решает, остаться на устройстве или вызвать ваш API. Система не пересылает сырую фразу на сервер.

Является ли Siri агентом, написано в предыдущей статье. Эта следует за одной командой: как фраза выбирает Intent, как параметры становятся JSON и почему API вызывается только после проверки.

Сначала запомните: Речь — не тело запроса. Заполненные параметры Intent — да. Дальше: фраза, тип параметра, id сущности, ваш JSON, затем API.

Пять шагов одной команды

Официальный вход — App Intents. См. Начало работы с App Intents. Siri, Команды и кнопка в приложении используют один Intent. Они различаются тем, кто заполняет параметры.

  1. 1
    Совпасть с фразой

    Говоримые фразы вы регистрируете в AppShortcutsProvider. Система выбирает по ним Intent. Она не отдаёт сырую фразу модели на импровизацию.

  2. 2
    Заполнить параметры

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

  3. 3
    Разрешить сущность

    Данные пользователя разрешает EntityQuery по id. В параметре лежит сущность, а не абзац, который её описывает.

  4. 4
    Выполнить

    В perform() вы читаете уже разрешённые значения. Если устройство может закончить действие, закончите его там.

  5. 5
    Ответить Siri

    Верните результат через IntentResult или диалог. У ошибки всё равно должна быть фраза, которую можно услышать. Не оставляйте её только в журнале сервера.

Три вида параметров

Типы описаны в Добавление параметров к app intent. Неверный тип сделает дальнейший JSON нестабильным.

ТипДля чегоВ JSON
Строка, число, датаПростой ввод вроде количества или заметкиСкопировать в parameters как есть
AppEnumНабор, фиксированный при компиляции, например самовывоз или доставкаХранить значение перечисления. Не использовать текст показа как ключ
AppEntityМеняющиеся данные пользователя, например один товарХранить только разрешённый id

Фиксированный набор — это AppEnum. Список, который человек меняет, — AppEntity плюс запрос. Если меняющийся список сделать перечислением, фразы перестанут совпадать, как только вариант устареет.

Сущность оставляет только id

Системе нужна сущность, которую можно показать и запросить. База данных остаётся источником. У сущности должны быть хотя бы стабильный id и читаемое имя.

Поля сущности, которые можно записать после разрешения. id — ключ, который потом использует API.
{
  "entity": "OrderItem",
  "id": "sku_42",
  "display": "Oat milk"
}

Реализуйте entities(for identifiers:) и загружайте объект по id. Если его нет, не придумывайте id и не продолжайте.

JSON параметров, который принадлежит вам

Документ ниже — контракт, который вы сохраняете в perform() до решения об API. Это не формат запроса Apple, и система не прочитает файл за вас.

Один разрешённый заказ. resolved равен true только после успешного запроса сущности.
{
  "intent": "StartOrder",
  "source": "siri",
  "parameters": {
    "item_id": "sku_42",
    "quantity": 1
  },
  "resolved": true
}

Когда вызывать API

  1. 1
    Закончить на устройстве

    Действие, которое меняет только локальное состояние, заканчивается в perform(). Сеть ему не нужна.

  2. 2
    Сначала проверить

    Если нужен сервер, сначала проверьте JSON схемой. Нет item_id или quantity меньше 1 — остановитесь на устройстве.

  3. 3
    Затем в сеть

    Отправляйте parameters в свой API только после проверки и если resolved равен true. Сервер ищет свою запись по id. Отображаемому имени он не доверяет.

  4. 4
    Сказать об ошибке понятно

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

Как стороннее приложение объявляет Intent и возвращает ошибку — следующая статья. Эта останавливается на пути данных.

Чем это отличается от старых статей

Siri — это агент?Эта статья
ВопросПланирует ли система сама и вызывает ли инструментыКакой JSON проходит одна команда на пути к API
Не ищите здесьЕщё один анонс продуктаСырую фразу как тело запроса, которое можно сохранить

Продуктовый рассказ об оборудовании и системных функциях — в почему iPhone 18 больше, чем обновление железа. Там поля параметров не разбираются.

Проверить этот JSON в JSONNote

  1. 1
    Форматировать

    Откройте запись параметров в форматировании JSON и убедитесь, что нет висячей запятой.

  2. 2
    Остановить пропущенные поля схемой

    Вставьте схему ниже в JSON Schema. Нет intent или item_id, либо quantity не положительное целое — в API это не отправляют.

  3. 3
    Затем проверить один пример

    Вставьте настоящий пример в проверку JSON. Особенно смотрите, не пустая ли строка item_id.

JSON Schema записи параметров. source допускает только siri, shortcut и app.
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": false,
  "required": ["intent", "source", "parameters", "resolved"],
  "properties": {
    "intent": { "type": "string", "minLength": 1 },
    "source": { "enum": ["siri", "shortcut", "app"] },
    "resolved": { "type": "boolean" },
    "parameters": {
      "type": "object",
      "required": ["item_id", "quantity"],
      "properties": {
        "item_id": { "type": "string", "minLength": 1 },
        "quantity": { "type": "integer", "minimum": 1 }
      }
    }
  }
}

Вопросы

Система отправляет всю фразу на мой сервер?

Не по этому пути. Сервер должен получить JSON параметров, который вы собираете и проверяете в perform(). Речь остаётся в распознавании на устройстве.

Почему в параметрах нет названия товара?

Названия меняются и совпадают. AppEntity использует стабильный id, запрос загружает объект по нему. API должен принимать только id.

Если схема прошла, можно вызывать API?

Смотрите ещё resolved. Верная форма при неразрешённой сущности значит, что человек ещё не выбрал объект. Вызов API превратит уточнение в неверный заказ.

Как разделить локальную работу и сетевой вызов?

То, что меняет только локальные данные, заканчивается в perform(). То, чему нужен ваш бэкенд, пишет JSON параметров, проверяет его и затем вызывает. Оба пути возвращают результат через IntentResult.

Команда с параметрами, заполненными вручную, идёт тем же путём?

Да, это тот же AppIntent. Разница в том, кто заполняет параметры: Siri по фразе, Команды — человек или предыдущее действие. Разрешение сущности, JSON и затвор API те же.

Итог

Путь одной команды: фраза выбирает Intent, параметры заполняются по типу, сущность сжимается до id, вы пишете JSON, API вызывается только после проверки, и диалог возвращает результат Siri.

Прямой вывод: не загружайте сырую фразу. Не вызывайте API, пока id не разрешён. Скажите об ошибке человеку, а не оставляйте её только в журнале сервера.

Дальше: зафиксируйте этот JSON параметров схемой, прежде чем подключать его к perform().

Назад в блог