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

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

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

يتطلّب هذا التنفيذ ضبط مشروعك compileSdk على مستوى واجهة برمجة التطبيقات 37 أو مستوى أعلى. يجب أن يستخدم مشروعك المكوّن الإضافي لنظام Gradle المتوافق مع Android‏ 9.1.0 أو إصدار أحدث.

لا يحتاج تطبيقك إلى التحقّق من توافق AppFunctions، لأنّ ذلك يتم تلقائيًا من خلال مكتبة AppFunctions Jetpack. تعرض AppFunctionManager مثيلاً إذا كانت الميزة متاحة، وتعرض قيمة فارغة إذا لم تكن متاحة.

الطلبات التابعة

أضِف تبعيات المكتبة المطلوبة إلى ملف build.gradle.kts (أو build.gradle) الخاص بالوحدة، واضبط المكوّن الإضافي KSP في وحدة التطبيق ذات المستوى الأعلى على النحو التالي:

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

تنفيذ منطق AppFunctions

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

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

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

نقاط رئيسية حول الرمز

  • يتم تلقائيًا تنفيذ AppFunction في سلسلة واجهة المستخدم في Android. لذلك، يجب أن تنفّذ العملية الطويلة ما يلي:
    • يجب تعريف AppFunction كدالة تعليق.
    • التبديل إلى مرسِل دالة coroutine مناسب عندما تؤدي العملية إلى حظر سلسلة التعليمات
  • عند ضبط isDescribedByKDoc على true، يتم ترميز وصف الدالة أو الوصف القابل للتسلسل كجزء من AppFunctionMetadata لمساعدة الوكيل في فهم كيفية استخدام AppFunction في التطبيق. بدءًا من الإصدار 1.0.0-beta01، لا يتضمّن isDescribedByKDoc قيمة تلقائية، لذا يجب ضبطها بشكل صريح.

تعريف خدمة AppFunction في ملف البيان

سجِّل بيان الخدمة الذي تم إنشاؤه من خلال KSP وسمة app_metadata داخل بيان الوحدة، على سبيل المثال، في src/main/AndroidManifest.xml. ينشئ برنامج تجميع KSP فئة الخدمة الملموسة (TaskAppFunctionService) التي توسّع فئة نقطة الدخول المجرّدة، بالإضافة إلى مخطط XML المقابل في دليل 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" />

اختياري: تفعيل أو إيقاف AppFunction في وقت التشغيل

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

لتقييد الوصول بأمان إلى AppFunctions التي تتطلّب حالة حساب معيّنة، اتّبِع الخطوات التالية:

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

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

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

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

لكل فئة @AppFunctionServiceEntryPoint، ينشئ برنامج التجميع فئة مقابلة تحتوي على ثوابت معرّف الدالة (باستخدام اللاحقة Ids). يمكنك استخدام ثوابت المعرّفات التي تم إنشاؤها مع طريقة setAppFunctionEnabled من AppFunctionManager لتغيير حالة تفعيل دالة أثناء وقت التشغيل.

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

اعتبارات بشأن أنواع الوظائف التي يجب توفيرها

يأتي الأمان دائمًا في المقام الأول. عند اختيار ميزات تطبيقك التي تريد إتاحتها كدوال AppFunctions، من المهم تذكُّر أنّ وكلاء النظام قد يعالجون طلبات المستخدمين على الخادم للاستفادة من إمكانات النماذج اللغوية الكبيرة المتقدّمة.

لتقديم تجربة مستخدم رائعة تتجنب أيضًا الكشف عن المعلومات الحساسة، نوصي باتباع الإرشادات التالية:

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

التحقّق من دمج AppFunction

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

يمكنك استخدام adb shell cmd app_function list-app-functions --package $myPackageName للاطّلاع على تفاصيل واجهات AppFunctions التي يوفّرها تطبيقك.

يمكنك أيضًا تنفيذ AppFunction مباشرةً من سطر الأوامر باستخدام معرّفها الصريح ("$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\"}}'"

لتجربة Android MCP والتحقّق من سير العمل من البداية إلى النهاية بدون الحاجة إلى أي طلبات، يُرجى تثبيت تطبيق Android وكيل اختبار AppFunctions وتشغيله على جهازك.

إذا كنت تتحقّق من عملية الدمج باستخدام مساعدين مستندين إلى المحادثة، مثل Gemini في "استوديو Android"، استخدِم مهارة تطوير AppFunctions أو قدِّم طلبًا مثل ما يلي:

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.

نقل البيانات من إصدارات واجهة برمجة التطبيقات الأقدم

في الإصدار 1.0.0-alpha10، قدّمت AppFunctions بنية @AppFunctionServiceEntryPoint في وقت التجميع لدمج تبعيات المكتبة واستبدال موفّري الإعدادات القدامى (AppFunctionConfiguration.Provider). وقد أزال الإصدار 1.0.0-alpha12 الموفّرين القدامى. في الإصدار 1.0.0-alpha13، تمت إعادة تسمية التعليق التوضيحي @AppFunction إلى @AppFunctionDeclaration، وفي الإصدار 1.0.0-beta01، تمت إعادة تسمية السمة appfn:description في ملف XML الخاص بالبيانات الوصفية للتطبيق إلى appfn:instructions.

إذا كان تطبيقك يستخدم إصدارًا قديمًا من AppFunctions (مثل 1.0.0-alpha09 أو 1.0.0-alpha10)، يمكنك برمجة عملية نقل البيانات باستخدام مهارة وكيل AppFunctions في بيئة تطوير متكاملة (IDE) مستندة إلى الذكاء الاصطناعي، مثل Gemini في "استوديو Android". تحتوي المهارة على قواعد نقل مخصّصة توجّه الوكيل لدمج تبعيات الإصدار، وإنشاء برنامج تضمين خدمة @AppFunctionServiceEntryPoint المطلوب، وفصل مَعلمات السياق، وتعديل بيانات البيان، وتطبيق عمليات إعادة التسمية في الإصدار 1.0.0-beta01.