如要將 Prompt API 的回覆內容剖析為特定格式 (例如 JSON),以便進一步處理,請使用 Structured Output API。
您可以使用 Kotlin 類別和註解,透過 Structured Output API 定義目標輸出結構。接著,Prompt API 會以 Kotlin 物件的形式傳回回應。
生成結構化輸出內容特別適合用於下列工作:
- 實體擷取:從非結構化文字中擷取結構化欄位 (例如活動名稱、日期、地點)。
- 分類:將輸入文字分類至預先定義的類別。
- 資料序列化:將非結構化使用者輸入內容轉換為適合資料庫儲存或 API 呼叫的格式。
必要條件
如要確認裝置是否支援 Structured Output API,請使用 isStructuredOutputFeatureAvailable() API。如果裝置支援結構化輸出 API,API 會傳回 true,否則會傳回 false。
suspend fun isStructuredOutputFeatureAvailable(): Boolean
結構化輸出 API 也須符合下列規定:
- Android API 級別 26 以上 (
minSdk26) - KSP 外掛程式 2.3.6 以上版本
限制
結構化輸出 API 有下列限制:
- 僅適用於 Kotlin。
- ProGuard 可能會干擾註解類別的剖析作業。如果剖析時發生錯誤,請將註解類別新增至保留規則,從 ProGuard 中排除這些類別,例如:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
設定專案
如要開始使用結構化輸出 API,請按照下列步驟操作:
在應用程式層級的
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") }
定義輸出結構
使用 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}")
}
}
}
處理完成原因和錯誤
使用結構化輸出 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(-104):註解類別的結構無效或包含不支援的型別。這通常是開發期間的設定錯誤。請檢查@Generable資料類別定義,確認所有屬性類型都受到支援,且沒有任何循環依附元件。GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105):模型產生的值無效或未通過限制驗證。這是執行階段錯誤。如果經常遇到這項錯誤,請考慮下列解決方案:- 調整提示指令,更嚴格地引導模型。
- 如果
@Guide註解中的限制 (例如最小值、最大值或清單大小限制) 對模型功能過於嚴格,請放寬限制。 - 在應用程式中實作備援策略,例如重試要求或顯示預設狀態。
計算詞元數
如要檢查結構化提示詞是否在輸入權杖限制內,請使用 countTokens() 方法計算詞元數。
由於結構化輸出要求需要指示模型結構定義結構,因此只計算原始提示文字的權杖 (使用 GenerateContentRequest 例項) 並不準確。如要取得準確的權杖數量,您必須將完整的 GenerateTypedContentRequest 執行個體 (包括目標類別和結構定義設定) 傳遞至 countTokens() 方法:
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse