Tạo đầu ra có cấu trúc

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 (minSdk 26)
  • 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:

  1. Thêm phần phụ thuộc com.google.mlkit:genai-prompt trong tệp build.gradle.kts (hoặc build.gradle) cấp ứng dụng, nếu bạn chưa thêm.

  2. Thêm trình bổ trợ KSP vào tệp build.gradle.kts cấ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"
    }
    
  3. 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 @Guide trê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 @Generable bắ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 @Generable củ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 @Guide nế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