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 AppFunctions API 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 giữa các phiên bản

Việc triển khai này yêu cầu bạn phải đặt dự án compileSdk thành cấp độ API 36 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 sẽ được tự động xử lý trong thư viện AppFunctions Jetpack. AppFunctionManager trả về một thực thể nếu tính năng được hỗ trợ và trả về giá trị rỗng nếu không.

Phần phụ thuộc

Thêm các phần phụ thuộc của thư viện bắt buộc vào tệp build.gradle.kts (hoặc build.gradle) của mô-đun, đồng thờ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ạ:

# Add this to your app module at the top level. For multi module applications,
# you only need to specify this once.
ksp {
  arg("appfunctions:aggregateAppFunctions", "true")
}

dependencies {
  implementation("androidx.appfunctions:appfunctions:1.0.0-alpha09")
  implementation("androidx.appfunctions:appfunctions-service:1.0.0-alpha09")
  // 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-alpha09")
}

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ể. Điều này liên quan đến 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.

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

package com.example.android.appfunctions


import androidx.appfunctions.AppFunctionSerializable
import androidx.appfunctions.AppFunctionContext
import androidx.appfunctions.AppFunctionElementNotFoundException
import androidx.appfunctions.AppFunctionInvalidArgumentException
import androidx.appfunctions.service.AppFunction
import javax.inject.Inject
...

// Developers can provide additional parameters in the constructor if needed.
// This requires a custom factory setup in the next step.
class TaskFunctions @Inject constructor(
  private val taskRepository: TaskRepository
) {
  /** The parameter to create the task. */
  @AppFunctionSerializable(isDescribedByKDoc = true)
  data class CreateTaskParams(
    /** The title of the task. */
    val title: String,
    /** The content of the task. */
     val content: String
  )

  /** The user-created task. */
  @AppFunctionSerializable(isDescribedByKDoc = true)
  data class Task(
    /** The ID of the task. */
    val id: String,
    /** The title of the task. */
    val title: String,
    /** The content of the task. */
    val content: String
  )

  /**
   * Creates a task based on [createTaskParams].
   *
   * @param createTaskParams The parameter to describe how to create the task.
   */
  @AppFunction(isDescribedByKDoc = true)
  suspend fun createTask(
    appFunctionContext: AppFunctionContext,
    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 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 diễn ra trong thời gian dài phải thực hiện những việc sau:
    • Khai báo AppFunction dưới dạng hàm tạm ngưng.
    • Chuyển sang 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á trong AppFunctionMetadata để giúp tác nhân hiểu cách sử dụng AppFunction của ứng dụng.

Không bắt buộc: Sử dụng Hilt để cung cấp một nhà máy AppFunction tuỳ chỉnh

Nếu lớp triển khai AppFunction của bạn yêu cầu các phần phụ thuộc trong hàm khởi tạo (như trong TaskRepository ở ví dụ trước), bạn cần cung cấp một nhà máy tuỳ chỉnh để hệ thống biết cách khởi tạo lớp đó. Đây là bước không bắt buộc và chỉ cần thiết nếu lớp hàm của bạn có các tham số của hàm khởi tạo. Ví dụ này cho thấy cách tạo một AppFunctionFactory tuỳ chỉnh và định cấu hình AppFunctionFactory đó trong lớp Application, bằng cách sử dụng Hilt để chèn phần phụ thuộc.

import android.app.Application
import androidx.appfunctions.service.AppFunctionConfiguration
import com.example.android.appfunctions.TaskFunctions
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject

@HiltAndroidApp
class TodoApplication : Application(), AppFunctionConfiguration.Provider {
  @Inject lateinit var taskFunctions: TaskFunctions
  override fun onCreate() {
    super.onCreate()
  }
  // This shows how AppFunctions works with Hilt.
  override val appFunctionConfiguration: AppFunctionConfiguration
    get() =
      AppFunctionConfiguration.Builder()
        .addEnclosingClassFactory(TaskFunctions::class.java) { taskFunctions }
        .build()
}

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

Sử dụng API AppFunctionManager để bật hoặc tắt rõ ràng các chức năng khi kiểm soát AppFunctions. Việc hạn chế 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 hoạt, 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 của bạn tại một thời điểm bất kỳ.

Để kiểm soát 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 2 bước:

Bước 1. Tắt chức năng theo mặc định

Để ngăn người dùng truy cập vào hàm 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ú giải @AppFunction thành false.

@AppFunction(isEnabled = false, isDescribedByKDoc = true)
suspend fun createTask(...) { ... }

Bước 2. Bật hàm một cách linh động trong thời gian chạy

Đối với mỗi lớp AppFunction, 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 đã tạo này cùng với phương thức setAppFunctionEnabled từ AppFunctionManagerCompat để thay đổi trạng thái đã bật của một hàm trong thời gian chạy.

import androidx.appfunctions.AppFunctionManager
// Assuming there is a hook API to observe user state or feature flags
suspend fun onFeatureEnabled() {
    try {
        AppFunctionManager.getInstance(context)
            .setAppFunctionEnabled(
                // Function ID is generated for developer to get
                TaskFunctionsIds.CREATE_TASK_ID,
                AppFunctionManagerCompat.APP_FUNCTION_STATE_ENABLED,
            )
    } catch (e: Exception) {
        // Handle exception: AppFunctions indexation may not be fully completed
        // upon initial app startup.
    }
}

suspend fun onFeatureDisabled() {
    AppFunctionManagerCompat.getInstance(context)
        .setAppFunctionEnabled(
            TaskFunctionsIds.CREATE_TASK_ID,
            AppFunctionManagerCompat.APP_FUNCTION_STATE_DISABLED,
        )
}

Những điểm cần cân nhắc 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 mà bạn muốn cung cấp dưới dạng AppFunction, bạn cần nhớ rằng các tác nhân hệ thống có thể xử lý các truy vấn của người dùng trên máy chủ để tận dụng các chức năng LLM nâng cao.

Để mang lại trải nghiệm tuyệt vời cho người dùng mà vẫn tránh để 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 tận dụng ngôn ngữ tự nhiên: Cung cấp các tác vụ mà người dùng có thể dễ dàng diễn đạt 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.
  • Quyền truy cập hạn chế: Tạo AppFunction chỉ cấp cho tác nhân quyền truy cập vào dữ liệu và các 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 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 phá huỷ: Cực kỳ thận trọng với các hàm thực hiện hành động phá huỷ (chẳng hạn như xoá dữ liệu). Mặc dù tác nhân có thể gọi các thao tác 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. Việc thêm nhiều bước xác nhận cũng rất hữu ích để thực sự đảm bảo rằng người dùng biết họ đượ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 AppFunctions đúng cách 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 | grep --after-context 10 $myPackageName để xem thông tin chi tiết về AppFunctions mà ứng dụng của bạn cung cấp.

Trong Gemini trong Android Studio hoặc các tác nhân khác mà bạn chọn, hãy đưa ra một 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.
Rely on the AppFunction description as instructions.