אם אתם צריכים לנתח את התגובות מ-Prompt API לפורמטים מסוימים, כמו JSON, כדי לבצע עיבוד נוסף, אתם יכולים להשתמש ב-Structured Output API.
באמצעות Structured Output API, מגדירים את מבנה פלט היעד באמצעות הערות ומחלקות Kotlin. לאחר מכן, Prompt API מחזיר תגובה בצורה של אובייקט Kotlin.
יצירת פלט מובנה שימושית במיוחד למשימות כמו:
- חילוץ ישויות: חילוץ שדות מובְנים (למשל, שם האירוע, תאריך, מיקום) מטקסט לא מובְנה.
- סיווג: סיווג טקסט קלט לקטגוריות מוגדרות מראש.
- סריאליזציה של נתונים: המרה של קלט לא מובנה של משתמשים לפורמט שמתאים לאחסון במסד נתונים או לקריאות ל-API.
דרישות מוקדמות
כדי לוודא ש-Structured Output API זמין במכשיר, משתמשים ב-isStructuredOutputFeatureAvailable() API. ה-API מחזיר true אם Structured Output API זמין במכשיר, ו-false אם הוא לא זמין.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
בנוסף, יש גם את הדרישות הבאות ל-Structured Output API:
- רמת API Android 26 ומעלה (
minSdk26) - פלאגין KSP בגרסה 2.3.6 ואילך
מגבלות
ל-Structured Output API יש את המגבלות הבאות:
- התכונה פועלת רק ב-Kotlin.
- יכול להיות ש-ProGuard יפריע לניתוח של הכיתה עם ההערות. אם מתקבלות שגיאות בניתוח, למשל:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
הגדרת הפרויקט
כדי להתחיל להשתמש ב-Structured Output API, פועלים לפי השלבים הבאים:
מוסיפים את ML Kit Prompt API כתלות בקובץ
build.gradle.kts(אוbuild.gradle) ברמת האפליקציה, אם עדיין לא עשיתם זאת.מוסיפים את הפלאגין KSP לקובץ
build.gradle.ktsברמת הפרויקט. משתמשים בגרסת פלאגין KSP שתואמת לגרסת Kotlin. מומלץ להשתמש בגרסה 2.3.6 של 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 |
לערכים true/false. | 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}")
}
}
}
טיפול בסיבות לסיום ובשגיאות
כשמשתמשים ב-Structured Output 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) לא מדויקת. כדי לקבל ספירת טוקנים מדויקת, צריך להעביר את מופע GenerateTypedContentRequest המלא, שכולל את הגדרות הסכימה והכיתה של היעד, לשיטה countTokens():
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse