メインコンテンツへスキップ

Capacitor アプリで Gemini Nano を使用する方法

Capacitor アプリで Gemini Nano を使用して Android 上でデバイス内 AI を実現:デバイス対応、AICore チェック、@capgo/capacitor-llm によるストリーミング、および Gemma へのフォールバックについて。

Capacitor アプリで Gemini Nano を使用する方法

Capacitor アプリで Gemini Nano を使用するには、以下をインストールしてください: @capgo/capacitor-llm、呼び出し: setModel({ path: 'Gemini Nano' }) Android上で、チャットを作成してその返答を textFromAi event経由でストリーミングします。このプラグインはGoogleのML Kit GenAI Prompt APIを通じてGemini Nanoと通信します。モデルはAndroidのAICoreシステムサービス内で動作するため、テキストが端末外に送信されることはありません。

このガイドでは、Gemini Nanoの動作原理、対応デバイス、具体的なプラグインの呼び出し方法、ストリーミング、クォータと制限、非対応端末向けのGemmaフォールバック、そして同じ code がiOSのApple Intelligenceにどのように到達するかについて解説します。

AndroidにおけるGemini Nanoの動作について

Gemini Nanoは、スマートフォン向けに構築されたGoogleの最小のGeminiモデルです。アプリにモデルを同梱する必要はありません。Androidの AICore サービスがモデルのダウンロード、アップデート、推論を実行し、アプリは以下の ML Kit GenAI API:

  • Prompt API を使用します: 自由形式のテキストプロンプト、このプラグインが使用する API (com.google.mlkit:genai-prompt).
  • 機能API: 要約、校正、書き換え、画像説明などの定型タスク。

GoogleはPrompt APIをベータ版として提供しており、SLAや廃止ポリシーも設定されていません。リリースごとに挙動が変わる可能性があるため、独自のラッパーを介して統合することをお勧めします。

推論がローカルで行われるため、クラウドモデルにはない3つのメリットがあります。オフラインで動作すること、トークンごとのコストがかからないこと、そしてユーザーのテキストがデバイス内に留まることです。トレードオフとして、モデルのサイズが小さくコンテキストや出力も制限されるため、リライト、メモの要約、メッセージの分類、フィールドの抽出、短い返信のドラフト作成など、適したタスクを選んでください。

サポートデバイスと要件

GoogleはML Kit GenAIサイトにて、Gemini Nanoのバージョン(nano-v2, nano-v3, nano-v4)ごとにAPIのサポートリストを公開しています。Prompt APIの場合、Pixel 9以降、最新のSamsung Galaxy SおよびZシリーズ、さらにOnePlus、OPPO、Xiaomi、vivo、Honor、realme、Motorolaなどの最新フラッグシップモデルが対象です。特定のデバイス向けに機能を実装する前に、必ず最新のリストを確認してください。

サポート対象デバイスには以下も必要です:

要件 詳細
Androidバージョン API レベル26以上(プラグインがこれをチェックします)
ブートローダー ロック済み。GoogleはアンロックされたブートローダーでのGenAI APIの利用をサポートしていません。
AICore 準備中および設定中。デバイスのセットアップやリセット直後は、完了まで時間がかかる場合があります。
アプリの状態 推論中は、アプリが最前面(フォアグラウンド)にある必要があります

Nanoのバージョンによって、同じプロンプトでも出力が異なる場合があります。使用する各バージョンについて、少なくとも1台のデバイスでプロンプトのテストを行ってください。

プラグインをインストール

bun add @capgo/capacitor-llm
bunx cap sync

このプラグインは Capacitor のメジャーバージョンに準拠しています。Capacitor 8 を使用する場合は 8.x を使用してください。Gemini Nano の利用に Android manifest の変更は不要です。詳細はこちら: プラグインのドキュメント と プラグインのページ.

ステップ 1: Gemini Nano のロードと準備完了の確認

setModel 専用のパスを使用して 'Gemini Nano' ML Kit にモデルのステータスを問い合わせます。ステータスが取得できると resolve し、それ以外の場合はステータスを含むメッセージとともに reject されます。

AICoreからのステータス プラグインの挙動
利用可能 setModel 解決済み、 readinessChange 発行 ready
ダウンロード可能 エラー: 「Gemini Nanoはダウンロード可能ですが、このデバイスにはまだダウンロードされていません」
ダウンロード中 ダウンロード中のステータスでエラーを返します
利用不可 エラー: 「このデバイスではGemini Nanoを利用できません」
import { Capacitor } from '@capacitor/core';
import { CapgoLLM } from '@capgo/capacitor-llm';

export async function loadGeminiNano(): Promise<boolean> {
  if (Capacitor.getPlatform() !== 'android') return false;

  try {
    await CapgoLLM.setModel({
      path: 'Gemini Nano',
      temperature: 0.3,
      topk: 16,
    });
  } catch (error) {
    console.warn('Gemini Nano not ready:', error);
    return false;
  }

  const { readiness } = await CapgoLLM.getReadiness();
  return readiness === 'ready';
}

このプラグイン自体がAICoreモデルのダウンロードを開始することはありません。デバイスが「downloadable」を返した場合、通常は他のアプリやシステムによるリクエスト後にモデルが届くため、機能を恒久的に非表示にするのではなく、後で再度起動して確認するようにしてください。

ステップ 2: チャットを作成して回答をストリームする

チャットは会話履歴を保持します。Androidでは、 sendMessage 生成開始と同時に解決され、テキストはイベント経由で届きます。生成が完了したときに解決される Promise でラップしてください。 aiFinished:

import { CapgoLLM } from '@capgo/capacitor-llm';

// Android sends deltas. iOS Apple Intelligence sends the full text so far.
// This merge handles both.
function mergeChunk(current: string, chunk: string) {
  return chunk.startsWith(current) ? chunk : current + chunk;
}

export async function ask(
  chatId: string,
  message: string,
  onText: (textSoFar: string) => void,
): Promise<string> {
  let text = '';
  let finish!: (value: string) => void;
  let fail!: (error: Error) => void;
  const done = new Promise<string>((resolve, reject) => {
    finish = resolve;
    fail = reject;
  });

  const handles = await Promise.all([
    CapgoLLM.addListener('textFromAi', (event) => {
      if (event.chatId !== chatId) return;
      text = mergeChunk(text, event.text);
      onText(text);
    }),
    CapgoLLM.addListener('aiFinished', (event) => {
      if (event.chatId === chatId) finish(text);
    }),
    CapgoLLM.addListener('generationError', (event) => {
      if (!event.chatId || event.chatId === chatId) fail(new Error(event.error));
    }),
  ]);

  // If the app is backgrounded mid-answer, neither event may arrive
  const timeout = setTimeout(() => fail(new Error('Generation timed out')), 60_000);

  try {
    await CapgoLLM.sendMessage({ chatId, message });
    return await done;
  } finally {
    clearTimeout(timeout);
    await Promise.all(handles.map((h) => h.remove()));
  }
}

実際の機能への活用例:例えば、カスタマーレビューへの返信案を作成する場合:

const INSTRUCTIONS =
  'You write short, polite replies to customer reviews for a small shop. ' +
  'Answer each point, stay under 60 words, never promise refunds.';

export async function draftReply(review: string, render: (t: string) => void) {
  const { id } = await CapgoLLM.createChat();
  return ask(id, `${INSTRUCTIONS}\n\nReview:\n${review}\n\nReply:`, render);
}

createChat() オプションは不要なので、最初のメッセージの冒頭に指示を記述してください。タスクごとに新しいチャットを作成します。Gemini Nano のパスでは、プラグインが履歴をメモリに保持して毎回メッセージと一緒に送信するため、長いチャットはすぐにインプット予算を消費してしまいます。

設計上の制限

制限 値 対応策
入力 1リクエストあたり約 4,000 トークン未満 (Google) チャットは短く保ち、長いドキュメントは要約する
出力 このプラグインはGemini Nanoのレスポンスを256トークンに制限します 短い出力を求めるか、長いタスクは分割してください
フォアグラウンド アプリが最前面にある時のみ推論を実行 バックグラウンドタスクでの実行は避けてください
クォータ アプリごとのクォータ、 BUSY およびバッテリークォータのエラー デバウンスを行い、キー入力のたびに生成されないようにする
並行処理 1チャットにつき1回の生成 ストリーミング中は送信ボタンを無効化する
速度 1段落につき数秒 常にUIへストリーミングする

クォータエラーは一時的なものとして扱う。エラーダイアログではなく、リトライオプションを表示する。

フォールバック: Gemini Nanoがない場合はGemma 4

現在使われているほとんどのAndroid端末にはGemini Nanoが搭載されていません。同じプラグインでLiteRT-LM経由のオープンなGemmaモデルを動かせるので、API は1つで済みます。モデルは1GBを超えることも多いため、ユーザーが同意した後に、できればWi-Fi環境でダウンロードしてください。

import { Preferences } from '@capacitor/preferences';
import { CapgoLLM } from '@capgo/capacitor-llm';

const GEMMA_URL =
  'https://huggingface.co/litert-community/gemma-4-E2B-it-litert-lm/resolve/main/gemma-4-E2B-it.litertlm?download=true';

