Se devi analizzare le risposte dell'API Prompt in determinati formati, ad esempio JSON, per un'ulteriore elaborazione, utilizza l'API Structured Output.
Con l'API Structured Output, definisci la struttura di output di destinazione utilizzando classi e annotazioni Kotlin. L'API Prompt 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 dell'evento, data, luogo) 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 per l'archiviazione del database o le 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 e false in caso contrario.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
L'API Structured Output presenta anche i seguenti requisiti:
- Livello API Android 26 o superiore (
minSdk26) - Versione del plug-in KSP 2.3.6 o superiore
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 tue 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:
Aggiungi l'API Prompt di ML Kit come dipendenza nel file a livello di app
build.gradle.kts(obuild.gradle), 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 KSP 2.3.6 o successive.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 di 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
@Guidenelle proprietà della classe per fornire descrizioni e vincoli che guidano l'output del modello.
L'esempio seguente definisce una struttura per l'estrazione delle 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
I seguenti tipi sono supportati all'interno di una classe annotata @Generable, 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, maximum |
Int / Long |
Per i numeri interi. | description, minimum, maximum |
Boolean |
Per i valori true/false. | description |
List<T> |
Per gli elenchi di tipi supportati o classi @Generable nidificate.
|
description, minItems, maxItems |
List<String> |
Per gli elenchi di valori String. |
description, enumValues, minItems,
maxItems |
Classe @Generable |
Per gli oggetti strutturati nidificati. | description |
Genera contenuti strutturati
Per richiedere l'output strutturato, utilizza la funzione helper generateTypedContentRequest per racchiudere 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,
// Instructs ML Kit to include the generated schema structure in the prompt
// sent to AICore. This should always be set to `true` unless the model
// already knows what output format to use.
includeSchemaInPrompt = true
)
// 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 completamento e gli errori
Quando utilizzi l'API Structured Output, devi gestire le potenziali eccezioni generate dall'API e controllare la proprietà finishReason nei candidati di risposta se la risposta analizzata è null.
Valori di finishReason
La proprietà finishReason può assumere uno dei seguenti valori:
TypedFinishReason.STOP: il modello ha completato la generazione correttamente e l'output corrisponde allo schema.TypedFinishReason.MAX_TOKENS: il modello si è arrestato 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 possibile analizzarlo nella classe Kotlin di destinazione.TypedFinishReason.STRUCTURE_NOT_ANNOTATED: la classe di destinazione o le relative classi nidificate non hanno l'annotazione@Generablerichiesta.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: la generazione è stata 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 esistano dipendenze cicliche.GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105): i valori generati dal modello non sono validi o non superano la verifica dei vincoli. Si tratta di un errore di runtime. Se si verifica spesso questo errore, valuta le seguenti soluzioni:- Perfezionare le istruzioni del prompt per guidare il modello in modo più rigoroso.
- Rilassare i vincoli (come i limiti di dimensione minima, massima o dell'elenco) nelle annotazioni
@Guidese sono troppo restrittivi per le funzionalità del modello. - Implementare una strategia di fallback 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 countTokens() metodo.
Poiché le richieste di output strutturato devono indicare al modello la struttura dello schema, il conteggio dei token solo sul testo del prompt non elaborato (utilizzando un'istanza GenerateContentRequest) non è accurato. Per ottenere un conteggio accurato dei token, devi passare l'istanza GenerateTypedContentRequest completa, che include la classe di destinazione e le configurazioni dello schema, al metodo countTokens():
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse