اگر لازم است پاسخهای Gemini Nano را برای پردازش بیشتر به قالبهای خاصی مثل JSON تجزیه کنید، از Structured Output API با دسترسی مستقیم به مدل استفاده کنید.
با Structured Output API، ساختار خروجی هدف را بااستفاده از کلاسها و حاشیهنویسیهای Kotlin تعریف میکنید. سپس دسترسی مستقیم به مدل پاسخی را در قالب شیء Kotlin شما برمیگرداند.
تولید برونداد ساختاریافته بهویژه برای کارهایی مثل موارد زیر مفید است:
- استخراج نهاد: استخراج فیلدهای ساختاریافته (برای مثال، نام رویداد، تاریخ، مکان) از نوشتار بدون ساختار.
- طبقهبندی: دستهبندی نوشتار ورودی در دستههای ازپیش تعریفشده.
- سریالسازی دادهها: تبدیل ورودی کاربر بدون ساختار به قالبی مناسب برای ذخیرهسازی پایگاه داده یا فراخوانیهای میانای برنامهسازی کاربردی.
پیشنیازها
برای تأیید اینکه Structured Output API در دستگاه دردسترس است، از
isStructuredOutputFeatureAvailable() API استفاده کنید. اگر Structured Output API در دستگاه دردسترس باشد، API مقدار true را برمیگرداند، و درغیراینصورت مقدار false را برمیگرداند.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
Structured Output API الزامات زیر را نیز دارد:
- Android API سطح ۲۶ یا بالاتر (
minSdk۲۶) - افزایه KSP نسخه ۲.۳.۶ یا بالاتر
محدودیتها
Structured Output API محدودیتهای زیر را دارد:
- فقط در Kotlin کار میکند.
- ProGuard ممکن است در تجزیه کلاس حاشیهنویسیشده شما تداخل ایجاد کند. کلاس حاشیهنویسیشدهتان را به قوانین نگهداری اضافه کنید تا اگر خطاهای تجزیه دریافت کردید، آنها را از ProGuard مستثنی کنید، برای مثال:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
پیکربندی پروژه
برای شروع به کار با Structured Output API، این مراحل را دنبال کنید:
وابستگی
com.google.mlkit:genai-promptرا در فایلbuild.gradle.kts(یاbuild.gradle) سطح برنامه اضافه کنید، اگر قبلاً این کار را نکردهاید.افزونه KSP را به فایل
build.gradle.ktsسطح پروژه اضافه کنید. از نسخه افزایه KSP که با نسخه Kotlin شما سازگار است استفاده کنید؛ توصیه میکنیم از KSP نسخه ۲.۳.۶ یا بالاتر استفاده کنید.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 |
برای مقادیر درست/غلط. | description |
List<T> |
برای فهرستهای انواع پشتیبانیشده یا کلاسهای @Generable تودرتو.
|
description، minItems، maxItems |
List<String> |
برای فهرستهای مقادیر String. |
description، enumValues، minItems،
maxItems |
کلاس @Generable |
برای اشیای ساختاریافته تودرتو. | 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
)
// 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}")
}
}
}
رسیدگی به دلایل پایان و خطاها
هنگام استفاده از Structured Output API، باید استثناهای احتمالی
ایجادشده توسط API را مدیریت کنید و ویژگی finishReason را در گزینههای پاسخ
بررسی کنید اگر پاسخ تجزیهشده تهی باشد.
مقادیر 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: تولید بهدلایل دیگر متوقف شد.
استثناها
Structured Output API ممکن است GenAiException را با کدهای خطای زیر
ارائه دهد:
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS(-۱۰۴): ساختار کلاس حاشیهنویسیشده نامعتبر است یا حاوی انواع پشتیبانینشده است. این معمولاً خطای پیکربندی زمان توسعه است. تعریف کلاس داده@Generableرا مرور کنید تا مطمئن شوید همه انواع ویژگیها پشتیبانی میشوند و هیچ وابستگی دوری وجود ندارد.GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-۱۰۵): مقادیر تولیدشده توسط مدل نامعتبر است یا درستیسنجی محدودیتها ناموفق است. این خطای زمان اجرا است. اگر مکرراً با این خطا مواجه میشوید، راهحلهای زیر را درنظر بگیرید:- پالایش دستورالعملهای پیامواره برای راهنمایی دقیقتر مدل.
- کاهش محدودیتها (مانند حداقل، حداکثر، یا محدودیتهای اندازه فهرست) در
حاشیهنویسیهای
@Guideاگر برای قابلیتهای مدل بیشازحد محدودکننده باشند. - پیادهسازی استراتژی بازگشت در برنامه، مانند امتحان مجدد درخواست یا نمایش وضعیت پیشفرض.
شمارش دادهواحدها
برای بررسی اینکه پیامواره ساختاریافته شما در محدوده دادهواحد ورودی قرار دارد یا نه،
تعداد دادهواحد را بااستفاده از روش countTokens() محاسبه کنید.
چون درخواستهای برونداد ساختاریافته باید به مدل درباره ساختار طرحواره دستور دهند، شمارش کردن نشانها فقط در نوشتار پیامواره خام (بااستفاده از نمونه GenerateContentRequest) دقیق نیست. برای دریافت تعداد دقیق دادهواحد، باید نمونه کامل GenerateTypedContentRequest را که شامل کلاس هدف و پیکربندیهای طرحواره شما است، به روش countTokens() ارسال کنید:
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse