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

หากต้องการแยกวิเคราะห์การตอบกลับจาก Prompt API เป็นรูปแบบใดรูปแบบหนึ่ง เช่น JSON เพื่อประมวลผลเพิ่มเติม ให้ใช้ Structured Output API

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

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

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

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

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

suspend fun isStructuredOutputFeatureAvailable(): Boolean

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

  • API ระดับ 26 ขึ้นไป ของ Android (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. เพิ่ม ML Kit Prompt API เป็นทรัพยากร Dependencyในไฟล์ 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,
    // 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}")
        }
    }
}

จัดการเหตุผลและข้อผิดพลาดในการสิ้นสุด

เมื่อใช้ Structured Output API คุณควรจัดการข้อยกเว้นที่อาจเกิดขึ้นซึ่ง API ส่งคืนมา และตรวจสอบพร็อพเพอร์ตี้ finishReason ในการตอบกลับ candidates หากการตอบกลับที่แยกวิเคราะห์แล้วเป็น 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