अपने ऐप्लिकेशन में AppFunctions API जोड़ना

इस गाइड में बताया गया है कि अपने Android ऐप्लिकेशन में AppFunctions API को कैसे इंटिग्रेट करें, किसी फ़ंक्शन के लिए लॉजिक कैसे लागू करें, और यह कैसे पुष्टि करें कि इंटिग्रेशन सही तरीके से काम कर रहा है.

वर्शन के साथ काम करने की सुविधा

इस सुविधा को लागू करने के लिए, ज़रूरी है कि आपके प्रोजेक्ट का compileSdk, एपीआई लेवल 36 या उससे ज़्यादा पर सेट हो.

आपके ऐप्लिकेशन को यह पुष्टि करने की ज़रूरत नहीं है कि AppFunctions काम करते हैं या नहीं. यह काम, AppFunctions Jetpack लाइब्रेरी में अपने-आप हो जाता है. AppFunctionManager अगर सुविधा काम करती है, तो एक इंस्टेंस दिखाता है. अगर सुविधा काम नहीं करती है, तो null दिखाता है.

डिपेंडेंसी

अपने मॉड्यूल की build.gradle.kts (या build.gradle) फ़ाइल में, ज़रूरी लाइब्रेरी डिपेंडेंसी जोड़ें. साथ ही, अपने टॉप-लेवल ऐप्लिकेशन मॉड्यूल में KSP प्लग इन को यहां दिखाए गए तरीके से कॉन्फ़िगर करें:

dependencies {
  implementation("androidx.appfunctions:appfunctions:1.0.0-alpha10")
  // 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-alpha10")
}

AppFunctions लॉजिक लागू करना

अपने Android ऐप्लिकेशन के लिए AppFunction लागू करने के लिए, एक ऐसी क्लास बनाएं जो 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.
     */
    @AppFunction(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 का इस्तेमाल कैसे किया जाए.

अपनी मेनिफ़ेस्ट फ़ाइल में AppFunction सेवा का एलान करना

अपने मॉड्यूल की मेनिफ़ेस्ट फ़ाइल में, KSP से जनरेट की गई सेवा के एलान और app_metadata प्रॉपर्टी को रजिस्टर करें. उदाहरण के लिए, src/main/AndroidManifest.xml में. KSP कंपाइलर, आपकी ऐब्स्ट्रैक्ट एंट्री पॉइंट क्लास को बढ़ाने वाली, सेवा की ठोस क्लास (TaskAppFunctionService) जनरेट करता है. साथ ही, आपकी 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 API का इस्तेमाल करें. गेट करने की सुविधा तब काम की हो सकती है, जब आपके ऐप्लिकेशन की कुछ सुविधाएं सभी उपयोगकर्ताओं के लिए उपलब्ध न हों. AppFunctions को डाइनैमिक तरीके से चालू या बंद करने पर, इंटेलिजेंस सिस्टम को यह पता होता है कि किसी भी समय आपके उपयोगकर्ता के लिए कौनसी सुविधाएं उपलब्ध हैं.

AppFunctions को सुरक्षित तरीके से गेट करने के लिए, दो चरणों वाली प्रोसेस अपनाएं. इसके लिए, ज़रूरी है कि खाते की स्थिति खास हो:

पहला चरण. डिफ़ॉल्ट रूप से, फ़ंक्शन को बंद करना

यह पक्का करने के लिए कि आपके फ़ीचर फ़्लैग की पुष्टि होने से पहले, फ़ंक्शन को ऐक्सेस न किया जा सके, अपने @AppFunction एनोटेशन के isEnabled पैरामीटर को false पर सेट करें.

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

दूसरा चरण. रनटाइम के दौरान, फ़ंक्शन को डाइनैमिक तरीके से चालू करना

हर AppFunction क्लास के लिए, कंपाइलर एक ऐसी क्लास जनरेट करता है जिसमें फ़ंक्शन आईडी कॉन्स्टैंट (Ids सफ़िक्स का इस्तेमाल करके) होते हैं. रनटाइम के दौरान, किसी फ़ंक्शन की चालू स्थिति को बदलने के लिए, जनरेट किए गए इन आईडी कॉन्स्टैंट का इस्तेमाल, AppFunctionManagerCompat के setAppFunctionEnabled तरीके के साथ किया जा सकता है.

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 का इस्तेमाल किया जा सकता है.

अपने ऐप्लिकेशन के AppFunctions की जानकारी देखने के लिए, adb shell cmd app_function list-app-functions | grep --after-context 10 $myPackageName का इस्तेमाल करें.

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 को ऐक्शन में देखने और बिना किसी प्रॉम्प्ट के एंड-टू-एंड वर्कफ़्लो की पुष्टि करने के लिए, अपने डिवाइस पर AppFunctions की जांच करने वाले एजेंट का Android ऐप्लिकेशन इंस्टॉल करें और उसे चलाएं.

अगर Android Studio में Gemini जैसे चैट पर आधारित असिस्टेंट का इस्तेमाल करके, अपने इंटिग्रेशन की पुष्टि की जा रही है, तो 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.
Rely on the AppFunction description as instructions.

एपीआई के पुराने वर्शन से माइग्रेट करना

वर्शन 1.0.0-alpha10 में, AppFunctions ने कंपाइल-टाइम @AppFunctionServiceEntryPoint आर्किटेक्चर लॉन्च किया है. इससे, लाइब्रेरी डिपेंडेंसी को एक जगह इकट्ठा किया जाता है और पुराने कॉन्फ़िगरेशन प्रोवाइडर (AppFunctionConfiguration.Provider) की जगह इसका इस्तेमाल किया जाता है.

अगर आपका ऐप्लिकेशन फ़िलहाल AppFunctions के पुराने वर्शन (जैसे, 1.0.0-alpha09) का इस्तेमाल करता है, तो Android Studio में Gemini जैसे एआई आईडीई में, AppFunctions एजेंट स्किल का इस्तेमाल करके, माइग्रेशन की प्रोसेस को ऑटोमेट किया जा सकता है. इस स्किल में, माइग्रेशन के खास नियम शामिल होते हैं. ये नियम, एजेंट को आपकी बिल्ड डिपेंडेंसी को एक जगह इकट्ठा करने, ज़रूरी @AppFunctionServiceEntryPoint सेवा रैपर बनाने, कॉन्टेक्स्ट पैरामीटर को अलग करने, और आपकी मेनिफ़ेस्ट फ़ाइल में दिए गए एलान को अपडेट करने में मदद करते हैं.