تولید خروجی ساختاریافته

اگر برای پردازش بیشتر نیاز دارید که پاسخ‌های API Prompt را به فرمت‌های خاصی مانند JSON تجزیه کنید، از API خروجی ساختاریافته استفاده کنید.

با استفاده از API خروجی ساختاریافته، شما ساختار خروجی هدف را با استفاده از کلاس‌ها و حاشیه‌نویسی‌های کاتلین تعریف می‌کنید. سپس API Prompt پاسخی را در قالب شیء کاتلین شما برمی‌گرداند.

تولید خروجی ساختاریافته به ویژه برای کارهایی مانند موارد زیر مفید است:

  • استخراج موجودیت : استخراج فیلدهای ساختاریافته (برای مثال، نام رویداد، تاریخ، مکان) از متن بدون ساختار.
  • طبقه‌بندی : دسته‌بندی متن ورودی به دسته‌های از پیش تعریف‌شده.
  • سریال‌سازی داده‌ها : تبدیل ورودی‌های بدون ساختار کاربر به فرمتی مناسب برای ذخیره‌سازی در پایگاه داده یا فراخوانی‌های API.

پیش‌نیازها

برای تأیید اینکه API خروجی ساختاریافته (Structured Output API) در دستگاه موجود است، از API isStructuredOutputFeatureAvailable() استفاده کنید. اگر API خروجی ساختاریافته در دستگاه موجود باشد، API true و در غیر این صورت false را برمی‌گرداند.

suspend fun isStructuredOutputFeatureAvailable(): Boolean

API خروجی ساختاریافته همچنین الزامات زیر را دارد:

  • اندروید API سطح ۲۶ یا بالاتر ( minSdk 26)
  • افزونه KSP نسخه ۲.۳.۶ یا بالاتر

محدودیت‌ها

API خروجی ساختاریافته محدودیت‌های زیر را دارد:

  • فقط در کاتلین کار می‌کند.
  • ممکن است ProGuard در تجزیه کلاس حاشیه‌نویسی شده شما اختلال ایجاد کند. کلاس حاشیه‌نویسی شده خود را به قوانین Keep خود اضافه کنید تا در صورت بروز خطا در تجزیه، آنها را از ProGuard حذف کنید، برای مثال:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }

پیکربندی پروژه

برای شروع کار با API خروجی ساختاریافته، این مراحل را دنبال کنید:

  1. اگر قبلاً این کار را نکرده‌اید، API مربوط به ML Kit Prompt را به عنوان یک وابستگی در فایل build.gradle.kts (یا build.gradle ) در سطح برنامه خود اضافه کنید.

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

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

تعریف ساختار خروجی

ساختار داده‌هایی را که می‌خواهید مدل با استفاده از کلاس‌های داده کاتلین برگرداند، تعریف کنید. دو حاشیه‌نویسی اصلی برای تعریف ساختار خروجی وجود دارد:

  • از حاشیه‌نویسی @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,
    // 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 در کاندیدهای پاسخ بررسی کنید.

مقادیر finishReason

خاصیت finishReason می‌تواند یکی از مقادیر زیر را بپذیرد:

  • TypedFinishReason.STOP : تولید مدل با موفقیت به پایان رسید و خروجی با طرحواره مطابقت دارد.
  • TypedFinishReason.MAX_TOKENS : مدل به دلیل رسیدن به حد مجاز توکن متوقف شد. خروجی ممکن است ناقص باشد.
  • TypedFinishReason.PARSE_CLASS_ERROR : تولید مدل تکمیل شد، اما JSON حاصل نمی‌توانست به کلاس کاتلین هدف تجزیه شود.
  • 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 ) دقیق نیست. برای دریافت تعداد دقیق توکن‌ها، باید نمونه کامل GenerateTypedContentRequest را که شامل کلاس هدف و پیکربندی‌های طرحواره شما است، به متد countTokens() ارسال کنید:

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