View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

WWDC25に戻る

  • 概要
  • トランスクリプト
  • コード
  • Foundation Modelフレームワークの詳細

    Foundation Modelフレームワークで開発のレベルを引き上げましょう。ガイド付き生成の仕組みを理解し、ガイド、正規表現、生成スキーマを使用してカスタムの構造化された応答を取得する方法を学べます。外部情報へのアクセスとアクションの実行をモデルが自発的に行うためのツール呼び出しを解説し、パーソナライズされた体験を実現する方法も紹介します。 このビデオの内容を十分理解できるよう、まず「Meet the Foundation Models framework」を視聴することをおすすめします。

    関連する章

    • 0:00 - イントロダクション
    • 0:49 - セッション
    • 7:57 - Generable
    • 14:29 - 動的スキーマ
    • 18:10 - ツール呼び出し

    リソース

    • Generate dynamic game content with guided generation and tools
    • Human Interface Guidelines: Generative AI
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC25

    • Appleプラットフォームでの機械学習/AIフレームワーク
    • Code Along:Foundation Modelフレームワークによる、アプリへのオンデバイスAIの組み込み
    • Foundation Modelフレームワークの紹介
  • このビデオを検索

    こんにちは Louisです 今日はFoundation Modelフレームワーク を 最大限に活用する方法を見ていきます

    ご存知かもしれませんが Foundation Modelフレームワークを 使用すると 便利なSwift APIによって オンデバイスの大規模言語モデルを 直接利用できます このフレームワークはmacOS、 iPadOS、iOS、visionOSで利用できます また オンデバイスで動作するため プロジェクトで使用するのも 簡単なインポートで完了します このビデオで取り上げるのは Foundation Modelでのセッションの仕組み Generableを使用して 構造化された出力を取得する方法 実行時に定義される動的スキーマを使用して 構造化された出力を取得する方法 ツール呼び出しを使用してモデル呼び出しを カスタム関数に組み込む方法です まずはシンプルにセッションを使用して テキストを生成してみましょう

    今 コーヒーショップを舞台にした ピクセルアートゲームを制作中なのですが Foundation Modelを使用して ゲーム内の会話やコンテンツを生成すれば より生き生きとしたゲームに なるのではないかと考えています

    モデルにプロンプトを送り プレイヤーの質問に応答させることで バリスタのユニークなセリフを生成できます これを実現するために カスタムの指示を伴う LanguageModelSessionを作成します これにより モデルに このセッションの目的を伝え プロンプトではユーザーの 入力を受け取ることができます これだけで かなり楽しくて新しい ゲーム要素を追加できます 「ここで働いてどれくらいですか?」 とバリスタさんに聞いて その返答を確認してみましょう

    これは完全にオンデバイスで 生成されました 大成功です では実際の仕組みは どうなっているのでしょうか Foundation Modelによる テキスト生成の仕組みを理解し 重要なポイントを確認しましょう セッションでrespond(to:)を呼び出すと まず セッションの指示とプロンプト (この場合はユーザーの入力)を取得し そのテキストをトークンに変換します トークンは小さな部分文字列であり 単語の場合もありますが 通常は数文字程度です 大規模言語モデルは 一連のトークンを 入力として受け取り トークンの新しいシーケンスを 出力として生成します Foundation Modelが処理する 具体的なトークンについて 心配する必要はありません APIがそれを適切に抽象化するため 気にせず利用できます ただし トークンにはコストがかかることを 理解しておく必要があります 指示とプロンプトの各トークンによって レイテンシが増加します モデルが応答トークンの 生成を開始するには 最初にすべての入力トークンを 処理する必要があります また トークンの生成には 計算コストもかかるため 出力する応答が長いほど 生成に時間がかかります

    LanguageModelSessionは ステートフルです 各respond(to:)呼び出しは トランスクリプトに記録されます

    トランスクリプトにはセッションに対する すべてのプロンプトと応答が含まれます

    これは デバッグやUIでの表示に役立ちます

    ただし セッションのサイズには 上限が設定されています 大量のリクエストを行ったり 大きなプロンプトを設定したり 大規模な出力を取得する場合 コンテキストの制限に達することがあります

    セッションが利用可能なコンテキストサイズ を超えるとエラーがスローされるため 適切に捕捉する準備をしておく 必要があります ゲーム内でキャラクターと話していて エラーが発生すると 会話が途切れてしまい残念ですね キャラクターのことをもっと知りたかったのに 幸いなことに このエラーから 回復する方法があります

    exceededContextWindowSize エラーを捕捉することです

    そうすれば 履歴のない 新しいセッションを始めることができます ただし このゲームでは キャラクターが突然会話の内容を すべて忘れてしまうことになります

    現在のセッションから トランスクリプトの一部を選択して 新しいセッションに引き継ぐこともできます

    セッションのトランスクリプトから エントリを取得し それを新しいエントリの配列に まとめることもできます

    このゲームのダイアログでは セッションのトランスクリプトの 最初のエントリ つまり指示を 取得できます また 最後のエントリとして 最後に成功した応答も取得できます それを新しいセッションに渡すことで キャラクターはしばらくの間 会話を続けられます ただし セッションのトランスクリプトには 最初のエントリとして 初期の指示が含まれています ゲームキャラクターの トランスクリプトを引き継ぐ場合 必ずその指示を含めることが重要です

    トランスクリプトから特定の 関連部分を選んで引き継ぐことで シンプルで効果的な 解決策になる可能性があります ただし 状況によっては それほど単純ではありません より多くのエントリを含む トランスクリプトを考えてみましょう 必ず最初に指示を引き継ぐことから 始める必要があります トランスクリプトに 関連性の高い エントリが多数含まれる可能性があるため このようなケースでは トランスクリプトを 要約することも1つの方法です

    外部ライブラリを利用して 実行することもできますし 場合によっては トランスクリプトの 一部を要約するのに Foundation Modelを 使用することもできます

    これが セッションの トランスクリプトでできる処理です 次に 応答が実際にどのように 生成されるかを簡単に見てみましょう このゲームでは バリスタに歩み寄ると プレイヤーはどんな質問でもできます しかし 新たに2つのゲームを始めて それぞれで まったく同じ質問をしても おそらく返ってくる答えは異なるでしょう どんな仕組みでしょうか そこで サンプリングの出番です

    モデルが出力を生成するときは トークンを1つずつ生成します モデルは 各トークンの出現確率の 分布を作成することで これを処理します デフォルトでは Foundation Modelは 一定の確率の範囲内でトークンを選択します 時には「ああ」と言って始まることもあれば 最初のトークンとして「ええと」を 選択する場合もあります この処理は生成されるすべての トークンに対して発生します このトークンの選択が サンプリングと呼ばれるものです デフォルトの動作は ランダムサンプリングです 様々な出力が得られることは ゲームなどのユースケースに最適です 一方で 決定論的な出力が 必要な場合もあります 例えば 再現性が求められる デモを作成する場合などです GenerationOptions APIを使用すると サンプリング手法を制御できます これを「greedy」に設定することで 決定論的な出力が得られます このように設定されていれば 同じプロンプトには同じ出力が返されます ただし セッションの状態も 同じであることが前提です さらに この条件が成り立つのは オンデバイスモデルのバージョンが 同じである場合のみです OSアップデートの一環として モデルが更新されると 同じプロンプトから異なる出力が 生成される場合があります サンプリングの設定を 「greedy」にしてもです ランダムサンプリングの「temperature」 で出力を調整することもできます 例えば temperatureを 0.5に設定すると 出力の変化は小さくなります これをより高い値に設定すると 同じプロンプトに対して 大きく異なる出力が生成されます また プロンプトで ユーザー入力を受け取る際に 未対応の言語である可能性もあります

    このような場合は 専用の unsupportedLanguageOrLocaleエラーを 捕捉することで処理できます これは UIにカスタムメッセージを 表示するのに適した方法です また モデルが特定の言語に対応しているか どうかを確認するAPIもあります 例えば ユーザーの現在の言語に 対応しているかどうかを確認し 対応していない場合は 注意書きを表示できます 以上がセッションに関する概要です プロンプトを入力すると 履歴が トランスクリプトに保存されます 必要に応じて サンプリングパラメータを設定して セッションの出力の ランダム性を制御できます もっと凝ったものにしましょう プレイヤーが移動するときにNPC (非操作キャラクター)を生成できます ここでもFoundation Modelを使用します ただし 今回は より複雑な出力が必要です 単なるテキストではなくNPCの名前や コーヒーの注文も生成したいと思います こんなときにGenerableが役立ちます 大規模言語モデルから構造化された出力を 取得するのは困難な場合があります 必要なフィールドを指定して プロンプトを作成し それを抽出するための解析コードを 用意することもできます ただし この方法は維持が難しく 非常に脆弱です 常に有効なキーが返されるとは限らないため メソッド全体が失敗する可能性があります 幸い Foundation Modelには Generableと呼ばれる優れたAPIがあります 構造体に@Generableマクロを 適用できます Generableとは何でしょうか? 実在する単語でしょうか? はい 存在します

    Generableは モデルが構造化データを 簡単に生成できるようにする仕組みで Swiftの型を使用します このマクロはコンパイル時に スキーマを生成し モデルはそれを使用して 期待される構造を生成します このマクロはイニシャライザも生成します これは セッションにリクエストを 実行するときに自動的に呼び出されます

    それでは この構造体の インスタンスを作成してみましょう 前と同じように セッションで 応答メソッドを呼び出します ただし 今回はgenerating引数を渡して 生成する型をモデルに指示します Foundation Modelは Generable型の詳細を プロンプトに自動的に組み込みます その情報はモデルのトレーニングに 使用された特定の形式に沿っています Generable型に含まれるフィールドを モデルに伝える必要はありません これにより ゲームでは 生成された 魅力的なNPCの登場が実現します

    Generableの機能は 見た目以上に強力です 低レベルの処理では 制約付きデコーディングが使用されます この手法により モデルは 特定のスキーマに沿った テキストを生成できます 先ほどのマクロが生成するスキーマを 思い出してください 前に説明したように LLMはトークンを生成し それらが後でテキストに変換されます Generableを使用すると そのテキストは型安全な方法で 自動的に解析されます トークンはループ処理で生成され これは 通常 デコードループと呼ばれます 制約付きデコーディングがなければ モデルは 誤って無効なフィールド名を 生成する可能性があります 「name」ではなく 「firstName」などです その結果 NPC型への解析に失敗します

    しかし 制約付きデコーディングでは モデルがこのような構造的なミスを 犯すのを防ぐことができます 生成される各トークンについて モデルの語彙にあるすべてのトークンの 分布が存在します 制約付きデコーディングは 無効な トークンを除外することで機能します 任意のトークンを選択するのではなく モデルは スキーマに従って 有効なトークンのみを選択できます

    モデルの出力を手動で解析する 必要がないということです つまり 本当に大切なことに 時間を使うことができます コーヒーショップでバーチャルゲストと 会話を楽しんだりできますね Generableは オンデバイスのLLMから 出力を取得するのに最適な方法です さらに多くのこともできます 構造体だけでなく 列挙型でも使用できるのです それを活用して私たちの出会いを よりダイナミックにしましょう ここでは 2つのcaseを定義した Encounter列挙型を追加しました この列挙型のcaseには関連する値を 格納することもできます この仕組みを使って コーヒーの注文を生成するか または 店長と話したがっている キャラクターを作成してみましょう

    それでは このゲームで何に 遭遇するか確認してみましょう

    あー 誰かが本当にコーヒーを 飲みたがっていますね

    当然ですが すべてのお客様が簡単に 対応できるとは限りません そこで NPCにレベルを導入して これをレベルアップしてみましょう Generableは Intを含む一般的なSwiftの 型のほとんどを直接サポートしています それでは levelプロパティを追加しましょう ただし 整数を生成したいわけでは ありません レベルを特定の範囲に限定したい場合は Guideを使用してこれを指定できます プロパティでGuideマクロを使用して 範囲を渡すことができます ここでも モデルは 制約付きデコーディングを使用して 値がこの範囲内になるよう保証します

    この機会に NPCに属性の配列も 追加しましょう

    再びGuideを使用できますが 今回は NPCのこの配列に正確に 3つの属性が必要であることを指定します 覚えておいてください Generable型のプロパティは ソースコードで宣言されている 順序で生成されます ここでは 最初にnameが生成され 次にlevel その次にattributes 最後にencounterです

    この順序は重要な場合があります プロパティの値が別のプロパティの 影響を受けることを 想定している場合などです プロパティごとのストリーム処理を 行うこともできるので 完全な出力が生成されるまで待たずに 処理する場合に便利です これでゲームはかなり楽しくなりました 友人と共有する準備がほぼ整いました しかし NPCの名前が 考えていたものと 少し違っていることに気づきました 私は名前と姓の両方を使いたいのです

    これにはガイドを使用できます ただし 今回は自然言語の 説明を指定します

    名前は「フルネーム」にするように 指定できます これは事実上 別のプロンプト入力の方法です プロンプトで個々のプロパティを 記述する代わりに Generable型で直接指定できます これにより モデルは これらの記述の 関連性をより強く認識できます ゲーム内を歩き回ってみると 新しい名前が 実際に使用されているのを確認できます ここで 様々な型に適用できる すべてのガイドの概要を示します

    intなどの一般的な数値型では 最小値、最大値、範囲を指定できます また 配列を使用すると 個数を制御したり 配列の要素型でガイドを指定したりできます

    文字列の場合 anyOfを使用して モデルに配列から選択させたり 正規表現パターンに制約することもできます

    正規表現パターンのガイドは 特に強力です テキストとの照合に正規表現を 使用するのはおなじみかもしれません しかし Foundation Modelでは 正規表現パターンを使用して 生成する文字列の構造を定義できます 例えば 名前を特定の接頭辞の セットに制限できます また 正規表現ビルダーの構文を 使用することもできます

    これで改めて正規表現への興味が わいたなら 時代を越えて名作となった 数年前の「Meet Swift Regex」を ご参照ください 要約すると Generableは 構造体と列挙型に適用できるマクロで モデルから構造化された出力を取得する 信頼性の高い方法を提供します 出力を解析する必要はなく さらに具体的な出力を取得するには プロパティにガイドを適用できます つまり Generableは コンパイル時に 構造が確定している場合に最適です このマクロはスキーマを自動生成し 指定した型のインスタンスを 出力として返します しかし 実行時に初めて構造が 判明するという場合もあります そこで役立つのが動的スキーマです ゲームにレベルクリエーターを追加して プレイヤーがゲーム内を歩き回る際に 遭遇するエンティティを 動的に定義できるようにしています 例えば プレイヤーは 謎解きの構造を作成できます その謎解きには質問と 多肢選択式の答えがあります コンパイル時にこの構造がわかっていれば Generable構造体を 定義するだけで済みます しかし レベルクリエーターでは プレイヤーが 思いつくあらゆる構造を作成できます

    DynamicGenerationSchemaを使用 することで 実行時にスキーマを作成できます コンパイル時に定義される構造体と同様に 動的スキーマにもプロパティのリストがあります レベルクリエーターを追加して プレイヤーの入力を受け取ることができます

    各プロパティには 名前と その型を定義する独自のスキーマがあります スキーマはあらゆるGenerable型に 使用でき これには組み込み型も含まれます Stringなどです

    動的スキーマには配列を含めることができ ここで 配列の要素のスキーマを指定します 重要なのは 動的スキーマは他の動的 スキーマへの参照を持つことができる点です そのためここでは 配列は実行時に定義 される カスタムスキーマを参照できます

    ユーザーの入力から 2つのプロパティを 持つ 謎解きのスキーマを作成できます 1つ目はquestionで これは文字列プロパティです 次に Answerというカスタム型の 配列プロパティです では 答えを作成してみましょう これには文字列とブール値の プロパティがあります 謎解きのanswersプロパティは その名前で解答スキーマを参照しています 次に DynamicGenerationSchemaの インスタンスを作成できます 各動的スキーマは独立しています つまり 謎解きの動的スキーマは 実際には解答の動的スキーマを 含んでいません 推論を開始する前に まず 動的スキーマを検証済み スキーマに変換する必要があります このとき 動的スキーマに不整合があると エラーが発生します 型参照が存在しないなどです

    検証済みのスキーマが得られたら通常どおり セッションのプロンプトを入力できます ただし 今回は出力の型が GeneratedContentインスタンスです これは動的な値を保持しています これは 動的スキーマのプロパティ名を 使用して照会できます ここでも Foundation Modelは ガイド付き生成を使用して 出力がスキーマと一致することを確認します 想定外のフィールドが 生成されることはありません 動的ではあっても 出力を手動で解析することを 心配する必要はありません

    これで プレイヤーがNPCに遭遇すると モデルは この動的コンテンツを 生成できます これを動的UIで表示します 私たちが遭遇したものを確認してみましょう 私は暗くも明るくも 苦くも甘くもなります 人を目覚めさせ 温かさをもたらします 私は何でしょう? コーヒーかホットチョコレート 答えはコーヒーだと思います 正解です プレイヤーは様々な楽しいレベルを 作れるのでとても楽しいと思います 要約すると Generableマクロを使用すれば コンパイル時に定義されるSwiftの型から 構造化された出力を簡単に生成できます 内部では Foundation Modelが スキーマを管理し GeneratedContentを独自の型の インスタンスに変換します 動的スキーマは非常によく似た仕組み ですが さらに細かい制御ができます スキーマは完全に実行時に制御でき GeneratedContentに 直接アクセスすることができます 次に ツール呼び出しを見てみましょう これにより モデルがユーザー独自の関数を 呼び出せるようになります 私はDLCの作成を考えています ダウンロードコンテンツによって ゲームに個人的な要素を追加するためです ツール呼び出しを使用すると モデルは自律的に情報を取得できます プレイヤーの連絡先とカレンダーを 統合すれば本当に楽しくなると思います 通常 サーバベースのモデルでは そのようなことはしません プレイヤーは ゲームが個人データを アップロードすることを喜ばないでしょう しかし Foundation Modelでは すべてがオンデバイスで処理されるため プライバシーを保護しながら これを実現できます

    Toolプロトコルを使用すれば ツールの定義は非常に簡単です まず 名前と説明を指定します これは APIによって自動的に プロンプトに挿入され これを基に モデルは適切なタイミングと 頻度でツールを呼び出します

    ツール名は短く 英語のテキストとして 読みやすいものにすることをお勧めします 略語を避け 説明は簡潔にまとめ 実装の詳細を 含めないでください なぜなら これらの文字列は プロンプトに そのまま入力されるからです 文字列が長くなるほどトークンが多くなり レイテンシーが増加する可能性があります 代わりに 名前に動詞を 含めることを検討してください findContactのようにします また 説明は1文程度にしてください いつものように 様々なバリエーションを 試して自分のツールに最適なものを 確認することが重要です

    次に ツールの入力を定義します このツールで ミレニアル世代など 特定の 世代と連絡を取りたいと考えています モデルはゲームの状態に基づいて 面白いケースを選ぶことができますが Arguments構造体を追加して それを Generableにすることができます モデルがこのツールを呼び出すことを 決定すると入力引数が生成されます Generableを使用することで ツールが常に 有効な入力引数を取得することが保証されます したがって モデルが異なる世代 例えば ゲームで対象外の アルファ世代などは生成されません

    次に呼び出し関数を実装します モデルは ツールを呼び出すことを 決定したときにこの関数を呼び出します この例では 次にContacts APIを 呼び出します そのクエリに対して連絡先の名前を返します

    ツールを使用するには それを セッションイニシャライザに渡します その後 モデルは追加の情報が 必要なときにツールを呼び出します これは 単に自分で連絡先を 取得するよりも強力です モデルは特定のNPCに 必要な場合にのみツールを呼び出し また ゲームの状態に基づいて おもしろい入力引数を選択できます NPCの年齢世代などです

    これは 通常のContacts APIを 使用しているので よくご存知かもしれません このツールは 最初に呼び出されたときに プレイヤーに通常の許可を求めます プレイヤーが連絡先への アクセスを許可しなくても Foundation Modelは以前と 同じようにコンテンツを生成できますが アクセスが許可されるとより個人的な 内容にすることができます

    別のNPCに遭遇するまで ゲーム内を少し歩き回ってみましょう 今回は 連絡先から名前が取得されます こんにちは Naomy Naomyが何を言うか聞いてみましょう

    君がコーヒー好きだったとはね LanguageModelSessionはツールの インスタンスを受け取ります これは ツールのライフサイクルを 制御することを意味します このツールのインスタンスは セッション全体で変わりません この例では FindContactsToolでランダムな キャラクターを取得しているだけなので 時々 同じ連絡先を 取得する可能性があります 現在 このゲームでは Naomyが複数います それは正しくありません たった1人しかいないのですから これを修正するために ゲームですでに 使用された連絡先を追跡できます FindContactToolに状態を追加できます そのために まずFindContactToolを クラスに変換します これにより 呼び出しメソッドから 状態を変更できます これで 選択された連絡先を追跡でき このcallメソッドでは 同じものは再度選択しません

    NPCの名前は私の 連絡先に基づいています しかし NPCとの会話はまだ不自然です 最後にもう1つツールを紹介しましょう 今度はカレンダーへのアクセス用ツールです

    このツールでは 連絡先の名前を渡す際に ゲーム内で続行中の ダイアログから取得します モデルがこのツールを呼び出すときに この連絡先とのイベントを取得するための 日付(年 月 日)を生成します このツールをセッション内で NPCとのダイアログに渡します

    そこで 友人のNaomyのNPCに 「What’s going on?」と尋ねると NPCは私たちが一緒に計画している 実際のイベントを答えることができます

    うわー 本物のNaomyと 会話しているみたいです

    ツール呼び出しの仕組みを 詳しく見てみましょう セッションの冒頭で ツールを渡すことから始めます 指示も一緒に渡します この例では 今日の日付などの 情報を含めます その後 ユーザーがセッションにプロンプトを 入力すると モデルはテキストを分析します この例では モデルはプロンプトが イベントを求めていることを理解しており カレンダーツールの呼び出しは妥当です

    このツールを呼び出すために モデルは最初に入力引数を生成します この場合 モデルはイベントを 取得する日付を生成する必要があります モデルは指示やプロンプトからの 情報を関連付けて それに基づいてツールの引数を 適切に補完することができます つまり この例では tomorrowの意味を推論する際に 指示にある今日の日付が使用されます ツールの入力が生成されると callメソッドが呼び出されます いよいよ見せ場です ツールは 必要に応じてあらゆる処理ができます ただし セッションはツールの 処理結果が返されるまで待機し それまで他の出力を生成しません

    ツールの出力は その後 トランスクリプトに記録されます これはモデルからの出力と同様です また ツールの出力に基づいて モデルはプロンプトに対する 応答を生成できます ツールは 1つのリクエストに対して 複数回呼び出される可能性があります そのような場合 ツールは 並列に呼び出されます ツールのcallメソッドからデータに アクセスするときは これに注意してください とても楽しかったですね このゲームは ランダムなコンテンツの生成に 私の個人的な連絡先と カレンダーの情報を利用しています 私のデータがデバイスから 外部に送信されることはありません 要約すると ツール呼び出しによって モデルはリクエスト中に外部データに アクセスするコードを実行できます これは 連絡先などの 個人情報である場合もあれば Web上のソースからの 外部データである場合もあります ツールは 特定のリクエスト内で複数回 呼び出される可能性があります モデルは そのコンテキストに 基づいてこれを決定します ツールは並列で呼び出すこともでき 状態を格納することもできます 沢山ありますね とりあえずコーヒーでも飲んでから 次に進みましょう 詳細を学ぶには プロンプトエンジニアリングに関する 専用のビデオを確認してください 設計や安全性のヒントも含まれています また 本物のNaomyに会いたい場合は コード解説付きのビデオを確認してください 私のように皆さんもFoundation Modelで 楽しんでいただければ幸いです ありがとうございました

    • 1:05 - Prompting a session

      import FoundationModels
      
      func respond(userInput: String) async throws -> String {
        let session = LanguageModelSession(instructions: """
          You are a friendly barista in a world full of pixels.
          Respond to the player’s question.
          """
        )
        let response = try await session.respond(to: userInput)
        return response.content
      }
    • 3:37 - Handle context size errors

      var session = LanguageModelSession()
      
      do {
        let answer = try await session.respond(to: prompt)
        print(answer.content)
      } catch LanguageModelSession.GenerationError.exceededContextWindowSize {
        // New session, without any history from the previous session.
        session = LanguageModelSession()
      }
    • 3:55 - Handling context size errors with a new session

      var session = LanguageModelSession()
      
      do {
        let answer = try await session.respond(to: prompt)
        print(answer.content)
      } catch LanguageModelSession.GenerationError.exceededContextWindowSize {
        // New session, with some history from the previous session.
        session = newSession(previousSession: session)
      }
      
      private func newSession(previousSession: LanguageModelSession) -> LanguageModelSession {
        let allEntries = previousSession.transcript.entries
        var condensedEntries = [Transcript.Entry]()
        if let firstEntry = allEntries.first {
          condensedEntries.append(firstEntry)
          if allEntries.count > 1, let lastEntry = allEntries.last {
            condensedEntries.append(lastEntry)
          }
        }
        let condensedTranscript = Transcript(entries: condensedEntries)
        // Note: transcript includes instructions.
        return LanguageModelSession(transcript: condensedTranscript)
      }
    • 6:14 - Sampling

      // Deterministic output
      let response = try await session.respond(
        to: prompt,
        options: GenerationOptions(sampling: .greedy)
      )
                      
      // Low-variance output
      let response = try await session.respond(
        to: prompt,
        options: GenerationOptions(temperature: 0.5)
      )
                      
      // High-variance output
      let response = try await session.respond(
        to: prompt,
        options: GenerationOptions(temperature: 2.0)
      )
    • 7:06 - Handling languages

      var session = LanguageModelSession()
      
      do {
        let answer = try await session.respond(to: userInput)
        print(answer.content)
      } catch LanguageModelSession.GenerationError.unsupportedLanguageOrLocale {
        // Unsupported language in prompt.
      }
      
      let supportedLanguages = SystemLanguageModel.default.supportedLanguages
      guard supportedLanguages.contains(Locale.current.language) else {
        // Show message
        return
      }
    • 8:14 - Generable

      @Generable
      struct NPC {
        let name: String
        let coffeeOrder: String
      }
      
      func makeNPC() async throws -> NPC {
        let session = LanguageModelSession(instructions: ...)
        let response = try await session.respond(generating: NPC.self) {
          "Generate a character that orders a coffee."
        }
        return response.content
      }
    • 9:22 - NPC

      @Generable
      struct NPC {
        let name: String
        let coffeeOrder: String
      }
    • 10:49 - Generable with enum

      @Generable
      struct NPC {
        let name: String
        let encounter: Encounter
      
        @Generable
        enum Encounter {
          case orderCoffee(String)
          case wantToTalkToManager(complaint: String)
        }
      }
    • 11:20 - Generable with guides

      @Generable
      struct NPC {
        @Guide(description: "A full name")
        let name: String
        @Guide(.range(1...10))
        let level: Int
        @Guide(.count(3))
        let attributes: [Attribute]
        let encounter: Encounter
      
        @Generable
        enum Attribute {
          case sassy
          case tired
          case hungry
        }
        @Generable
        enum Encounter {
          case orderCoffee(String)
          case wantToTalkToManager(complaint: String)
        }
      }
    • 13:40 - Regex guide

      @Generable
      struct NPC {
        @Guide(Regex {
          Capture {
            ChoiceOf {
              "Mr"
              "Mrs"
            }
          }
          ". "
          OneOrMore(.word)
        })
        let name: String
      }
      
      session.respond(to: "Generate a fun NPC", generating: NPC.self)
      // > {name: "Mrs. Brewster"}
    • 14:50 - Generable riddle

      @Generable
      struct Riddle {
        let question: String
        let answers: [Answer]
      
        @Generable
        struct Answer {
          let text: String
          let isCorrect: Bool
        }
      }
    • 15:10 - Dynamic schema

      struct LevelObjectCreator {
        var properties: [DynamicGenerationSchema.Property] = []
      
        mutating func addStringProperty(name: String) {
          let property = DynamicGenerationSchema.Property(
            name: name,
            schema: DynamicGenerationSchema(type: String.self)
          )
          properties.append(property)
        }
      
        mutating func addArrayProperty(name: String, customType: String) {
          let property = DynamicGenerationSchema.Property(
            name: name,
            schema: DynamicGenerationSchema(
              arrayOf: DynamicGenerationSchema(referenceTo: customType)
            )
          )
          properties.append(property)
        }
        
        var root: DynamicGenerationSchema {
          DynamicGenerationSchema(
            name: name,
            properties: properties
          )
        }
      }
      
      var riddleBuilder = LevelObjectCreator(name: "Riddle")
      riddleBuilder.addStringProperty(name: "question")
      riddleBuilder.addArrayProperty(name: "answers", customType: "Answer")
      
      var answerBuilder = LevelObjectCreator(name: "Answer")
      answerBuilder.addStringProperty(name: "text")
      answerBuilder.addBoolProperty(name: "isCorrect")
      
      let riddleDynamicSchema = riddleBuilder.root
      let answerDynamicSchema = answerBuilder.root
      
      let schema = try GenerationSchema(
        root: riddleDynamicSchema,
        dependencies: [answerDynamicSchema]
      )
      
      let session = LanguageModelSession()
      let response = try await session.respond(
        to: "Generate a fun riddle about coffee",
        schema: schema
      )
      let generatedContent = response.content
      let question = try generatedContent.value(String.self, forProperty: "question")
      let answers = try generatedContent.value([GeneratedContent].self, forProperty: "answers")
    • 18:47 - FindContactTool

      import FoundationModels
      import Contacts
      
      struct FindContactTool: Tool {
        let name = "findContact"
        let description = "Finds a contact from a specified age generation."
          
        @Generable
        struct Arguments {
          let generation: Generation
              
          @Generable
          enum Generation {
            case babyBoomers
            case genX
            case millennial
            case genZ            
          }
        }
        
        func call(arguments: Arguments) async throws -> ToolOutput {
          let store = CNContactStore()
              
          let keysToFetch = [CNContactGivenNameKey, CNContactBirthdayKey] as [CNKeyDescriptor]
          let request = CNContactFetchRequest(keysToFetch: keysToFetch)
      
          var contacts: [CNContact] = []
          try store.enumerateContacts(with: request) { contact, stop in
            if let year = contact.birthday?.year {
              if arguments.generation.yearRange.contains(year) {
                contacts.append(contact)
              }
            }
          }
          guard let pickedContact = contacts.randomElement() else {
            return ToolOutput("Could not find a contact.")
          }
          return ToolOutput(pickedContact.givenName)
        }
      }
    • 20:26 - Call FindContactTool

      import FoundationModels
      
      let session = LanguageModelSession(
        tools: [FindContactTool()],
        instructions: "Generate fun NPCs"
      )
    • 21:55 - FindContactTool with state

      import FoundationModels
      import Contacts
      
      class FindContactTool: Tool {
        let name = "findContact"
        let description = "Finds a contact from a specified age generation."
         
        var pickedContacts = Set<String>()
          
        ...
      
        func call(arguments: Arguments) async throws -> ToolOutput {
          contacts.removeAll(where: { pickedContacts.contains($0.givenName) })
          guard let pickedContact = contacts.randomElement() else {
            return ToolOutput("Could not find a contact.")
          }
          return ToolOutput(pickedContact.givenName)
        }
      }
    • 22:27 - GetContactEventTool

      import FoundationModels
      import EventKit
      
      struct GetContactEventTool: Tool {
        let name = "getContactEvent"
        let description = "Get an event with a contact."
      
        let contactName: String
          
        @Generable
        struct Arguments {
          let day: Int
          let month: Int
          let year: Int
        }
          
        func call(arguments: Arguments) async throws -> ToolOutput { ... }
      }

Developer Footer

  • ビデオ
  • WWDC25
  • Foundation Modelフレームワークの詳細
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習とAI
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン