Se devi analizzare le risposte di Gemini Nano in determinati formati, ad esempio JSON, per un'ulteriore elaborazione, utilizza l'API Structured Output con accesso diretto al modello.
Con l'API Structured Output, definisci la struttura di output di destinazione utilizzando classi e annotazioni Kotlin. L'accesso diretto al modello restituisce quindi una risposta sotto forma di oggetto Kotlin.
La generazione di output strutturato è particolarmente utile per attività come le seguenti:
- Estrazione di entità: estrazione di campi strutturati (ad esempio nome, data e luogo dell'evento) da testo non strutturato.
- Classificazione: categorizzazione del testo di input in categorie predefinite.
- Serializzazione dei dati: conversione dell'input utente non strutturato in un formato adatto all'archiviazione nel database o alle chiamate API.
Prerequisiti
Per verificare che l'API Structured Output sia disponibile sul dispositivo, utilizza l'API isStructuredOutputFeatureAvailable(). L'API restituisce true se l'API Structured Output è disponibile sul dispositivo, altrimenti restituisce false.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
L'API Structured Output prevede anche i seguenti requisiti:
- Livello API Android 26 o versioni successive (
minSdk26) - Plug-in KSP versione 2.3.6 o successive
Limitazioni
L'API Structured Output presenta le seguenti limitazioni:
- Funziona solo in Kotlin.
- ProGuard potrebbe interferire con l'analisi della classe annotata. Aggiungi la tua classe annotata alle regole di conservazione per escluderla da ProGuard se ricevi errori di analisi, ad esempio:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
Configura il progetto
Per iniziare a utilizzare l'API Structured Output, segui questi passaggi:
Aggiungi la dipendenza
com.google.mlkit:genai-promptnel filebuild.gradle.kts(obuild.gradle) a livello di app, se non l'hai già fatto.Aggiungi il plug-in KSP al file
build.gradle.ktsa livello di progetto. Utilizza una versione del plug-in KSP compatibile con la tua versione di Kotlin. Ti consigliamo la versione 2.3.6 o successive di KSP.dependencies { ... classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6" }Aggiungi le dipendenze del compilatore strutturato al file
build.gradle.ktsa livello di app:dependencies { ... ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1") }
Definisci la struttura dell'output
Definisci la struttura dei dati che vuoi che il modello restituisca utilizzando le classi di dati Kotlin. Esistono due annotazioni principali per definire la struttura di output:
- Utilizza l'annotazione
@Generableper definire la classe come target per l'output strutturato. - Utilizza le annotazioni
@Guidesulle proprietà della classe per fornire descrizioni e vincoli che guidano l'output del modello.
L'esempio seguente definisce una struttura per l'estrazione di informazioni sulle piante:
import com.google.mlkit.genai.schema.annotations.Generable
import com.google.mlkit.genai.schema.annotations.Guide
@Generable
data class PlantList(
@Guide(description = "The list of plants found", minItems = 1, maxItems = 5)
val plants: List<Plant>
)
@Generable("Information about a plant species")
data class Plant(
@Guide(description = "The common name of the plant")
val commonName: String,
@Guide(description = "The full latin scientific name of the plant")
val scientificName: String,
@Guide(
description = "The maximum height of the plant in centimeters.",
minimum = 1.0,
maximum = 10000.0
)
val maxHeightCm: Int,
@Guide(description = "Whether the plant is poisonous or not")
val isPoisonous: Boolean?,
@Guide(
description = "The primary continent where this plant is native to",
enumValues = ["Africa", "Antarctica", "Asia", "Australia", "Europe", "North America", "South America"]
)
val nativeContinent: String
)
Tipi e vincoli supportati
All'interno di una classe annotata @Generable sono supportati i seguenti tipi, insieme ai rispettivi vincoli @Guide:
| Tipo | Descrizione | Vincoli @Guide supportati |
|---|---|---|
String |
Per il testo. | description, enumValues |
Double/Float |
Per i numeri in virgola mobile. | description, minimum e maximum |
Int/Long |
Per i numeri interi. | description, minimum e maximum |
Boolean |
Per i valori vero/falso. | description |
List<T> |
Per elenchi di tipi supportati o classi @Generable nidificate.
|
description, minItems e maxItems |
List<String> |
Per gli elenchi di valori String. |
description, enumValues, minItems,
maxItems |
Classe @Generable |
Per oggetti strutturati nidificati. | description |
Genera contenuti strutturati
Per richiedere un output strutturato, utilizza la funzione helper generateTypedContentRequest per aggregare il prompt standard e specificare la classe di output di destinazione.
// 1. Initialize your GenerativeModel as usual
val generativeModel = Generation.getClient()
// 2. Prepare the prompt text
val promptText = "List some common plants found in California."
val baseRequest = GenerateContentRequest.Builder(TextPart(promptText)).build()
// 3. Create the typed request, specifying the target class (e.g., PlantList)
val typedRequest = generateTypedContentRequest(
generateContentRequest = baseRequest,
outputClass = PlantList::class
)
// 4. Run the inference
try {
val typedResponse = generativeModel.generateContent(typedRequest)
// 5. Access the parsed object
// The response candidates contain the parsed object of type T (PlantList in this case)
val plantList: PlantList? = typedResponse.candidates.firstOrNull()?.response
if (plantList != null) {
// Process the structured data
for (plant in plantList.plants) {
Log.d("StructuredOutput", "Found plant: ${plant.commonName} (${plant.scientificName})")
}
} else {
Log.e("StructuredOutput", "Failed to parse response into the desired structure.")
// Inspect finish reason for details
val finishReason = typedResponse.candidates.firstOrNull()?.finishReason
Log.d("StructuredOutput", "Finish reason: $finishReason")
}
} catch (e: GenAiException) {
// Handle API errors
when (e.errorCode) {
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS -> {
Log.e("StructuredOutput", "The class structure is not supported.")
}
GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE -> {
Log.e("StructuredOutput", "The model generated values that violate the schema constraints.")
}
else -> {
Log.e("StructuredOutput", "API error: ${e.message}")
}
}
}
Gestisci i motivi di interruzione e gli errori
Quando utilizzi l'API Structured Output, devi gestire le potenziali eccezioni generate dall'API e ispezionare la proprietà finishReason nei candidati della risposta se la risposta analizzata è null.
Valori di finishReason
La proprietà finishReason può assumere uno dei seguenti valori:
TypedFinishReason.STOP: il modello ha terminato la generazione correttamente e l'output corrisponde allo schema.TypedFinishReason.MAX_TOKENS: il modello si è interrotto perché ha raggiunto il limite di token. L'output potrebbe essere incompleto.TypedFinishReason.PARSE_CLASS_ERROR: il modello ha completato la generazione, ma il JSON risultante non è stato analizzato nella classe Kotlin di destinazione.TypedFinishReason.STRUCTURE_NOT_ANNOTATED: nella classe target o nelle sue classi nidificate manca l'annotazione@Generableobbligatoria.TypedFinishReason.STRUCTURE_VALUES_INVALID: i valori generati hanno violato i vincoli definiti nelle annotazioni@Guide(ad esempio, valore fuori intervallo, dimensione dell'elenco fuori dai limiti).TypedFinishReason.OTHER: generazione interrotta per altri motivi.
Eccezioni
L'API Structured Output potrebbe generare GenAiException con i seguenti
codici di errore:
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS(-104): la struttura della classe annotata non è valida o contiene tipi non supportati. In genere si tratta di un errore di configurazione in fase di sviluppo. Esamina la definizione della classe di dati@Generableper verificare che tutti i tipi di proprietà siano supportati e che non siano presenti dipendenze circolari.GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105): i valori generati dal modello non sono validi o non superano la verifica dei vincoli. Questo è un errore di runtime. Se riscontri spesso questo errore, prendi in considerazione le seguenti soluzioni:- Perfezionare le istruzioni del prompt per guidare il modello in modo più rigoroso.
- Allentare i vincoli (come i limiti minimi, massimi o di dimensione dell'elenco) nelle annotazioni
@Guidese sono troppo restrittivi per le funzionalità del modello. - Implementare una strategia di riserva nell'app, ad esempio riprovare la richiesta o visualizzare uno stato predefinito.
Contare i token
Per verificare se il prompt strutturato rientra nel limite di token di input, calcola il conteggio dei token utilizzando il metodo countTokens().
Poiché le richieste di output strutturato devono istruire il modello sulla struttura dello schema, il conteggio dei token solo sul testo del prompt non elaborato (utilizzando un'istanza GenerateContentRequest) non è accurato. Per ottenere un conteggio dei token accurato, devi passare l'istanza GenerateTypedContentRequest completa, che include le configurazioni della classe e dello schema di destinazione, al metodo countTokens():
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse