Si vous devez analyser les réponses de l'API Prompt dans certains formats, tels que JSON, pour un traitement ultérieur, utilisez l'API Structured Output.
Avec l'API Structured Output, vous définissez la structure de sortie cible à l'aide de classes et d'annotations Kotlin. L'API Prompt renvoie ensuite une réponse sous la forme de votre objet Kotlin.
La génération de résultats structurés est particulièrement utile pour les tâches suivantes :
- Extraction d'entités : extraction de champs structurés (par exemple, nom de l'événement, date, lieu) à partir de texte non structuré.
- Classification : catégorisation du texte saisi dans des catégories prédéfinies.
- Sérialisation des données : conversion des entrées utilisateur non structurées dans un format adapté au stockage dans une base de données ou aux appels d'API.
Prérequis
Pour vérifier que l'API Structured Output est disponible sur l'appareil, utilisez l'API isStructuredOutputFeatureAvailable(). L'API renvoie true si l'API Structured Output est disponible sur l'appareil, et false dans le cas contraire.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
L'API Structured Output présente également les exigences suivantes :
- Android (niveau d'API 26 ou version ultérieure) :
minSdk26 - Plug-in KSP version 2.3.6 ou ultérieure
Limites
L'API Structured Output présente les limites suivantes :
- Ne fonctionne qu'en Kotlin.
- ProGuard peut interférer avec l'analyse de votre classe annotée. Ajoutez votre classe annotée à vos règles de conservation pour l'exclure de ProGuard si vous rencontrez des erreurs d'analyse, par exemple :
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
Configurer le projet
Pour commencer à utiliser l'API Structured Output, procédez comme suit :
Ajoutez l'API ML Kit Prompt en tant que dépendance dans le fichier
build.gradle.kts(oubuild.gradle) au niveau de votre application, si vous ne l'avez pas déjà fait.Ajoutez le plug-in KSP au fichier
build.gradle.ktsau niveau du projet. Utilisez une version du plug-in KSP compatible avec votre version de Kotlin. Nous vous recommandons la version 2.3.6 ou ultérieure de KSP.dependencies { ... classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6" }Ajoutez les dépendances du compilateur structuré à votre fichier
build.gradle.ktsau niveau de l'application :dependencies { ... ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1") }
Définir la structure de sortie
Définissez la structure des données que vous souhaitez que le modèle renvoie à l'aide des classes de données Kotlin. Il existe deux annotations principales pour définir la structure de sortie :
- Utilisez l'annotation
@Generablepour définir la classe comme cible pour la sortie structurée. - Utilisez les annotations
@Guidesur les propriétés de classe pour fournir des descriptions et des contraintes qui guident la sortie du modèle.
L'exemple suivant définit une structure pour extraire des informations sur les plantes :
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
)
Types et contraintes acceptés
Les types suivants sont acceptés dans une classe annotée @Generable, ainsi que leurs contraintes @Guide respectives :
| Type | Description | Contraintes @Guide acceptées |
|---|---|---|
String |
Pour le texte. | description, enumValues |
Double/Float |
Pour les nombres à virgule flottante. | description, minimum, maximum |
Int/Long |
Pour les nombres entiers. | description, minimum, maximum |
Boolean |
Pour les valeurs "true"/"false". | description |
List<T> |
Pour obtenir la liste des types ou des classes @Generable imbriquées acceptés.
|
description, minItems, maxItems |
List<String> |
Pour les listes de valeurs String. |
description, enumValues, minItems,
maxItems |
Classe @Generable |
Pour les objets structurés imbriqués. | description |
Générer du contenu structuré
Pour demander une sortie structurée, utilisez la fonction d'assistance generateTypedContentRequest pour encapsuler votre requête standard et spécifier la classe de sortie cible.
// 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}")
}
}
}
Gérer les motifs de fin et les erreurs
Lorsque vous utilisez l'API Structured Output, vous devez gérer les exceptions potentielles générées par l'API et inspecter la propriété finishReason dans les candidats de réponse si la réponse analysée est nulle.
Valeurs finishReason
La propriété finishReason peut prendre l'une des valeurs suivantes :
TypedFinishReason.STOP: le modèle a terminé la génération avec succès et la sortie correspond au schéma.TypedFinishReason.MAX_TOKENS: le modèle s'est arrêté, car il a atteint la limite de jetons. La sortie peut être incomplète.TypedFinishReason.PARSE_CLASS_ERROR: le modèle a terminé la génération, mais le fichier JSON obtenu n'a pas pu être analysé dans la classe Kotlin cible.TypedFinishReason.STRUCTURE_NOT_ANNOTATED: la classe cible ou ses classes imbriquées ne comportent pas l'annotation@Generablerequise.TypedFinishReason.STRUCTURE_VALUES_INVALID: les valeurs générées ne respectaient pas les contraintes définies dans les annotations@Guide(par exemple, valeur hors plage, taille de la liste hors limites).TypedFinishReason.OTHER: la génération a été arrêtée pour d'autres raisons.
Exceptions
L'API Structured Output peut générer GenAiException avec les codes d'erreur suivants :
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS(-104) : la structure de la classe annotée n'est pas valide ou contient des types non compatibles. Il s'agit généralement d'une erreur de configuration au moment du développement. Examinez la définition de votre classe de données@Generablepour vérifier que tous les types de propriétés sont compatibles et qu'il n'y a pas de dépendances circulaires.GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105) : les valeurs générées par le modèle ne sont pas valides ou ne respectent pas les contraintes. Il s'agit d'une erreur d'exécution. Si vous rencontrez fréquemment cette erreur, essayez les solutions suivantes :- Affiner les instructions de votre requête pour guider le modèle de manière plus stricte.
- Relâchez les contraintes (comme les limites de taille minimale, maximale ou de liste) dans vos annotations
@Guidesi elles sont trop restrictives pour les capacités du modèle. - Mettre en œuvre une stratégie de secours dans votre application, par exemple en réessayant la requête ou en affichant un état par défaut.
Compter les jetons
Pour vérifier si votre requête structurée respecte la limite de jetons d'entrée, calculez le nombre de jetons à l'aide de la méthode countTokens().
Étant donné que les requêtes de sortie structurée doivent indiquer au modèle la structure du schéma, le décompte des jetons sur le texte brut du prompt (à l'aide d'une instance GenerateContentRequest) n'est pas précis. Pour obtenir un nombre de jetons précis, vous devez transmettre l'instance GenerateTypedContentRequest complète, qui inclut votre classe cible et vos configurations de schéma, à la méthode countTokens() :
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse