Apple Foundation Models:オンデバイスLLMフレームワークを解説する
Foundation Models フレームワークを使えば、アプリは Apple Intelligence を支えるのと同じオンデバイスの大規模言語モデルに、無料かつオフラインで直接アクセスできます1。API キーも、トークンごとの課金も、ネットワークの往復も、デバイス外へ出るデータもありません。かつてはクラウドのLLMとプライバシーレビューを意味していた一群の機能が、いまやコストはほぼゼロに丸め込まれます。その代償が能力です。オンデバイスモデルは小さく、コンテキストウィンドウは有限で、フレームワークは「何をして何をしないか」をはっきりと線引きしています。その境界線を知ることが、すべてなのです。
本稿はフレームワークそのもののリファレンスです。実際に呼び出す型、これを使う価値を生む唯一の機能、そして手を止めてもっと大きなものに頼るべき地点を扱います。
要点(TL;DR)
LanguageModelSessionが入口です。1つ作ってrespond(to:)を呼べば、テキストが返ってきます。複数ターンのコンテキストはセッションの中に保持され、単発の処理には毎回新しいセッションを与えます2。- ガイド付き生成こそ、このフレームワークを使う理由です。 Swift の型に
@Generableを付ければ、自分でパースしなければならない文字列ではなく、値が埋められ型チェック済みのその型がモデルから返ってきます3。 Toolプロトコルを使うと、モデルが生成の途中であなたのコードを呼び出してデータを取得したりアクションを実行したりし、その結果を回答へ織り込めます4。- 何かをする前に必ず
SystemLanguageModel.default.availabilityを確認しましょう。対象外のデバイス、Apple Intelligence がオフの状態、あるいはモデルのダウンロード中には、モデルは存在しません5。 - コンテキストウィンドウは現実に存在し、しかも小さいものです。
SystemLanguageModel.default.contextSizeが、プロンプトとレスポンスで共有されるトークン予算を報告します6。これを見越して設計しなければ、セッションは例外を投げます。 - iOS 26 と Apple Intelligence 対応デバイスが必要です。その下限を下回れば、フレームワークそのものが存在しません。
このフレームワークが何であり、何でないか
Foundation Models はクラウドのエンドポイントを包んだラッパーではありません。モデルはデバイス上に存在し、OS とともに出荷され、Neural Engine 上で動作します。このたった1つの事実が、API のあらゆる設計判断と、あなたがそれを使ってくだすあらゆる判断を方向づけます。
得られるもの——テキスト生成、要約、分類、抽出、短文のリライト、そして構造化出力。いずれもオンデバイスで、いずれも無料です。得られないもの——フロンティアモデルです。Apple がこのオンデバイスモデルを作ったのは、アプリの中で完結する焦点の定まった言語タスクのためであって、際限のない推論でも、長大な文書の分析でも、知識を問いただせる世界知識のためでもありません。Apple 自身がそう述べており、この位置づけは重要です。なぜなら、放っておけばAPIが踏み越えさせてしまう期待値を、ここで定めてくれるからです1。
トラブルを避けるための心構えはこうです。オンデバイスモデルを、速くて、プライベートで、無料のインターンだと思いましょう。文章を整えるのは抜群にうまいが、事実を知るのは絶望的に苦手な存在です。素材と明確なタスクを手渡してください。答えようのない問いを投げてはいけません。
LanguageModelSession:入口
すべてのやり取りはセッションから始まります。
import FoundationModels
let session = LanguageModelSession()
let response = try await session.respond(to: "Summarize this review in one sentence: \(reviewText)")
print(response.content)
セッションは会話の状態を保持します。respond(to:) を呼ぶたびに進行中のトランスクリプトへ追記されるので、保持し続けたセッションはそれ以前のやり取りを覚えています。チャット機能では、まさにそれが望むものでしょう。互いに独立した単発のタスク(これを要約する、あれを分類する)では、呼び出しごとに新しいセッションを作りましょう。そうすれば古いコンテキストが紛れ込んでトークン予算を食いつぶすことがありません2。
respond(to:) は async throws です。モデルが処理する間は中断し、リクエストがコンテキストウィンドウを超えたとき、モデルが利用できないとき、あるいはガードレールが内容を拒否したときには例外を投げます。そのどれもが、見過ごしてよい例外ケースではなく、あなたが処理すべき現実の分岐です。
応答性の高い UI のためには、待つのではなくストリーミングしましょう。streamResponse(to:) はモデルが生成するそばから部分的な出力を返すので、3秒間の停止が、形になりながら現れていくテキストへと変わります7。
ガイド付き生成:フレームワークの元を取らせる機能
ここからが、入場料に見合う部分です。たいていのLLM統合は、コードの3分の1をモデルから妥当なJSONを引き出すための手なずけに費やし、残りの3分の2を、それでも失敗したときの防御に費やします。Foundation Models はその作業を丸ごと消し去ります。
Swift の型に @Generable を付け、セッションにそれを生成するよう頼めば、値が埋められ型安全なその型のインスタンスがモデルから返ってきます3。
@Generable
struct Recipe {
@Guide(description: "The dish name")
let title: String
@Guide(description: "Ingredients, each as 'quantity item'")
let ingredients: [String]
@Guide(description: "Total minutes, start to finish", .range(5...240))
let minutes: Int
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "A weeknight pasta for two.",
generating: Recipe.self
)
let recipe = response.content // a Recipe, not a String
パースは不要です。JSONDecoder も要りません。不正な出力のためのリトライループも不要です。@Guide マクロは個々のフィールドを制約します——モデルが指示として読む説明文と、数値範囲や出力が一致すべき正規表現といった任意の制限です8。フレームワークは 5 から 240 の間の数字をモデルに丁寧にお願いするのではなく、デコードそのものを制約して、そのフィールドが他の形では返ってこないようにします。
ここで強いられる規律こそが、本当の価値です。出力の型を、まず Swift で、コンパイラのチェックを受けながら設計するのです。モデルは、あなたがリバースエンジニアリングする散文を返すのではなく、あなたが定義した契約を満たします。抽出、フォーム入力、そして言語をデータへ変えるあらゆる機能において、ガイド付き生成はデモと出荷可能なコードの違いそのものです。
知っておく価値のある制御が1つあります。respond(to:generating:) は includeSchemaInPrompt を既定で true にしており、これがあなたの型の形をプロンプトへ注入してモデルをそちらへ寄せます。モデルが学習段階から、あるいはセッションの前のターンから既にその形式を知っている場合を除き、有効のままにしておきましょう。モデルがまだ見たことのない形式について、トークン節約のためにこれをオフにすると、ゴミが返ってくるのが落ちです9。
ツール呼び出し:モデルにあなたのコードへ手を伸ばさせる
ガイド付き生成は、出てくるものを形づくります。ツール呼び出しは、入っていくものを変えます。ツールとは、モデルが持っていない情報を取得したりアクションを実行したりするために、生成の途中で呼び出せるあなたのコードの一片であり、その結果を使って回答を続けます4。
ツールは Tool プロトコルに準拠します——name、モデルがいつ呼ぶか判断するために読む description、@Generable な Arguments 型、そして実際の処理を行う call(arguments:) メソッドです4。
struct FindContacts: Tool {
let name = "findContacts"
let description = "Find a specific number of contacts from the address book"
@Generable
struct Arguments {
@Guide(description: "How many contacts to return", .range(1...10))
let count: Int
}
func call(arguments: Arguments) async throws -> [String] {
// Fetch contacts, return formatted names.
}
}
let session = LanguageModelSession(tools: [FindContacts()])
let response = try await session.respond(to: "Draft a dinner invite to three of my contacts.")
流れはこうです。モデルが連絡先を必要だと判断し、検証済みの count を伴ってあなたのツールを呼び、あなたがデータを返すと、モデルが実際の名前を使って招待状を書きます。引数はガイド付き生成と同じ仕組みを通って型チェック済みで届くので、自由文からモデルの意図をパースする必要は一切ありません。ツールの説明文は、モデルがそれに手を伸ばすかどうかを左右する唯一のレバーです。だから、何の前提も持たない別のエンジニアが読んで正しく使えるよう、関数のドキュメントのように書きましょう。
ここはまた、Foundation Models がエージェントという物語の残り全体と出会う継ぎ目でもあります。オンデバイスモデルが呼ぶツールと、Apple Intelligence が呼ぶ App Intent は、面こそ違えど同じ形をしています——名前と説明と型を持つ能力です。能力を一度設計すれば、両方を通じて公開できるのです。
可用性:飛ばせない確認
モデルは常にそこにあるわけではありません。Apple Intelligence に対応していないデバイスでは存在せず、ユーザーがオフにしているときも、OS がまだモデルのアセットをダウンロードしている間も存在しません。モデルが存在する前提でコードを出荷すれば、テストしたこともないユーザー層に対して、クラッシュするか、静かに劣化するか、ハングします。
SystemLanguageModel.default.availability を確認し、その理由ごとに分岐しましょう5。
switch SystemLanguageModel.default.availability {
case .available:
// Show the intelligence feature.
case .unavailable(.deviceNotEligible):
// Hide it. This device will never have the model.
case .unavailable(.appleIntelligenceNotEnabled):
// Prompt the user to turn on Apple Intelligence.
case .unavailable(.modelNotReady):
// Downloading or otherwise not ready yet. Try again later.
case .unavailable(let other):
// Unknown reason. Fail closed.
}
3つの理由は、3つの異なる製品上の対応を求めます。これらを一緒くたにすることが、こうした機能が壊れて感じられる最も多い原因です。deviceNotEligible は恒久的です——機能を隠し、しつこく催促しないこと。appleIntelligenceNotEnabled はユーザーが握る設定です——一度きりの案内なら妥当でしょう。modelNotReady は一時的です——リトライし、エラーを表示しないこと。利用不可の経路も、うまくいく経路と同じ気配りで作りましょう。実際に一定の割合のデバイスにとっては、それが唯一の経路なのですから。
モデルが利用可能で、リクエストが来るとわかっているときは、セッションの prewarm() がモデルを温めておき、最初の本番レスポンスがより速く着地します10。ユーザーがまさに操作しようとしている画面では価値がありますが、当てずっぽうに呼べば無駄になります。
コンテキストウィンドウ、そしてそれでは足りなくなる地点
SystemLanguageModel.default.contextSize は、モデルが内側で働くトークン予算を報告します。そしてこの予算は共有です——プロンプトとレスポンスを合わせて収まらなければなりません6。その数値はクラウドモデルに比べれば小さく、現実の入力ではすぐに痛感します。長い文書、すべてを含むチャット履歴、肥大したツールの結果——どれもが予算を吹き飛ばし、respond に例外を投げさせ得ます。
そこから2つの失敗モードが続き、どちらもあなたが防ぐべきものです。1つ目はじわじわ進む浸食です。複数ターンのセッションがトランスクリプトを溜め込み、もう1ターンで溢れてしまう。これは、無関係な作業には新しいセッションを始め、1ターンあたりの入力を絞ることで管理します。2つ目は、単発の過大なリクエストです。20ページの PDF は収まりません、それきりです。チャンクに分け、各チャンクを要約し、その要約の上で推論する(LLMエンジニアにはおなじみの map-reduce です)か、あるいはそのタスクがオンデバイスモデルには向かない形なのだと受け入れましょう。
コンテキストウィンドウは、このフレームワークで本当に大切な判断——オンデバイスに留まるか、それとも離れるか——のための、最もきれいなシグナルです。
Foundation Models を使うべきでないとき
このフレームワークは無料で、プライベートで、オフラインです。だからこそ、どこにでも手を伸ばしたくなります。こらえましょう。次のときは、これを越えて手を伸ばしてください。
- 本物の推論や、世界知識の広がりが必要なとき。 オンデバイスモデルは設計からして小さいのです。際限のない推論、コード生成、深い分析は、フロンティアのクラウドモデルの領分です。それらをオンデバイスモデルに求めれば、自信満々の、間違った答えが返ってきます。
- 入力がコンテキストウィンドウに収まらず、チャンク分割では意味が壊れてしまうとき。すべてを一度に見渡す必要があるタスクもあります。
- 自分で制御できるモデルが必要なとき——特定のチェックポイント、ファインチューン、カスタムの重み、OS アップデートをまたいだ決定的なバージョン管理。Apple はモデルを、あなたの都合ではなく自社の予定で出荷し更新します。
- iOS 26 未満、あるいは対象外のデバイスにいるとき。 フレームワークはそもそもそこに無く、可用性の確認が実行のたびにそう告げます。
フレームワークがカバーしないオンデバイスのケース(カスタムモデル、独自の重み、デバイス上での学習)には、その下の層である Core ML と Apple の MLX があります。本当にスケールが必要なケースには、プライバシー境界の向こうにあるクラウドのLLMが、いまも誠実な答えです。Foundation Models はそのどちらの代わりにもなりません。これは、すでに手元にあるテキストへの、焦点の定まった言語作業に対する正しい第一の選択肢であり、それ以外のすべてに対しては誤った選択肢です。
このフレームワークが報いるスキルは、プロンプトの技巧ではありません。スコープに対する審美眼です——モデルが得意とするタスクを与えること、必要なものをちょうど捉える @Generable 型を設計すること、そして作業がデバイスの手に余る瞬間を見抜くこと。そうした勘どころで作れば、オンデバイスモデルは驚くほど多くの実作業を無料でこなします。それを無視すれば、入力がたった1トークン長すぎたすべてのユーザーで壊れる機能を、出荷することになります。
-
Apple Developer, “Foundation Models” framework overview. Apple はこのフレームワークを、Apple Intelligence を支えるオンデバイスモデルへのアクセスとして説明し、際限のない推論や世界知識ではなく、テキスト生成・要約・分類・構造化出力といった焦点の定まった言語タスクに適すると位置づけています。 ↩↩
-
Apple Developer, “LanguageModelSession” および “Generating content and performing tasks with Foundation Models”. セッションは複数ターンのコンテキストを保持します。Apple のガイダンスは、それぞれ独立した単発のやり取りごとに新しいセッションを作ることです。 ↩↩
-
Apple Developer, “Generable” および “Prompting an on-device foundation model”.
@Generableマクロにより、フレームワークは文字列ではなく、値が埋められ型チェック済みの Swift の値を返せます。 ↩↩ -
Apple Developer, “Tool” protocol.
protocol Tool<Arguments, Output>: Sendableを定義し、必須のname、description、parameters: GenerationSchemaに加えてcall(arguments:) async throws -> Outputを持ちます。Arguments型はConvertibleFromGeneratedContentに準拠し、通常は@Generableとして宣言されます。 ↩↩↩ -
Apple Developer, “SystemLanguageModel.Availability” および その
UnavailableReason. ケースは.availableと.unavailable(...)で、理由にはdeviceNotEligible、appleIntelligenceNotEnabled、modelNotReadyがあります。SystemLanguageModel.default.isAvailableが便利なブール値です。 ↩↩ -
Apple Developer, “SystemLanguageModel.contextSize”. (
SystemLanguageModel.defaultを通じて到達する)インスタンスプロパティで、最大コンテキストサイズ——入力プロンプトと生成レスポンスを合わせた総トークン数——として文書化されています。 ↩↩ -
Apple Developer, “LanguageModelSession.streamResponse(to:)”. モデルが生成するそばから部分的な出力をストリーミングし、UI の逐次更新に使えます。 ↩
-
Apple Developer, “Guide(description:_:)”.
@Generableプロパティに自然言語の説明と任意の制約(数値範囲、正規表現によるガイド)を付与するピアマクロです。iOS 26.0+ が必要です。 ↩ -
Apple Developer, “respond(to:schema:includeSchemaInPrompt:options:)”.
includeSchemaInPromptは既定でtrueです。Apple の解説は、モデルが期待される形式を既に知っている場合を除き、既定のままにすることを推奨しています。 ↩ -
Apple Developer, “LanguageModelSession.prewarm()”. 来ると分かっているリクエストに先立ってモデルのリソースを読み込むようフレームワークに依頼し、初回レスポンスの遅延を減らします。 ↩
-
著者による関連分析:Appleの Foundation Models によるオンデバイスLLM、Foundation Models 向けカスタムアダプター、Foundation Models のユースケース、そして Foundation Models 上のエージェント型ワークフロー。App Intents とツール面の議論は App Intents はあなたのアプリへの Apple の新しいAPIである で展開しています。 ↩