Strukturierte Ausgabe generieren

Wenn Sie Antworten von Gemini Nano zur weiteren Verarbeitung in bestimmte Formate wie JSON parsen müssen, verwenden Sie die Structured Output API mit direktem Modellzugriff.

Mit der Structured Output API definieren Sie die Zielausgabestruktur mithilfe von Kotlin-Klassen und ‑Anmerkungen. Der direkte Modellzugriff gibt dann eine Antwort in Form Ihres Kotlin-Objekts zurück.

Das Generieren strukturierter Ausgaben ist besonders für die folgenden Aufgaben nützlich:

  • Entitätsextraktion: Strukturierte Felder (z. B. Ereignisname, Datum, Ort) aus unstrukturiertem Text extrahieren.
  • Klassifizierung: Eingabetext in vordefinierte Kategorien einteilen.
  • Datenserialisierung: Unstrukturierte Nutzereingaben werden in ein Format umgewandelt, das für die Speicherung in einer Datenbank oder für API-Aufrufe geeignet ist.

Vorbereitung

Mit der isStructuredOutputFeatureAvailable() API können Sie prüfen, ob die Structured Output API auf dem Gerät verfügbar ist. Die API gibt true zurück, wenn die Structured Output API auf dem Gerät verfügbar ist, andernfalls false.

suspend fun isStructuredOutputFeatureAvailable(): Boolean

Für die Structured Output API gelten außerdem die folgenden Anforderungen:

  • Android API-Level 26 oder höher (minSdk 26)
  • KSP-Plug-in-Version 2.3.6 oder höher

Beschränkungen

Für die Structured Output API gelten die folgenden Einschränkungen:

  • Funktioniert nur in Kotlin.
  • ProGuard kann das Parsen Ihrer annotierten Klasse beeinträchtigen. Fügen Sie die annotierte Klasse zu den Beibehaltungsregeln hinzu, um sie von ProGuard auszuschließen, wenn beim Parsen Fehler auftreten, z. B.:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }

Projekt konfigurieren

So verwenden Sie die Structured Output API:

  1. Fügen Sie die Abhängigkeit com.google.mlkit:genai-prompt in die Datei build.gradle.kts (oder build.gradle) auf App-Ebene ein, falls noch nicht geschehen.

  2. Fügen Sie das KSP-Plug-in der build.gradle.kts-Datei auf Projektebene hinzu. Verwenden Sie eine KSP-Plug-in-Version, die mit Ihrer Kotlin-Version kompatibel ist. Wir empfehlen KSP-Version 2.3.6 oder höher.

    dependencies {
        ...
        classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6"
    }
    
  3. Fügen Sie der Datei build.gradle.kts auf App-Ebene die Abhängigkeiten für den strukturierten Compiler hinzu:

    dependencies {
        ...
        ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1")
    }
    

Ausgabestruktur definieren

Definieren Sie die Struktur der Daten, die das Modell zurückgeben soll, mithilfe von Kotlin-Datenklassen. Es gibt zwei Hauptannotationen zum Definieren der Ausgabestruktur:

  • Verwenden Sie die Annotation @Generable, um die Klasse als Ziel für die strukturierte Ausgabe zu definieren.
  • Verwenden Sie die @Guide-Annotationen für die Klasseneigenschaften, um Beschreibungen und Einschränkungen anzugeben, die die Ausgabe des Modells steuern.

Im folgenden Beispiel wird eine Struktur zum Extrahieren von Pflanzeninformationen definiert:

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
)

Unterstützte Typen und Einschränkungen

Die folgenden Typen werden in einer mit @Generable annotierten Klasse unterstützt, zusammen mit den entsprechenden @Guide-Einschränkungen:

Typ Beschreibung Unterstützte @Guide-Einschränkungen
String Für Text. description, enumValues
Double/Float Für Gleitkommazahlen. description, minimum, maximum
Int/Long Für ganze Zahlen. description, minimum, maximum
Boolean Für Wahr/Falsch-Werte. description
List<T> Für Listen mit unterstützten Typen oder verschachtelten @Generable-Klassen. description, minItems, maxItems
List<String> Für Listen mit String-Werten. description, enumValues, minItems, maxItems
Klasse @Generable Für verschachtelte strukturierte Objekte. description

