Nếu bạn cần phân tích cú pháp phản hồi từ Gemini Nano 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 có quyền truy cập trực tiếp vào mô hình.
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 lớp và chú thích Kotlin. Sau đó, quyền truy cập trực tiếp vào mô hình 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 kết quả có cấu trúc đặc biệt hữu ích cho các nhiệm 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 thành đị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 API Đầu ra có cấu trúc có trên thiết bị, hãy sử dụng API isStructuredOutputFeatureAvailable(). API này trả về true nếu API Đầu ra có cấu trúc có sẵn trên thiết bị và trả về false nếu không có.
suspend fun isStructuredOutputFeatureAvailable(): Boolean
API Đầu ra có cấu trúc 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ó các hạn chế sau:
- Chỉ hoạt động trong Kotlin.
- ProGuard có thể can thiệp vào quá trình phân tích cú pháp của lớp được chú thích. Thêm lớp chú thích vào quy tắc lưu giữ để loại trừ các lớp đó khỏi ProGuard nếu bạn gặp lỗi khi 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 API Đầu ra có cấu trúc, hãy làm theo các bước sau:
Thêm phần phụ thuộc
com.google.mlkit:genai-prompttrong tệpbuild.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 phiên bản trình bổ trợ KSP tương thích với phiên bản Kotlin của bạn; bạn nên dùng KSP phiên bản 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 của 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ó hai 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ục tiêu cho kết quả có cấu trúc. - Sử dụng chú thích
@Guidetrên các thuộc tính của lớp để cung cấp nội dung mô tả và quy tắc ràng buộc hướng dẫn kết quả 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à quy tắc ràng buộc đượ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 quy tắc ràng buộc @Guide tương ứng:
| Loại | Mô tả | Các quy tắ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 giá trị đúng/sai. | description |
List<T> |
Đối với danh sách các loại được hỗ trợ hoặc lớp @Generable lồng nhau.
|
description, minItems, maxItems |
List<String> |
Đối với danh sách 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 nhau. | description |
Tạo nội dung có cấu trúc
Để yêu cầu kết quả có cấu trúc, hãy sử dụng hàm trợ giúp generateTypedContentRequest để gói câu lệnh chuẩn và chỉ định lớp kết quả 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
)
// 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 API Đầu ra có cấu trúc, bạn nên xử lý các ngoại lệ có thể xảy ra do API gửi 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à 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 đã hoàn tất việc tạo thành công và kết quả 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 hoàn chỉnh.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 thu được 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 đó thiếu chú thích@Generablebắt buộc.TypedFinishReason.STRUCTURE_VALUES_INVALID: Các giá trị được tạo vi phạm các quy tắc 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 vì 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à 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@Generablecủa bạn để kiểm tra xem tất cả các loại thuộc tính có được hỗ trợ hay không 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 vượt qua được quy trình xác minh quy tắ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 quy tắc ràng buộc (như giới hạn tối thiểu, tối đa hoặc kích thước danh sách) trong chú thích
@Guidenếu các quy tắc đó quá hạn chế đối với khả năng của mô hình. - Triển khai chiến lược dự phòng trong ứng dụng, 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 số token
Để 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 mã thông báo đầu vào hay không, hãy tính số lượng mã thông báo bằng phương thức countTokens().
Vì các yêu cầu đầu ra có cấu trúc cần hướng dẫn mô hình về cấu trúc giản đồ, nên việc đếm mã thông báo chỉ dựa trên văn bản câu lệnh thô (bằng cách sử dụng một thực thể 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 thực thể GenerateTypedContentRequest hoàn chỉnh (bao gồm cả cấu hình giản đồ và lớp mục tiêu) đến phương thức countTokens():
suspend fun <T : Any> countTokens(request: GenerateTypedContentRequest<T>): CountTokensResponse