Учебник

Как Apple AI Agent связывает App, API и инструменты? Какую роль играет JSON?

Системный Agent идёт через App Intents, модель в App — через Tool Foundation Models. JSON не украшение, а общая форма для контрактов аргументов, структурированного вывода и HTTP-нагрузок.

«Apple AI Agent» часто воспринимают как название продукта. На практике вы столкнётесь как минимум с двумя совершенно разными цепочками вызовов: Apple Intelligence выбирает App и действие за пользователя, или ваш App запускает on-device модель, которая решает, какие инструменты вызывать. Обе цепочки касаются App, API и инструментов, но владелец сессии, кто генерирует аргументы и где появляется JSON — разные. Разделите эти слои, иначе у Schema, логов и отладки не будет опоры.

Сначала: кто запускает модель?

Начните с вопроса, кто инициировал этот вывод. Если человек говорит с Siri или системным Apple Intelligence, модель работает на стороне системы (на устройстве или Private Cloud Compute). Ваш App — лишь маршрутизируемая возможность. Вы открываете App Intents: типизированные действия, сущности и параметры. Системный Agent решает, когда вызывать, ваш код выполняет perform. Официальный вход: Документация App Intents.

Если пользователь уже в вашем App и вы запускаете LanguageModelSession Foundation Models, это другая цепочка: ваш процесс ведёт модель, вы внедряете инструменты. Система не выберет «какой App открыть», потому что сессия уже в App. Инструменты могут читать контакты, календарь, вызывать ваш сетевой API и писать результаты в transcript. Описание фреймворка: Foundation Models. Для вызова инструментов на уровне сессии см. WWDC25 Meet the Foundation Models framework.

Третий сценарий всё чаще встречается: внешний хост (Claude, ChatGPT, свой Agent) вызывает ваш сервис через MCP или обычный HTTPS. Модель не на стеке Apple, но нагрузка почти всегда JSON. Одни и те же доменные функции могут обслуживать App Intents, Foundation Models Tool и HTTP — отличаются только адаптеры.

Цепочка вызовов Кто запускает модель Что предоставляет App
Системный Agent Apple Intelligence App Intents / App Entity
Agent в App Ваш LanguageModelSession Протокол Tool и аргументы @Generable
Внешний Agent Сторонний хост JSON-RPC или REST JSON

Не сводите три цепочки к одной «универсальной Agent-схеме». При отладке сначала определите цепочку: сбой системной маршрутизации → декларации Intent и типы параметров; хаотичные вызовы инструментов в App → name, description и генерируемая форма Arguments; несовпадение внешнего API → HTTP JSON и Schema. Смешайте — и покажется, что «модель творит чепуху».

App Intents: как системный Agent достигает вашего App

Для системного Agent App — не «открыть и посмотреть», а каталог обнаруживаемых возможностей. Через AppIntent вы объявляете имена действий, описания на естественном языке, типы параметров и результаты. Spotlight, Shortcuts, Siri и Apple Intelligence делят этот каталог. Когда кто-то говорит «отметить этот счёт оплаченным», система должна сопоставить фразу с вашим MarkInvoicePaid, а не дать модели тыкать по интерфейсу.

Связка — в параметрах. Система превращает речь в типизированные значения: enum, даты, ссылки AppEntity. В Swift вы видите структуры, а не прозу. Между процессами и фреймворками этой структуре всё равно нужна сериализуемая форма — отладка, логи и воспроизведение на сервере заканчиваются JSON или эквивалентным списком свойств. Проектируйте параметры Intent как набор полей «записываемых в JSON-объект» — и адаптеры API станут дешевле.

Типичный компромисс: должен ли Intent бить в сеть? Короткие действия можно завершить в perform. Как только нужны auth, пагинация или идемпотентность, perform делает только «проверить параметры + вызвать доменный сервис»; сервис отправляет JSON-запрос. Системному Agent не нужен ваш REST-путь — ему нужна семантика успеха/неудачи Intent. Путь нужен вам: биллинг, аудит и retry живут на слое API.

Foundation Models: как модель в App вызывает инструменты

Связь Agent в App больше похожа на «дать модели спецификацию функций». Вы реализуете Tool: дайте name, description, плюс @Generable Arguments. Фреймворк пишет это в prompt, модель решает, когда вызывать. При вызове сначала генерируются аргументы, затем фреймворк выполняет call(arguments:) и вставляет возвращаемое значение (обычно String или генерируемый тип) обратно в transcript. Модель пишет финальный ответ. Вы не просите её выдумывать URL — вы просите выбрать из разрешённых инструментов.

Генерация аргументов — структурированный вывод, а не «ответьте в JSON». @Generable и динамические Schema фиксируют поля, enum и вложенные объекты на этапе декодирования. В Swift вы получаете типизированные экземпляры; для сохранения, логов или URLSession кодируйте в JSON. Apple описывает это как language understanding, structured output и tool calling — см. обзор Foundation Models.

struct FindOrders: Tool {
  let name = "findOrders"
  let description = "Find recent orders by customer email and status."

  @Generable
  struct Arguments {
    var email: String
    var status: String
    var limit: Int
  }

  func call(arguments: Arguments) async throws -> String {
    // Call your domain API, then return a compact summary.
    return "3 orders, latest is paid"
  }
}

В этом фрагменте модель не выдумывает имена полей. Она должна заполнить email, status, limit. description решает, будет ли выбран: слишком широкий — срабатывает на всё; слишком узкий — молчит, когда нужен. Держите вывод инструмента компактным — влить весь JSON заказа в transcript быстро съест окно контекста. Верните сводку и вызовите второй инструмент за id, если нужны детали.

Инструменты можно выстраивать в цепочку. Модель использует вывод первого как вход второго; фреймворк выполняет по порядку. Обеспечьте идемпотентность на доменном слое: отметить один и тот же orderId оплаченным дважды нельзя списать дважды. Цикл Agent не видит ограничений вашей БД. Валидные JSON-аргументы — не то же самое, что верный бизнес-результат.

Какую роль играет JSON

Типы Swift — контракт компиляции, JSON — контракт выполнения. Как только Agent выходит за пределы процесса — вызовы backend, файлы, другая модель, тестовые фикстуры — форма должна стать языконезависимым текстом. JSON играет на этой цепочке минимум три роли. Смешайте две — получите «парсится, но все поля неверны».

1. Формат обмена аргументами инструментов

Foundation Models представляет структурированные значения внутри как GeneratedContent. Самый полезный вид при отладке — «как этот Arguments выглядит в JSON». Имена, опциональность, массив или объект должны быть стабильны. Логируйте аргументы как JSON, чтобы сопоставить с логами доступа к API в продакшене: дошёл ли тот же customerId до сервера?

{
  "email": "ada@example.com",
  "status": "paid",
  "limit": 5
}

2. Schema для структурированного вывода

Даже без инструментов, если нужна только сводка по счёту, нужен Schema: обязательные ключи, значения enum, сумма как number или string. На стороне Apple используют @Generable; облачные модели обычно JSON Schema. Когда оба описывают один доменный объект, делите одну таблицу полей, чтобы App не говорил totalCents, а API — amount. Общие правила написания Schema: Understanding JSON Schema.

3. Нагрузка от App к API

Как только call инструмента касается сети, JSON — тело HTTP. Agent не заменяет проектирование API: заголовки auth, ключи идемпотентности и объекты ошибок — ваша зона. Модель заполняет только «бизнес-параметры»; транспорт, пагинация и rate limit — обычные backend-задачи. Оформляйте ошибки как стабильный JSON (code, message, retryable), чтобы модель могла сменить инструмент или объяснить сбой.

Три слоя могут делить один документ Schema: параметры Intent ⊂ Arguments инструмента ⊂ HTTP body. Подмножества упрощают тесты: один валидный JSON-фикстур, затем API, Tool, адаптер Intent. Надмножество (HTTP с тремя лишними внутренними полями) тоже допустимо, но не показывайте эти поля модели — иначе она начнёт «любезно» заполнять ключи, которые вы не хотели публиковать.

Подключение HTTP API и MCP