Strukturierte Inhalte generieren

Wenn Sie eine strukturierte Ausgabe anfordern möchten, verwenden Sie die Hilfsfunktion generateTypedContentRequest, um Ihren Standard-Prompt zu umschließen und die Zielausgabeklasse anzugeben.

// 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}")
        }
    }
}

Gründe für das Beenden und Fehler verarbeiten

Wenn Sie die Structured Output API verwenden, sollten Sie potenzielle Ausnahmen behandeln, die von der API ausgelöst werden, und das Attribut finishReason in den Antwortkandidaten prüfen, wenn die geparste Antwort null ist.

finishReason-Werte

Die Property finishReason kann einen der folgenden Werte haben:

  • TypedFinishReason.STOP: Das Modell hat die Generierung erfolgreich abgeschlossen und die Ausgabe entspricht dem Schema.
  • TypedFinishReason.MAX_TOKENS: Das Modell wurde beendet, weil das Tokenlimit erreicht wurde. Die Ausgabe ist möglicherweise unvollständig.
  • TypedFinishReason.PARSE_CLASS_ERROR: Das Modell hat die Generierung abgeschlossen, aber das resultierende JSON konnte nicht in die Kotlin-Zielklasse geparst werden.
  • TypedFinishReason.STRUCTURE_NOT_ANNOTATED: In der Zielklasse oder ihren verschachtelten Klassen fehlt die erforderliche @Generable-Annotation.
  • TypedFinishReason.STRUCTURE_VALUES_INVALID: Die generierten Werte haben gegen die in den @Guide-Annotationen definierten Einschränkungen verstoßen (z. B. Wert außerhalb des Bereichs, Listengröße außerhalb der Grenzen).
  • TypedFinishReason.OTHER: Die Generierung wurde aus anderen Gründen beendet.

Ausnahmen

Die Structured Output API kann GenAiException mit den folgenden Fehlercodes ausgeben:

  • GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS (-104): Die Struktur der annotierten Klasse ist ungültig oder enthält nicht unterstützte Typen. Dies ist in der Regel ein Konfigurationsfehler während der Entwicklung. Prüfen Sie die Definition der Datenklasse @Generable, um sicherzustellen, dass alle Property-Typen unterstützt werden und keine Zirkelbezüge vorhanden sind.
  • GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE (-105): Die vom Modell generierten Werte sind ungültig oder bestehen die Einschränkungsprüfung nicht. Dies ist ein Laufzeitfehler. Wenn dieser Fehler häufig auftritt, können Sie Folgendes versuchen:
    • Prompt-Anweisungen verfeinern, um das Modell genauer zu steuern.
    • Lockerung der Einschränkungen (z. B. Mindest-, Höchst- oder Listengrößenlimits) in Ihren @Guide-Anmerkungen, wenn sie für die Funktionen des Modells zu restriktiv sind.
    • Implementieren Sie eine Fallback-Strategie in Ihrer App, z. B. die Anfrage wiederholen oder einen Standardstatus anzeigen.

Tokens zählen

Wenn Sie prüfen möchten, ob Ihr strukturierter Prompt innerhalb des Limits für Eingabe-Tokens liegt, berechnen Sie die Tokenanzahl mit der Methode countTokens().

Da bei Anfragen für strukturierte Ausgaben das Modell über die Schemastruktur informiert werden muss, ist das Zählen von Tokens nur für den Rohtext des Prompts (mit einer GenerateContentRequest-Instanz) nicht genau. Wenn Sie eine genaue Tokenanzahl erhalten möchten, müssen Sie die vollständige GenerateTypedContentRequest-Instanz, die Ihre Zielklasse und Schemakonfigurationen enthält, an die countTokens()-Methode übergeben:

suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse