افزودن «میانای برنامه‌سازی کاربردی AppFunctions» به برنامه

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

سازگاری نسخه

این پیاده‌سازی مستلزم آن است که پروژه compileSdk شما روی سطح میانای برنامه‌سازی کاربردی ۳۷ یا بالاتر تنظیم شده باشد. پروژه شما باید از «افزایه Android Gradle» نسخه ۹.۱.۰ یا بالاتر استفاده کند.

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

وابستگی‌ها

وابستگی‌های کتابخانه موردنیاز را به فایل 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 را به‌عنوان تابع تعلیق اعلام کنید.
    • وقتی عملیات می‌تواند رشته را مسدود کند، به توزیع‌کننده کوروتین مناسبی بروید.
  • وقتی 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 در زمان اجرا

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

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

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

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

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

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

برای هر کلاس @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 دردسترس قرار گیرد، مهم است به‌خاطر داشته باشید که عوامل سیستم ممکن است پُرسمان‌های کاربر را در سرور پردازش کنند تا از قابلیت‌های پیشرفته LLM بهره ببرند.

برای ارائه تجربه کاربری عالی که از افشای اطلاعات حساس نیز جلوگیری می‌کند، توصیه می‌کنیم این دستورالعمل‌ها را دنبال کنید:

  • عملکردهایی که از زبان طبیعی بهره می‌برند: کارهایی را دردسترس قرار دهید که بیان آن‌ها در مکالمه برای کاربر آسان‌تر از پیمایش دستی در واسط کاربر باشد.
  • محدود کردن دسترسی: 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 Studio به‌طور خودکار انجام دهید. این مهارت حاوی قوانین انتقال اختصاصی است که به نماینده کمک می‌کند وابستگی‌های ساخت شما را ادغام کند، پوشاننده سرویس @AppFunctionServiceEntryPoint موردنیاز را ایجاد کند، پارامترهای بافت را جدا کند، اظهارنامه‌های مانیفست شما را به‌روز کند، و تغییر نام‌های 1.0.0-beta01 را اعمال کند.