export async function loadGemma(onProgress: (pct: number) => void) {
  const saved = await Preferences.get({ key: 'gemmaPath' });
  if (saved.value) {
    try {
      await CapgoLLM.setModel({ path: saved.value, modelType: 'litertlm', maxTokens: 4096 });
      return;
    } catch {
      // File missing or corrupt, download again
    }
  }

  const sub = await CapgoLLM.addListener('downloadProgress', (e) => onProgress(e.progress));
  try {
    const { path } = await CapgoLLM.downloadModel({
      url: GEMMA_URL,
      filename: 'gemma-4-E2B-it.litertlm',
    });
    await Preferences.set({ key: 'gemmaPath', value: path });
    await CapgoLLM.setModel({ path, modelType: 'litertlm', maxTokens: 4096 });
  } finally {
    await sub.remove();
  }
}

次に、起動時にエンジンを選択します:

export async function initOnDeviceAI(userAcceptedDownload: boolean, onProgress: (p: number) => void) {
  if (await loadGeminiNano()) return 'gemini-nano';
  if (userAcceptedDownload) {
    await loadGemma(onProgress);
    return 'gemma';
  }
  return 'none';
}

どちらも不可能な場合は、機能を非表示にするかクラウドモデルを呼び出します。ローカルAIでコアフローをブロックしないでください。

iOSでも同じ code

iOSでは、プラグインはFoundation Modelsフレームワークを通じてApple Intelligenceを使用します。変更するのは1行だけです:

await CapgoLLM.setModel({ path: 'Apple Intelligence' });

createChat, sendMessage、イベントは変わらず、 mergeChunk 上記のヘルパーはすでに iOS のストリーミング形式を処理します。デバイス要件は異なります(iOS 26 および Apple Intelligence 対応デバイス)。次のガイド「 Capacitor アプリでの Apple Intelligence 」でその部分を説明しています。

テスト

  • 対応する実機を使用してください。エミュレータでは Gemini Nano を搭載した AICore は提供されません。
  • 次の setModel 拒否メッセージをログに記録してください。AICore が報告する正確なステータスがわかります。
  • セットアップ直後またはリセット後のデバイスでは、オンラインの状態で AICore の初期セットアップが完了するまで待ってから、再試行してください。
  • サポートする中で最も低い Nano バージョンでプロンプトをテストしてください。短く明示的なプロンプトの方が、バージョン間で安定します。

トラブルシューティング

「このデバイスでは Gemini Nano は利用できません」 デバイスが Google のリストに含まれていない、ブートローダーがアンロックされている、または AICore のセットアップがまだ完了していない可能性があります。

「Gemini Nanoはダウンロード可能ですが、未ダウンロードです」 デバイスは対応していますが、モデルが存在しません。Gemmaへのフォールバックを提案するか、後で再試行してください。

「チャットセッションが見つかりません」 呼び出しが行われました sendMessage 新しいチャットを作成する前に setModel。モデルのロード時に既存のチャットはクリアされるため、新しく作成してください。

「モデルの準備ができていません」 setModel に失敗したか、呼び出されていません createChat.

出力が文章の途中で停止しました。 出力上限に達しました。より短い回答を求めるか、タスクを分割してください。

アプリがバックグラウンドに移行した際にエラーが発生します。 推論はフォアグラウンドのみで動作します。一時停止時にUI状態をキャンセルし、ユーザーに再試行を促してください。

プライバシーとストア審査

Gemini Nanoを使用すると、プロンプトとレスポンスがデバイス内に保持されるため、データ安全性のフォーム記入が簡素化されます。ただし、GoogleのML Kitの規約にはメトリクスの処理に関する記述があるため、プライバシーポリシーにはデバイス上でのAI利用について記載してください。また、Gemmaをフォールバックとして追加する場合は、モデルのダウンロードがHugging Faceから行われるため、そのネットワークリクエストについても言及してください。

AI機能のプロンプトやUIコピーは頻繁に変更されます。リリース後にこれらを調整する必要がある場合は、 Capgo の Live Update ストア審査を待たずに新しいWeb code を配信できます。ハイブリッドアプリにおけるAIの全体像については、こちらをご覧ください: Capacitor がAIモバイルアプリに適している理由.

まとめ

Gemini Nanoを利用すれば、対応しているAndroid端末上で、Capacitor アプリに無料・プライベート・オフラインのテキスト生成機能を提供できます。以下の方法でロードし、 setModel({ path: 'Gemini Nano' })ストリーミング処理を行い、 textFromAiプロンプトと出力を短く保ち、フォアグラウンドおよびクォータのルールを遵守してください。また、Gemini Nano非対応の多くのデバイスのために、Gemmaやクラウドへのフォールバックを用意しておきましょう。

Capacitor アプリ向けの Live Update

Webレイヤーのバグが本番に出たときは、アプリストアの審査を何日も待たずに Capgo で修正を配信できます。ネイティブの変更は通常の審査フローに乗せたまま、ユーザーにはバックグラウンドでアップデートが届きます。

最新のブログ記事

Capgo は、本格的なプロフェッショナルモバイルアプリの開発に必要な最高のインサイトを提供します。