สร้างเอาต์พุตที่มีโครงสร้าง

หากต้องการแยกวิเคราะห์คำตอบจาก Gemini Nano เป็นรูปแบบที่เฉพาะเจาะจง เช่น JSON เพื่อประมวลผลเพิ่มเติม ให้ใช้ Structured Output API ที่มีสิทธิ์เข้าถึงโมเดลโดยตรง

เมื่อใช้ Structured Output API คุณจะกำหนดโครงสร้างเอาต์พุตเป้าหมายโดยใช้คลาสและคำอธิบายประกอบ Kotlin จากนั้นการเข้าถึงโมเดลโดยตรงจะแสดงผลลัพธ์ในรูปแบบของออบเจ็กต์ Kotlin

การสร้างเอาต์พุตที่มีโครงสร้างมีประโยชน์อย่างยิ่งสำหรับงานต่อไปนี้

  • การดึงข้อมูลเอนทิตี: การดึงข้อมูลช่องที่มีโครงสร้าง (เช่น ชื่อเหตุการณ์ วันที่ สถานที่) จากข้อความที่ไม่มีโครงสร้าง
  • การแยกประเภท: การจัดหมวดหมู่ข้อความที่ป้อนเป็นหมวดหมู่ที่กำหนดไว้ล่วงหน้า
  • การแปลงข้อมูลเป็นอนุกรม: การแปลงข้อมูลที่ผู้ใช้ป้อนซึ่งไม่มีโครงสร้างให้อยู่ในรูปแบบที่เหมาะสำหรับการจัดเก็บฐานข้อมูลหรือการเรียก API

ข้อกำหนดเบื้องต้น

หากต้องการยืนยันว่า Structured Output API พร้อมใช้งานในอุปกรณ์ ให้ใช้ API isStructuredOutputFeatureAvailable() API จะแสดงผลเป็น true หาก Structured Output API พร้อมใช้งานในอุปกรณ์ และแสดงผลเป็น false หากไม่พร้อมใช้งาน

suspend fun isStructuredOutputFeatureAvailable(): Boolean

Structured Output API ยังมีข้อกำหนดต่อไปนี้ด้วย

  • Android API ระดับ 26 ขึ้นไป (minSdk 26)
  • ปลั๊กอิน KSP เวอร์ชัน 2.3.6 ขึ้นไป

ข้อจำกัด

Structured Output API มีข้อจำกัดต่อไปนี้

  • ใช้ได้ใน Kotlin เท่านั้น
  • ProGuard อาจรบกวนการแยกวิเคราะห์คลาสที่มีคำอธิบายประกอบ เพิ่มคลาสที่มีคำอธิบายประกอบลงในกฎการเก็บรักษาเพื่อยกเว้นคลาสเหล่านั้นจาก ProGuard หากคุณได้รับข้อผิดพลาดในการแยกวิเคราะห์ เช่น
# Keep classes used by structured output for deserialization for release builds.
-keep class com.google.mlkit.genai.demo.kotlin.Plant { *; }

กำหนดค่าโปรเจ็กต์

หากต้องการเริ่มต้นใช้งาน Structured Output API ให้ทำตามขั้นตอนต่อไปนี้

  1. เพิ่มทรัพยากร Dependency com.google.mlkit:genai-prompt ในไฟล์ build.gradle.kts (หรือ build.gradle) ระดับแอป หากยังไม่ได้ดำเนินการ

  2. เพิ่มปลั๊กอิน KSP ลงในไฟล์ build.gradle.kts ระดับโปรเจ็กต์ ใช้ปลั๊กอิน KSP เวอร์ชันที่เข้ากันได้กับ Kotlin เวอร์ชันของคุณ เราขอแนะนำ KSP เวอร์ชัน 2.3.6 ขึ้นไป

    dependencies {
        ...
        classpath "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:2.3.6"
    }
    
  3. เพิ่มทรัพยากร Dependency ของคอมไพเลอร์ที่มีโครงสร้างลงในไฟล์ build.gradle.kts ระดับแอป

    dependencies {
        ...
        ksp("com.google.mlkit:genai-schema-compiler:1.0.0-alpha1")
    }
    

กำหนดโครงสร้างเอาต์พุต

กำหนดโครงสร้างของข้อมูลที่คุณต้องการให้โมเดลแสดงผลโดยใช้คลาสข้อมูล Kotlin มีคำอธิบายประกอบหลัก 2 รายการสำหรับกำหนดโครงสร้างเอาต์พุต ดังนี้

  • ใช้คำอธิบายประกอบ @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 สำหรับค่าจริง/เท็จ 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
)

// 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: การสร้างหยุดลงเนื่องจากสาเหตุอื่นๆ

ข้อยกเว้น

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