Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Creating an English conversation app using the ...

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers. →

Creating an English conversation app using the Foundation Models framework

Avatar for asashin

asashin

June 19, 2026

More Decks by asashin

Other Decks in Technology

Transcript

  1. 自己紹介 あさしん @asashin227 株式会社スタメン エンジニアリングマネージャ iOS開発 / Claude Code・AIエージェント /

    自作キーボード 最近はオンデバイスAIで英会話アプリを試作中 今日は Apple の Foundation Models で英会話アプリを作った話を LT で mobile.stmn #18 / 2026-06-19 2
  2. 作ったもの:オンデバイス英会話アプリ 端末内のLLMと英語で会話して練習 会話 / ロールプレイ / ディベート の複数モード 会話を自動で添削:文法フィードバック・CEFRレベル推定 発音評価・ヒント・学習統計・カリキュラム

    … 学習を一通り サーバーに会話を送らない(オンデバイス完結) SwiftUI製。AIは Foundation Models(オンデバイス)で動かしている mobile.stmn #18 / 2026-06-19 3
  3. Foundation Models framework とは Apple が提供する オンデバイスLLM(iOS 26+ / Apple

    Intelligence) LanguageModelSession で respond / ストリーミング / 構造化出力 / ツール 前提:iPhone 15 Pro 以降+Apple Intelligence 有効・実機必須 メリット 内容 無料 API課金なし(端末内で推論) オフライン 通信不要で動く プライバシー 会話が端末から出ない 英会話はターン数が膨大 → クラウドAPIだと課金がきつい。オンデバイスが効く mobile.stmn #18 / 2026-06-19 4
  4. availability でデバイスを出し分け(実コード) SystemLanguageModel の可用性を判定して、AI機能の出し方を分岐。 switch SystemLanguageModel.default.availability { case .available: return

    .foundationModels // オンデバイスAI が使える case .unavailable(let reason): switch reason { case .modelNotReady: // モデルDL 中 return .downloading case .appleIntelligenceNotEnabled, // AI 無効 .deviceNotEligible: // 非対応端末 return .cloudAPIOnly // → クラウドAPI へ退避 @unknown default: return .cloudAPIOnly } } 「実機必須/未対応端末」をここで吸収して画面・プロバイダを出し分け mobile.stmn #18 / 2026-06-19 5
  5. 最小コード:セッション生成 → 応答取得 import FoundationModels // ① セッション生成:system 指示は instructions

    に(履歴とは分離) let session = LanguageModelSession( instructions: "You are a friendly English tutor. Reply in simple English." ) // ② ユーザー発話を渡し、モデルに考えさせて応答を受け取る let response = try await session.respond(to: userText) let reply = response.content // モデルの返答テキスト 逐次表示は session.streamResponse(to:) (累積スナップショットが返る) mobile.stmn #18 / 2026-06-19 7
  6. @Generable とは?(guided generation) 「自由テキスト」ではなく “この型で答えて” とモデルに制約をかける仕組み。 構造体に @Generable → その型に沿った出力をモデルが生成

    @Guide で各フィールドに説明・制約(値域・列挙など)を与える 受け取りは respond(to:, generating: 型.self).content → 型そのもので返る 利点 JSONパース不要(壊れたJSON・try? の失敗が原理的に起きない) 文字列プロンプトに「JSONで返して」と書かなくてよい 非対応モデルは LanguageModelError.unsupportedGenerationGuide を投げる(黙って劣化し ない) 出力の“形”をコードで保証=AI機能をただのSwiftの型として扱える mobile.stmn #18 / 2026-06-19 8
  7. 設計②:@Generable で型安全な構造化出力 構造体に @Generable を付けるだけで、型安全に直接受け取れる(JSONパース不要) @Generable struct GrammarFeedback { @Guide(description:

    " 誤りがあれば true") var hasError: Bool var correction: String var explanation: String } // 呼び出し側:型を渡すと、その型で返ってくる let fb = try await session.respond( to: text, generating: GrammarFeedback.self ).content // → GrammarFeedback (誤り箇所・訂正・説明) 数秒かかるので 会話終了後に非同期実行 すればUX影響なし mobile.stmn #18 / 2026-06-19 9
  8. 対策の実コード:GenerationOptions 出力を短く・安定にして“非力”をカバー(アプリの実コード) 。 guard SystemLanguageModel.default.isAvailable else { throw … }

    let options = GenerationOptions( temperature: 0.7, // 既定1.0 は高め → 会話は0.7 が落ち着く maximumResponseTokens: 200 // 長すぎる応答を抑える ) let response = try await session.respond(to: prompt, options: options) temperature / maximumResponseTokens で短く・安定に トークン計測 response.usage ・ model.contextSize も用意あり(本アプリは未使用・シン プル運用) 軽く絞るほどオンデバイスは安定する mobile.stmn #18 / 2026-06-19 11
  9. その他のハマりどころ try? のサイレント失敗:AIが ```json 付きで返すと黙ってデフォルト値に落ちる → @Generable を使えばそもそもJSONパース自体が不要 temperature は既定1.0が高め

    → 会話は 0.7 が落ち着く テストでシミュレータが実モデルを実走し1呼び出し19秒 → Mockをクロージャ注入で0.1秒 に mobile.stmn #18 / 2026-06-19 12
  10. 拡張:同じAPIのまま Claude へ(ハイブリッド) Claude for Foundation Models で、同じ LanguageModelSession のままクラウドのClaude

    に切替可能 使い分け:軽い会話=オンデバイス/重い処理・Web検索・大コンテキスト=Claude 429(レート制限)時はオンデバイスに退避、という分岐をアプリ側で自動化 ※ Claude for Foundation Models は iOS 27+(ベータ)が前提(要確認) プロバイダ切替UIは隠しつつインターフェースは温存 → 後から足せる設計に mobile.stmn #18 / 2026-06-19 13
  11. コード:429 ならオンデバイスに退避 同じ LanguageModelSession API なので、モデルを差し替えるだけ。 // クラウド(Claude) 用セッション(本番は .proxied

    で鍵を埋めない) let claude = LanguageModelSession( model: ClaudeLanguageModel(name: .sonnet4_6, auth: .proxied(headers: headers)) ) do { let reply = try await claude.respond(to: userText).content } catch LanguageModelError.rateLimited { // 429 → オンデバイスに退避(同じAPI ) let reply = try await onDevice.respond(to: userText).content } 重い処理はClaude/レート制限時はオンデバイス、を同じコードで mobile.stmn #18 / 2026-06-19 14
  12. 今後の展望:iOS 27 で広がる(WWDC 2026) オンデバイスAIの選択肢が純正で増える。今日の構成の“本命”が来る。 LanguageModel プロトコルが外部モデルに正式対応 → Claude/Gemini 等を同じAPIで(今

    日のハイブリッドが本格化) PrivateCloudComputeLanguageModel (Apple純正サーバーモデル) APIキー不要・プロンプト非保存・32Kコンテキスト=オンデバイスで足りない時の純正 エスカレーション先 DynamicProfile :1セッションを状態で切替 → 会話/ロールプレイ/ディベートのモード切替 をきれいに システムツール(OCR / Spotlight=ローカルRAG)→ 教材・履歴を絡めた英会話へ ※ いずれも iOS 27(ベータ・2026 Fall 正式予定) 。要キャッチアップ mobile.stmn #18 / 2026-06-19 15