Apple Foundation Models完全ガイド: iOSアプリのオンデバイスLLM実践活用法#
なぜAppleはiOSの中にLLMを埋め込んだのか#
1年前のWWDC 2025で、AppleはApple Intelligenceを支えているのと同じオンデバイス言語モデルへのアクセスを、新しいフレームワーク Foundation Models を通じて開発者に開放しました。APIキーは不要、トークン課金もなく、ユーザーデータが端末から出ることもありません。モデルはすでに端末上にあり、iOS 26 / macOS 26 / iPadOS 26 / visionOS 26では、わずか数行のSwiftコードから呼び出せます。
これは単なる宣伝文句ではなく、アプリでどんな機能を作るべきかという判断そのものを変えました。私は現役のiOSエンジニアであり、この記事は基調講演の要約ではありません。実際に手を動かして検証したAPI、実際にぶつかる制約、そしてオンデバイスLLMがいつ正解でいつそうでないかについての率直な話です。
2026年半ばまでにフレームワークはもう一巡しています。WWDC 2026でAppleは第3世代のApple Foundation Models(AFM 3)を発表し、さらに同じSwift APIにサードパーティのLLMプロバイダーを接続できる仕組み——専用セッション「Bring an LLM provider to the Foundation Models framework」のテーマ——を別途公開しました。これは重要なシグナルです。Foundation Modelsは自社モデルの単なるラッパーから、オンデバイスとハイブリッド推論を統合するインターフェースへと変わりつつあります。
ベースとなるオンデバイスモデルは密なアーキテクチャで、パラメータ数はおよそ30億(Appleは現行世代でAFM 3 Coreと呼んでいます)。要約、エンティティ抽出、テキスト理解と推敲、短い対話、短い創作生成という特定のタスクセットに最適化されています。汎用チャットボットでも検索エンジンの代替でもありません——Appleのドキュメント自体が、世界に関する一般知識の情報源として設計されていないと明記しています。
一部の端末——現行世代では12GBのRAMを搭載したハイエンドiPhone——では拡張モデルが利用できます。AFM 3 Core Advancedは約200億パラメータのスパースアーキテクチャで、リクエストごとに10億〜40億パラメータのみを活性化します。開発者にとっては、同じ1行のコードでも端末によって明確に異なる「頭脳」が動くということを意味し、iPhone 15 ProとiPhone 17 Proで同じ品質の応答を期待すべきではありません。
モデルとのやり取りは2つの型が中心です。
SystemLanguageModel— モデル本体、その可用性、特化モード(useCase、例えばタグ付けやエンティティ抽出をすぐ使える.contentTaggingなど)へのエントリーポイント。LanguageModelSession— 会話セッション。実際にリクエストを送るのはここで、履歴(transcript)、指示、ツールを保持します。
Availabilityの分岐: 「動かない」理由は3つあり、同じ問題ではない#
Foundation Modelsについて書かれた記事にありがちな間違いは、利用不可を単一の状態として扱うことです。実際には SystemLanguageModel.default.availability は本質的に異なる3つの理由のいずれかを返し、これらを混同すると、確実に「壊れている」と感じる機能を出荷することになります。
import FoundationModels
func checkFoundationModelsAvailability() -> String {
switch SystemLanguageModel.default.availability {
case .available:
return "✅ モデルは利用可能です"
case .unavailable(.deviceNotEligible):
// A13以前 — この端末では今後も表示されない
return "❌ この端末はApple Intelligenceに対応していません"
case .unavailable(.appleIntelligenceNotEnabled):
// 対応端末だが設定でApple Intelligenceがオフ
return "⚠️ 設定でApple Intelligenceを有効にしてください"
case .unavailable(.modelNotReady):
// モデルがダウンロード中 — 一時的な状態
return "⏳ モデルをダウンロード中です。しばらくお待ちください"
case .unavailable(let reason):
return "❓ 利用不可: \(reason)"
}
}deviceNotEligible は恒久的な状態です。古いハードウェアでは機能を隠し、二度と催促しないでください。appleIntelligenceNotEnabled はユーザーが管理する設定であり、有効化を促す丁寧な案内は一度なら適切です。modelNotReady はダウンロード中の一時的な状態で、リトライが必要であり、エラー表示は不適切です。この3つを一つの汎用的な「利用不可」画面にまとめてしまうことが、「AIが壊れている」というユーザーの不満の最も多い原因です。
現時点で対応しているプラットフォームはiOS、iPadOS、macOS、visionOS。watchOSとtvOSはフレームワークの対象外です。
構造化出力: JSONを手でパースする代わりの@Generable#
Foundation Models以前は、構造化された出力を得るためにモデルにJSONを返すよう頼み、手でパースし、括弧の前後に余計なテキストが付かないことを祈るしかありませんでした。@Generable マクロはこのバグの種類そのものを取り除きます。コンパイル時にスキーマを生成し、モデルは実際のSwift型の値を返すことが保証されます。
import FoundationModels
@Generable
struct TripSummary {
@Guide(description: "旅行の短いタイトル、6語以内")
var title: String
@Guide(description: "旅行のハイライト", .count(3))
var highlights: [String]
@Guide(description: "気分の総合スコア(1〜5)")
var moodScore: Int
}
func summarizeTrip(notes: String) async throws -> TripSummary {
let session = LanguageModelSession(
instructions: "あなたは旅のメモを簡潔に要約するアシスタントです。"
)
let response = try await session.respond(
to: "ユーザーのメモ: \(notes)",
generating: TripSummary.self
)
return response.content
}@Guide はフィールドに対してモデル向けの自然言語の説明だけでなく、.count() や .maximumCount() といったプログラム的な制約も付与できます。これらは単に「お願いする」のではなく、生成空間を実際に狭めます。
ストリーミングとツール呼び出し#
レスポンシブなUIのために、構造化出力は部分ごとにストリーミングできます。@Generable が生成する型には、フィールドがすべてオプショナルになった「部分」バージョンが自動的に用意されます。
func streamTripSummary(notes: String) async throws {
let session = LanguageModelSession()
let stream = session.streamResponse(
to: "次のメモから旅行を要約してください: \(notes)",
generating: TripSummary.self
)
for try await partial in stream {
// partial: TripSummary.PartiallyGenerated — フィールドはオプショナルで
// 段階的に埋まっていく。ライブUI更新に便利
print(partial)
}
}またプロンプトにないデータ——例えばユーザーの現在地の都市——が必要な場合、Tool プロトコルで記述したツールをモデル自身が呼び出せます。
struct CurrentLocationTool: Tool {
let name = "getCurrentLocation"
let description = "ユーザーの現在の都市を返す"
@Generable
struct Arguments {
@Guide(description: "地区レベルの精度が必要かどうか")
var preciseArea: Bool
}
func call(arguments: Arguments) async throws -> ToolOutput {
let city = arguments.preciseArea ? "リスボン、アルファマ地区" : "リスボン"
return ToolOutput(city)
}
}
let session = LanguageModelSession(
tools: [CurrentLocationTool()],
instructions: "ユーザーの都市が必要なときはgetCurrentLocationを使ってください。"
)モデルはその説明文をもとに、ツールを呼ぶかどうかを自分で判断します。これは、多くの人が従来のNLPパイプラインで慣れ親しんできた手動のインテントルーティングとは根本的に異なる思考モデルです。
境界線はどこにあるか: コンテキスト、タスク、ハードウェア#
主な実用上の制約はセッションのコンテキストウィンドウのサイズです。オンデバイスモデルのコンテキストウィンドウ管理に関するAppleの技術ノートTN3193によれば、セッション全体の上限は指示・履歴・応答分の余地を含めておよそ4,096トークンです。GPTやClaudeのようなクラウドモデルと比べると桁違いに小さく、長い文書や長いチャット履歴、ボリュームのあるfew-shotプロンプトは1つのセッションに収まりません。要約する、削る、新しいセッションを作るといった対処が必要です。
上限に近づくと、正式に超える前にモデルが失敗し始めることがあります。応答を生成する余地が物理的に足りなくなるためです。実践的なルールとして、応答自体の余白を確保し、入力にウィンドウ全体を使い切らないことです。
2つ目の制約は品質です。汎用チャットボットでも世界知識の情報源でもありません。30億(あるいは疎な200億)パラメータのモデルは、すでにある文章を絞り込んで変換することには長けていますが、「〜について教えて」のようなオープンな質問には向いていません。3つ目はハードウェアの断片化です。拡張モデルのAFM 3 Core AdvancedはすべてのApple Intelligence対応端末で使えるわけではなく、十分なRAMがある端末に限られます。基本モデルへの縮退がフローを壊さないようUXを設計してください。
オンデバイスLLMかクラウドか: 正直に選ぶための表#
| 基準 | オンデバイス(Foundation Models) | クラウドLLM(API) |
|---|---|---|
| データプライバシー | データは端末から出ない | データはプロバイダーのサーバーに送られる |
| コスト | 無料、トークン課金なし | トークン/リクエストごとの課金 |
| オフライン動作 | ネットワークなしで動作 | 接続が必須 |
| レイテンシ | ネットワーク往復なしで低い | ネットワークとサーバーの混雑状況に依存 |
| 難しいタスクの品質 | 限定的(要約・抽出・短文) | 推論と知識で大幅に優れる |
| コンテキストウィンドウ | セッションあたり約4,096トークン | 数万〜数十万トークン |
| 利用可能性 | Apple Intelligence対応端末のみ | インターネットのあるあらゆる端末 |
| モデルのカスタマイズ | 重みのファインチューニング不可 | ファインチューニング、システムプロンプト、モデル選択が可能 |
この表からの実践的な結論は、オンデバイスLLMはプライバシー要件が高くコストゼロな、限定的で具体的なタスクのための道具であり、クラウドAPIの万能な代替ではないということです。
すべてのタスクにLLMが必要なわけではない——導入前のチェックリスト#
MeteoHealth(/projects/meteohealth/)——天候と体調を結びつけるアプリ——の開発では、私はMLやLLMではなく、端末上で動く古典的な統計エンジンをあえて選びました。気圧、湿度、ユーザーの症状の相関は決定論的な統計手法で計算されます。結果は再現可能で説明可能であり、生成テキストのトークンを一切消費しません。
これはリソース不足による妥協ではなく、タスクに応じた道具の意図的な選択です。オンデバイスであってもLLMは、「気圧がXhPa下がったら偏頭痛リスクが上がった」のような透明なロジックが必要な場面に予測不可能性を持ち込みます。Foundation Modelsは、入力が非構造化テキストで出力が構造化データまたはテキストであるタスク——メモの要約、エンティティ抽出、タイトル生成、アプリ内の短い対話アシスタント——に適しています。数値系列の決定論的な計算にはLLMは不要で、算術で十分な場所にLLMを持ち込むのは利益のない複雑さです。
Foundation Models導入前のチェックリスト#
unavailableの3つの理由をそれぞれ個別に確認する——それぞれ異なるUXが必要で、1つのエラー画面では済まない。- JSONを手でパースする代わりに
@Generable/@Guideを使う——バグの種類そのものをなくせる。 - セッションが失敗し始めてからではなく、事前にコンテキストウィンドウ(約4,096トークン)を見積もっておく。
- 30億パラメータのモデルに百科事典的な知識を期待しない——テキスト変換の道具であり、汎用チャットボットではない。
- LLMに頼る前に、生成モデルなしの普通のコードで解決できないか自問する。統計が生成に勝ることもある。
Foundation Modelsは話題性のためだけの流行ではありません。無料でプライベート、オフラインでも動くLLMを数行のSwiftコードで使えることは、開発者が長年望んできたことです。だからこそ、「うちにもAIがある」からではなく、本当に代替手段より優れている場面で的確に使うことが重要です。
参考リンク:



