Если вам необходимо преобразовать ответы от Prompt API в определенные форматы, например JSON, для дальнейшей обработки, используйте Structured Output API.
С помощью Structured Output API вы определяете целевую структуру выходных данных, используя классы Kotlin и аннотации. Затем Prompt API возвращает ответ в виде вашего объекта Kotlin.
Создание структурированных выходных данных особенно полезно для решения следующих задач:
- Извлечение сущностей : извлечение структурированных полей (например, название события, дата, местоположение) из неструктурированного текста.
- Классификация : Разделение входного текста на предопределенные категории.
- Сериализация данных : преобразование неструктурированного пользовательского ввода в формат, подходящий для хранения в базе данных или для вызовов API.
Предварительные требования
Чтобы проверить доступность API структурированного вывода на устройстве, используйте функцию isStructuredOutputFeatureAvailable() . Функция возвращает true если API структурированного вывода доступен на устройстве, и false в противном случае.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
API для структурированного вывода также предъявляет следующие требования:
- Android API уровня 26 или выше (
minSdk26) - Версия плагина KSP 2.3.6 или выше.
Ограничения
API структурированного вывода имеет следующие ограничения:
- Работает только на Kotlin.
- ProGuard может мешать разбору вашего аннотированного класса. Добавьте ваш аннотированный класс в правила сохранения , чтобы исключить его из обработки ProGuard в случае ошибок разбора, например:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
Настройка проекта
Для начала работы с API структурированного вывода выполните следующие шаги:
Если вы еще этого не сделали, добавьте API ML Kit Prompt в качестве зависимости в файл
build.gradle.kts(илиbuild.gradle) на уровне вашего приложения.Добавьте плагин KSP в файл
build.gradle.ktsвашего проекта. Используйте версию плагина KSP, совместимую с вашей версией Kotlin; мы рекомендуем версию KSP 2.3.6 или выше.dependencies { ... classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6" }Добавьте зависимости структурированного компилятора в файл
build.gradle.ktsна уровне приложения:dependencies { ... ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1") }
Определите структуру выходных данных
Определите структуру данных, которые должна возвращать модель, используя классы данных Kotlin. Для определения структуры выходных данных используются две основные аннотации:
- Используйте аннотацию
@Generable, чтобы определить класс как целевой для структурированного вывода. - Используйте аннотации
@Guideдля свойств класса, чтобы предоставить описания и ограничения, которые определяют выходные данные модели.
В следующем примере представлена структура для извлечения информации о растениях:
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
)
Поддерживаемые типы и ограничения
В классе, аннотированном @Generable , поддерживаются следующие типы данных, а также соответствующие им ограничения @Guide :
| Тип | Описание | Поддерживаемые ограничения @Guide |
|---|---|---|
String | Для текста. | description , enumValues |
Double / Float | Для чисел с плавающей запятой. | description , minimum , maximum |
Int / Long | Для целых чисел. | description , minimum , maximum |
Boolean | Для значений true/false. | description |
List<T> | Для получения списков поддерживаемых типов или вложенных классов с аннотацией @Generable . | description , minItems , maxItems |
List<String> | Для списков String значений. | description , enumValues , minItems , maxItems |
@Generable class | Для вложенных структурированных объектов. | description |
Генерация структурированного контента
Для запроса структурированного вывода используйте вспомогательную функцию generateTypedContentRequest , чтобы обернуть стандартный запрос и указать целевой класс вывода.
// 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}")
}
}
}
Обрабатывать причины завершения и ошибки.
При использовании API структурированного вывода следует обрабатывать потенциальные исключения, генерируемые API, и проверять свойство finishReason в кандидатах ответа, если проанализированный ответ равен null.
значения finishReason
Свойство finishReason может принимать одно из следующих значений:
-
TypedFinishReason.STOP: Генерация модели успешно завершена, и выходные данные соответствуют схеме. -
TypedFinishReason.MAX_TOKENS: Модель остановилась, поскольку достигла лимита токенов. Вывод может быть неполным. -
TypedFinishReason.PARSE_CLASS_ERROR: Генерация модели завершена, но полученный JSON не может быть преобразован в целевой класс Kotlin. -
TypedFinishReason.STRUCTURE_NOT_ANNOTATED: В целевом классе или его вложенных классах отсутствует необходимая аннотация@Generable. -
TypedFinishReason.STRUCTURE_VALUES_INVALID: Сгенерированные значения нарушают ограничения, определенные в аннотациях@Guide(например, значение выходит за пределы допустимого диапазона, размер списка выходит за рамки допустимого). -
TypedFinishReason.OTHER: Генерация остановлена по другим причинам.
Исключения
API структурированного вывода может генерировать исключение GenAiException со следующими кодами ошибок:
-
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS(-104): Структура аннотированного класса недопустима или содержит неподдерживаемые типы. Обычно это ошибка конфигурации на этапе разработки. Проверьте определение класса данных с аннотацией@Generable, чтобы убедиться, что поддерживаются все типы свойств и отсутствуют циклические зависимости. -
GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105): Значения, сгенерированные моделью, недопустимы или не прошли проверку ограничений. Это ошибка выполнения. Если вы часто сталкиваетесь с этой ошибкой, рассмотрите следующие решения:- Уточните ваши инструкции, чтобы более строго направлять модель.
- Смягчите ограничения (например, минимальное, максимальное значение или ограничения размера списка) в аннотациях
@Guide, если они слишком ограничительны для возможностей модели. - Внедрение в ваше приложение стратегии резервного копирования, например, повторная попытка запроса или отображение состояния по умолчанию.
Подсчет токенов
Чтобы проверить, соответствует ли ваш структурированный запрос лимиту входных токенов, вычислите количество токенов с помощью метода countTokens() .
Поскольку запросы структурированного вывода должны указывать модели структуру схемы, подсчет токенов только на основе исходного текста запроса (с использованием экземпляра GenerateContentRequest ) неточен. Для получения точного подсчета токенов необходимо передать в метод countTokens() полный экземпляр GenerateTypedContentRequest , включающий конфигурацию целевого класса и схемы:
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse