2.2K Views
November 19, 24
スライド概要
「食べログ x Sansan モバイル勉強会」での発表資料です。
https://sansan.connpass.com/event/335031/
フリーランスiOSエンジニア 「エンジニアと人生」コミュニティ主宰
Assistant Schemas入門 〜 アプリをApple Intelligence対応にする 〜
自己紹介 • 堤 修一 • @shu223 (GitHub, Zenn, Qiita, note, Docswell, 𝕏, YouTube, Podcast, etc...) • 書籍(商業出版4冊、個人出版多数 @BOOTH):
2024年9月、iPhone 16 シリーズ発売
完全にApple Intelligence推し
Apple Intelligeceの全体像
今回はこのへんの話
Siri powered by Apple Intelligence • より自然な音声に • 言語理解力アップ • 画面を認識(パーソナルコンテキスト) • アプリをまたがっていろんなアクションが行えるように
Apple Intelligence におけ る Siri の位置付け整理 Siri はパーソナルエージェント • デバイスに常駐(いつでもどこから でも起動できる) • 自然言語でのコミュニケーション • パーソナルコンテキストを理解 • ユーザーの要求を理解し、各種アプ リを跨いでアクションを実行 4 4 スキーマ予測とインテント実行は中段のPersonal Intelligent Systemが担う
本日のテーマ: 賢くなったSiriに「アプリの機能を使っ てもらう」には?
賢くなったSiriに「アプリの機能を使ってもらう」には? 前提の話 スキーマ適合に必要な実装 • Assistant Schemasとは • 概要 • App Intent domainsとは • コード解説 • Apple Intelligenceを使った リクエストのライフサイクル • App Intents Toolbox
Assistant Schemas • こういうのがたくさん定義されてて、そ のひとつひとつがアシスタントスキーマ • 写真の作成やメールの送信など、それぞ れ何かしらのインテント(App Intents) に対応している • それぞれInputとOutputが定義されてい て、その間に我々が定義できる perform メソッドがある
Assistant Schemas • 100種定義されている • {ドメイン}.{スキーマ}
App Intent domains • ドメイン: 特定の種類の機能に特化 して設計されたスキーマの集まり
App Intent domains • 今年6月時点ですぐ使えたのはMailと Photosの2つだけ
App Intent domains • 現在では12種類のドメインが利用可 能 • iOS 18.2ベータが出たあたりでド キュメントにそっと追加されてい た • 利用はiOS 16〜
Apple Intelligenceを使っ たリクエストのライフサイク ル 1. ユーザーリクエスト
Apple Intelligenceを使っ たリクエストのライフサイク ル 1. ユーザーリクエスト 2. Apple Intelligenceのモデルがスキー マを予測
Apple Intelligenceを使っ たリクエストのライフサイク ル 1. ユーザーリクエスト 2. Apple Intelligenceのモデルがスキー マを予測 3. Apple Intelligenceのツールボックス がインテントを呼び出してアクショ ンを実行
Apple Intelligenceを使っ たリクエストのライフサイク ル 1. ユーザーリクエスト 2. Apple Intelligenceのモデルがスキー マを予測 3. Apple Intelligenceのツールボックス がインテントを呼び出してアクショ ンを実行 4. 結果表示/出力
App Intents Toolbox • デバイス上の全アプリからのApp Intentsが、スキーマごとにグループ 化されて格納されている • モデルが予測したスキーマに適合す るインテントを呼び出してアクショ ンを実行
重要なポイント Apple Intelligenceのモデルはスキーマを予測するように学習 されている → 自分のアプリのインテントをスキーマに適合させることで、 モデルにそれを使ってもらえるようになる
つまり!
「Apple Intelligence対応」に必要なこと • アプリのコア機能を(すべて )インテント化し 3 • スキーマに適合させる 3 Apple Intelligence時代のApp Intents設計 の「どのApp Intentを作成するか」参照
App Intents とは • アプリの「意図(インテント)」をシステムに伝えるもの • アプリの機能をアプリ外の様々な場所から使えるようになる
App Intentsの実装
インテントをスキーマに適合させる
インテントに struct CreateAlbumIntent: AppIntent { } ...
インテントに1行追加するだけ! @AssistantIntent(schema: .photos.createAlbum) struct CreateAlbumIntent: AppIntent { } ...
インテントに1行追加するだけ! @AssistantIntent(schema: .photos.createAlbum) struct CreateAlbumIntent: AppIntent { } ... → @AssistantIntent マクロを用いて AppIntent を photos ドメインの createAlbum スキーマに適合させている
ほんと?
コードをちゃんと読んで スキーマ適合に必要な実装 を洗い出す
Assistant Schemasの公式サンプル Making your app’s functionality available to Siri • プロジェクト名は AssistantSchemasExample • 2024年6月から公開されているが、11月に更新あり
「アルバムを開く」インテント @AssistantIntent(schema: .photos.openAlbum) struct OpenAlbumIntent: OpenIntent { } ...
デモ
OpenAlbumIntent のコード全体 @AssistantIntent(schema: .photos.openAlbum) struct OpenAlbumIntent: OpenIntent { var target: AlbumEntity @Dependency var library: MediaLibrary @Dependency var navigation: NavigationManager @MainActor func perform() async throws -> some IntentResult { let albums = library.albums(for: [target.id]) guard let album = albums.first else { } throw IntentError.noEntity navigation.openAlbum(album) } } return .result()
// コメントアウトしてもビルド可 @Dependency var library: MediaLibrary // コメントアウトしてもビルド可 @Dependency var navigation: NavigationManager @MainActor func perform() async throws -> some IntentResult { // コメントアウトしてもビルド可 let albums = library.albums(for: [target.id]) guard let album = albums.first else { } throw IntentError.noEntity navigation.openAlbum(album) } return .result()
単純化 @AssistantIntent(schema: .photos.openAlbum) struct OpenAlbumIntent: OpenIntent { var target: AlbumEntity ... func perform() async throws -> some IntentResult { ... } } // アルバムを開く処理 return .result()
OpenIntent -> AppIntent に変えてみる @AssistantIntent(schema: .photos.openAlbum) // OpenIntent -> AppIntentに変えるとビルドエラー // > Intent does not conform to SystemIntent `OpenIntent` required // > by Assistant schema intent `OpenMediaAlbumIntent` struct OpenAlbumIntent: AppIntent { var target: AlbumEntity ...
• @AssistantIntent(schema: .photos.openAlbum) マクロによって OpenIntent への準拠が強制される • OpenIntent プロトコルではtarget プロパティが必須 public protocol OpenIntent : SystemIntent { associatedtype Value : AppValue } var target: Self.Value { get set } • AppValue プロトコルに準拠する型は AppEnum または AppEntity
AlbumEntity -> AssetEntity に変えてみる @AssistantIntent(schema: .photos.openAlbum) struct OpenAlbumIntent: OpenIntent { // AlbumEntity -> AssetEntityに変えるとエラー // > Parameter `target` type does not match required // > Assistant schema intent parameter type `PhotoAlbumEntity` var target: AssetEntity ...
AlbumEntity: スキーマ .photos.album に適合 @AssistantEntity(schema: .photos.album) struct AlbumEntity: IndexedEntity AssetEntity: スキーマ .photos.asset に適合 @AssistantEntity(schema: .photos.asset) struct AssetEntity: IndexedEntity
@AssistantIntent(schema: .photos.openAlbum) マ クロによって OpenIntent への適合が強制される ↓ • target プロパティが必須 • target の型は .photos.album に適合した AppEntity であることが必須
結局やっぱりこう書く以外にない @AssistantIntent(schema: .photos.openAlbum) struct OpenAlbumIntent: OpenIntent { var target: AlbumEntity ... func perform() async throws -> some IntentResult { ... } } // アルバムを開く処理 return .result()
AlbumEntity のコード全体 @AssistantEntity(schema: .photos.album) struct AlbumEntity: IndexedEntity { static let defaultQuery = AlbumQuery() ... var albumType: AlbumType var displayRepresentation: DisplayRepresentation { } } DisplayRepresentation( ... )
IndexedEntity -> AppEntity に変えてみる @AssistantEntity(schema: .photos.album) // IndexedEntity -> AppEntityに変えると、AlbumIntentのtargetプロパティの箇所でエラー // > `AppIntents.AppEntity` type `AlbumEntity` conforming to Assistant schema // > intent `PhotoAlbumEntity` referenced by Parameter `target` must conform // > to `AppIntents.IndexedEntity` or provide a default `EntityStringQuery` struct AlbumEntity: AppEntity { static let defaultQuery = AlbumQuery() ...
albumTypeは.photos.albumTypeに適合したAppEnum型であることが必須 @AssistantEntity(schema: .photos.album) struct AlbumEntity: IndexedEntity { // AppEntityの必須プロパティ static let defaultQuery = AlbumQuery() ... // コメントアウトするとエラー // > Missing required property `albumType` // > from Assistant schema entity `PhotoAlbumEntity` var albumType: AlbumType // InstanceDisplayRepresentable(<- DisplayRepresentable <- AppEntity) // の必須プロパティ var displayRepresentation: DisplayRepresentation {
AlbumType のコード全体 @AssistantEnum(schema: .photos.albumType) enum AlbumType: String, AppEnum { case custom static let caseDisplayRepresentations: [AlbumType: DisplayRepresentation] = [ } ] .custom: "Custom"
AlbumQuery のコード全体
struct AlbumQuery: EntityQuery {
...
func entities(for identifiers: [AlbumEntity.ID]) async throws -> [AlbumEntity] { ... }
}
func suggestedEntities() async throws -> [AlbumEntity] { ... }
entities(for:), suggestedEntities()の実装が必須
(システムがインテント実行時に呼ぶために必要)
芋づる式に色々と強制される • • • .photos.openAlbumスキーマ に適合するAppIntents • .photos.openAlbumに適合す るAppEntity・・・① • OpenIntentsに適合 • • targetプロパティが必須、型 は① IndexedEntity プロトコル に準拠する • defaultQuery プロパティを • albumType プロパティを持 つ、型は③ .photos.albumTypeスキーマ に適合するAppEnum・・・② EntityQuery・・・③ • 必須メソッドの返り値の型は 持つ、型は②
難しい !
難しい !
実装自体は簡単 1. スキーマ指定して、 2. コンパイラに従って書けばいいだけ
(補足)@Dependency と AppDependencyManager • 時間に余裕があれば口頭で話す • あとで記事に書きます
動作確認方法 • ショートカットで確認する • iOS 18.2ベータでもApple Intelligence/Siriによるインテント 実行はサポートされていない • いつになるかはまだ不明
実装してリリースしてみた Somato • 2021年につくった個人開発アプリ • 100枚/秒の超高速で写真を表示する • 「写真いっぱい撮ってるけど、一生振 り返る暇ないな…」という課題意識か らつくった • 「アルバムを開く」インテント搭載 • Assistant Schemas適合済み 【1000枚を10秒で見る】写真を超高速で表示するアプリをご紹介しま す。 - マメ
Assistant Schemas実装のまとめ • マクロとプロトコルにより色々と強制され、芋づる式にあれ これ必要になる • ショートカットで動作確認はできるが、本当にAssistant Schemas対応できているか確認する手段は今のところない • リリースも可能 • (App Storeの文言内で"Apple Intelligence対応"などと言及するとリジェクトされる)
まとめ • Apple IntelligenceでパーソナルエージェントとしてのSiriが大幅に強化 • スキーマに準拠したインテントを用意しておけばインテリジェントにアプリ • その実装を実際にやってみた の機能を使ってくれる • 色々と芋づる式に強制される • その中身を追うとややこしいが、気にしなければ簡単 • スキーマを指定して、コンパイラに従ってコード書くだけ