Salta al contenuto principale

Come usare Gemini Nano in un'app Capacitor

Usa Gemini Nano in un'app Capacitor per l'AI on-device su Android: supporto device, controlli AICore, streaming con @capgo/capacitor-llm e fallback con Gemma.

Illustrazione delle funzionalità AI all'interno di un'app mobile Capacitor

Per usare Gemini Nano in un'app Capacitor, installa @capgo/capacitor-llm, chiama setModel({ path: 'Gemini Nano' }) su Android, poi crea una chat e trasmetti la risposta in streaming tramite l' textFromAi evento. Il plugin comunica con Gemini Nano tramite Google ML Kit GenAI Prompt API, che esegue il modello all'interno del servizio di sistema AICore di Android, così il testo non lascia mai il telefono.

Questa guida spiega come funziona Gemini Nano, quali dispositivi lo supportano, le chiamate esatte al plugin, lo streaming, quote e limiti, un fallback con Gemma per i telefoni non supportati e come lo stesso code raggiunge Apple Intelligence su iOS.

Come funziona Gemini Nano su Android

Gemini Nano è il modello Gemini più piccolo di Google, progettato per i telefoni. Le app non lo includono nel bundle. Il servizio AICore di Android lo scarica, lo aggiorna ed esegue l'inferenza; le app vi accedono tramite le API ML Kit GenAI:

  • Prompt API: prompt testuali in formato libero, il API che usa questo plugin (com.google.mlkit:genai-prompt).
  • API delle funzionalità: task fissi come riassunto, correzione di bozze, riscrittura e descrizione di immagini.

Google rilascia il Prompt API in beta, senza SLA né policy di deprecazione: aspettati cambiamenti di comportamento tra le release e tieni l'integrazione dietro un tuo piccolo wrapper.

Poiché l'inferenza è locale, ottieni tre cose che un modello cloud non può darti: funziona offline, non ha costi per token e il testo dell'utente resta sul dispositivo. Il compromesso è un modello più piccolo con contesto e output brevi, quindi scegli task adatti: riscrittura, riassunto di una nota, classificazione di un messaggio, estrazione di campi, bozze di risposte brevi.

Dispositivi supportati e requisiti

Google pubblica l'elenco dei dispositivi supportati per API sul sito ML Kit GenAI, raggruppato per versione di Gemini Nano (nano-v2, nano-v3, nano-v4). Per il Prompt API copre Pixel 9 e successivi, i recenti flagship Samsung Galaxy S e Z, e i recenti flagship di OnePlus, OPPO, Xiaomi, vivo, Honor, realme, Motorola e altri. Controlla l'elenco aggiornato prima di promettere una funzionalità su un dispositivo specifico.

Ogni dispositivo supportato richiede anche:

Requisito Dettaglio
Versione Android API livello 26 o superiore (il plugin lo verifica)
Bootloader Bloccato. Google non supporta le API GenAI su bootloader sbloccati
AICore Presente e configurato. Subito dopo un setup o un reset del dispositivo può servire un po' di tempo per completare
Stato dell'app L'app deve essere in primo piano durante l'inferenza

Versioni diverse di Nano possono restituire output differenti per lo stesso prompt. Testa i tuoi prompt su almeno un dispositivo per ogni versione che ti interessa.

Installa il plugin

bun add @capgo/capacitor-llm
bunx cap sync

Il plugin segue la major version di Capacitor: usa la 8.x con Capacitor 8. Non sono necessarie modifiche all'Android manifest per Gemini Nano. Riferimento completo: docs del plugin e pagina del plugin.

Passo 1: carica Gemini Nano e verifica la disponibilità

setModel con il percorso speciale 'Gemini Nano' interroga ML Kit sullo stato del modello. La promise si risolve quando lo stato è disponibile, altrimenti viene rifiutata con un messaggio che include lo stato:

Stato da AICore Comportamento del plugin
disponibile setModel si risolve, readinessChange emette ready
scaricabile Rifiuta: “Gemini Nano è scaricabile ma non scaricato su questo dispositivo”
download in corso Rifiuta con lo stato download in corso
non disponibile Rifiuta: “Gemini Nano non è disponibile su questo dispositivo”
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';
}

Il plugin non avvia da solo il download del modello AICore. Se un dispositivo segnala che il modello è scaricabile, di solito arriva dopo che un'altra app o il sistema lo richiede; quindi ricontrolla a un avvio successivo invece di nascondere la funzionalità in modo permanente.

Passo 2: Crea una chat e trasmetti la risposta

Una chat mantiene la cronologia della conversazione. Su Android, sendMessage si risolve non appena inizia la generazione e il testo arriva tramite eventi. Incapsulalo in una promise che si risolve su 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()));
  }
}

Usalo per una funzionalità reale, ad esempio per redigere una risposta a una recensione di un cliente:

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() non accetta opzioni, quindi inserisci le istruzioni all'inizio del primo messaggio. Crea una nuova chat per ogni task. Nel percorso Gemini Nano, il plugin mantiene la cronologia in memoria e la invia con ogni messaggio, quindi le chat lunghe esauriscono rapidamente il budget di input.

Limiti da considerare nel design

Limite Valore Cosa fare
Input Sotto i 4.000 token per richiesta (Google) Mantieni le chat brevi, riduci i documenti lunghi
Output Il plugin limita le risposte di Gemini Nano a 256 token Chiedi output brevi, dividi i task lunghi
Foreground Inferenza solo quando l'app è in primo piano Non eseguirlo mai da task in background
Quota Quote per app, BUSY e errori di quota batteria Debounce, evita di generare a ogni tasto
Concorrenza Una generazione per chat Disabilita il pulsante invio durante lo streaming
Velocità Qualche secondo per un paragrafo Stream sempre verso la UI

Tratta un errore di quota come temporaneo. Mostra un'opzione di retry anziché un dialogo di errore.

Fallback: Gemma 4 quando Gemini Nano non è disponibile

La maggior parte degli smartphone Android in uso oggi non ha Gemini Nano. Lo stesso plugin può eseguire modelli Gemma aperti tramite LiteRT-LM, così mantieni un unico API. Il modello è grande, spesso oltre un gigabyte, quindi scaricalo solo dopo che l'utente ha acconsentito, idealmente su 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();
  }
}

Poi scegli il motore all'avvio:

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';
}

Se nessuna delle due opzioni è possibile, nascondi la funzionalità o chiama un modello cloud. Non bloccare i flussi principali sull'AI locale.

Lo stesso code su iOS

Su iOS il plugin usa Apple Intelligence tramite il framework Foundation Models. Cambia una riga:

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

createChat, sendMessage, e gli eventi restano gli stessi, e il mergeChunk l'helper sopra gestisce già il formato streaming per iOS. I requisiti del dispositivo variano (iOS 26 e un dispositivo compatibile con Apple Intelligence). La nostra guida su Apple Intelligence in un'app Capacitor tratta questo aspetto.

Test

  • Usa un telefono reale supportato. Gli emulatori non forniscono AICore con Gemini Nano.
  • Logga il setModel messaggio di rifiuto. Ti indica esattamente quale stato riporta AICore.
  • Su un dispositivo appena configurato o resettato, attendi che AICore completi la configurazione iniziale online, poi riprova.
  • Testa i tuoi prompt sulla versione Nano più bassa che supporti. Prompt brevi ed espliciti sono più stabili tra le versioni.

Risoluzione problemi

“Gemini Nano non è disponibile su questo dispositivo”. Il dispositivo non è nella lista di Google, il bootloader è sbloccato o AICore non è ancora configurato.

“Gemini Nano è scaricabile ma non scaricato”. Il dispositivo lo supporta, ma il modello non è presente. Offri il fallback Gemma o riprova più tardi.

“Sessione chat non trovata”. Hai chiamato sendMessage con una chat creata prima di un nuovo setModel. Caricare un modello cancella le chat esistenti, quindi creane una nuova.

“Modello non pronto”. setModel ha fallito o non è stato chiamato prima di createChat.

L'output si interrompe a metà frase. Hai raggiunto il limite di output. Chiedi una risposta più breve o dividi il task.

Errori quando l'app passa in background. L'inference avviene solo in foreground. Annulla lo stato UI in pausa e lascia che l'utente riprovi.

Privacy e revisione degli store

Con Gemini Nano, prompt e risposte restano sul dispositivo, il che semplifica il modulo Sicurezza dei dati. I termini di ML Kit di Google citano ancora l'elaborazione delle metriche, quindi descrivi l'uso dell'IA on-device nella tua privacy policy. Se aggiungi il fallback Gemma, il download del modello arriva da Hugging Face: menziona anche quella richiesta di rete.

I prompt e i testi UI per le funzioni IA cambiano spesso. Se devi metterli a punto dopo il rilascio, Live updates di Capgo ti permettono di pubblicare nuovi code web senza attendere la revisione degli store. Per una panoramica sull'IA nelle app ibride, leggi perché Capacitor è ideale per le app mobile AI.

In sintesi

Gemini Nano offre alle app Capacitor una generazione di testo gratuita, privata e offline sui telefoni Android supportati. Caricalo con setModel({ path: 'Gemini Nano' }), fai streaming tramite textFromAi, mantieni prompt e output brevi, rispetta le regole di foreground e quota, e tieni un fallback Gemma o cloud per i molti dispositivi che non lo supportano.

Live updates per le app Capacitor

Quando un bug nel web-layer è live, rilascia la fix tramite Capgo invece di aspettare giorni l'approvazione degli store. Gli utenti ricevono l'update in background, mentre le modifiche native restano nel normale processo di review.

Ultimi articoli dal blog

Capgo ti offre gli insight migliori che ti servono per creare un'app mobile davvero professionale.