View in English

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

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法
  • 概要
  • トランスクリプト
  • Code Along:Foundation Modelフレームワーク

    Foundation ModelフレームワークでAppleのオンデバイスLLMにアクセスする方法を、実際にコーディングしながら学びましょう。このオンラインセッションでは、Appleのエキスパートと一緒に、生成AI機能をサンプルアプリに組み込むコードをXcodeで記述します。基本的なテキスト生成などのコア機能はもちろん、ガイド付き生成による構造化データの出力、ストリーミング返信による動的なUIアップデート、データを取得しアクションを実行するツールの呼び出しなど高度な機能の実装にも取り組みます。
    どなたでもこのセッションにご参加いただけます。コーディングには、最新のmacOS Tahoe 26およびXcode 26を実行する、Apple Intelligence対応のAppleシリコン搭載Macが必要です。

    関連する章

    • 0:00:00 - イントロダクション
    • 0:03:33 - リソースとシステム要件
    • 0:05:04 - 前提条件と設定
    • 0:06:30 - スタータープロジェクトを確認する
    • 0:10:32 - 1章:Foundation Modelフレームワークの基本
    • 0:15:50 - 1.2章:指示によりモデルをガイドする
    • 0:18:46 - 第1.3章:モデルの可用性を処理する
    • 0:20:50 - 1.4章:ビューで可用性を処理する
    • 0:24:07 - 1.5章:旅程ジェネレータを作成する
    • 0:27:07 - 1.6章:ビューを更新しテキスト出力を表示する
    • 0:32:43 - 2章:構造化出力を生成する
    • 0:34:36 - 2.1章:シンプルな構造化出力を生成する
    • 0:37:52 - 2.2章:入れ子になった構造化出力を生成する
    • 0:41:58 - 2.3章:旅程ジェネレータをリファクタリングする
    • 0:44:48 - 2.4章:ビューを更新し構造化データを表示する
    • 0:49:22 - 第3章:プロンプティングのテクニック
    • 0:50:25 - 3.1章:PromptBuilderでプロンプトを作成する
    • 0:53:23 - 3.2章:ワンショットプロンプティング
    • 0:55:31 - 3.3章:例で旅程ジェネレータを更新する
    • 0:59:11 - 4章:ストリーミング応答
    • 0:59:51 - 第4.1章:旅程ジェネレータを更新しストリーミングを行う
    • 1:02:52 - 4.2章:ビューを更新しストリーミングコンテンツをレンダリングする
    • 1:08:16 - 5章:ツールの呼び出し
    • 1:11:12 - 5.1章:FindPointsOfInterestToolを構築する
    • 1:16:32 - 5.2章:FindPointsOfInterestToolへのアクセス権をモデルに付与する
    • 1:23:07 - 5.3章:旅程ジェネレータを更新しツールを使用する
    • 1:27:39 - 6章:パフォーマンスと最適化
    • 1:34:20 - 6.1章:モデルを事前ウォームアップする
    • 1:36:56 - 6.2章:プロンプトを最適化する
    • 1:41:16 - まとめと次のステップ

    リソース

    • Foundation Models Code-Along Project Files
    • Foundation Models Code-Along Instructions
      • HDビデオ
      • SDビデオ
  • このビデオを検索

    皆さん こんにちは「Code along with the Foundation Models framework」へようこそ Shashankです AppleでTechnology Evangelistを務める 私が本日は デバイス上の生成AI機能を 直接アプリに統合する方法を説明します プロンプティングの基本から 構造化された出力の生成 ストリーミング応答などを取り上げます Slidoには素晴らしい エキスパートチームがいます ご不明な点などは そちらにお問い合わせください まずは概要を簡単に説明し 全員の認識を合わせたいと思います WWDC24で発表されたApple Intelligenceは 大規模な基盤モデルを搭載し OSの中核に組み込まれています これに伴い導入された システムレベルの機能には 作文ツールやジェン文字などがあります 基盤となるモデルを求める多くの声を受け WWDC25ではFoundation Model フレームワークが発表されました パワフルなSwift APIを介して Apple Intelligenceの基盤と同じ デバイス上の大規模言語モデルに 直接アクセスできます デベロッパにとって オンデバイスアプローチには 大きなメリットがあります すべてローカルで実行されるため ユーザーデータのプライバシーが保たれます 機能は完全にオフラインで動作し アカウントの設定や APIキーの管理は不要です これらのリクエストについて 皆さんやアプリのユーザーに 費用はかかりません すべてOSの一部であるため アプリのサイズに影響はありません

    本日は一緒にアプリを構築します まずランドマークを一覧表示する 単純な静的アプリから始め 動的な旅行プランナーに変換します リッチな構造の旅程を カスタムUI用に生成し 生成された結果をリアルタイムで ストリーミングする方法を学びます カスタムツールへのアクセス権を モデルに付与し POI(名所や見どころ)を 見つける方法も学びます 最後にアプリを最適化して パフォーマンスを向上させます

    これから構築する最終的なアプリを 簡単に確認しましょう

    これが完成したアプリで 私のMacで実行されています セッションが終わる頃には 皆さんのMacでも実行されているはずです まずSwiftUIで構築した シンプルでクリーンな 有名ランドマークのリストから始めます ランドマークを選びます セレンゲティにしましょう

    詳細ビューをクリックすると ヘッダー画像と説明が表示されます 下部に旅程生成ボタンがあります クリックすると アプリがデバイス上のモデルを呼び出して 完全な3日間の旅行プランを生成します 画面をよく見ていてください

    UIがリアルタイムで自動的に構築されます 最初にタイトル、次に説明 そして日ごとのプラン このストリーミングAPIは 4章で組み込みます 動的で優れたユーザー体験を生み出します これは単なるコードブロックではなく リッチで構造化された応答です これは2章で学びます 日には固有のセクションがあり タイトル、サブタイトル、地図が含まれます ホテル1やレストラン1などの 名前が表示されています これらはランダムではありません このアプリはツール呼び出しを使って このような名前を取得します これは5章で説明します Foundation Modelフレームワークにより リッチで構造化された インテリジェントな体験を創出し アプリにシームレスに統合できます 本日はこれを構築していきます 本日のCode Alongを最大限に活用するには 3つのリソースが重要になります まずはXcode スタートアッププロジェクトです 定型的なUIとアセットが すべてそろっています developer.apple.com または Developerアプリで視聴している場合は ページ下部のリソースの中にあります YouTubeの場合は説明にリンクがあります 2つ目はWebページの手順ガイドです 信頼できる情報源で あらゆる手順とコードスニペットが 含まれています コピー&ペーストすれば タイプミスを避けられます 最後はライブストリームで 私や舞台裏のエキスパートチームが 質問に答えます このプロジェクトを皆さんと構築しながら 各変更の理由を説明します 設定に移動してプロジェクトを設定する前に 本日のセッションのシステム要件を 簡単に確認しましょう どなたでも視聴しながら学習できます ただし 私と一緒にライブで コーディングする場合は macOS TahoeとXcode 26を搭載した AppleシリコンベースのMacが必要です Apple Intelligenceを オンに設定しておくこともお忘れなく 私は本日Macで直接アプリを 構築して実行しますが 皆さんはXcode 26を使って iOS 26を搭載した最近のiPhoneで 試すこともできます

    ではCode Alongガイドの 前提条件のセクションに進み スタートアッププロジェクトを ダウンロードして設定しましょう

    ガイドには前提条件の セクションが表示されます まずリンクをクリックして プロジェクトファイルをダウンロードします

    ダウンロードすると zipファイルが見つかります macOSにより自動的に解凍されます フォルダ名は 「FoundationModelsCodeAlong」です これが本日使う スタートアッププロジェクトです 作業開始に必要なすべてのビュー、モデル、 プレースホルダコードが含まれています プロジェクトを開けば準備完了です

    最初に行うのはデベロッパチームの設定です プロジェクトナビゲータで プロジェクトファイルを選択します

    ターゲットを選択します

    「Signing & Capabilities」をクリックします 「Team」でドロップダウンを選択し チームを選択します

    すべてが正しく機能していることを 確認するには Xcodeツールバーで 「My Mac」を 実行デスティネーションとして選択します

    Run(実行)ボタンをクリックします プロジェクトが構築され実行されます command + Rを使ってもOKです

    ここにあるアプリに これから 生成AI機能を構築して追加します ここが出発点です このセッションで パワフルな機能を追加していきます ではスタートアッププロジェクトを さっと見てみましょう

    まず Playground.swiftファイルが 「Playgrounds」フォルダにあります ここでプロンプトを反復処理し Foundation Model APIを単独でテストします アプリ全体を構築して 実行する必要はありません プロンプトに問題がなければ コードをアプリに移動します

    次は「ViewModels」フォルダです ここで最も重要なファイルは ItineraryGenerator.swiftです 基盤モデルセッションの作成と管理 フレームワークAPIの呼び出しや 結果の処理のコアロジックが すべてここにあります 最後に「Views」フォルダです

    ここにはすべてのSwiftUIコードがあります このCode Alongでは UIがほぼ事前構築されているので Foundation Modelフレームワークに 集中できます ここにはいくつかのファイルがあり わかりやすいように 主な編集ファイルには番号が付いています

    今回の取り組みでは 旅程ジェネレータの出力を取得し これらのビューに書き出して リッチでインタラクティブなUIを アプリに表示させます

    これらのファイルを確認すると 次のようにフォーマットされた コメントに気付くと思います Mark、Code Along、Chapter、番号 それぞれの番号は 章とセクションに対応しており Code Alongガイドと同じ番号になっています Xcodeの検索ナビゲータを使って 章の番号を検索し 未処理のコード変更をすべて確認できます

    ここに章の番号を入力すると コード変更がすべて表示されます 各手順が完了すると これらのコメントは削除され Code Along全体の進行状況を追跡できます

    ざっくり言うと 3つの簡単な手順を踏みます まずプレイグラウンドで試します 次にビューモデルにコアロジックを実装し 最後に結果をビューに表示します それぞれのビューを詳しく見てみましょう

    最初の画面は出発点であり ランドマークのメインリストです これにはLandmarksView.swiftを利用します 本日このファイルには触れません 設定は完了しているため 閲覧して目的地を選択できます ランドマークをタップすると 詳細画面に移動します このビューを制御するのはランドマークの DetailView.swiftファイルです 主な役割目はFoundation Modelフレームワークが デバイス上で利用できるかを確認し それに基づいて表示するUIを 決定することです

    次はLandmarkTripViewです 役割は旅程の生成ボタンを表示することです ここでモデルから返される未加工の 非構造化テキストを最初に表示します

    最後に旅程ビューで目標達成となります このビューでは豊富に構造化された 旅程データをレンダリングします このCode Alongの最後には 完成しているはずです

    それでは本題に入っていきましょう このCode Alongは6つの章で構成されています まずは基本です モデルのプロンプティングにより テキストを生成させる方法を学びます 次にシンプルなテキストだけでなく 構造化されたSwift型をモデルから取得し モデル出力をカスタムビューに 簡単にマッピングする方法を見ていきます 次にプロンプティングの テクニックについて説明します モデルの精度を向上させるには 質の高い例をプロンプトに直接 提供する必要があります 次に モデルの応答をストリーミングし UIをリアルタイムで更新して ユーザー体験を向上させる方法を学びます 次はツールの呼び出しです ツールにより モデルに独自のカスタム関数と データへのアクセスを提供し 機能をパワフルに拡張できます 最後はパフォーマンスの最適化により 生成機能を高速化し 応答性を高めます ではFoundation Modelフレームワークの 基本から見ていきましょう Foundation Modelフレームワークを使うと デバイス上の大規模言語モデル(LLM)に プロンプトを送信できます LLMはプロンプトの推論や テキストの生成を行えます 例えば パリへの3日間の旅程を 生成するようにリクエストすると モデルが詳細なプランを返します

    モデルのプロンプティングを開始するには セッションを作成する必要があります フレームワークはステートフル言語モデル セッションという発想に基づき構築され プロンプトと応答の履歴が すべて維持されます この章では 基盤モデルのプロンプトと セッションを理解します まずプレイグラウンドから始めて APIの感覚をつかみます 言語モデルセッションを作成し モデルから最初の応答を取得します そして簡潔な指示を追加して トーンとコンテンツを形作ります 次に様々な状態を適切に処理するための 可用性APIを見ていきます 理解できたらアプリに切り替え ビューモデルの旅程ジェネレータを更新し 未加工のテキスト出力をビューに表示します ではCode Alongガイドを確認しましょう

    1章の目標は デバイス上の言語モデルに 最初のリクエストを行うことです Xcode Playgroundを使って シンプルなテキストプロンプトを送信し 結果を確認します これはモデルの基本的な 動作の理解に役立ちます

    このコードブロックをコピーして XcodeのPlayground.swiftファイルに 貼り付けます 右上隅にある コピーボタンを使うと便利です コードを段階的に追加しながら 結果を説明していきます Xcodeを見てみましょう Playground.swiftファイルを開きます モデルのプロンプティングには 3つの簡単な手順が必要です 1つ目はFoundation Model フレームワークのインポートですが これは完了しています 次はプレイグラウンドの作成です

    プレイグラウンドマクロを使って プレイグラウンドを作成すると 右側にキャンバスが表示されます 表示されない場合は エディタオプションをクリックして 「Canvas」の横にチェックマークがあるか 確認してください 更新ボタンをクリックすると プレイグラウンドブロックに含まれる すべてのコードが実行されます 現時点では何も追加していないため 出力は表示されません モデルのプロンプティングの手順2は セッションの作成です

    ここで変数はlet session = LanguageModelSessionとなっていて プレイグラウンドキャンバスに session変数の内容が 自動的に表示されます 各種ツールについては 後の章で説明します トランスクリプトには モデルで行う会話が すべて含まれています

    手順3はモデルのプロンプティングです

    let response = try await session.respond ここでのプロンプトは "パリでの3日間の旅程を生成して"です 非同期リクエストであるため応答を待ちます

    これを行うと キャンバスの右側に response変数が表示され いくつかのプロパティを確認できます 最初はpromptです promptには"パリでの3日間の 旅程を生成して"と表示されています 次のプロパティは String型のcontentです これをクリックすると パリへの3日間の旅程が詳細に表示されます "こちらがパリを散策する3日間の旅程です パリを代表する定番スポットや体験を 盛り込んでいます"とあり 1日目午前/午後など 日ごとのプランが表示されます

    ガイドに戻って 重要なトピックについて説明しましょう Session.responseを最初に呼び出す際は わずかに遅延が発生することがあります これはリクエストを処理する前に デバイス上の言語モデルを メモリに読み込む必要があるためです 最初のリクエストにより システムがモデルを読み込むことで 初期遅延が発生します 対処方法は後の章で説明します また 出力は 構造化されていない自然言語テキストであり 読むのは簡単ですが カスタムSwift UIでは使いづらいです 次の章では 構造化された出力を生成するために 未加工のテキストではなく Swift型を使います 重要なのは 旅程全体のデータが 決してデバイスから離れないということです プライバシーは確保され オフラインで機能します おめでとうございます これで Foundation Modelフレームワークを使って デバイス上の基盤モデルを プロンプティングできました

    最後にもう1つ プレイグラウンドに戻りましょう

    Appleは常に モデルを改善したいと考えており 皆さんからフィードバックがあれば キャンバスのこれらのボタンを使って いつでも共有できます Code Alongガイドのセクション1.2 「指示によりモデルをガイドする」に 進みましょう

    ここでの目標はより一貫性があり より質の高い結果を得ることです それにはモデルに指示を出す必要があります 指示は単一セッション内の会話全体に対する 永続的なルール またはペルソナと考えてください このコードをもう一度 プレイグラウンドにコピーして実行します そして これらの指示を追加します

    Playground.swiftファイルに戻り

    instructions変数を新たに追加します "あなたの仕事はユーザーのための 旅程を作成することです" "各日にアクティビティ、ホテル、 レストランが必要です" "必ずタイトル、簡単な説明、 日ごとの計画を入れてください" このような指示を instructions引数を使って言語モデル セッションに渡すことができます

    指示を渡すと キャンバスはコード変更を自動的に検出し 結果を更新します 応答のコンテンツプロパティに 私たちが行ったリクエストが 追加されています アクティビティ、ホテル、レストランです ここで確認できます アクティビティ、ホテル、レストラン ここで疑問が浮かびます 指示とプロンプトは何が違うのでしょうか? 確認しましょう

    指示はペルソナの定義 ルールの設定 応答に必要な形式の指定に使います これはデベロッパが出すものです 一方 プロンプトは アプリユーザーが出します モデルはプロンプトよりも優先的に 指示に従うようトレーニングされているため ユーザーがプロンプトを使って ガイダンスを無視するようモデルに要求する プロンプトインジェクション攻撃からの 保護に役立ちます 原則として 指示は静的なものにし ユーザー入力を挿入しません

    また 指示はセッションの ライフサイクルを通じて維持されます インタラクションはすべて セッションのトランスクリプトに記録され 最初の指示が常に最初のエントリとなります

    さて モデルのプロンプティングにより 応答を得られるようになりました ただし 考慮すべきこととして アプリがApple Intelligenceを利用できない デバイスで実行された場合 機能しない機能が表示されると ユーザー体験が低下する可能性があります 例えば デバイスが Apple Intelligenceに対応していない場合や デバイスがApple Intelligenceに 対応していても オフになっている場合 または モデルアセットがダウンロード中で 使用準備ができていない場合です このようなケースの対処方法を 詳しく見ていきましょう Code Alongガイドに戻ります Code Alongガイドのセクション1.3 「Handling Model Availability (モデルの可用性を処理する)」

    モデルには可用性のためのAPIがあります

    Xcodeでこのswitchブロックの各ケースと それらがアプリにとって何を意味するのか 詳しく見てみましょう

    Playground.swiftファイルに戻ります プレイグラウンドの優れた機能として 同じSwiftファイルに ブロックを複数追加できます

    新しい#Playgroundブロックを追加し 可用性コードを入れました よし

    APIを見てみましょう 複数のプレイグラウンドの出力を 確認することもできます 2番目のプレイグラウンドは キャンバスでは2番目のタブとなります 私のMacは Apple Intelligenceに対応しています 基礎モデルが利用可能で 準備OKということです これらのケースを詳しく見ていきましょう

    最初のケースはavailableです これは利用可能ということで モデルが読み込まれ 生成リクエストを行う準備ができています

    unavailable(.deviceNotEligible)と 表示されている場合 モデルがApple Intelligenceに 対応していないということです 生成UIを非表示にし 別のエクスペリエンスにする必要があります

    unavailable(.appleIntelligenceNotEnabled) については デバイスは対応しているものの Apple Intelligenceは オフに設定されています ユーザーに有効にするよう促しましょう

    Unavailable(.modelNotReady)は 一時的な状態で モデルアセットがダウンロード中 などという場合です ベストプラクティスは ユーザーにやり直すよう指示することです これらの機能をアプリに追加する 準備が整いました Code Alongガイドを進めましょう

    1章のアプリセクションに来ました このセクションでは ランドマークのDetailView.swiftを更新し モデルの可用性を確認して 利用できない場合にメッセージを表示します コードブロックをコピーします マークされたコメントを検索して コード変更を挿入する場所を確認します ではやってみましょう Xcodeプロジェクトに移動し

    「Views」フォルダにある LandmarkDetailView.swiftをクリックします 繰り返しになりますが 検索ナビゲータを使って この章で行うすべてのコード変更を いつでも確認できます まずはモデルインスタンスを追加します

    private let model = SystemLanguageModel.defaultとなります プレイグラウンドで使ったコードと まったく同じであるため 見覚えがありますね コードを追加したので このコメントを削除します 検索ナビゲータから消えます 次に行うコード変更は placeholder availabilityコードの削除です 完全に便宜上のものであるため削除します そうすると Xcode上にすぐに 可用性が定義されていない旨が 表示されますが モデルがあるため修正は簡単です model.availabilityです

    このコードも削除します よし このコード変更により ファイルにすべての変更が加えられました

    ここに追加した可用性チェックは プレイグラウンドと同じものなので 見覚えがあるはずですが どのようにテストしますか? 複数のテストデバイスを使えない 場合もあるでしょう ありがたいことに簡単な方法があります プロジェクトのスキーム設定には 利用不可をシミュレートする オプションがあります 見てみましょう 「FoundationModelsCodeAlong」を クリックし 「Edit Scheme」をクリックして 下にスクロールすると オプションの1つに 「Simulated Foundation Models Availability」があります

    これをクリックすると いくつかのオプションがありますが どのオプションも プレイグラウンドで扱ったものであるため 見覚えがありますね 「Apple Intelligence Not Enabled」を クリックして閉じ

    アプリを構築して実行します

    ここにアプリがあります サハラ砂漠を選択しましょう メッセージがありますね 「Trip PlannerはApple Intelligenceが 有効になっていないため 使用できません」

    これと同じメッセージが 利用不可のビューにもあります

    OKです これを元に戻して Code Alongを通して 機能を追加できるようにします

    よし Code Alongガイドの セクション1.5に進みましょう

    アプリの旅程ジェネレータを更新する 準備が整いました 言語モデルセッションを初期化し generateItineraryという関数を定義して ビューからモデルを呼び出します Code Alongですでに実装済みであるため コードには見覚えがあるはずです これをアプリに移行します ではXcodeに移動して ItineraryGenerator.swiftファイルを開きます 「ViewModels」フォルダにあります

    検索ナビゲータを使って すべてのコード変更を探し 進捗状況を追跡します よし ItineraryGenerator.swiftファイルで 最初に行う変更は セッションプロパティの追加です さっそくやりましょう session変数を定義し LanguageModelSessionとします

    Xcodeにセッションが初期化されていない 旨が表示されるので このセッションをinit関数で 初期化します

    OK 追加できました

    instructions変数を追加し プレイグラウンドと同じ指示を使います "あなたの仕事はユーザーのための 旅程を作成することです" "各日にアクティビティ、ホテル、 レストランが必要です" "必ずタイトル、簡単な説明、 日ごとの計画を入れてください" セッションは LanguageModelSessionで 指示に渡します さて 3番目かつ最後の変更は generateItinerary関数の更新です この関数をビューから呼び出すと プロンプトを送信して 応答を受け取ることができます ではコードを変更しましょう

    OK 追加できました まずはlet prompt =

    Generate a \(dayCount)-day Itinerary to \landmark.name dayCountはデフォルトで3 landmark.nameはユーザーがアプリを開いて クリックするランドマークの名前です この名前を収集しプロンプトに渡します その特定のランドマークに対する 応答を生成できます 次はlet response = try await session.respond これをプロンプトに渡します 最後に response変数は 「.content」プロパティです プレイグラウンドキャンバスにもありますが これには構造化されていない あらゆる自然テキストが含まれます この文字列を旅程コンテンツに割り当てます

    これにはビューモデルのコード変更が すべて含まれるので これでビューから呼び出す準備が整いました Code Alongガイドの セクション1.6に進みます 1章最後のセクションです LandmarkTripViewを更新し 旅程ジェネレータから出力を取得して アプリに表示します これらのコメントに従って コードを変更します Xcodeに移動します

    「Views」をクリックします

    そして「LandmarkTripView」 よし

    最初に行うコード変更は ビューモデルの ItineraryGeneratorクラスに ローカル変数を追加することです

    ItineraryGeneratorという Itinerary型ができたので コメントを削除します

    次に行うコード変更は ビュー読み込み時のインスタンスの作成です

    タスクは次のように変更します let generator = ItineraryGeneratorとします これはViewModelクラスで ランドマークに渡します ユーザーがクリックした ランドマークに関する情報であり これをプロンプトに渡します ItineraryGeneratorはここで保持します いま行ったコード変更は削除します

    次に行う変更はビュー自体の更新です ビューを見てみましょう デフォルトではブール型変数 requestedItineraryとなっていて falseに設定されています falseに設定されているので 最初のビューをここに読み込みます テキストフィールドには ランドマークの名前が入ります landmark.nameとなっています こちらは簡単な説明で landmark.shortDescriptionとなっています これが表示されるのは ユーザーが旅程を生成するよう モデルに要求していない場合です ですが リクエストされた旅程が falseに設定されている場合は 新しいビューを読み込み モデルの出力を入力する必要があります 今から実装してみましょう このelseケースを削除し

    新しいelseケースを追加します ここでは else if let content = itineraryGenerator? .itineraryContentです 前述のように itineraryContentは文字列変数であり モデルの出力となります そのコンテンツを取得し テキストビューで更新します 変更を行ったのでコメントも削除します

    あと少しです このビューで最後にもう1つ変更を行います スクロールダウンすると

    定義したボタンが画面下部に表示されます 現在このボタンは非表示になっています ここで2か所小さなコード変更を行います 1つは ボタンを表示させたいので これをコメントアウトするか このようにそのまま削除します ここにコードを挿入し

    ユーザーがボタンをタップしたときに 旅程が生成されるようにします では追加しましょう

    await itineraryGeneratorとして generateItinerary関数を呼び出します 前述のように この関数はプロンプトを受け取り モデルに渡して出力を取得します この章で行うコード変更は以上です これで アプリを構築して 実行する準備が整ったので この実行ボタンをクリックして アプリを構築し実行します

    これがアプリです

    サハラ砂漠をクリックすると 「Generate Itinerary」ボタンが表示され この「Generate Itinerary」ボタンを クリックすると プロンプトと指示が デバイス上のLLMに送信され 応答が非同期に トークンごとにデバイス上で生成されます

    旅程が表示されましたね おめでとうございます Foundation Modelフレームワークを使って 完全に機能するデバイス上の生成AI機能を 初めて構築しました 数行のSwiftコードだけで Apple Intelligenceの力を 引き出すことができました

    素晴らしいですが ここにあるのはテキストのかたまりです ホテル名を引き出して地図上に 表示する場合はどうすればよいでしょうか? このままではリッチな体験とはいえません これについては2章の ガイド付き生成で扱います モデルから直接Swift構造体を使って 出力を取得する方法について説明します では 1章を簡単におさらいしましょう

    この章では セッションを作成し モデルのプロンプティングにより 基本的なテキストを 返す方法を学びました 指示によりモデルの出力を ガイドする方法を確認し 可用性APIを使って 様々な可用性の状態を処理する 方法を説明しました 最後に ビューモデルとビューを更新し これらの機能をアプリに統合しました

    1章は以上です

    未加工のテキストを 生成できるようになったので 次はモデルから構造化データを取得して リッチなUIを構築してみましょう

    LLMと連携する際の 基本的な課題から始めます デフォルトでは 構造化されていないテキストが表示されます 生成した旅程などです 人間が読むことはできますが アプリデベロッパにとっては 作業しづらい場合があります 例えば 1日目のホテルを確実に抽出して 地図上にプロットします 複雑な文字列解析コードを 記述する必要がありますが モデルの出力が変更されると 破損する可能性があります 代わりに必要なのは アプリのロジックに直接マッピングされる 構造化データです

    ネストされた高度な構造を Swift構造体を使って実装する 必要があります この旅程オブジェクトには オブジェクトの配列や アクティビティオブジェクトの 配列などが必要です ここでガイド付き生成が登場します Foundation Modelフレームワークが 提供するAPIにより 出力の形式を正確に指定できます Swift構造体がある場合は @Generableを適用するだけです モデルはネイティブのSwift型を使って 構造化データを生成できます

    この章ではまず プレイグラウンドで 単純な構造体を定義し 生成可能なマクロを適用します それに基づいて構築し ネストされた複雑なデータ構造を モデルで生成できるようにします 最後に アプリに戻り 旅程ジェネレータをリファクタリングして 新たに構造化された旅程を出力し ビューを更新してリッチなUIで表示します

    Code Alongガイドに戻ります

    2章「構造化された出力を生成する」に 進みます 私たちの目標は シンプルな文字列だけではなく 構造化されたタイプセーフなSwiftデータを モデルから直接取得することです これにより 脆弱な文字列解析なしで リッチなカスタムUIを構築できます

    このコードをプレイグラウンドにコピーして 出力を確認します SimpleItineraryという 新しい構造体を追加し 何が起きるかを見ていきましょう Xcode Playgroundファイルに移動し これらのコードを変更します

    追加したばかりの 2番目のプレイグラウンドを削除し ここに

    SimpleItineraryという 新しい構造体を追加します この構造体の内容や これを基盤モデルコードに組み込んで 出力を生成する方法を説明します まず この構造体には いくつかのプロパティがあります titleはString型です descriptionもString型です daysはString配列です

    モデルでこれらのフィールドを生成し ガイドを提供することで 情報を追加できます ガイドにはdescription引数があり "An exciting name for the trip" となっています この変数について タイトルを生成する必要があることを モデルに伝えるもので 説明にも "A short, engaging description of the trip"とあり 日数も同様です 今できるのは これをモデルに提供することで それにはgenerating引数を使います 先ほどはsession.respondと プロンプトだけでしたが 新しいgenerating引数を追加して

    指定するのは

    SimpleItinerary.selfです

    キャンバスを更新します コードが実行されるので 出力を見ていきましょう

    ここにresponseがあります contentプロパティを詳しく見てみましょう 先ほどこのコンテンツは文字列でした よく見ると struct SimpleItineraryとなっています これを開きます ここで定義した構造体と 出力が1対1で一致しています ですから タイトルは "パリで過ごす至福のひととき"です これがtitleタイトルプロパティです ここにdescriptionがあります ここにはString配列ですね daysはString配列で 日ごとのアクティビティプランが 表示されます

    よし Code Alongガイドに戻ります

    セクション2.2です 旅程は文字列やString配列である 必要はありません ネストされた構造体も追加できます 完全な旅程構造体を確認し アプリで構築しましょう ここでコードを少し変更します ここではSimpleItineraryを Itinerary.selfに置き換えるだけでOKです 一連のコードを作って この旅程構造体について説明します Xcodeに戻ります

    シンプルな旅程を削除し

    SimpleItineraryを Itineraryに置き換えます さて 旅程はどのようになるでしょうか? command +クリックで 定義を開くか 「Models」フォルダに移動して Itinerary.swiftファイルをクリックすると Itineraryという 新しい構造体が表示されます シンプルな旅程にはなかった フィールドもありますね 詳しく見てみましょう titleはString型です descriptionやrationaleがあります daysを見ると String配列ではなくなっています DayPlanの配列であり それ自身の構造体になっています 独自のtitle subtitle destination activitiesもあります これはActivityという別の構造体の配列で type、title、descriptionがあり 型はenumです これもGenerableです enumを使えば モデルは事前定義された 特定のcaseを生成できます 例えば ここでの型はsightseeing foodAndDining、shopping、 hotelAndLodgingです 一番上までスクロールすると 別の方法でモデルによる 生成を制約できます enumを使ってdestinationNameを表します anyOfというガイドがあり ModelData.landmarkとなっています これでモデルに 目的地名を生成するよう指示できます アプリを開くとランドマークの1つとして 表示されるはずです セレンゲティやグランドキャニオン サハラ砂漠などです 出力はこれらのうちの1つになります 旅程の構造体はこのようになります アプリでは実際にこれが使用されます Swift Playgroundに戻ります 申し上げたように 目的地名は リストにある名前のうち1つが表示されます パリはリストに入っていません 実際にリストに載っているものに変更します グランドキャニオンにしましょう

    Canvasがコード変更を検出するので 出力を見てみましょう

    ここにresponseがあります Contentが含まれており よく見ると struct Itineraryであり SimpleItineraryから 更新されています これを開きます title、destinationName、 description、rationale、daysがあり DayPlanの配列になっています これを開くと複数の日があり アクティビティはActivity構造体の 型などになっています

    ここで注意すべき点として @Generableを適用すると 完全に構成可能になります フレームワークでは この複雑なオブジェクト全体が トップダウンで構築され 構造的な正確性が確保されます これをアプリに統合します ではCode Alongガイドを進めましょう 2章のアプリセクションに入ります このセクションでは 旅程ジェネレータを更新し プレイグラウンドでテストした 旅程のGenerable構造体を 使用できるようにします

    このコードをもう一度コピーし コード変更を行っていきましょう 旅程ジェネレータに移動します

    「ViewModels」フォルダにあります

    検索ナビゲータで「Chapter 2」に設定すると この章で行うコード変更を すべて確認できます

    ここで行う最初のコード変更は itineraryContentの更新です Stringではなく Itineraryの型にする必要があります まずこの変数の名前を「itinerary」に変更し Stringを「Itinerary」に更新します

    コード変更を行ったので コメントは削除できます

    次に行うコード変更ですが generateItinerary関数まで下にスクロールすると すぐに気付くと思いますが itineraryContentが存在しません 先ほど追加したためです itineraryに更新しましょう エラーが発生していますが これは現在 session.respondから来たコンテンツが 文字列だからです

    前回のプレイグラウンドと同様に generating引数を追加し Itinerary.selfと入力します

    これでモデルはitinerary型の値を 出力できるようになりました コード変更を行ったので コメントを削除します

    さて 最後の変更は 指示で提供している構造に関する 追加のガイダンスの削除です "各日にアクティビティ、ホテル、 レストランが必要です" "必ずタイトル、簡単な説明、 日ごとの計画を入れてください" これらの情報はすべて 旅程のGenerable構造体にあります 指示で再度提供する必要はありません Generableを使うもう1つの利点は プロンプトがとてもシンプルになり パフォーマンス向上にも役立つことです このコメントは削除します

    このセクションで行うコード変更は以上です 旅程ジェネレータビューモデルを更新して Generable構造体を 生成できるようになりました ではセクション2.4 「ビューを更新し構造化データを 表示する」に進みましょう このセクションでは LandmarkTripViewを更新し 旅程ビューを生成します 前のセクションで見た 未加工のテキストではありません 非常に簡単なコード変更であるため さっそくLandmarkTripViewに進みましょう

    「Views」フォルダの2番目にあります

    コード変更はここで行います

    先ほど モデル出力が生成されたときに このビューを読み込みましたが 文字列はもう生成されないので テキストビューは使えません 最初にこれを更新する必要があります 続いてこれをテキストではなく 別のビューで更新します 旅程からフィールドを抽出し リッチなUIに入力できるようにします これを更新されたビューに置き換えて どうなるか見てみましょう

    このようになりました ガイドからコピー&ペースト することもできます 詳しく見てみましょう Itinerary = itineraryGenerator?.itinerary となりました テキストビューではなく ItineraryViewがあり ランドマークや 生成された旅程を取り込みます この旅程ビューがあるのは 「Views」フォルダですが まだ見ていないので 確認してみましょう 3番目のファイルですが command +クリックで開くこともできます この章ではこのファイルに コード変更を行いませんが ここにあるコメントによると おそらく変更が必要です 後の章で行うことになりそうです このビューではモデルが生成した 旅程を取得し フィールドを抽出して 最初のデモで見たリッチなUIを作成します bodyを詳しく見てみると 入力された旅程のタイトルや 説明を抽出でき 下にスクロールすると 日ごとのアクティビティを抽出できます DayViewという専用のビューがあり ForEachを使ってこれらをループさせ すべてのプロパティを抽出して レイアウトします 文字列を解析して更新するよりも はるかに簡単です

    ではスライドに進みましょう ガイド付き生成の主な利点は 構造的な正確さを 根本的に確保することです それには制約付きデコードという 手法を用います この手法により モデルが生成する文字列、数値、配列や デベロッパが定義する カスタムデータ構造などを 制御できるようになります

    また プロンプトがとてもシンプルになり 望む動作に焦点を当てられるようになります 特定の出力形式に合わせた モデルのプロンプティングは不要です モデルの精度向上にもつながり 最適化により推論が高速化します まとめると この章では モデルから構造化データを 取得する方法を確認しました Generableマクロを使って 独自のSwift型を定義し それらをネストして複雑なデータ構造を 構築する方法を確認しました

    アプリを更新し リッチなユーザーインターフェイスで 構造化データを生成しレンダリングしました

    このモデルを構築し 行ったすべての変更を確認しましょう

    これがアプリです サハラ砂漠をクリックして 旅程を生成しましょう 先ほどと同様に プロンプトと指示を受け取り モデルに送信しますが 今回は大量のテキストではなく 旅程型を生成し すべてのフィールドを抽出して 新しい旅程ビューを使って アプリに入力します

    いいですね

    この章は以上です

    さて これで 構造化データをモデル出力として 入手できるようになりました ここでギアを切り替えて 出力の品質や 一貫性を向上させるために プロンプティングのテクニックに注目します 優れたプロンプトは モデルにすべきことを伝え より効果的に出力を引き出します 高品質の例を Generable型のインスタンスとして 直接プロンプトに追加できます

    これにより 求める応答の型に応じて モデルにより良いアイデアを提供できます この章では 生成されるコンテンツの 品質向上に焦点を当てます まずはプレイグラウンドに戻り Prompt Builder APIを使って 動的なプロンプトを作成します 次にワンショット プロンプティングについて確認し プロンプトに高品質の例を提供して モデルの精度を向上させます 最後に 学んだことを アプリの旅程ジェネレータに統合します

    ではCode Alongガイドを進めましょう

    3章「プロンプティングの テクニック」に入ります ここでの目標はモデルの出力の品質と 信頼性を向上させることです まず Prompt Builder APIを使って 動的プロンプトを追加する 方法を説明します

    プレイグラウンドに移動して見てみましょう まずはこのコードブロックを Playground.swiftファイルにコピーします

    Xcodeで Playground.swiftファイルに移動します

    いいですね ここで行う重要なコード変更は Prompt Builder APIを使った プロンプトの追加です 先ほどsession.respondに to引数を追加しました "グランドキャニオンでの 3日間の旅程を生成して" という文字列です プロンプトを文字列として 定義するのではなく Prompt Builder APIを使って 値をクロージャに渡すことができます 主な利点はSwift条件分岐 などを追加できることです このすぐ上に ブール型変数があり 現在trueに設定されています Prompt Builder API内で このブール型を使って 条件付きでプロンプトを更新します kidFriendlyブール型がtrueの場合 次の追加情報をプロンプトに挿入します "旅程は子ども向けである必要があります" session.respondの呼び出しを更新し この新しいプロンプトを追加して

    キャンバスを更新します

    出力を見てみましょう

    response変数 contentがあります rationaleを開いて確認します "この旅程は子どもに安全で 魅力的かつ教育的な体験を提供します 年齢に適したアクティビティや 宿泊施設を利用しながら グランドキャニオンの 美しい自然を満喫できます" モデルがリクエストに応えていますね 条件分岐になっています この利点は プロンプトがすばやく動的に変わることです ユーザーがアプリで選択した内容や ユーザーの環境設定を反映し プロンプトが更新されます

    では Code Alongガイドの セクション3.2に戻りましょう ここでの目標はより高度な プロンプティング手法である ワンショットプロンプティングを使って 高品質の応答を モデルに正確に示すことです では Code Alongを進めましょう

    Prompt Builder APIのディスクロージャ内で 別のコードを追加します "望ましい形式の例です 内容をコピーしないでください" その例がこちらです 見てみましょう Itinerary.exampleTripToJapanと なっています これは何でしょう? これをcommand +クリックするか 「Models」フォルダに移動し 旅程をクリックして下にスクロールします 日本への旅行の例がここで定義されています 最初に気付くのは 例を含む大きな文字列 ではないということです これは旅程のGenerableの インスタンスであり プロパティがすべて入力されています ここではtitle、 destinationName、description、 rationale、days そしてすべてのプロパティが 手動で入力されています プレイグラウンドに戻ります ここには出力があります 追加情報が含まれていて ワンショットの例で 応答のトーンと品質をガイドしています

    重要なのは Itinerary.exampleTripToJapanを プロンプトに直接に埋め込んでいる点です 模範的な例といえます コンテンツをコピーしないよう モデルに明示的に伝えます データを繰り返すだけでなく 型と構造から学ぶためです ではガイドに戻りましょう

    3章のアプリセクションに進みます ワンショットプロンプティングアプローチを アプリに統合します

    ここで行うコード変更は 「ViewModels」フォルダにある 旅程ジェネレータで プロンプトを更新し例を追加することです では コードを変更しましょう Xcodeに戻ります 「ViewModels」にある 旅程ジェネレータをクリックします 検索ナビゲータを使って セクション3をクリックします ここで行う必要がある コード変更が表示されます

    generateItinerary関数内で プロンプトを定義し このプロンプトを置き換えます

    前のプロンプトを削除します

    プレイグラウンドのときと同じように 「let prompt =」とPrompt Builder API を使って クロージャを渡します 以前と同じ文字列が含まれていますが 追加情報として Itinerary.exampleTripToJapanという Itinerary型があります すべてのガイダンスが 含まれているだけでなく このプロンプトの一部である スキーマも含まれています

    変更を行ったので コメントを取り除くことができます 3章でやるべき変更はすべて完了したので アプリを構築して実行する準備ができました アプリを構築し確認しましょう

    セレニティを選択し 「Generate Itinerary」をクリックします モデルがプロンプトや指示 追加の例を受け取り 最終的な出力を生成します 見てみましょう

    アプリはしっかり機能しています これを閉じてスライドに進みましょう この章ではプロンプティングの テクニックに焦点を当てました プロンプトビルダーを使って プロンプトを動的に構築する方法を学び ワンショットプロンプティングを使って モデルの出力の品質と一貫性を向上させる 方法を確認しました これらを適用しアプリを更新して プロンプトに詳細な例を追加しました @Generableは構造を強制しますが ワンショットの例はモデルに 構造内の関係と型を伝えます モデルは与えられた例から 望ましいトーンを認識し アプリに設定されているトーンに合わせて テキストを生成します

    出力に必ずしも 大きな違いが出るわけではありませんが 生成されたコンテンツの品質を 大幅に向上させる 重要な要素です プロンプトのテクニックに関する セクションは以上です

    キリがいいので小休止しましょう 10分休憩を取ります この時間を利用してコードを確認したり コーヒーを飲んだり 足を伸ばしたりしてください 休憩の後には 実に興味深いトピックが待っています ストリーミングで UIをリアルタイムで更新し ツールの呼び出しでモデルの機能を拡張して パフォーマンスの最適化で締めくくります では10分後に またあとで

    おかえりなさい リフレッシュできたでしょうか では再開しましょう 質の高いプロンプトを用意して 応答をリアルタイムでストリーミングし ユーザー体験を向上させましょう この章では 旅程ジェネレータの リファクタリングに焦点を当て ストリーミングAPIを使用し モデルの応答をストリーミングすることで ユーザー体験を向上させます モデルによる応答の生成に合わせて 部分的に生成されたコンテンツを処理します ビューを更新して生成中の旅程を レンダリングし 応答性を向上させます ではガイドを進めましょう 4章「ストリーミング応答」に入ります この章の目標は 応答をストリーミングし 生成しながら旅程を表示して ユーザー体験を劇的に向上させることです まず 旅程ジェネレータ ファイルを更新します このセクションには プレイグラウンドが含まれていません アプリで直接やる方が ストリーミング応答を理解しやすいからです では Xcodeに移動して 旅程ジェネレータを開きましょう

    検索ナビゲータを使って4章に更新し ここで行うすべてのコード変更を確認します 旅程から見ていきます 最初に行う変更は itinerary変数を更新し Itinerary.PartiallyGenerated型に することです

    PartiallyGeneratedとは何でしょうか? すべてのプロパティがオプションである 構造体のミラー版と考えてください @Generableがこれを自動的に定義します 随時受信する データを適切に表すことができます これが最初のコードの変更です コメントを削除します

    次に行うコード変更は下の方ですね 前述のように generateItinerary関数には session.respondへの 非同期呼び出しが含まれていて プロンプトを渡し Generableを渡して 出力を受け取ります それに代わって必要なのは 応答を生成し 応答をストリーミングするモデルです そこでこのコードを置き換えて session.streamResponseという 新しいAPIにします 見てみましょう

    session.respondを session.streamResponseに置き換え 残りの引数は同じにしました 引き続きプロンプトを渡し generating引数にitineraryを追加します しかし ここにawaitがありません 代わりにあるのが streamという非同期シーケンスです これはループして すべての出力を旅程に割り当てます これらすべてのオプションが含まれます try await partial response in streamを partialresponse.contentを使って 抽出することで その時点で生成されたものの スナップショットを 毎回取得できます コードを変更したので コメントを削除します

    これには旅程ジェネレータに加えるべき すべてのコード変更が含まれています Code Alongガイドに戻り セクション4.2に進みます ビューを更新する準備が整いました 部分的に生成されたフィールドは オプションであるため if let文を使って オプションを確実にアンラップできます このセクションではそれを行います 今から更新する旅程ビューは 前の章でプレビューしたばかりですが そのままコード変更を行います Xcodeに移動し

    「Views」フォルダをクリックして

    旅程ビューをクリックします

    一番上に旅程があるので PartiallyGenerated型で更新します ビューモデルで定義したものですね ここにあるすべてのGenerableに対して コード変更を行う必要があります 旅程だけでなく ネストされたすべてのGenerableもです 一番下までスクロールすると DayPlanを含むDayViewがありますので これもPartiallyGeneratedにします コード変更を行ったらコメントを削除します さらに下に行くとActivity配列があります ここも変更しておきましょう

    以上がGenerableへの 主なコード変更となります 一番上まで戻ると Xcodeでいくつかのエラーが出ています ですから さらにコード変更が必要ですが これらはオプションであるため アンラップする必要があります やってみましょう

    このようになりました if let title = itinerary.titleです if letはオプションを適切に処理できます ここにtitleがあるので itineraryから抽出する必要はありません ですから削除します それでtitleを処理できます descriptionでも同じ手順を繰り返します

    if letを使ってテキストビューを更新し descriptionを追加します

    rationaleでも繰り返します

    別のフィールドでも繰り返します daysですね

    このような感じです アクセスするすべての旅程フィールドや プロパティについてこれを繰り返し 確実にアンラップします では ここでも これまでと同じようにやっていきましょう Code Alongガイドに戻り 完全に更新されたファイルの コピー&ペースト作業を すべてのプロパティに対して行います スクロールするとステップ3がありますので すべてのプロパティに対して繰り返します title、description、 rationaleを変更しましたが すべてのDayPlanとActivityビューで 行う必要があります このCode Alongではどうするかというと 「Show the updated Views」をクリックすると すべてのコード変更が表示されます ここでは 右上にある 「Copy」ボタンをクリックし Xcodeの ItineraryView.swiftファイルに戻り すべてのコードを 更新されたコードに置き換えます 検索ナビゲータで確認し コメントが残っていなければ コード変更は完了です いくつかのコード変更をお見せしましたが これをすべてのプロパティに対して 行う必要があります 4章で行うコード変更は以上です 簡単にまとめると ビューモデルに行う変更では PartiallyGeneratedを使って ビューを更新し オプションをアンラップしました これで このアプリを実行する 準備が整いました 実行をクリックすると アプリが構築され実行されます アプリが表示されました サハラ砂漠をクリックし 「Generate Itinerary」をクリックします

    先ほどの非同期呼び出しとは異なり 生成に合わせて 応答がストリーミングされています これは優れたユーザー体験であり アプリユーザーは すべての旅程が読み込まれる前に コンテンツの利用を開始できます

    この章では ユーザー体験を大きく向上させました アプリをリファクタリングして ストリーミングAPIを使い PartiallyGeneratedコンテンツを ビューモデルで処理する方法を学びました 最後に ビューを更新して 生成されている旅程をリアルタイムに 表示できるようにしました

    ストリーミング応答に関する 4章は以上です これでアプリが良い感じになりました もっとスマートにするために ツールの呼び出しで モデルに新しい機能を追加しましょう

    まず ツールの呼び出しの概念を説明します プロンプトに指定する内容に加えて モデルにはトレーニングデータから得る 独自のコア知識があります ただしモデルはOSに組み込まれているため その知識は時間的に固定されています 例えば 現在のクパティーノの 天気について尋ねても モデルには確認する手段がありません リアルタイムデータや動的データが 必要な状況に対応するため ツールの呼び出しをサポートしています 仕組みはこうです セッションのトランスクリプトがあります セッションにツールを指定した場合 セッションは指示に加えツールの定義を モデルに提示します 例では プロンプトはモデルに 目的地を伝えます

    ツールを呼び出すことで応答を強化できると モデルが判断した場合 1つ以上のツール呼び出しが生成されます この例では ツール呼び出しが2件生成されています レストランとホテルのクエリです この段階で Foundation Modelフレームワークが これらのツール用に記述したコードを 自動的に呼び出します 次に ツールの出力をセッションの トランスクリプトに自動的に挿入します

    最後に モデルはツールの出力と すべてのトランスクリプトを 最終的な応答に組み込みます

    これまで見てきたように モデルは非常に創造的であり リクエストのたびに 若干異なる旅程を提示します このランダム性は創造性には適していますが 予測可能な動作が必要な場合には 不向きかもしれません ツールの呼び出しのような高度な機能 特にテストやデバッグでは モデルが一貫して動作することを 確認する必要があります 想定通りに ツールが呼び出されるようにしたいですね これを実現するために リクエストに小さな変更を加えます 生成オプションAPIを使って 貪欲サンプリングを行います 貪欲サンプリングでは モデルは創造的にならず 常に最も明白な次のトークンを選択します これにより決定論的な出力を得られます アプリではモデルが毎回 確実にツールを呼び出します

    この章では POIを見つけるツールに着目します そのツールを言語モデルセッションに提供し モデルに使用方法を指示します アプリでは ツールを旅程ジェネレータに統合し 現実のデータを旅程に取り込みます ではCode Alongガイドを進めましょう 5章「ツールの呼び出し」ですね

    旅程にはモデルが生成した ホテルやレストランの名前が表示されますが 最新ではない可能性があります ここでの目標はモデルにツールを提供し Swiftコードを呼び出して 指定したホテルやレストランの 名前を取得することです

    コード変更を行って 最初にツールを構築し その後アプリで使ってみましょう Xcodeに移動して

    「ViewModels」フォルダをクリックすると 新しいファイル FindPointsOfInterestToolがあります

    クリックします ここにはFindPointsOfInterestToolという クラスがあり Toolプロトコルに準拠していて いくつかのプロパティを 定義する必要があります 順に見ていきましょう では コード変更を行って どのようなことが起きるか説明します 最初に行う変更は ツールへの名前と説明の追加です やってみましょう

    ツールのnameは "findPointsOfInterest"で descriptionは "ランドマークのPOI見つける"とします これはモデルがツールを呼び出す タイミングを理解する上で重要です nameとdescriptionを使って ツールを呼び出すタイミングを決めます 次に行う変更は下の方ですね ここで検索ナビゲータを開いて 必要なすべてのコード変更を確認します

    ここでコードを変更してカテゴリを定義し ツールがPOIを 検索できるようにします

    まずはGenerable enumを追加します

    カテゴリがenumになり ホテルやレストランが含まれています もちろん博物館やキャンプ場など 他のケースも含まれます これを次のコードチェーンで使って 引数を更新します

    ここに引数構造体があります これを更新し どうなるかお話しします

    引数構造体には let pointOfInterestというプロパティがあり 型はCategoryとして 定義されています このPOIはホテル またはレストランです Guideもあります Guideのdescriptionには "このタイプの目的地を探します"と あります この引数はツールと モデルのコントラクトです モデルがツールを呼び出す場合 この引数をツールに渡して ツールがホテルやレストラン あるいはカテゴリにアクセスし 応答を返します

    引数を更新しました 続いてcall関数を更新します

    この関数はツールの核心となる部分です 引数を受け取り アクションを実行し セッションのトランスクリプトに 出力を返します モデルはそれを確認して使用します では変更します

    どのようになったかを 順を追って説明します

    まずはlet results = await getSuggestionsですが 定義していなかったので ざっと説明します 基本的に この関数は callメソッドを呼び出して 特定のPOIを取得します resultsは出力の一部になります そして return文にあるように resultsを文字列出力として挿入し モデルに返します モデルはこの情報に加え プロンプトと指示を使って 最終的な応答を生成します 最後のコード変更はこの関数の定義です getSuggestionsという プレースホルダ関数があります これを更新します

    できました getSuggestions内には switchブロックがあり categoryを取得します restaurantの場合は レストラン1、レストラン2、 レストラン3を返します 同様にhotelの場合は ホテル1、ホテル2、ホテル3を返します このデモではハードコーディングされた データを使っています 実際のアプリではMapKitなどのAPIや サーバサイドAPIを呼び出して 実際のライブデータを取得します

    ツールのコード変更が終わったので これでツールが完全に定義されました Code Alongガイドに戻り セクション5.2に進みます

    では このツールをテストしてみましょう プレイグラウンドに移動して ツールをモデルに提供し 結果を確認します 前と同じようにコピー&ペーストし コードを行ごとに実行して 何が起こるかを説明します Xcodeに戻り Playground.swiftファイルに切り替えます このセクションでは前のコードを クリーンアップしゼロから始めます

    ここに空のプレイグラウンドがあります

    まず指示を追加します

    プレイグラウンドの優れた機能として Xcodeプロジェクトのすべてのデータ構造に アクセスできます アプリを構築する必要はありません ここでは landmark変数を作成し 「Models」フォルダの ModelData.swiftで定義されている モデルデータにアクセスします ModelData.landmarkは 表示されるランドマークの1つに アクセスします 具体的には 最初のランドマークにアクセスします たしかサハラ砂漠でしたね アクセスするランドマークのリストは アプリ実行時に得られるものと同じです それを取得し 「ViewModels」フォルダの FindPointsOfInterestToolを定義します ツールのインスタンスを作成し その情報を使って ランドマークを渡します 最後に 前と同じような指示があります 小さなコード変更が2件あります 1つ目は文字列ではなく 指示ビルダーになっています プロンプトビルダーに似ており クロージャを渡して 指示を提供します 2つ目の変更点は ツールの呼び出しには非常に重要で "このランドマークでは 常に「FindPointsOfInterestTool」を使って ホテルやレストランを見つけること" となっています この指示はモデルに ツールを呼び出してPOIの応答を 取得する必要があると伝えています 続いて

    LanguageModelSessionを作成します 先ほどのコード変更と同様に LanguageModelSessionと定義し instructionsを渡しますが そこに新しい引数であるtoolsを追加します ツールは配列にすることができます ここにはPOIツールしかありません 配列であるため複数のツールを提供し モデルはプロンプトと指示を推論して 呼び出すツールと 応答のタイミングを決定できます そのためセッションにツールを含めました 次にプロンプトを定義します

    プロンプト自体に変更はありません そして最後に モデルを呼び出します

    コード変更は不要ですが スライドで簡単に説明した optionsを追加します GenerationOptionsで サンプリングをgreedyに設定してあるので 常に一貫性と再現性に優れ 決定論的な出力が得られます 残りのプロンプトと指示が 一貫していればですが ここでキャンバスを確認します

    応答が生成されました こちらがコンテンツです

    title、description、rationale、daysがあります そのうちの1日"day 0到着"を選び アクティビティを見てみましょう

    activity 0、activity 1、 activity 2を開きます よく見ると activity 1のdescriptionにこうあります "Enjoy a traditional Moroccan dinner at Restaurant 1" titleはこうです "Dine at Restaurant 1" 同様に activities 2のtitleはこうです "Stay at Hotel 1"そして"Unwind at Hotel 1" これがツールの出力で モデルの出力に挿入されています モデルがプロンプトや指示 ランドマーク名を受け取り ツールを呼び出し ホテル名とレストラン名を取得し トランスクリプトに挿入して 応答を生成しました トランスクリプト自体を見てみましょう

    ここではセッション自体の 一時的な変数を作成し inspectSessionにキャプチャします これをやることで セッションやトランスクリプトをよく見て ツール呼び出しの処理を確認できます ここに作成した inspectSessionがあります では これらのプロパティを見ていきます toolsがあります 1つのツールが指定されています transcriptを見ると entriesに6つの要素があります ここにはinstructionsがあります transcriptでは 常に最初のエントリになります 最初のリクエストとなる promptがあります toolCallsがあります モデルはツールを呼び出す必要があることを 自律的に判断しました toolOutputがあります フレームワークがツールを実行し 出力をトランスクリプトに挿入しました そして こちらがresponseです モデルが元のプロンプトを合成し ツールがデータを出力して 最終的な応答を生成します ツール呼び出しが2件あります リクエストの対象が

    レストランとホテルの両方だからです これはtoolCallsで確認できます レストランとホテルのリクエストがあります

    Code Alongガイドに戻りましょう

    ツールの仕組みを確認し ツールを定義して プレイグラウンドでツールをテストしました これでItineraryGenerator.swift ファイルを更新し ツールを組み込む準備が整いました それがセクション5.3で行うことです

    ItineraryGenerator.swiftの コードを変更します これをファイルにコピー&ペーストします ここで行う主な変更は 指示を更新し ツールのインスタンスを作成して 言語モデルセッションに渡すことです Xcodeに移動し

    ItineraryGenerator.swiftを開きます 検索ナビゲータを起動し 5章に設定して コード変更を開始します

    最初に行う変更は指示の更新です

    前の指示を削除します 新しい指示には 定義した pointOfInterestToolが含まれていて この追加テキストがモデルに ツールを呼び出して POIを取得するよう求めます 言語モデルセッションも 更新する必要があるので tools引数を使います

    複数のツールを受け入れることができるので この配列に ツールを渡します

    以上2件のコード変更を イニシャライザで行う必要があります 変更したので これらのコメントを削除して 変更をトラッキングできるようにします

    ここで行う最後の変更は generateItineraryメソッドにあります

    前述のように 決定論的な出力を得たいのであれば 貪欲サンプリングを使います デフォルトでは ランダムサンプリングが行われます ここでは session.streamResponseの後 promptを渡した後 generating引数を渡した後に 渡せるものは

    optionsです 整理して理解しやすくしましょう

    session.streamResponseがあり promptがあり generating引数があり 最後にoptionsがあります 生成オプションが含まれていて サンプリングがgreedyに設定されています 行うべきコード変更はこれですべて完了です コメントを確実に削除しましょう

    検索ナビゲータに5章が表示されなければ コード変更が完了したということであるため これでアプリを構築して実行できます

    実行ボタンをクリックして アプリを構築し実行します

    アプリが表示されました 標準的なユーザーフローに従い サハラ砂漠をクリックします 「Generate Itinerary」ボタンが 表示されています クリックします ストリーミングAPIと ツールが含まれているので 受け取った指示やプロンプトを ツール定義とともにモデルに送信します ご覧のように 「Stay at Hotel 1」「Dine at Restaurant 1」と表示されています これらはツールの応答であり セッショントランスクリプトに 挿入されたものです モデルは指示 プロンプト ツール呼び出し、ツール応答など あらゆる情報を用い 合成し 旅程のGenerableという形式で 出力を生成します

    上出来です ではスライドに戻ってまとめましょう この章では ツールの呼び出しでモデルを強化しました 独自の引数とcall関数を持つ カスタムツールについて説明しました 言語モデルセッションに ツールを提供する方法 さらに ツールをいつどのように使うかを モデルに指示する方法を学びました 最後に ツールをアプリに統合して POIを取得し 生成された旅程に追加しました ツールの呼び出しに関する 5章は以上です

    このCode Alongの仕上げとして いくつかのテクニックで パフォーマンスを最適化し 生成機能の応答性を高めましょう

    Code Alongガイドに移動し 6章「パフォーマンスと最適化」に 進みます アプリの機能の実装は完成しましたが アプリのパフォーマンスを上げるには まずボトルネックがどこにあるかを 理解する必要があります 測定できないものは最適化できません ですから Instrumentsという パワフルなデベロッパツールを使います

    Xcodeに移動します

    これまでと少し違うことをします 実行ボタンを長押しすると いくつかのオプションが表示されます 「Run」「Test」「Profile」「Analyze」 今回は「Profile」をクリックします これでアプリが構築され Xcode Instrumentsが起動します

    構築完了を待ちます

    これがXcode Instrumentsです 空白のテンプレートを選択し

    Instrumentsを開いたら プラス記号をクリックして Foundation Modelsを検索します

    これでアプリを プロファイリングする準備が整いました

    記録をクリックすると

    アプリが起動するので 普通にアプリを使ってみます 個人的にはサハラ砂漠が面白そうです タイトルの説明を読んだところ いいですね 「Generate Itinerary」をクリックすると 素敵な旅程が表示されます 結果がストリーミングされています これに目を通せば 様々なアクティビティプランを確認できます

    では 記録を停止します

    では Instrumentsの中身を 詳しく見てみましょう

    ここにある複数のトラックで それぞれ何が起こっているかを確認し 潜在的なボトルネックを特定して 対処しましょう 最初のトラックは応答です 青いバーはセッション全体を表します ユーザーが「Generate Itinerary」を クリックすると セッションが作成され モデルが指示やプロンプトを受け取り 出力を生成します それらはすべて青いバーで表されます

    2行目はアセットの読み込みです よく見ると セッションが始まってから 少し遅れて モデルアセットが読み込まれています これがモデルアセットです セッションの開始時から モデルの読み込み終了まで モデルは応答を生成していません 大まかに見て約700ミリ秒です ほぼ1秒ですね 3番目のトラックを見ると ここで最初のトークンが生成されています すべてのモデルが読み込まれてから トークンの生成プロセスが開始され 最初のトークンから すべての応答が生成されるまで続きます ですから ここには パフォーマンス向上の機会があります アセットを事前に読み込むことができれば セッションの開始と同時に 生成プロセスを開始できる可能性があります 対処できそうなボトルネックの1つです 2つ目のボトルネックですが まずは下部で 「Inference」セクションを選択します よく見ると ここに最大トークン数があります 現在1044に達していることがわかります このトークン数にはセッションに 追加されたものがすべて含まれます 指示、プロンプト、ツールが含まれます 旅程のGenerableもすべて含まれます あらゆるものが含まれていますが トークンの数はモデルの パフォーマンスに影響を与えるので 削減できるかどうかを確認しましょう これが対処できそうな ボトルネックの2つ目です さて session.respondを呼び出すと まだメモリにない場合 OSがモデルを読み込みます 事前ウォームアップはリクエストに先立ち モデルを事前に読み込むので セッションを高速に開始できます アプリでは ランドマークをタップしたユーザーが すぐにリクエストを行う可能性が高いです 事前ウォームアップなら「Generate Itinerary」ボタンが押される前に 事前にモデルを読み込んでおけます ユーザーが説明を読み終える頃には モデルの準備は整っているでしょう

    リクエスト遅延を短縮できる 別の最適化も見てみましょう モデルに提供されるGenerable構造体は 構造化された出力の生成に役立ちますが トークン数が増加し 最初の処理時間に影響を与えます 3章では exampleTripToJapanという 旅程の例を渡しました 指示にはGenerableスキーマの 完全な例が含まれているので 多くの場合 スキーマ定義自体を フロントから除外できます これでスペースを節約し モデルを高速化できます

    Xcode Instrumentsにより アプリのボトルネックを特定できました ここでは いくつかの最適化を アプリに直接実装します まず ランドマークがタップされたときに prewarmメソッドを呼び出して セッションを事前ウォームアップします フレームワークが実行され 旅程が要求される前に モデルの読み込みが開始されます 第2に ワンショットの例は 非常に詳細であるため プロンプトの完全なスキーマ定義は冗長です includeSchemaInPromptを falseに設定することで削除できます streamResponse呼び出しで この変更を行います これにより入力トークンの数が 大幅に削減されます

    Code Alongガイドを進めて これから行うコード変更を見てみましょう 6章のアプリセクションに進みます まずはモデルの事前ウォームアップです コード変更は旅程ジェネレータに 反映されるので そこでprewarm関数を追加し ビューにも追加することで ビューの読み込み時にprewarmメソッドを 呼び出せるようになります 旅程ジェネレータとLandmarkTripViewに 変更を加えます Xcodeに移動します Instrumentsを開いたままにして 最適化の効果を確認しましょう Xcodeに移動して 旅程ジェネレータをクリックします もう開いていますね 検索ナビゲータを使って6章を開きます

    よし

    prewarmに行う最初の変更は prewarmコードの追加です このプレースホルダ関数を prewarmModelと定義しました ここでは セッションで prewarmメソッドを呼び出します

    非常に簡単です

    ビューから関数を呼び出して モデルを事前ウォームアップできます プロンプトの内容が事前にわかっている場合 promptPrefixを使って prewarmメソッドを更新することもできます

    session.prewarm関数内にある promptPrefixというオプションの引数に プロンプトを提供することで モデルはユーザーが指定しそうな プロンプトを認識し 事前ウォームアップできます そこで クロージャを持つ プロンプトを渡します "\(landmark.name)の3日間の旅程を生成して" これでパフォーマンスがさらに向上します

    次に行うコード変更はLandmarkTripViewです 「Views」フォルダに LandmarkTripViewがあります ここではタスクを更新し モデルが実際に読み込まれたときに prewarmメソッドを呼び出せるようにします やってみましょう

    これも簡単です 先ほど定義したgenerator.prewarmModel 関数の呼び出しと同じ要領です モデルの事前ウォームアップのための すべてのコード変更が含まれます Code Alongガイドに戻り 先ほど説明した2つ目の 最適化を確認しましょう トークンの最大数の削減です セクション6.2でプロンプトを最適化します

    ここでも旅程ジェネレータで コードを変更します includeSchemaInPromptという 引数を追加しfalseに設定します 変更を行って 何が起きるかを簡単に説明します

    旅程ジェネレータに戻ります

    session.streamResponseでは プロンプト Generable オプションを渡します includeSchemaInPromptという

    新しい引数を追加しfalseに設定します これによりモデルは 旅程のスキーマを除外できます exampleTripToJapanを Instrumentsにすでに渡していて 模範的な例と構造が 含まれているということです スキーマの追加をスキップできるので 最大トークン数の削減につながります

    変更を行ったのでコメントも削除します 6章で行う変更は以上です アプリをもう一度プロファイリングする 準備が整いました やってみましょう 「Profile」オプションを もう一度クリックすると アプリが構築され すぐにプロファイラーが起動します

    Xcodeが構築中です プロファイラーが再起動しました 記録するとアプリが再起動し アプリを使用する 同じプロセスが繰り返されます

    記録をクリックします

    ここにアプリがあります まったく同じ手順を行います サハラ砂漠をクリックします タイトルを読みます 説明もよさそうです 旅程を生成しましょう

    旅程が生成されていますね いいですね 日ごとのプラン、食事をするレストラン、 泊まるホテルが 表示されています 実行を完了し プロファイリングを終了します

    先ほどと同じことをして出力を確認し 最適化がアプリに与えた影響を 見てみましょう 最初に気付くのは セッション開始前に アセットの読み込みに成功していることです prewarm関数のおかげです ユーザーが詳細ビューをクリックした時点で アセットを読み込み タスクにprewarm関数を追加して prewarmメソッドを呼び出しました ユーザーがタイトルと説明を読むまでに モデルは読み込まれていました セッションのスタート時点をよく見ると セッションが始まるのとほぼ同時に 出力の生成も始まっています モデルがすでに読み込まれているため セッションが開始されました 語彙の準備が始まり トークンの生成が始まり 応答がはるかに速くなっています 2つ目の最適化と その効果についても見てみましょう この推論では トークンの最大数が700に減少しています 先ほどは1000だったため プロンプトからスキーマを除外して トークンの最大数を700に減らしました モデルが最初のトークンを素早く処理し 応答の生成をより速く 開始できるようになりました

    素晴らしい 最後の章では パフォーマンスについて見てきました モデルを事前ウォームアップして アプリの応答性を高める方法と 不要なスキーマを除外して プロンプトを最適化する方法を学びました 簡単かつ効果的な2つの方法で 生成機能のパフォーマンスを 向上させることができます

    最後に 私たちが構築した アプリを見てみましょう

    Xcodeに戻り構築して実行します

    よし 皆さんのマシンにも アプリのこの画面が 表示されていることと思います 始まりはランドマークの 簡単なSwiftリストでした セレンゲティを選択すると 詳細ビューが表示されます 最後にもう一度 旅程の生成をタップしてみましょう

    UIがリアルタイムで自動的に構築されます これは4章のストリーミングAPIであり Session.stream応答を使い コンテンツが部分的に生成されています 2章では@Generableを使って リッチで構造化された応答を取得しました 5章ではツールの呼び出しを使って POIを見つけました モデルがインテリジェントに決定し 取得したデータです

    さて 本日取り上げたのは 基本的なテキスト生成、ガイド付き生成 ストリーミング、ツールの呼び出し、 パフォーマンスの最適化などですが 探求すべきことはまだあります 今回取り上げなかった高度なトピックには カスタムモデルアダプタのトレーニング 動的ランタイムスキーマ ガードレールやエラー処理などがあります これらのトピックについて詳しくは WWDC25のFoundation Model フレームワークに関する他のビデオを ご覧になることを 強くお勧めします

    Slidoには 素晴らしい質問がたくさんありますが 回答を得られない場合は ぜひデベロッパフォーラム developer.apple.com/forumsで 質問してみてください 本日完成させたサンプルプロジェクトや いくつかの追加機能のダウンロードは Foundation Modelフレームワークの ドキュメントから可能です 最後に 本日中にアンケートが届くので 今回のセッションについて フィードバックをお願いします それでは Code Alongにご参加いただき ありがとうございました またお会いしましょう さよなら

Developer Footer

  • ビデオ
  • Meet With Apple
  • Code Along:Foundation Modelフレームワーク
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • Apple Intelligence
    • 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(英語)
    • Mini Apps Partner 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 © 2026 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン