Учебник
Как 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 и оркестрацию нескольких инструментов.