পরবর্তী প্রক্রিয়াকরণের জন্য প্রম্পট এপিআই (Prompt API) থেকে প্রাপ্ত প্রতিক্রিয়াগুলোকে JSON-এর মতো নির্দিষ্ট ফরম্যাটে পার্স করার প্রয়োজন হলে, স্ট্রাকচার্ড আউটপুট এপিআই (Structured Output API) ব্যবহার করুন।
স্ট্রাকচার্ড আউটপুট এপিআই (Structured Output API) ব্যবহার করে আপনি কোটলিন ক্লাস এবং অ্যানোটেশনের সাহায্যে কাঙ্ক্ষিত আউটপুট কাঠামো নির্ধারণ করেন। এরপর প্রম্পট এপিআই (Prompt API) আপনার কোটলিন অবজেক্টের আকারে একটি প্রতিক্রিয়া ফেরত দেয়।
নিম্নলিখিত কাজগুলোর জন্য কাঠামোগত আউটপুট তৈরি করা বিশেষভাবে উপযোগী:
- সত্তা নিষ্কাশন : অসংগঠিত পাঠ্য থেকে সংগঠিত ক্ষেত্রসমূহ (যেমন, ঘটনার নাম, তারিখ, স্থান) নিষ্কাশন করা।
- শ্রেণিবিন্যাস : ইনপুট টেক্সটকে পূর্বনির্ধারিত বিভাগগুলিতে ভাগ করা।
- ডেটা সিরিয়ালাইজেশন : ব্যবহারকারীর অসংগঠিত ইনপুটকে ডাটাবেস স্টোরেজ বা এপিআই কলের জন্য উপযুক্ত ফরম্যাটে রূপান্তর করা।
পূর্বশর্ত
ডিভাইসে স্ট্রাকচার্ড আউটপুট এপিআই উপলব্ধ আছে কিনা তা যাচাই করতে, isStructuredOutputFeatureAvailable() এপিআইটি ব্যবহার করুন। ডিভাইসে স্ট্রাকচার্ড আউটপুট এপিআই উপলব্ধ থাকলে এপিআইটি true রিটার্ন করে, অন্যথায় false রিটার্ন করে।
suspend fun isStructuredOutputFeatureAvailable(): Boolean
স্ট্রাকচার্ড আউটপুট এপিআই-এর জন্য নিম্নলিখিত আবশ্যকতাগুলোও রয়েছে:
- অ্যান্ড্রয়েড এপিআই লেভেল ২৬ বা তার বেশি (
minSdk২৬) - KSP প্লাগইন সংস্করণ 2.3.6 বা উচ্চতর
সীমাবদ্ধতা
স্ট্রাকচার্ড আউটপুট এপিআই-এর নিম্নলিখিত সীমাবদ্ধতাগুলো রয়েছে:
- শুধুমাত্র কোটলিনে কাজ করে।
- ProGuard আপনার অ্যানোটেড ক্লাসের পার্সিং-এ হস্তক্ষেপ করতে পারে। পার্সিং-এর সময় ত্রুটি পেলে, ProGuard থেকে সেগুলোকে বাদ দেওয়ার জন্য আপনার অ্যানোটেড ক্লাসটি আপনার কিপ রুলস- এ যোগ করুন, উদাহরণস্বরূপ:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
প্রকল্প কনফিগার করুন
স্ট্রাকচার্ড আউটপুট এপিআই ব্যবহার শুরু করতে, এই ধাপগুলো অনুসরণ করুন:
আপনার অ্যাপ-স্তরের
build.gradle.kts(বাbuild.gradle) ফাইলে ML Kit Prompt API-কে একটি ডিপেন্ডেন্সি হিসেবে যোগ করুন , যদি আগে থেকে তা করা না থাকে।আপনার প্রোজেক্ট-স্তরের
build.gradle.ktsফাইলে KSP প্লাগইনটি যোগ করুন। আপনার Kotlin সংস্করণের সাথে সামঞ্জস্যপূর্ণ একটি KSP প্লাগইন সংস্করণ ব্যবহার করুন; আমরা 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") }
আউটপুট কাঠামো সংজ্ঞায়িত করুন
কোটলিন ডেটা ক্লাস ব্যবহার করে মডেল থেকে আপনি যে ডেটা ফেরত পেতে চান তার কাঠামো নির্ধারণ করুন। আউটপুট কাঠামো নির্ধারণের জন্য দুটি প্রধান অ্যানোটেশন রয়েছে:
- স্ট্রাকচার্ড আউটপুটের জন্য ক্লাসটিকে টার্গেট হিসেবে নির্ধারণ করতে
@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}")
}
}
}
সমাপ্তির কারণ এবং ত্রুটিগুলি পরিচালনা করুন
স্ট্রাকচার্ড আউটপুট এপিআই ব্যবহার করার সময়, এপিআই দ্বারা নিক্ষিপ্ত সম্ভাব্য এক্সেপশনগুলো হ্যান্ডেল করা উচিত এবং পার্স করা রেসপন্সটি নাল (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: অন্যান্য কারণে জেনারেশন বন্ধ হয়ে গেছে।
ব্যতিক্রম
স্ট্রাকচার্ড আউটপুট এপিআই নিম্নলিখিত ত্রুটি কোড সহ 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