Когда инструмент в App вызывает REST, кодируйте явно. Не отправляйте GeneratedContent напрямую как Data. Сопоставьте с моделью Codable, затем JSONEncoder. Тогда вы контролируете raw values enum, форматы дат и стратегию ключей (snake_case). Модель производит доменные значения; кодировщик отвечает за детали протокола.

{
  "tool": "markInvoicePaid",
  "arguments": {
    "invoiceId": "inv_9f2",
    "paidAt": "2026-08-20T09:00:00Z"
  }
}

MCP превращает «имя инструмента + объект параметров» в JSON-RPC. Для разработчиков Apple это лишь третий адаптер: тот же markInvoicePaid(invoiceId:paidAt:), App Intent через perform, Foundation Models через Tool.call, MCP через tools/call. Не переписывайте бизнес-логику для MCP. Внешние Agents чаще шлют неверные типы (числа как строки), поэтому сервер всё равно должен валидировать — «уже JSON» не значит доверять.

Пишите границы приватности в описаниях инструментов. On-device модель, читающая календарь, не даёт права POSTить события на ваш сервер. Вывод для модели может быть локальной сводкой; JSON для загрузки собирайте только когда пользователь явно хочет синхронизацию. Политику вывода данных для системного и in-app Agent проверяйте отдельно и различайте источник в логах.

Как проверять JSON в продакшене

При интеграции Agent ещё один prompt менее полезен, чем три снимка рядом: JSON аргументов от модели, отправленный HTTP JSON и JSON от сервера. Если формы расходятся, баг почти всегда в слое сопоставления, а не «модель недостаточно умна». Форматируйте, валидируйте и сравнивайте локально — быстрее, чем смотреть на Optional в консоли Xcode.

Сначала прогоните образец параметров в браузере: Форматирование JSON чтобы подтвердить parse; Проверка JSON чтобы поймать хвостовые запятые и неверные типы; закрепите общие поля Intent / Tool / API с JSON Schema; затем JSON Diff чтобы сравнить «вывод модели» и «фактическое тело запроса». Cloud Structured Output отличается по API, но «сначала контракт, потом разбор» то же — см. Как Gemini API выводит JSON.

Держите три файла фикстур: tool-args.valid.json, http-body.valid.json, http-error.json. В CI валидируйте первые два одним Schema. Фикстур ошибки проверяет, говорит ли Agent пользователю понятную причину сбоя, а не проглатывает retryable: true.

Частые вопросы FAQ

Могут ли App Intents и Tool Foundation Models делить одни параметры?

Делите доменную модель, но не предполагайте, что система напрямую выполнит ваш протокол Tool. Intent — для системного обнаружения и разрешений; Tool — для prompt текущей сессии. Поставьте слой сопоставления между ними. JSON-фикстуры тестируют доменную модель, а не тип одного фреймворка.

Почему не дать модели выводить полный HTTP-запрос?

URL, заголовки и подписи не должны выдумываться моделью. Она заполняет бизнес-поля; клиент отправляет фиксированный шаблон. Иначе одна галлюцинация ударит по неверной среде или с неверной авторизацией.

Конфликтуют ли JSON и @Generable?

Нет. @Generable — генерация и декодирование на стороне Swift; JSON — форма между языками и сетями. Сначала стабилизируйте таблицу полей, затем генерируйте Swift-макросы и JSON Schema.

Должны ли возвращаемые значения инструментов быть JSON?

Не обязательно. Модель может видеть краткую текстовую сводку; сервер должен получать JSON. Не смешивайте оба вывода в одной строке для жёсткого parse.

Итог и следующие шаги

Agents на стеке Apple — не одна розетка, а три комбинируемые линии: системный Agent находит и вызывает App через App Intents; модель в App вызывает ваш код через Tool; внешний хост вызывает тот же доменный сервис через JSON. JSON заставляет аргументы, Schema и API-нагрузки говорить одной формой. Типобезопасность закрывает compile time, Schema — runtime, бизнес-проверки — истину.

Дальше: перечислите три–пять доменных действий. Для каждого напишите минимальный JSON-объект и Schema, затем решите, где оно появится — Intent, Tool или HTTP. Стабилизируйте форму, прежде чем добавлять prompts и оркестрацию нескольких инструментов.