在應用程式中加入 AppFunctions API

本指南說明如何在 Android 應用程式中整合 AppFunctions API、實作函式邏輯,以及確認整合作業是否正常運作。

版本相容性

如要實作這項功能,專案的 compileSdk 必須設為 API 級別 36 以上。

應用程式不必驗證是否支援 AppFunctions,因為 AppFunctions Jetpack 程式庫會自動處理這項作業。如果系統支援這項功能,AppFunctionManager 會傳回執行個體,否則會傳回空值。

依附元件

在模組的 build.gradle.kts (或 build.gradle) 檔案中新增必要的程式庫依附元件,並在頂層應用程式模組中設定 KSP 外掛程式,如下所示:

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

實作 AppFunctions 邏輯

如要為 Android 應用程式實作 AppFunction,請建立實作特定 AppFunctions 邏輯的類別。這包括為參數和回應建立可序列化的資料類別,然後在函式方法中提供核心邏輯。

下列程式碼顯示在 TODO 應用程式中建立工作的實作範例,包括定義自訂參數和回應型別,以及使用存放區的主要函式邏輯。

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

程式碼重點

  • 根據預設,AppFunction 實作會在 Android UI 執行緒中執行。因此,長時間執行的作業應執行下列動作:
    • 將 AppFunction 宣告為暫停函式。
    • 如果作業可能會封鎖執行緒,請切換至合適的協同程式調度工具。
  • 如果 isDescribedByKDoc 設為 true,函式說明或可序列化說明會編碼為 AppFunctionMetadata 的一部分,協助代理程式瞭解如何使用應用程式的 AppFunction。

選用:使用 Hilt 提供自訂 AppFunction 工廠

如果 AppFunction 實作類別需要在建構函式中依附元件 (如上例中的 TaskRepository),您需要提供自訂 Factory,系統才能瞭解如何例項化。這是選用步驟,只有在函式類別有建構函式參數時才需要。這個範例說明如何建立自訂 AppFunctionFactory,並在 Application 類別中設定,使用 Hilt 進行依附元件插入。

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

選用:在執行階段切換 AppFunction 可用性

在控管 AppFunctions 時,請使用 AppFunctionManager API 明確啟用或停用函式。如果應用程式的某些功能不適用於所有使用者,閘道功能就非常實用。動態啟用或停用 AppFunctions,智慧系統就能確切掌握使用者在任何時間可用的功能。

如要安全地控管需要特定帳戶狀態的 AppFunction,請按照下列兩步驟程序操作:

步驟 1:預設停用這項功能

如要避免在驗證功能旗標前存取函式,請將 @AppFunction 註解的 isEnabled 參數設為 false

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

步驟 2:在執行階段動態啟用函式

針對每個 AppFunction 類別,編譯器會產生對應的類別,其中包含函式 ID 常數 (使用 Ids 後置字元)。您可以使用這些產生的 ID 常數和 AppFunctionManagerCompatsetAppFunctionEnabled 方法,在執行階段變更函式的啟用狀態。

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

可用的功能類型注意事項

安全始終是我們的首要考量。選擇要以 AppFunctions 形式提供的應用程式功能時,請務必注意,系統代理程式可能會在伺服器上處理使用者查詢,以運用進階 LLM 功能。

為提供優質的使用者體驗,同時避免揭露私密資訊,建議您遵循下列規範:

  • 可透過自然語言執行的功能:提供使用者可透過對話輕鬆表達的工作,而非手動操作使用者介面。
  • 縮小存取範圍:建立 AppFunction,只允許代理程式存取履行使用者特定要求所需的資料和動作。
  • 非私密資訊:只分享非高度個人或機密的資料,或使用者在動作情境中明確同意分享的資料。
  • 明確確認任何破壞性動作:請務必謹慎使用會執行破壞性動作 (例如刪除資料) 的函式。雖然代理程式可能會叫用這些意圖,但應用程式應加入自己的確認步驟,並使用清楚明確的語言說明意圖。此外,新增多個確認步驟也有助於確保使用者瞭解系統要求他們執行的動作。

驗證 AppFunction 整合

如要驗證是否已正確整合 AppFunctions,可以使用 adb shell cmd app_function

使用 adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName 查看應用程式提供的 AppFunctions 詳細資料。

在 Android Studio 內建 Gemini 或您選擇的其他代理中,提供下列提示。

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.