تولید برونداد ساختاریافته

اگر لازم است پاسخ‌های 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، این مراحل را دنبال کنید:

  1. وابستگی com.google.mlkit:genai-prompt را در فایل build.gradle.kts (یا build.gradle) سطح برنامه اضافه کنید، اگر قبلاً این کار را نکرده‌اید.

  2. افزونه KSP را به فایل build.gradle.kts سطح پروژه اضافه کنید. از نسخه افزایه KSP که با نسخه Kotlin شما سازگار است استفاده کنید؛ توصیه می‌کنیم از KSP نسخه ۲.۳.۶ یا بالاتر استفاده کنید.

    dependencies {
        ...
        classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6"
    }
    
  3. وابستگی‌های کامپایلر ساختاریافته را به فایل 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