Nếu bạn cần phân tích cú pháp các phản hồi từ Prompt API thành một số định dạng nhất định (chẳng hạn như JSON) để xử lý thêm, hãy sử dụng Structured Output API.
Với Structured Output API, bạn xác định cấu trúc đầu ra mục tiêu bằng cách sử dụng các chú thích và lớp Kotlin. Sau đó, Prompt API sẽ trả về một phản hồi dưới dạng đối tượng Kotlin của bạn.
Việc tạo đầu ra có cấu trúc đặc biệt hữu ích cho các tác vụ như sau:
- Trích xuất thực thể: Trích xuất các trường có cấu trúc (ví dụ: tên sự kiện, ngày, vị trí) từ văn bản không có cấu trúc.
- Phân loại: Phân loại văn bản đầu vào thành các danh mục được xác định trước.
- Chuyển đổi tuần tự dữ liệu: Chuyển đổi dữ liệu đầu vào không có cấu trúc của người dùng sang định dạng phù hợp để lưu trữ cơ sở dữ liệu hoặc lệnh gọi API.
Điều kiện tiên quyết
Để xác minh rằng Structured Output API có trên thiết bị, hãy sử dụng API isStructuredOutputFeatureAvailable(). API này trả về true nếu Structured Output API có trên thiết bị và false nếu không.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
Structured Output API cũng có các yêu cầu sau:
- Android API cấp 26 trở lên (
minSdk26) - Trình bổ trợ KSP phiên bản 2.3.6 trở lên
Các điểm hạn chế
Structured Output API có những hạn chế sau:
- Chỉ hoạt động trong Kotlin.
- ProGuard có thể gây trở ngại cho quá trình phân tích cú pháp lớp được chú thích của bạn. Thêm lớp được chú thích vào các quy tắc lưu giữ để loại trừ các lớp đó khỏi ProGuard nếu bạn gặp lỗi phân tích cú pháp, ví dụ:
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }
Định cấu hình dự án
Để bắt đầu sử dụng Structured Output API, hãy làm theo các bước sau:
Thêm Prompt API của Bộ công cụ học máy làm phần phụ thuộc trong tệp
build.gradle.kts(hoặcbuild.gradle) cấp ứng dụng, nếu bạn chưa thêm.Thêm trình bổ trợ KSP vào tệp
build.gradle.ktscấp dự án. Sử dụng một phiên bản trình bổ trợ KSP tương thích với phiên bản Kotlin của bạn; chúng tôi đề xuất phiên bản KSP 2.3.6 trở lên.dependencies { ... classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6" }Thêm các phần phụ thuộc trình biên dịch có cấu trúc vào tệp
build.gradle.ktsở cấp ứng dụng:dependencies { ... ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1") }
Xác định cấu trúc đầu ra
Xác định cấu trúc của dữ liệu mà bạn muốn mô hình trả về bằng cách sử dụng các lớp dữ liệu Kotlin. Có 2 chú thích chính để xác định cấu trúc đầu ra:
- Sử dụng chú thích
@Generableđể xác định lớp làm mục tiêu cho đầu ra có cấu trúc. - Sử dụng chú giải
@Guidetrên các thuộc tính của lớp để cung cấp nội dung mô tả và các ràng buộc hướng dẫn đầu ra của mô hình.
Ví dụ sau đây xác định một cấu trúc để trích xuất thông tin về thực vật:
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
)
Các loại và giới hạn được hỗ trợ
Các loại sau đây được hỗ trợ trong một lớp được chú thích @Generable, cùng với các ràng buộc @Guide tương ứng:
| Loại | Mô tả | Các ràng buộc @Guide được hỗ trợ |
|---|---|---|
String |
Đối với văn bản. | description, enumValues |
Double/Float |
Đối với số dấu phẩy động. | description, minimum, maximum |
Int/Long |
Đối với số nguyên. | description, minimum, maximum |
Boolean |
Đối với các giá trị đúng/sai. | description |
List<T> |
Đối với danh sách các loại được hỗ trợ hoặc các lớp @Generable lồng nhau.
|
description, minItems, maxItems |
List<String> |
Đối với danh sách các giá trị String. |
description, enumValues, minItems,
maxItems |
Lớp @Generable |
Đối với các đối tượng có cấu trúc lồng ghép. | description |
Tạo nội dung có cấu trúc
Để yêu cầu đầu ra có cấu trúc, hãy sử dụng hàm trợ giúp generateTypedContentRequest để bao bọc lời nhắc tiêu chuẩn và chỉ định lớp đầu ra mục tiêu.
// 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}")
}
}
}
Xử lý lý do kết thúc và lỗi
Khi sử dụng Structured Output API, bạn nên xử lý các trường hợp ngoại lệ tiềm ẩn do API này tạo ra và kiểm tra thuộc tính finishReason trong các đề xuất phản hồi nếu phản hồi được phân tích cú pháp là giá trị rỗng.
Giá trị finishReason
Thuộc tính finishReason có thể nhận một trong các giá trị sau:
TypedFinishReason.STOP: Mô hình đã tạo thành công và đầu ra khớp với giản đồ.TypedFinishReason.MAX_TOKENS: Mô hình đã dừng vì đạt đến giới hạn mã thông báo. Kết quả có thể chưa đầy đủ.TypedFinishReason.PARSE_CLASS_ERROR: Mô hình đã hoàn tất quá trình tạo, nhưng không thể phân tích cú pháp JSON kết quả thành lớp Kotlin mục tiêu.TypedFinishReason.STRUCTURE_NOT_ANNOTATED: Lớp mục tiêu hoặc các lớp lồng nhau của lớp này thiếu chú thích@Generablebắt buộc.TypedFinishReason.STRUCTURE_VALUES_INVALID: Các giá trị được tạo đã vi phạm những ràng buộc được xác định trong chú thích@Guide(ví dụ: giá trị nằm ngoài phạm vi, kích thước danh sách nằm ngoài giới hạn).TypedFinishReason.OTHER: Đã dừng tạo do các lý do khác.
Ngoại lệ
Structured Output API có thể gửi GenAiException kèm theo các mã lỗi sau:
GenAiException.STRUCTURED_OUTPUT_INVALID_CLASS(-104): Cấu trúc của lớp được chú thích không hợp lệ hoặc chứa các loại không được hỗ trợ. Đây thường là một lỗi cấu hình trong thời gian phát triển. Xem lại định nghĩa lớp dữ liệu@Generableđể kiểm tra xem tất cả các loại thuộc tính đều được hỗ trợ và không có sự phụ thuộc vòng tròn nào.GenAiException.STRUCTURED_OUTPUT_INVALID_VALUE(-105): Các giá trị do mô hình tạo ra không hợp lệ hoặc không xác minh được các ràng buộc. Đây là lỗi thời gian chạy. Nếu bạn thường xuyên gặp phải lỗi này, hãy cân nhắc các giải pháp sau:- Tinh chỉnh hướng dẫn trong câu lệnh để hướng dẫn mô hình một cách chặt chẽ hơn.
- Nới lỏng các ràng buộc (chẳng hạn như giới hạn kích thước tối thiểu, tối đa hoặc danh sách) trong chú thích
@Guidenếu các ràng buộc đó quá hạn chế đối với các chức năng của mô hình. - Triển khai một chiến lược dự phòng trong ứng dụng của bạn, chẳng hạn như thử lại yêu cầu hoặc hiển thị trạng thái mặc định.
Đếm mã thông báo
Để kiểm tra xem câu lệnh có cấu trúc của bạn có nằm trong giới hạn token đầu vào hay không, hãy tính số token bằng phương thức countTokens().
Vì các yêu cầu về đầu ra có cấu trúc cần hướng dẫn mô hình về cấu trúc lược đồ, nên việc chỉ tính số lượng mã thông báo trên văn bản lời nhắc thô (bằng cách sử dụng một phiên bản GenerateContentRequest) là không chính xác. Để nhận được số lượng mã thông báo chính xác, bạn phải truyền toàn bộ thực thể GenerateTypedContentRequest (bao gồm cả cấu hình lớp và giản đồ mục tiêu) đến phương thức countTokens():
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse