إضافة واجهة برمجة التطبيقات AppFunctions إلى تطبيقك

يشرح هذا الدليل كيفية دمج AppFunctions API في تطبيق Android، وتنفيذ منطق دالة، والتحقّق من أنّ عملية الدمج تعمل بشكل صحيح.

التوافق مع الإصدارات

يتطلّب هذا التنفيذ ضبط compileSdk لمشروعك على المستوى 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

لتنفيذ AppFunction لتطبيق Android، أنشِئ فئة تنفّذ منطق AppFunctions المحدّد. يتضمّن ذلك إنشاء فئات بيانات قابلة للتسلسل للمَعلمات والاستجابات، ثم توفير المنطق الأساسي ضمن طريقة الدالة.

يعرض الرمز البرمجي التالي مثالاً على تنفيذ دالة لإنشاء مهمة في الـ TODO app، بما في ذلك تحديد المَعلمات وأنواع الاستجابات المخصّصة ومنطق الدالة الرئيسي باستخدام مستودع.

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. لذلك، يجب أن تنفّذ العملية الطويلة ما يلي:
    • التعريف عن AppFunction كدالة تعليق
    • التبديل إلى أداة توزيع روتينية مناسبة عندما يمكن أن تحظر العملية سلسلة المحادثات
  • عند ضبط isDescribedByKDoc على true، يتم ترميز وصف الدالة أو الوصف القابل للتسلسل كجزء من AppFunctionMetadata لمساعدة الوكيل في فهم كيفية استخدام AppFunction في التطبيق.

اختياري: استخدام Hilt لتوفير مصنع AppFunction مخصّص

إذا كانت فئة تنفيذ AppFunction تتطلّب تبعيات في الدالة الإنشائية (مثل TaskRepository في المثال السابق)، عليك توفير مصنع مخصّص لكي يعرف النظام كيفية إنشاء مثيل له. هذه خطوة اختيارية وليست ضرورية إلا إذا كانت فئة الدالة تتضمّن مَعلمات الدالة الإنشائية. يوضّح هذا المثال كيفية إنشاء 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 في وقت التشغيل

استخدِم AppFunctionManager API لتفعيل الدوال أو إيقافها بشكل صريح عند التحكّم في AppFunctions. يمكن أن يكون التحكّم مفيدًا عندما لا تكون ميزات معيّنة في تطبيقك متاحة لجميع المستخدمين. من خلال تفعيل AppFunctions أو إيقافها ديناميكيًا، يعرف نظام الذكاء الاصطناعي الميزات المتاحة للمستخدم في أي وقت.

للتحكّم بأمان في AppFunctions التي تتطلّب حالة حساب معيّنة، اتّبِع عملية من خطوتين:

الخطوة 1: إيقاف الدالة تلقائيًا

لمنع الوصول إلى الدالة قبل التحقّق من علامة الميزة، اضبط المَعلمة isEnabled للتعليق التوضيحي @AppFunction على false.

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

الخطوة 2: تفعيل الدالة ديناميكيًا في وقت التشغيل

بالنسبة إلى كل فئة 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، من المهم تذكُّر أنّ وكلاء النظام قد يعالجون طلبات بحث المستخدمين على الخادم للاستفادة من إمكانات النماذج اللغوية الكبيرة المتقدّمة.

لتقديم تجربة رائعة للمستخدم وتجنُّب عرض المعلومات الحسّاسة، ننصحك باتّباع هذه الإرشادات:

  • الوظائف التي تستفيد من اللغة الطبيعية: أتح المهام المتاحة التي يسهل على المستخدم التعبير عنها في المحادثة بدلاً من التنقّل اليدوي في واجهة المستخدم.
  • الوصول المحدود: أنشِئ AppFunctions لا تمنح الوكيل إلا إمكانية الوصول إلى البيانات والإجراءات المطلوبة لتلبية طلب المستخدم المحدّد.
  • المعلومات غير الحسّاسة: لا تشارك إلا البيانات غير الشخصية للغاية أو السرية، أو البيانات التي يوافق المستخدم صراحةً على مشاركتها في سياق الإجراء.
  • تأكيد واضح لأي إجراء مدمِّر: توخَّ الحذر الشديد عند استخدام الدوال التي تنفّذ إجراءات مدمِّرة (مثل حذف البيانات). على الرغم من أنّ الوكيل قد يستدعيها، يجب أن يتضمّن تطبيقك خطوة تأكيد خاصة به ويستخدم لغة واضحة لا لبس فيها بشأن النوايا. من المفيد أيضًا إضافة أكثر من خطوة تأكيد واحدة للتأكّد من أنّ المستخدم على علم بما يُطلب منه فعله.

التحقّق من عملية دمج AppFunction

للتحقّق مما إذا كنت قد دمجت AppFunctions بشكل صحيح، يمكنك استخدام adb shell cmd app_function.

استخدِم adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName للاطّلاع على تفاصيل AppFunctions التي يوفّرها تطبيقك.

في Gemini في "استوديو Android" أو غيره من الوكلاء الذين تختارهم، قدِّم طلبًا مثل ما يلي.

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.