
【WWDC25】 セッションメモ 「Meet the Foundation Models framework」 でAppleのオンデバイスLLMについて学ぶ
はじめに
iOSアプリエンジニアのリルオッサです。
WWDC25では新しい情報がAppleから発信されているので、セッションを色々とみながらメモしていき、皆さまに共有していきたいと思います。
詳細について気になる方はぜひ公式の動画をみてください。
Meet the Foundation Models framework
概要
Apple Intelligenceを支えるオンデバイスの大規模言語モデルを活用する方法を学びましょう。この概要セッションでは、Swiftのデータ構造のガイド付き生成、応答性の高い体験を実現するストリーミング、コンテキスト管理のためのデータソースとセッションの統合を可能にするツール呼び出しなど、このフレームワークの機能を幅広く網羅しています。このセッションには、参加のための前提条件はありません。
動画
簡単まとめ
- オンデバイス、オフラインで使用できるLLMが登場
#Playgroundでトライ&エラーを素早く繰り返しプロンプトを向上させよう- 生成モデルのレスポンスを指定した型で受け取ることができる
- 段階的にレスポンスを受け取ることができ、それを描画できる
- なんだか可能性無限大なすごく楽しそうなフレームワーク
- Apple Intelligence対応デバイスでないと使用できない
メモ

新しく登場するFoundation Modelsフレームワークを使って、デバイス上のLLMにアクセスできるようになります!

Foundation Modelsの嬉しいポイント
- オンデバイス実行
- オフライン実行
- OS自体に組み込まれているため、アプリサイズが大きくならない
- モデルに出入りするデータのプライベートに保たれる
あとは、無料で使えるというのも開発する側にとっては嬉しいのではないでしょうか。
最小実装は、たったこれだけです。
import FoundationModels
let session = LanguageModelSession()
let response = try await session.respond(to: "What's a good name for a trip to Japan? Respond only with a title")
たったこれだけで簡単に使えると色んなことに試してみたくなりますね。
Playgourndマクロ

Xcode 26の新機能として、#Playgroundが使えるようになります!
#Playgroundは、Xcode上でコードを簡単に試すことができる機能ですが、Foundation Modelsフレームワークとの相性がとてもよく、プロンプトのトライ&エラーを迅速に試すことができます。
以下のようにforループを使って、各レスポンスの値を確認することができるので、良さそうなプロンプトを見つける手段として良さそうです。
import FoundationModels
import Playgrounds
#Playground {
let session = LanguageModelSession()
for landmark in ModelData.shared.landmarks {
let response = try await session.respond(to: "What's a good name for a trip to \(landmark.name)? Respond only with a title")
}
}
とても簡単に使えるのですが、開発者としては改めて以下の点は頭に入れておく必要がありそうです。
オンデバイスモデルはデバイス規模のモデルであることを念頭に置くことが重要です。これは、要約、抽出、分類など、様々なユースケースに最適化されています。世界知識や高度な推論といった、通常はサーバー規模のLLMが使用されるタスク向けには設計されていません。

実際に使用して、「あともう少し、精度が、、」なんて感じたら、継続的に改善は続けているようなのでフィードバックをどんどん送ってみんなでいいものにしていきましょう!
Guided Generation

Fundation Modelsの@Generableと@Guideを使うことで、レスポンスの値を指定したインスタンスの型にあった形で受け取ることができるようになります。
以下の例では、SearchSuggestionsがsearchTermsという変数を持っていますが、@Guideに記述されているような4つの検索候補一覧の文字列というガイドに沿って出力させることができるようになります。
@Generable
struct SearchSuggestions {
@Guide(description: "A list of suggested search terms", .count(4))
var searchTerms: [String]
}
| 改善前 | 改善後 |
|---|---|
![]() |
![]() |
プロンプトで出力形式を指定する必要がなくなりました。
そして、指定した型でレスポンスが受け取れるのでとても扱いやすそうです。
また、@Generableが利用できる型は、プリミティブ型やまたその配列、@Generableな型。
// Composing Generable types
@Generable struct Itinerary {
var destination: String
var days: Int
var budget: Float
var rating: Double
var requiresVisa: Bool
var activities: [String]
var emergencyContact: Person
var relatedItineraries: [Itinerary]
}
この@Generableと@Guideを活用することで、レスポンスの構造の正確性を根本的に保証できる。
Snapshot streaming

streamResponseメソッドを使用することで、段階的にレスポンスの出力差分を取得できます。
let stream = session.streamResponse(
to: "Craft a 3-day itinerary to Mt. Fuji.",
generating: Itinerary.self
)
for try await partial in stream {
print(partial)
}
例えば、その差分を段階的に画面上に反映することができます。

セッションの例では、itinerary(旅程)を生成させ、その生成結果を画面に反映させていました。
ただ、出力が完了するまで待たせられるだけだと退屈ですが、この方法によって段階的に画面に描画できるため、体感的な待ち時間も少なくなりそうですね。
Tool calling

Toolを使用することでモデルが以下のような行動を取れるようになります。
- 問題解決のために必要な情報や行動を特定する
- 追加の情報にアクセスする
- 根拠に基づいたレスポンスに近づけれる
- モデルがアクションを実行できる

モデルがToolを使ってレスポンスの向上ができると判断した場合、自動的にToolにアクセスして、その出力を活用して最終的なアウトプットを向上させます。
Toolの定義例は以下となっており、
import WeatherKit
import CoreLocation
import FoundationModels
struct GetWeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve the latest weather information for a city"
@Generable
struct Arguments {
@Guide(description: "The city to fetch the weather for")
var city: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
///...
}
}
name: このToolの名称。呼び出しのために使用するdescription: このToolの説明文。これによってモデルがいつこのToolを呼ぶべきか判断することができる
Toolが呼び出されると、callメソッドが実行されますが任意でArgumentsも指定することができる。
callメソッドの中は自由に記述できます。セッションの例ではCoreLocationやWeatherKitのAPIを呼び出していましたが、もちろん自前のサーバー通信系メソッドを実行することも可能です。
Toolの使用するには対象のsessionの生成時に指定するだけです。
// Attaching tools to a session
let session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "Help the user with weather forecasts."
)
let response = try await session.respond(
to: "What is the temperature in Cupertino?"
)
print(response.content)
// It’s 71˚F in Cupertino!
Instructions

モデルに役割を伝えて、ルールに沿って動くように指示を出すことができます。
この指示はオプショナルで、カスタム指示を渡さない場合はデフォルトの指示が渡されるので注意が必要。
モデルがプロンプトよりこの指示を優先するように訓練されているため、それぞれの役割については理解する必要がある。
instructions: この指示は開発者側が行うものprompt: ユーザーからの入力
Multi-turn interactions
単一のセッションでは、過去のやりとりを参照して理解することもできる。
また、session.transcriptでやりとり自体を確認したり、それを用いて画面描画なども可能です。
// Multi-turn interactions
let session = LanguageModelSession()
let firstHaiku = try await session.respond(to: "Write a haiku about fishing")
print(firstHaiku.content)
// Silent waters gleam,
// Casting lines in morning mist—
// Hope in every cast.
let secondHaiku = try await session.respond(to: "Do another one about golf")
print(secondHaiku.content)
// Silent morning dew,
// Caddies guide with gentle words—
// Paths of patience tread.
print(session.transcript) // (Prompt) Write a haiku about fishing
// (Response) Silent waters gleam...
// (Prompt) Do another one about golf
// (Response) Silent morning dew...
isResponding
セッションが出力生成中は、session.isRespondingがtrueになるので、その値を用いてローディングを表示したり、ボタンを非活性にしたりすることができる。
おわりに
個人的にはこの新しいフレームワークが今回のWWDC発表の1番の推しです。
これによってオンデバイスでできることが、とても広がるのではないかとワクワク&ドキドキしています。
このフレームワークを使用したいとなった場合に注意してほしいのが、macOS 26とXcode 26の組み合わせでないと使用できません。この時点ですでにハードルが高いですが、試してみる価値はありそうです。
まだまだ改善できるポイントが多そうなフレームワークではありますが、どんどん使ってどんどんフィードバックしていきたいと思います!
あと、このTシャツ欲しい、、、

関連セッション












