Se você precisar analisar as respostas do Gemini Nano em determinados formatos, como JSON, para processamento adicional, use a API Structured Output com acesso direto ao modelo.
Com a API Structured Output, você define a estrutura de saída de destino usando classes e anotações do Kotlin. O acesso direto ao modelo retorna uma resposta na forma do objeto Kotlin.
A geração de saída estruturada é particularmente útil para tarefas como as seguintes:
- Extração de entidades: extração de campos estruturados (por exemplo, nome do evento, data, local) de textos não estruturados.
- Classificação: categorizar o texto de entrada em categorias predefinidas.
- Serialização de dados: conversão de entradas não estruturadas do usuário em um formato adequado para armazenamento em banco de dados ou chamadas de API.
Pré-requisitos
Para verificar se a API Structured Output está disponível no dispositivo, use a
API isStructuredOutputFeatureAvailable(). A API vai retornar true se a
API Structured Output estiver disponível no dispositivo e false caso contrário.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
A API Structured Output também tem os seguintes requisitos:
- Nível 26 ou mais recente da API do Android (
minSdk26) - Plug-in do KSP versão 2.3.6 ou mais recente
Limitações
A API Structured Output tem as seguintes limitações:
- Funciona apenas em Kotlin.
- O ProGuard pode interferir na análise da classe anotada. Adicione a classe anotada às regras de manutenção para excluí-las do ProGuard se houver erros de análise, por exemplo:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
Configurar projeto
Para começar a usar a API Structured Output, siga estas etapas:
Adicione a dependência
com.google.mlkit:genai-promptao arquivobuild.gradle.kts(oubuild.gradle) no nível do app, caso ainda não tenha feito isso.Adicione o plug-in do KSP ao arquivo
build.gradle.ktspara envolvidos no projeto. Use uma versão do plug-in do KSP compatível com sua versão do Kotlin. Recomendamos a versão 2.3.6 ou mais recente do KSP.dependencies { ... classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6" }Adicione as dependências do compilador estruturado ao arquivo
build.gradle.ktsno nível do app:dependencies { ... ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1") }
Definir a estrutura de saída
Defina a estrutura dos dados que você quer que o modelo retorne usando classes de dados do Kotlin. Há duas anotações principais para definir a estrutura de saída:
- Use a anotação
@Generablepara definir a classe como um destino para a saída estruturada. - Use as anotações
@Guidenas propriedades da classe para fornecer descrições e restrições que orientam a saída do modelo.
O exemplo a seguir define uma estrutura para extrair informações de plantas:
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
)
Tipos e restrições compatíveis
Os tipos a seguir são compatíveis com uma classe anotada @Generable,
além das respectivas restrições @Guide:
| Tipo | Descrição | Restrições de @Guide aceitas |
|---|---|---|
String |
Para texto. | description, enumValues |
Double / Float |
Para números de ponto flutuante. | description, minimum, maximum |
Int / Long |
Para números inteiros. | description, minimum, maximum |
Boolean |
Para valores verdadeiro/falso. | description |
List<T> |
Para listas de tipos compatíveis ou classes @Generable aninhadas.
|
description, minItems, maxItems |
List<String> |
Para listas de valores String. |
description, enumValues, minItems,
maxItems |
Classe @Generable |
Para objetos estruturados aninhados. | description |
Gerar conteúdo estruturado
Para solicitar uma saída estruturada, use a função auxiliar generateTypedContentRequest
para encapsular o comando padrão e especificar a classe de saída de destino.
// 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}")
}
}
}
Tratar motivos de encerramento e erros
Ao usar a API Structured Output, você precisa lidar com possíveis exceções
geradas pela API e inspecionar a propriedade finishReason nos candidatos de resposta
se a resposta analisada for nula.
Valores de finishReason
A propriedade finishReason pode usar um dos seguintes valores:
TypedFinishReason.STOP: o modelo terminou de gerar com sucesso, e a saída corresponde ao esquema.TypedFinishReason.MAX_TOKENS: o modelo foi interrompido porque atingiu o limite de tokens. A saída pode estar incompleta.TypedFinishReason.PARSE_CLASS_ERROR: o modelo concluiu a geração, mas o JSON resultante não pôde ser analisado na classe Kotlin de destino.TypedFinishReason.STRUCTURE_NOT_ANNOTATED: a classe de destino ou as classes aninhadas não têm a anotação@Generablenecessária.TypedFinishReason.STRUCTURE_VALUES_INVALID: os valores gerados violaram as restrições definidas nas anotações@Guide(por exemplo, valor fora do intervalo, tamanho da lista fora dos limites).TypedFinishReason.OTHER: a geração foi interrompida por outros motivos.
Exceções
A API Structured Output pode gerar GenAiException com os seguintes
códigos de erro:
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS(-104): a estrutura da classe anotada é inválida ou contém tipos incompatíveis. Normalmente, esse é um erro de configuração no momento do desenvolvimento. Revise a definição da classe de dados@Generablepara verificar se todos os tipos de propriedade são compatíveis e se não há dependências circulares.GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105): os valores gerados pelo modelo são inválidos ou não passam na verificação de restrições. Esse é um erro de tempo de execução. Se você encontrar esse erro com frequência, considere as seguintes soluções:- Refinar as instruções do comando para orientar o modelo de forma mais rigorosa.
- Flexibilizar as restrições (como limites de tamanho mínimo, máximo ou de lista) nas
anotações
@Guidese forem muito restritivas para os recursos do modelo. - Implementar uma estratégia de fallback no app, como repetir a solicitação ou exibir um estado padrão.
Contar tokens
Para verificar se o comando estruturado está dentro do limite de tokens de entrada, calcule
a contagem de tokens usando o método countTokens().
Como as solicitações de saída estruturada precisam instruir o modelo sobre a estrutura do esquema, contar tokens apenas no texto bruto do comando (usando uma
instância GenerateContentRequest) não é preciso. Para receber uma contagem de tokens
precisa, transmita a instância GenerateTypedContentRequest completa, que
inclui as configurações de classe e esquema de destino, para o método
countTokens():
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse