Thêm AppFunctions API vào ứng dụng

Hướng dẫn này giải thích cách tích hợp API AppFunctions vào ứng dụng Android, triển khai logic cho một hàm và xác minh rằng quá trình tích hợp hoạt động đúng cách.

Khả năng tương thích của phiên bản

Cách triển khai này yêu cầu compileSdk của dự án của bạn phải được đặt thành cấp độ API 37 trở lên. Dự án của bạn phải sử dụng Trình bổ trợ Android cho Gradle phiên bản 9.1.0 trở lên.

Ứng dụng của bạn không bắt buộc phải xác minh xem AppFunctions có được hỗ trợ hay không; việc này được xử lý tự động trong thư viện Jetpack AppFunctions. AppFunctionManager trả về một thực thể nếu tính năng này được hỗ trợ và trả về giá trị rỗng nếu không được hỗ trợ.

Phần phụ thuộc

Thêm các phần phụ thuộc thư viện bắt buộc vào tệp build.gradle.kts (hoặc build.gradle) của mô-đun, rồi định cấu hình trình bổ trợ KSP trong mô-đun ứng dụng cấp cao nhất như minh hoạ:

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")
}

Triển khai logic AppFunctions

Để triển khai AppFunction cho ứng dụng Android, hãy tạo một lớp triển khai logic AppFunctions cụ thể. Quá trình này bao gồm việc tạo các lớp dữ liệu có thể chuyển đổi tuần tự cho các tham số và phản hồi, sau đó cung cấp logic cốt lõi trong phương thức hàm.

Mã sau đây cho thấy một ví dụ về cách triển khai để tạo việc cần làm trong ứng dụng TODO, bao gồm cả việc xác định các tham số tuỳ chỉnh và loại phản hồi cũng như logic hàm chính bằng cách sử dụng kho lưu trữ.

@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)
}

Các điểm chính về mã

  • Theo mặc định, một phương thức triển khai AppFunction sẽ chạy trong luồng giao diện người dùng Android. Do đó, một thao tác chạy trong thời gian dài nên làm như sau:
    • Khai báo AppFunction dưới dạng hàm tạm ngưng.
    • Chuyển sang một trình điều phối coroutine phù hợp khi thao tác có thể chặn luồng.
  • Khi isDescribedByKDoc được đặt thành true, nội dung mô tả hàm hoặc nội dung mô tả có thể chuyển đổi tuần tự sẽ được mã hoá dưới dạng một phần của AppFunctionMetadata để giúp tác nhân hiểu cách sử dụng AppFunction của ứng dụng. Kể từ phiên bản 1.0.0-beta01, isDescribedByKDoc không có giá trị mặc định, vì vậy, hãy đặt giá trị này một cách rõ ràng.

Khai báo dịch vụ AppFunction trong tệp kê khai

Đăng ký nội dung khai báo dịch vụ do KSP tạo và thuộc tính app_metadata bên trong tệp kê khai mô-đun, ví dụ: trong src/main/AndroidManifest.xml. Trình biên dịch KSP tạo lớp dịch vụ cụ thể (TaskAppFunctionService) mở rộng lớp điểm truy cập trừu tượng, cùng với giản đồ XML tương ứng trong thư mục 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" />

Không bắt buộc: Bật/tắt phạm vi cung cấp AppFunction trong thời gian chạy

Sử dụng API AppFunctionManager để bật hoặc tắt các hàm một cách rõ ràng khi kiểm soát AppFunctions. Tính năng kiểm soát có thể hữu ích khi một số tính năng của ứng dụng không được cung cấp cho tất cả người dùng. Bằng cách bật hoặc tắt AppFunctions một cách linh động, hệ thống thông minh sẽ biết chính xác những tính năng nào có sẵn cho người dùng tại bất kỳ thời điểm nào.

Để kiểm soát một cách an toàn các AppFunction yêu cầu một trạng thái tài khoản cụ thể, hãy làm theo quy trình gồm hai bước sau:

Bước 1. Tắt hàm theo mặc định

Để ngăn hàm này truy cập được trước khi cờ tính năng của bạn được xác minh, hãy đặt tham số isEnabled của chú thích @AppFunctionDeclaration thành false.

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

Bước 2. Bật chức năng này một cách linh hoạt trong thời gian chạy

Đối với mỗi lớp @AppFunctionServiceEntryPoint, trình biên dịch sẽ tạo một lớp tương ứng chứa các hằng số mã nhận dạng hàm (sử dụng hậu tố Ids). Bạn có thể sử dụng các hằng số mã nhận dạng được tạo này cùng với phương thức setAppFunctionEnabled từ AppFunctionManager để thay đổi trạng thái bật của một hàm trong thời gian chạy.

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
    }
}

Những điểm cần lưu ý về các loại chức năng cần cung cấp

Bảo mật luôn là yếu tố quan trọng nhất. Khi chọn những chức năng của ứng dụng để cung cấp dưới dạng AppFunctions, bạn cần nhớ rằng các tác nhân hệ thống có thể xử lý truy vấn của người dùng trên máy chủ để tận dụng các chức năng nâng cao của LLM.

Để mang lại trải nghiệm người dùng tuyệt vời, đồng thời tránh làm lộ thông tin nhạy cảm, bạn nên làm theo các nguyên tắc sau:

  • Chức năng được hưởng lợi từ ngôn ngữ tự nhiên: Cung cấp các tác vụ mà người dùng dễ dàng thể hiện trong cuộc trò chuyện hơn là thông qua thao tác điều hướng giao diện người dùng theo cách thủ công.
  • Thu hẹp quyền truy cập: Tạo AppFunctions chỉ cấp cho tác nhân quyền truy cập vào dữ liệu và hành động cần thiết để thực hiện yêu cầu cụ thể của người dùng.
  • Thông tin không nhạy cảm: Chỉ chia sẻ dữ liệu không mang tính cá nhân hoặc bí mật cao, hoặc dữ liệu mà người dùng đồng ý chia sẻ một cách rõ ràng trong bối cảnh của hành động.
  • Xác nhận rõ ràng cho mọi hành động có tính huỷ diệt: Hãy cực kỳ thận trọng với các hàm thực hiện hành động có tính huỷ diệt (như xoá dữ liệu). Mặc dù tác nhân có thể gọi các hành động này, nhưng ứng dụng của bạn phải có bước xác nhận riêng và sử dụng ngôn ngữ rõ ràng, không mơ hồ về ý định. Bạn cũng nên thêm nhiều bước xác nhận để đảm bảo người dùng thực sự biết họ đang được yêu cầu làm gì.

Xác minh việc tích hợp AppFunction

Để xác minh xem bạn đã tích hợp đúng cách AppFunctions hay chưa, bạn có thể sử dụng adb shell cmd app_function.

Sử dụng adb shell cmd app_function list-app-functions --package $myPackageName để xem thông tin chi tiết về AppFunctions mà ứng dụng của bạn cung cấp.

Bạn cũng có thể thực thi AppFunction trực tiếp từ dòng lệnh bằng giá trị nhận dạng rõ ràng ("$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\"}}'"

Để trải nghiệm Android MCP trong thực tế và xác minh quy trình làm việc toàn diện mà không cần bất kỳ câu lệnh nào, hãy cài đặt và chạy ứng dụng Android Tác nhân kiểm thử AppFunctions trên thiết bị của bạn.

Nếu bạn đang xác minh quá trình tích hợp bằng các trợ lý dựa trên cuộc trò chuyện như Gemini trong Android Studio, hãy sử dụng kỹ năng phát triển AppFunctions hoặc đưa ra câu lệnh như sau:

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.

Di chuyển từ các phiên bản API thấp hơn

Trong phiên bản 1.0.0-alpha10, AppFunctions đã ra mắt một cấu trúc @AppFunctionServiceEntryPoint thời gian biên dịch giúp hợp nhất các phần phụ thuộc của thư viện và thay thế các trình cung cấp cấu hình cũ (AppFunctionConfiguration.Provider). Phiên bản 1.0.0-alpha12 đã xoá các trình cung cấp cũ. Phiên bản 1.0.0-alpha13 đã đổi tên chú thích @AppFunction thành @AppFunctionDeclaration và phiên bản 1.0.0-beta01 đã đổi tên thuộc tính appfn:description trong tệp XML siêu dữ liệu của ứng dụng thành appfn:instructions.

Nếu ứng dụng của bạn dùng phiên bản AppFunctions cũ hơn (chẳng hạn như 1.0.0-alpha09 hoặc 1.0.0-alpha10), thì bạn có thể tự động di chuyển bằng kỹ năng tác nhân AppFunctions trong một IDE AI như Gemini trong Android Studio. Kỹ năng này chứa các quy tắc di chuyển chuyên biệt hướng dẫn tác nhân hợp nhất các phần phụ thuộc bản dựng, tạo trình bao bọc dịch vụ @AppFunctionServiceEntryPoint bắt buộc, tách riêng các tham số ngữ cảnh, cập nhật nội dung khai báo tệp kê khai và áp dụng các tên mới trong phiên bản 1.0.0-beta01.