เพิ่ม AppFunctions API ลงในแอป

คู่มือนี้อธิบายวิธีผสานรวม AppFunctions API เข้ากับแอป Android, ใช้ตรรกะสำหรับฟังก์ชัน และยืนยันว่าการผสานรวมทำงานอย่างถูกต้อง

ความเข้ากันได้ของเวอร์ชัน

การติดตั้งใช้งานนี้กำหนดให้โปรเจ็กต์ compileSdk ต้องตั้งค่าเป็นระดับ API 37 ขึ้นไป โปรเจ็กต์ต้องใช้ปลั๊กอิน Android Gradle 9.1.0 ขึ้นไป

แอปของคุณไม่จำเป็นต้องยืนยันว่ารองรับ AppFunctions หรือไม่ เนื่องจากระบบจะจัดการเรื่องนี้โดยอัตโนมัติภายในไลบรารี Jetpack ของ AppFunctions AppFunctionManager จะแสดงผลอินสแตนซ์หากรองรับฟีเจอร์ และแสดงผล Null หากไม่รองรับ

ความสัมพันธ์

เพิ่มทรัพยากร Dependency ของไลบรารีที่จำเป็นลงในไฟล์ build.gradle.kts (หรือ build.gradle) ของโมดูล และกำหนดค่าปลั๊กอิน KSP ในโมดูลแอประดับบนสุด ดังนี้

dependencies {
  implementation("androidx.appfunctions:appfunctions:1.0.0-beta01")
  // If this project uses any Kotlin source, use Kotlin Symbol Processing (KSP)
  // See Add the KSP plugin to your project
  ksp("androidx.appfunctions:appfunctions-compiler:1.0.0-beta01")
}

ใช้ตรรกะ AppFunctions

หากต้องการใช้ AppFunction สำหรับแอป Android ให้สร้างคลาสที่ใช้ตรรกะ AppFunctions ที่เฉพาะเจาะจง ซึ่งรวมถึงการสร้างคลาสข้อมูลที่ทำให้เป็นอนุกรมได้สำหรับพารามิเตอร์และการตอบกลับ จากนั้นจึงระบุตรรกะหลักภายในเมธอดฟังก์ชัน

โค้ดต่อไปนี้แสดงตัวอย่างการติดตั้งใช้งานสำหรับการสร้างงานในแอปสิ่งที่ต้องทำ ซึ่งรวมถึงการกำหนดพารามิเตอร์ที่กำหนดเอง ประเภทการตอบกลับ และตรรกะฟังก์ชันหลักโดยใช้ที่เก็บข้อมูล

@RequiresApi(36)
@AndroidEntryPoint
@AppFunctionServiceEntryPoint(
    serviceName = "TaskAppFunctionService",
    appFunctionXmlFileName = "task_app_function_service",
)
abstract class BaseTaskAppFunctionService : AppFunctionService() {
    @Inject internal lateinit var taskRepository: TaskRepository

    /**
     * Creates a task based on [createTaskParams].
     *
     * @param createTaskParams The parameter to describe how to create the task.
     */
    @AppFunctionDeclaration(isDescribedByKDoc = true)
    suspend fun createTask(
        createTaskParams: CreateTaskParams,
    ): Task = withContext(Dispatchers.IO) {
        // Developers can use predefined exceptions to let the agent know
        // why it failed.
        if (createTaskParams.title == null && createTaskParams.content == null) {
            throw AppFunctionInvalidArgumentException("Title or content should be non-null")
        }

        val id = taskRepository.createTask(
            createTaskParams.title,
            createTaskParams.content
        )

        return@withContext taskRepository
            .getTask(id)
            ?.toTask()
            ?: throw AppFunctionElementNotFoundException("Task not found for ID = $id")
    }

    // Maps internal TaskEntity
    private fun TaskEntity.toTask() = Task(id = id, title = title, content = description)
}

ประเด็นสำคัญเกี่ยวกับโค้ด

  • โดยค่าเริ่มต้น การใช้งาน AppFunction จะทำงานในเธรด UI ของ Android ดังนั้น การดำเนินการที่ใช้เวลานานควรทำดังนี้
    • ประกาศ AppFunction เป็นฟังก์ชันระงับ
    • เปลี่ยนไปใช้ Coroutine Dispatcher ที่เหมาะสมเมื่อการดำเนินการอาจบล็อกชุดข้อความ
  • เมื่อตั้งค่า isDescribedByKDoc เป็น true ระบบจะเข้ารหัสคำอธิบายฟังก์ชันหรือคำอธิบายที่ทำให้เป็นอนุกรมได้เป็นส่วนหนึ่งของ AppFunctionMetadata เพื่อช่วยให้ Agent เข้าใจวิธีใช้ AppFunction ของแอป ตั้งแต่เวอร์ชัน 1.0.0-beta01 เป็นต้นไป isDescribedByKDoc จะไม่มีค่าเริ่มต้น ดังนั้นให้ตั้งค่าอย่างชัดเจน

ประกาศบริการ AppFunction ในไฟล์ Manifest

ลงทะเบียนการประกาศบริการที่สร้างโดย KSP และพร็อพเพอร์ตี้ app_metadata ภายในไฟล์ Manifest ของโมดูล เช่น ใน src/main/AndroidManifest.xml คอมไพเลอร์ KSP จะสร้างคลาสบริการที่เป็นรูปธรรม (TaskAppFunctionService) ซึ่งขยายคลาสจุดแรกเข้าที่เป็นนามธรรม พร้อมกับสคีมา XML ที่เกี่ยวข้องในไดเรกทอรี assets/

<service
    android:name="com.example.snippets.ai.TaskAppFunctionService"
    android:permission="android.permission.BIND_APP_FUNCTION_SERVICE"
    android:exported="true"
    tools:targetApi="36">
    <property
        android:name="android.app.appfunctions.schema"
        android:value="app_functions_schema.xsd" />
    <property
        android:name="android.app.appfunctions.v2"
        android:value="task_app_function_service.xml" />
    <intent-filter>
        <action android:name="android.app.appfunctions.AppFunctionService" />
    </intent-filter>
</service>
<property
    android:name="android.app.appfunctions.app_metadata"
    android:resource="@xml/app_metadata" />

ไม่บังคับ: เปิด/ปิดความพร้อมใช้งานของ AppFunction ขณะรันไทม์

ใช้ AppFunctionManager API เพื่อเปิดหรือปิดใช้ฟังก์ชันอย่างชัดเจนเมื่อกำหนดเกต AppFunctions การกำหนดเกณฑ์จะมีประโยชน์เมื่อฟีเจอร์บางอย่างของแอปไม่พร้อมให้บริการแก่ผู้ใช้บางราย การเปิดหรือปิดใช้ AppFunctions แบบไดนามิกจะช่วยให้ระบบอัจฉริยะทราบได้อย่างแน่ชัดว่าฟีเจอร์ใดพร้อมใช้งานสำหรับผู้ใช้ ณ เวลาใดเวลาหนึ่ง

หากต้องการกำหนดเกต AppFunctions ที่ต้องใช้สถานะบัญชีที่เฉพาะเจาะจงอย่างปลอดภัย ให้ทำตามกระบวนการ 2 ขั้นตอนต่อไปนี้

ขั้นตอนที่ 1 ปิดใช้ฟังก์ชันนี้โดยค่าเริ่มต้น

หากต้องการป้องกันไม่ให้เข้าถึงฟังก์ชันได้ก่อนที่จะยืนยันแฟล็กฟีเจอร์ ให้ตั้งค่าพารามิเตอร์ isEnabled ของคำอธิบายประกอบ @AppFunctionDeclaration เป็น false

@AppFunctionDeclaration(isEnabled = false, isDescribedByKDoc = true)
suspend fun createTask(
    createTaskParams: CreateTaskParams,
): Task = TODO()

ขั้นตอนที่ 2 เปิดใช้ฟังก์ชันแบบไดนามิกที่รันไทม์

สำหรับคลาส @AppFunctionServiceEntryPoint แต่ละคลาส คอมไพเลอร์จะสร้างคลาสที่เกี่ยวข้องซึ่งมีค่าคงที่รหัสฟังก์ชัน (โดยใช้คำต่อท้าย Ids) คุณสามารถใช้ค่าคงที่ของรหัสที่สร้างขึ้นเหล่านี้ร่วมกับเมธอด setAppFunctionEnabled จาก AppFunctionManager เพื่อเปลี่ยนสถานะการเปิดใช้ของฟังก์ชันขณะรันไทม์ได้

suspend fun onFeatureEnabled(context: Context) {
    try {
        AppFunctionManager.getInstance(context)
            ?.setAppFunctionEnabled(
                BaseTaskAppFunctionServiceIds.CREATE_TASK_ID,
                AppFunctionManager.APP_FUNCTION_STATE_ENABLED,
            )
    } catch (e: Exception) {
        // Handle exception: AppFunctions indexation may not be fully completed
        // upon initial app startup.
    }
}

suspend fun onFeatureDisabled(context: Context) {
    try {
        AppFunctionManager.getInstance(context)
            ?.setAppFunctionEnabled(
                BaseTaskAppFunctionServiceIds.CREATE_TASK_ID,
                AppFunctionManager.APP_FUNCTION_STATE_DISABLED,
            )
    } catch (e: Exception) {
        // Handle exception
    }
}

ข้อควรพิจารณาเกี่ยวกับประเภทของฟังก์ชันการทำงานที่ควรมีให้บริการ

ความปลอดภัยเป็นสิ่งสำคัญที่สุดเสมอ เมื่อเลือกความสามารถของแอปที่จะแสดงเป็น AppFunction โปรดทราบว่าตัวแทนระบบอาจประมวลผลคำค้นหาของผู้ใช้บนเซิร์ฟเวอร์เพื่อใช้ประโยชน์จากความสามารถ LLM ขั้นสูง

เราขอแนะนำให้ปฏิบัติตามหลักเกณฑ์ต่อไปนี้เพื่อมอบประสบการณ์ที่ยอดเยี่ยมแก่ผู้ใช้และหลีกเลี่ยงการเปิดเผยข้อมูลที่ละเอียดอ่อน

  • ฟังก์ชันการทำงานที่ได้ประโยชน์จากภาษาธรรมชาติ: ทำให้งานที่ผู้ใช้แสดงออกในการสนทนาได้ง่ายกว่าการไปยังส่วนต่างๆ ของ UI ด้วยตนเองพร้อมใช้งาน
  • จำกัดการเข้าถึง: สร้าง AppFunction ที่ให้สิทธิ์ Agent เข้าถึงเฉพาะข้อมูลและการดำเนินการที่จำเป็นต่อการดำเนินการตามคำขอที่เฉพาะเจาะจงของผู้ใช้เท่านั้น
  • ข้อมูลที่ไม่ละเอียดอ่อน: แชร์เฉพาะข้อมูลที่ไม่ใช่ข้อมูลส่วนบุคคลหรือข้อมูลลับที่มีความละเอียดอ่อนสูง หรือข้อมูลที่ผู้ใช้ให้ความยินยอมอย่างชัดแจ้งในการแชร์ในบริบทของการดำเนินการ
  • การยืนยันที่ชัดเจนสำหรับการดำเนินการที่ทำลายล้าง: โปรดระมัดระวังอย่างยิ่งกับฟังก์ชันที่ดำเนินการที่ทำลายล้าง (เช่น การลบข้อมูล) แม้ว่า Agent อาจเรียกใช้การดำเนินการเหล่านี้ แต่แอปของคุณควรมีขั้นตอนการยืนยันของตัวเองและใช้ภาษาที่ชัดเจนและไม่กำกวมเกี่ยวกับเจตนา นอกจากนี้ การเพิ่มขั้นตอนการยืนยันมากกว่า 1 ขั้นตอนยังช่วยให้มั่นใจได้ว่าผู้ใช้ทราบถึงสิ่งที่ระบบขอให้ทำ

ยืนยันการผสานรวม AppFunction

หากต้องการยืนยันว่าคุณผสานรวม AppFunctions อย่างถูกต้องหรือไม่ ให้ใช้ adb shell cmd app_function

ใช้ adb shell cmd app_function list-app-functions --package $myPackageName เพื่อดูรายละเอียดของ AppFunctions ที่แอปของคุณมีให้

นอกจากนี้ คุณยังเรียกใช้ AppFunction ได้โดยตรงจากบรรทัดคำสั่งโดยใช้ตัวระบุที่ชัดเจน ("$enclosingClassName#$methodName") ดังนี้

adb shell "cmd app_function execute-app-function \
  --package com.example.android.appfunctions \
  --function 'com.example.android.appfunctions.BaseTaskAppFunctionService#createTask' \
  --parameters '{\"createTaskParams\": {\"title\": \"Buy milk\", \"content\": \"From grocery store\"}}'"

หากต้องการสัมผัสประสบการณ์การใช้งาน Android MCP และยืนยันเวิร์กโฟลว์ตั้งแต่ต้นจนจบโดยไม่ต้องใช้พรอมต์ ให้ติดตั้งและเรียกใช้แอป Android ตัวแทนทดสอบ AppFunctions ในอุปกรณ์

หากคุณกำลังยืนยันการผสานรวมโดยใช้ผู้ช่วยที่ทำงานผ่านแชท เช่น Gemini ใน Android Studio ให้ใช้ทักษะการพัฒนา AppFunctions หรือระบุพรอมต์ เช่น

Execute `adb shell cmd app_function` to learn how the tool works, then act as a
chat agent aiming to invoke AppFunctions to fulfil user prompts for this app.
Use the AppFunction descriptions to choose functions and parameters.

ย้ายข้อมูลจาก API เวอร์ชันที่ต่ำกว่า

ในเวอร์ชัน 1.0.0-alpha10 AppFunctions ได้เปิดตัวสถาปัตยกรรม @AppFunctionServiceEntryPoint เวลาคอมไพล์ที่รวมทรัพยากร Dependency ของไลบรารีและแทนที่ผู้ให้บริการการกำหนดค่าเดิม (AppFunctionConfiguration.Provider) เวอร์ชัน 1.0.0-alpha12 ได้นำผู้ให้บริการเดิมออก เวอร์ชัน 1.0.0-alpha13 เปลี่ยนชื่อคำอธิบายประกอบ @AppFunction เป็น @AppFunctionDeclaration และเวอร์ชัน 1.0.0-beta01 เปลี่ยนชื่อแอตทริบิวต์ appfn:description ในไฟล์ XML ข้อมูลเมตาของแอปเป็น appfn:instructions

หากแอปใช้ AppFunctions เวอร์ชันก่อนหน้า (เช่น 1.0.0-alpha09 หรือ 1.0.0-alpha10) คุณสามารถย้ายข้อมูลโดยอัตโนมัติได้โดยใช้ความสามารถของ Agent สำหรับ AppFunctions ใน IDE ของ AI เช่น Gemini ใน Android Studio ทักษะนี้มีกฎการย้ายข้อมูลเฉพาะที่แนะนำ Agent ให้รวมทรัพยากร Dependency ของบิลด์ สร้าง Wrapper บริการ @AppFunctionServiceEntryPoint ที่จำเป็น แยกพารามิเตอร์บริบท อัปเดตการประกาศไฟล์ Manifest และใช้การเปลี่ยนชื่อ 1.0.0-beta01