API مربوط به AppFunctions را به برنامه خود اضافه کنید.

این راهنما توضیح می‌دهد که چگونه API مربوط به AppFunctions را در برنامه اندروید خود ادغام کنید، منطق یک تابع را پیاده‌سازی کنید و تأیید کنید که ادغام به درستی کار می‌کند.

سازگاری نسخه

این پیاده‌سازی مستلزم آن است که compileSdk پروژه شما روی سطح API 36 یا بالاتر تنظیم شده باشد.

برنامه شما لازم نیست تأیید کند که آیا AppFunctions پشتیبانی می‌شود یا خیر؛ این کار به طور خودکار در کتابخانه AppFunctions Jetpack انجام می‌شود. AppFunctionManager در صورت پشتیبانی از ویژگی، یک نمونه را برمی‌گرداند و در غیر این صورت مقدار null را برمی‌گرداند.

وابستگی‌ها

وابستگی‌های کتابخانه‌ای مورد نیاز را به فایل 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

برای پیاده‌سازی یک 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 در نخ رابط کاربری اندروید اجرا می‌شود. بنابراین، یک عملیات طولانی‌مدت باید موارد زیر را انجام دهد:
    • تابع AppFunction را به عنوان یک تابع suspend تعریف کنید.
    • وقتی عملیات می‌تواند نخ را مسدود کند، به یک توزیع‌کننده کوروتین مناسب تغییر دهید.
  • وقتی isDescribedByKDoc روی true تنظیم شده باشد، توضیحات تابع یا توضیحات قابل سریال‌سازی به عنوان بخشی از AppFunctionMetadata کدگذاری می‌شود تا به عامل کمک کند نحوه استفاده از AppFunction برنامه را درک کند.

اختیاری: از Hilt برای ارائه یک AppFunction factory سفارشی استفاده کنید

اگر کلاس پیاده‌سازی 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 در زمان اجرا

از API AppFunctionManager برای فعال یا غیرفعال کردن صریح توابع هنگام تنظیم AppFunctions خود استفاده کنید. تنظیم می‌تواند زمانی مفید باشد که ویژگی‌های خاصی از برنامه شما برای همه کاربران در دسترس نباشد. با فعال یا غیرفعال کردن پویای AppFunctions، سیستم اطلاعاتی دقیقاً می‌داند کدام ویژگی‌ها در هر زمان معین برای کاربر شما در دسترس هستند.

برای مسدود کردن ایمن AppFunctionهایی که به وضعیت حساب خاصی نیاز دارند، یک فرآیند دو مرحله‌ای را دنبال کنید:

مرحله ۱. عملکرد را به طور پیش‌فرض غیرفعال کنید

برای جلوگیری از دسترسی به تابع قبل از تأیید feature flag، پارامتر isEnabled از حاشیه‌نویسی @AppFunction را روی false تنظیم کنید.

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

مرحله ۲. فعال کردن پویای تابع در زمان اجرا

برای هر کلاس AppFunction ، کامپایلر یک کلاس متناظر حاوی ثابت‌های شناسه تابع (با استفاده از پسوند Ids ) تولید می‌کند. می‌توانید از این ثابت‌های شناسه تولید شده در کنار متد setAppFunctionEnabled از AppFunctionManagerCompat برای تغییر وضعیت فعال بودن یک تابع در زمان اجرا استفاده کنید.

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هایی ایجاد کنید که فقط به عامل (agent) دسترسی به داده‌ها و اقداماتی را می‌دهند که برای انجام درخواست خاص کاربر لازم است.
  • اطلاعات غیرحساس : فقط داده‌هایی را به اشتراک بگذارید که خیلی شخصی یا محرمانه نباشند، یا داده‌هایی که کاربر صراحتاً در چارچوب عمل به اشتراک‌گذاری آنها رضایت داده باشد.
  • تأیید بدون ابهام برای هرگونه اقدام مخرب : در مورد توابعی که اقدامات مخرب انجام می‌دهند (مانند حذف داده‌ها) بسیار محتاط باشید. اگرچه ممکن است عامل آنها را فراخوانی کند، برنامه شما باید مرحله تأیید خود را داشته باشد و از زبانی واضح و بدون ابهام در مورد اهداف استفاده کند. همچنین اضافه کردن بیش از یک مرحله تأیید مفید است تا مطمئن شوید که کاربر از آنچه از او خواسته می‌شود آگاه است.

تأیید ادغام AppFunction

برای تأیید اینکه آیا AppFunctions را به درستی ادغام کرده‌اید، می‌توانید از adb shell cmd app_function استفاده کنید.

برای مشاهده جزئیات AppFunctionهایی که برنامه شما ارائه می‌دهد، adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName استفاده کنید.

در Gemini در اندروید استودیو یا سایر agent های مورد نظر خود، یک prompt مانند زیر ارائه دهید.

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.