У Foundation Models есть неприятная черта, которую не лечат ни промпты, ни архитектура: фреймворк работает только там, где включён Apple Intelligence. Устройство постарше, регион без поддержки, человек выключил функцию в настройках — и ваша фича просто отсутствует.
В iOS 27 обходной путь стал официальным. Протокол LanguageModel открыт, и модель, экспортированную через Core AI, можно подставить в ту же LanguageModelSession — с теми же промптами, инструментами и структурированным выводом. В этой статье собираю всю цепочку: от списка моделей в терминале до первого ответа в приложении.
Это третья часть; про сам Core AI и про новое в Foundation Models — отдельно. Требования: macOS 27, iOS 27 и Xcode 27 или новее.
Когда это вообще нужно#
Три причины, по которым Apple сама предлагает брать не свою модель:
- нужны специализированные возможности конкретной модели;
- нужна поддержка устройств, на которых Apple Intelligence нет;
- нужна кросс-платформенность — одна и та же модель у вас и на сервере, и в приложении.
Есть и четвёртая, неозвученная, но очевидная: встроенная модель обновляется вместе с ОС. Она уже менялась в 26.4 и снова в 27, и каждый раз Apple писала «перепроверьте промпты». Своя модель в бандле меняется тогда, когда вы этого захотели.
Достать модель: реестр и экспорт#
Экспортом занимается открытый Swift-пакет coreai-models — в нём и рецепты экспорта, и утилиты. Всё начинается в терминале: ставим пакетный менеджер uv, клонируем репозиторий и заходим в папку coreai-models.
Дальше смотрим, что вообще поддерживается:
uv run coreai.model.registry --list-models # модели реестра и пресеты экспортаВ выводе понадобится колонка HF_ID — это идентификатор для экспорта. Первую модель берите около 0.6B параметров: быстро качается и спокойно живёт на устройстве. Разбираться с квантизацией семибиллионника на этапе «работает ли вообще» — лишняя переменная.
Модели специализируются под то железо, на котором будут работать, поэтому платформа задаётся при экспорте:
uv run coreai.llm.export HF_ID # экспорт под macOS
uv run coreai.llm.export HF_ID --platform iOS # та же модель под iOSНа выходе — папка ресурсов: .aimodel плюс токенизатор и всё, что модели нужно. Её целиком кладём в приложение.
Отдельно загляните в models внутри coreai-models: у каждой модели свой README с точным рецептом и собственными требованиями. Универсальной команды «экспортируй что угодно» здесь нет, и это честнее, чем обещание, которое ломается на второй модели.
Подключить пакет#
CoreAILanguageModel живёт в том же coreai-models. Добавляется как обычная зависимость: File > Add Package Dependencies, ищем coreai-models, добавляем. В таблице продуктов напротив CoreAILM будет None — выберите там своё приложение, иначе пакет подключится, а модуль не появится. Это самый частый способ потратить двадцать минут на «почему не импортируется».
Собственно код#
Вся связка — четыре строки:
import FoundationModels
import CoreAILanguageModels
// Папка ресурсов, которую вы экспортировали и положили в бандл.
guard let modelURL = Bundle.main.url(forResource: "The model name",
withExtension: nil) else {
// Ресурс не найден.
return
}
// Загружаем модель и создаём сессию поверх неё.
let model = try await CoreAILanguageModel(resourcesAt: modelURL)
let session = LanguageModelSession(model: model)withExtension: nil — не опечатка: указывается папка, а не файл.
CoreAILanguageModel соответствует протоколу LanguageModel, поэтому сессия создаётся ровно тем же инициализатором, что и для встроенной модели. Дальше в коде фичи разницы нет вообще:
let response = try await session.respond(
to: "Выдели ключевые пункты из этой расшифровки встречи: \(meetingTranscript)."
)Стриминг, инструменты, @Generable, GenerationOptions — всё переносится без единой правки. Именно это и есть смысл упражнения: слой вашей фичи не знает, какая модель под ним.
Загрузка асинхронна, и это видно пользователю#
За try await в инициализаторе CoreAILanguageModel прячется реальная работа: перед первым запросом фреймворк компилирует модель и поднимает токенизатор. Вызовете его в момент, когда человек уже нажал кнопку, — человек и подождёт.
Рабочая схема — грузить заранее, когда до запроса остаётся хотя бы секунда-две: открылся экран, человек начал печатать, запустился сценарий. Там же имеет смысл прогреть и сессию через prewarm(promptPrefix:), чтобы инструкции и определения инструментов посчитались в KV-кэш до первого respond.
Если модель крупная, одной асинхронности мало — стоит идти в AOT-компиляцию coreai-build и в явное управление кэшем специализаций. Это материал первой статьи, и для языковых моделей там есть отдельная тонкость: expectFrequentReshapes в SpecializationOptions. У LLM длина последовательности растёт на токен за шаг, и оптимизация под каждую новую форму съедает больше, чем экономит.
Reasoning-модели ведут себя правильно из коробки#
Открытые модели с цепочкой рассуждений — отдельная головная боль при ручной интеграции: их промежуточный текст надо как-то отделять от ответа, и обычно это заканчивается регулярками по тегам.
Core AI распознаёт такой вывод сам и кладёт его в транскрипт отдельным сегментом рассуждений. В response.content он не попадает — человек видит только ответ. А вы можете прочитать рассуждения, когда разбираетесь, почему ответ получился странным.
Рассуждает модель или нет — зависит от того, что вы экспортировали, и проверяется явно:
if model.capabilities.contains(.reasoning) {
// Модель поддерживает рассуждения.
}Что мерить#
Core AI сам выбирает движок под устройство — GPU, CPU или Neural Engine, в зависимости от того, как модель экспортирована. Управлять этим напрямую из кода фичи не нужно, но проверить результат стоит.
Смотреть — в Instruments, инструментом Foundation Models: он показывает время загрузки ассетов, счётчики токенов и длительность каждого запроса. Первое, на что я бы посмотрел, — отношение кэшированных входных токенов к общему числу входных между ходами диалога. Если оно низкое, значит префикс пересчитывается каждый раз, и дело не в модели, а в том, как устроена сессия.
Чем платите#
Подведу без иллюзий. Вы получаете независимость от Apple Intelligence и контроль над версией модели. Взамен берёте на себя три вещи, которые раньше делала система: вес модели в поставке (или её скачивание и обновление), специализацию на устройстве с её задержкой при первом запуске, и качество — открытая модель на 0.6B не обязана быть лучше встроенной, и это надо проверять на своих данных, а не на ощущениях.
Поэтому порядок я бы держал такой: сначала встроенная модель и честная оценка фичи на ней. Core AI — когда упёрлись в доступность Apple Intelligence или в конкретную способность, которой у системной модели нет. Хорошая новость в том, что переход между этими вариантами стоит одну строчку, а не переписывание фичи.



