1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FlutterでOS純正のオンデバイスLLM(Apple Foundation Models / Gemini Nano)を統一APIで呼んでみた

1
Posted at

これまで何本か、「AIエージェント(Claude Code)がモバイルアプリを操作する」側の検証記事を書いてきた。今回は逆方向で、モバイルアプリ自体にOS純正のオンデバイスAI(LLM)を組み込むとどうなるかを試した。

iOSにはFoundation Modelsフレームワーク(Apple Intelligence)、AndroidにはGemini Nano(ML Kit GenAI/AICore)という、それぞれのOSが持つオンデバイスLLMがある。flutter_gemma_builtin_aiというFlutterパッケージを使うと、この2つを同じDart APIで呼び分けられるらしい。実際にiOS/Android両方で動かして、どこまで「クロスプラットフォーム」なのかを確かめた。

検証環境: Flutter 3.44.4。macOS 26.5 / Apple M5 (Apple Silicon)。iOSシミュレータ(iPhone 17 Pro, iOS 26.5)。Androidエミュレータ(Pixel 10 Pro XL AVD)、およびAndroid実機(Xiaomi Redmi、Android 16)。flutter_gemma 1.4.2 / flutter_gemma_builtin_ai 0.1.0。

まず、このMacでApple Intelligenceが本当に動くか確認した

Flutterアプリを書く前に、そもそもこのマシンでApple純正のオンデバイスLLMが動くのか、swiftコマンドで直接確認してみた。

import FoundationModels

let model = SystemLanguageModel.default
print("availability: \(model.availability)")
let session = LanguageModelSession()
do {
    let response = try await session.respond(to: "日本語で一言、挨拶してください。")
    print("RESPONSE: \(response.content)")
} catch {
    print("ERROR: \(error)")
}

実行結果(実際の出力):

availability: available
RESPONSE: こんにちは!

Apple Siliconのマシンで、日本語のプロンプトに対してオンデバイスで(ネットワーク不要で)実際に応答が返ってきた。これを見てから、Flutterアプリの実装に進んだ。

flutter_gemma_builtin_aiというパッケージ

flutter_gemma本体はGemmaモデルをバンドル/ダウンロードして動かすパッケージだが、flutter_gemma_builtin_aiはOSが最初から持っているモデルを使う。モデルファイルのダウンロードが不要で、「OSの機能を有効にするだけ」という位置づけ。

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  FlutterGemma.initialize(
    inferenceEngines: const [BuiltInAiEngine()],
  );
  runApp(const OnDeviceAiApp());
}

プラットフォームによってモデル指定を変えるだけで、あとのコードは共通:

final spec = defaultTargetPlatform == TargetPlatform.android
    ? BuiltInAiModels.geminiNano
    : BuiltInAiModels.appleFoundationModels;

await FlutterGemma.installModel(
  modelType: ModelType.general,
  fileType: ModelFileType.builtIn,
).fromBundled(spec.name).install();

final model = await FlutterGemma.getActiveModel(maxTokens: 1024);
final chat = await model.createChat();
await chat.addQueryChunk(
  const Message(text: '日本語で一言、挨拶してください。', isUser: true),
);
final result = await chat.generateChatResponse();

「Availabilityを確認」ボタンと「プロンプトを実行」ボタンだけの、シンプルな検証アプリを作った。

ビルドでハマった2点

  • iOS: flutter runTarget Integrity (Xcode): The package product 'flutter-gemma' requires minimum platform version 16.0 for the iOS platform, but this target supports 13.0で失敗。デフォルトのIPHONEOS_DEPLOYMENT_TARGET = 13.016.0に上げて解決。
  • Android: パッケージのREADMEに「Consumer apps require minSdk 26」と明記されていたので、minSdkを26に固定。

検証結果: iOSは動いた、Androidはハードウェアで弾かれた

iOS(iPhone 17 Proシミュレータ)

「Availabilityを確認」をタップ。

status: BuiltInAiAvailability.available

iOSでBuiltInAiAvailability.availableが表示された

続けて「プロンプトを実行」。実際にApple Foundation Modelsが生成した応答がアプリ内に表示された。

response: TextResponse("こんにちは!")

iOSアプリ内でApple Foundation Modelsが生成した実際の応答

(LLMの出力なので、同じプロンプトでも実行のたびに文言は変わりうる。ここに載せているのは実際に得られた1回分の出力。)

シミュレータはホストMacの実行環境をそのまま使うため、Apple SiliconかつApple Intelligenceが有効なMac上であれば、実機のiPhoneを用意しなくてもFoundation Modelsの動作を確認できた。なおflutter_gemma_builtin_aiのREADMEによれば、実機で同じ機能を使うにはiPhone 15 Pro以降かつApple Intelligenceが有効であることが必要で、こちらも無条件で動くわけではない。

Android(Pixel 10 Pro XL AVD)

同じコードのまま「Availabilityを確認」をタップ。

status: BuiltInAiAvailability.unavailableDeviceUnsupported

AndroidエミュレータでunavailableDeviceUnsupportedが表示された

flutter_gemma_builtin_aiのREADMEには「Gemini Nano(AICore)はPixel 9+ / Galaxy S25+の実機が必要」と書かれている。AVD(ソフトウェアエミュレータ)にはAICoreを担う専用チップが無いので、これは想定通りの結果だった。続けて「プロンプトを実行」をタップすると、ensureReady()が例外を投げてキャッチされた。

response: ERROR: BuiltInAiUnavailableException(BuiltInAiAvailability.unavailableDeviceUnsupported): Built-in AI is not available: BuiltInAiAvailability.unavailableDeviceUnsupported

Android側でBuiltInAiUnavailableExceptionが投げられた

クラッシュや無限待ちにはならず、型付きの例外として明確に失敗する設計になっていた。

Android実機(Xiaomi Redmi)でも試した

「エミュレータだから動かないだけでは?」という疑問が残るので、手元にあったAndroid実機(Xiaomi Redmi、Android 16)でも同じアプリを動かしてみた。この端末はPixel 9+でもGalaxy S25+でもない。

status: BuiltInAiAvailability.unavailableDeviceUnsupported

Android実機(Xiaomi Redmi)でも同じくunavailableDeviceUnsupportedになった

結果はエミュレータと同じunavailableDeviceUnsupported。「プロンプトを実行」でも同じBuiltInAiUnavailableExceptionが投げられた。つまり「エミュレータだから動かない」わけではなく、AICore非対応の実機でも同じく弾かれることが確認できた。逆にPixel 9+ / Galaxy S25+のようなAICore対応実機なら動くはずだが、そちらは手元に無く未確認で、READMEの記載を根拠にしている。

まとめ

  • 統一Dart API経由でOS純正オンデバイスLLMを呼ぶコードは、iOS/Android共通で書けた。 実際に分岐するのは使用モデルを指定する1行だけ。
  • iOS(Apple SiliconのMac上のシミュレータ)は実際に動いた。 availability()availableを返し、実際に日本語の応答をオンデバイスで生成できた。
  • Androidはエミュレータでも、Gemini Nano非対応の実機でも動かない。 Gemini Nano(AICore)はPixel 9+ / Galaxy S25+相当の専用ハードウェアが必要で、AVDだけでなく手元のAndroid実機(Xiaomi Redmi)でもunavailableDeviceUnsupportedという同じ型付きエラーになった。「実機なら動く」わけではない。
  • 「クロスプラットフォームで書ける」ことと「クロスプラットフォームで動く」ことは別で、OS・実機のハードウェア要件によって実際の挙動は大きく非対称になる、という実例だった。

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?