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

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

CapacitorアプリでGemini Nanoを使い、AndroidでオンデバイスAIを実現:デバイス対応、AICoreチェック、@capgo/capacitor-llmでのストリーミング、Gemmaフォールバックまで。

Capacitor モバイルアプリ内で動作する AI 機能のイラスト

CapacitorアプリでGemini Nanoを使うには、次をインストール @capgo/capacitor-llm、呼び出し setModel({ path: 'Gemini Nano' }) Android で、チャットを作成し、応答をストリーミングで textFromAi イベント。このプラグインは Google の ML Kit GenAI Prompt API を通じて Gemini Nano と通信し、モデルは Android の AICore システムサービス内で実行されるため、テキストは端末外に送信されません。

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

Gemini NanoがAndroidで動作する仕組み

Gemini Nano は Google が提供する最小の Gemini モデルで、スマートフォン向けに設計されています。アプリはこれをバンドルしません。Android の AICore サービスがダウンロード、更新、推論を実行し、アプリは ML Kit GenAI API:

  • 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 resolve し、 readinessChange emit する ready
ダウンロード可能 Reject する: 「Gemini Nano はダウンロード可能ですが、このデバイスではダウンロードされていません」
ダウンロード中 ダウンロード中のステータスで Reject する
利用不可 Reject する: 「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 生成が始まるとすぐに resolve し、テキストはイベント経由で届きます。これを、指定のタイミングで resolve する 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段落につき数秒 常に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 では、プロンプトと応答がデバイス上に保持されるため、Data safety フォームの記入が簡単になります。Google の ML Kit の規約にはメトリクス処理の記述がまだ残っているので、プライバシーポリシーでオンデバイス AI の利用について説明してください。Gemma フォールバックを追加する場合、モデルは Hugging Face からダウンロードされるため、そのネットワークリクエストについても記載してください。

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

まとめ

Gemini Nano は、対応する Android スマートフォンで、Capacitor アプリに無料・プライベート・オフラインのテキスト生成を提供します。読み込むには setModel({ path: 'Gemini Nano' })を使用し、ストリーミングするには textFromAi、プロンプトと出力は短く保ち、フォアグラウンドとクォータのルールを遵守し、対応していない多くのデバイス用にGemmaまたはクラウドフォールバックを用意してください。

Capacitor アプリ向けの Live Updates

Web レイヤーのバグが本番で発生したら、App Store の審査を何日も待たずに Capgo で修正を配信しましょう。ネイティブの変更は通常の審査フローに残したまま、ユーザーはバックグラウンドでアップデートを受け取れます。

最新のブログ記事

Capgoは、真にプロフェッショナルなモバイルアプリを作るために必要な最高のインサイトを提供します。