Foundation Modelsには、プロンプトでも設計でも直せない厄介な性質があります。このフレームワークはApple Intelligenceが有効な環境でしか動きません。少し古いデバイス、対応していない地域、あるいは設定でオフにされている — それだけで、あなたの機能はただ存在しないことになります。
iOS 27で、その迂回路が公式になりました。LanguageModelプロトコルが開かれ、Core AIでエクスポートしたモデルを同じLanguageModelSessionに入れられます。プロンプトもツールも構造化出力もそのままで。この記事では、ターミナルでモデル一覧を出すところからアプリで最初の応答を受け取るところまで、鎖を最後までたどります。
これは第3回です。Core AI本体とFoundation Modelsの新機能は別稿にあります。要件はmacOS 27、iOS 27、Xcode 27以降。
そもそも、どんなときに要るのか#
Apple自身が挙げている、他社のモデルを持ち込む理由は三つです。
- そのモデルにしかない専門的な能力が必要なとき
- Apple Intelligence非対応のデバイスをサポートしたいとき
- クロスプラットフォームで揃えたいとき — サーバーとアプリで同じモデルを使う
明言されていないけれど明らかな四つめもあります。組み込みモデルはOSと一緒に更新されます。26.4で一度変わり、27でまた変わり、そのたびにAppleは「プロンプトを検証してください」と書きました。自分のバンドルに入れたモデルは、自分が決めたときに変わります。
モデルを手に入れる — レジストリとエクスポート#
エクスポートを担うのはオープンソースのSwiftパッケージcoreai-modelsで、エクスポートのレシピとユーティリティの両方が入っています。出発点はターミナルです。パッケージマネージャuvを入れ、リポジトリをクローンし、coreai-modelsディレクトリへ移動します。
次に、何がサポートされているかを見ます。
uv run coreai.model.registry --list-models # レジストリ対応モデルとエクスポートのプリセット出力で要るのはHF_IDの列です。エクスポート時に使う識別子です。最初のモデルは0.6B規模あたりにしてください。ダウンロードが速く、デバイス上でも無理なく動きます。「そもそも動くのか」を確かめている段階で70億パラメータの量子化と格闘するのは、変数が一つ多すぎます。
モデルは動かす先のハードウェアに合わせて特殊化されるので、プラットフォームはエクスポート時に指定します。
uv run coreai.llm.export HF_ID # macOS向けにエクスポート
uv run coreai.llm.export HF_ID --platform iOS # 同じモデルをiOS向けに出てくるのはリソースのフォルダです。.aimodelに加えてトークナイザなど、そのモデルに必要なものが入っています。このフォルダごとアプリに追加します。
coreai-modelsの中のmodelsディレクトリにも目を通してください。モデルごとにREADMEがあり、正確なレシピとそのモデル固有の要件が書かれています。「何でもエクスポートできる」万能コマンドはありません。二つめのモデルで破綻する約束よりは、そのほうが誠実です。
パッケージをつなぐ#
CoreAILanguageModelはそのcoreai-modelsの中にあります。追加は普通の依存関係と同じです。File > Add Package Dependenciesでcoreai-modelsを検索し、追加します。製品の一覧でCoreAILMの横がNoneになっているので、そこで自分のアプリを選んでください。選ばないとパッケージは付くのにモジュールが現れません。「なぜimportできないのか」で20分を溶かす定番の道です。
コード本体#
橋渡しは4行で済みます。
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 — すべて一行も直さずに移ります。この作業の意味はまさにそこで、機能のレイヤは下にどのモデルがいるかを知りません。
ロードは非同期で、それは利用者に見える#
CoreAILanguageModelのイニシャライザのtry awaitは、実際の仕事を抱えています。最初のリクエストの前に、フレームワークはモデルをコンパイルしトークナイザを立ち上げます。誰かがボタンを押したその瞬間に呼べば、その誰かが待つことになります。
使える形は、リクエストまで1〜2秒はあるタイミングで先に読み込むことです。画面が開いた、入力が始まった、フローが動き出した。同じ場所でセッションもprewarm(promptPrefix:)で温めておけば、指示とツール定義が最初のrespondより前にKVキャッシュへ入ります。
モデルが大きい場合は非同期だけでは足りません。coreai-buildの事前コンパイルと、特殊化キャッシュの明示的な管理が要ります。そこは第1回の領域で、言語モデルにはさらに固有の勘どころがあります。SpecializationOptionsのexpectFrequentReshapesです。LLMは系列長が1トークンずつ伸びるため、形状ごとの最適化が取り返すより多くを食います。
推論モデルは最初から正しく振る舞う#
思考の連鎖を出力するオープンモデルは、手で統合すると面倒です。途中のテキストを答えから切り離さなければならず、たいていタグへの正規表現に行き着きます。
Core AIはその出力を自分で見分け、トランスクリプトへ推論セグメントとして流します。response.contentには入らないので、利用者に見えるのは答えだけです。それでいて、答えが妙だった理由を調べたいときには推論のほうを読めます。
モデルが推論するかどうかはエクスポートした内容次第で、明示的に確認できます。
if model.capabilities.contains(.reasoning) {
// このモデルは推論に対応している。
}何を測るか#
Core AIはデバイスに合わせて実行エンジンを自分で選びます。GPU、CPU、Neural Engineのどれかは、モデルをどうエクスポートしたかで決まります。機能側のコードから操作するものではありませんが、結果は確認する価値があります。
見る場所はInstrumentsのFoundation Models instrumentです。アセットのロード時間、トークン数、リクエストごとの所要時間が出ます。私が最初に見るのは、ターン間でのキャッシュ済み入力トークン÷入力トークン総数です。これが低ければ接頭部が毎回計算し直されているということで、問題はモデルではなくセッションの組み方にあります。
何を支払うか#
取引について幻想は持たずに。得られるのはApple Intelligenceからの独立と、モデルのバージョンに対する主導権です。引き換えに、これまでシステムがやっていた三つを引き受けます。配布物の中でのモデルの重さ(あるいはそのダウンロードと更新)、初回起動のレイテンシを伴うデバイス上の特殊化、そして品質 — 0.6Bのオープンモデルが組み込みより良い保証はどこにもなく、それは印象ではなく自分のデータで確かめるものです。
ですから私が保つ順序はこうです。まず組み込みモデルで、機能を正直に評価する。Core AIは、Apple Intelligenceの利用可否の壁に当たったとき、あるいはシステムのモデルに無い能力が要るとき。救いは、この二つを行き来する費用が書き直しではなく一行だということです。



