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

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

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

इस सुविधा को लागू करने के लिए, ज़रूरी है कि आपका प्रोजेक्ट compileSdk, एपीआई लेवल 37 या इसके बाद के वर्शन पर सेट हो. आपके प्रोजेक्ट में Android Gradle प्लगिन 9.1.0 या इसके बाद के वर्शन का इस्तेमाल किया जाना चाहिए.

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

डिपेंडेंसी

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

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 का लॉजिक लागू करना

अपने 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.
     */
    @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 को suspend फ़ंक्शन के तौर पर एलान करें.
    • जब कोई कार्रवाई थ्रेड को ब्लॉक कर सकती है, तो किसी सही कोरूटीन डिस्पैचर पर स्विच करें.
  • isDescribedByKDoc को true पर सेट करने पर, फ़ंक्शन के ब्यौरे या सीरियलाइज़ किए जा सकने वाले ब्यौरे को AppFunctionMetadata के हिस्से के तौर पर एन्कोड किया जाता है. इससे एजेंट को यह समझने में मदद मिलती है कि ऐप्लिकेशन के AppFunction का इस्तेमाल कैसे करना है. वर्शन 1.0.0-beta01 से, isDescribedByKDoc की कोई डिफ़ॉल्ट वैल्यू नहीं है. इसलिए, इसे साफ़ तौर पर सेट करें.

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

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

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

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

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

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

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

हर @AppFunctionServiceEntryPoint क्लास के लिए, कंपाइलर एक ऐसी क्लास जनरेट करता है जिसमें फ़ंक्शन आईडी के कॉन्सटैंट होते हैं. इसके लिए, Ids सफ़िक्स का इस्तेमाल किया जाता है. जनरेट किए गए इन आईडी कॉन्स्टेंट का इस्तेमाल, AppFunctionManager के 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 के तौर पर उपलब्ध कराना है, यह चुनते समय यह ध्यान रखना ज़रूरी है कि सिस्टम एजेंट, सर्वर पर उपयोगकर्ता की क्वेरी को प्रोसेस कर सकते हैं. इससे, एलएलएम की बेहतर सुविधाओं का फ़ायदा लिया जा सकता है.

उपयोगकर्ताओं को बेहतरीन अनुभव देने और संवेदनशील जानकारी को ज़ाहिर होने से बचाने के लिए, हमारा सुझाव है कि आप इन दिशा-निर्देशों का पालन करें:

  • नैचुरल लैंग्वेज से फ़ायदा पाने वाले फ़ंक्शन: ऐसे टास्क उपलब्ध कराएं जिन्हें उपयोगकर्ता, यूज़र इंटरफ़ेस (यूआई) पर मैन्युअल तरीके से नेविगेट करने के बजाय, बातचीत के ज़रिए आसानी से बता सके.
  • ऐक्सेस सीमित करना: ऐसे AppFunctions बनाएं जो एजेंट को सिर्फ़ उस डेटा और उन कार्रवाइयों का ऐक्सेस दें जो उपयोगकर्ता के अनुरोध को पूरा करने के लिए ज़रूरी हैं.
  • संवेदनशील जानकारी न हो: सिर्फ़ ऐसा डेटा शेयर करें जो बहुत ज़्यादा निजी या गोपनीय न हो. इसके अलावा, ऐसा डेटा शेयर करें जिसे शेयर करने के लिए उपयोगकर्ता ने साफ़ तौर पर सहमति दी हो.
  • नुकसान पहुंचाने वाली किसी भी कार्रवाई के लिए साफ़ तौर पर पुष्टि करना: नुकसान पहुंचाने वाली कार्रवाइयां (जैसे, डेटा मिटाना) करने वाले फ़ंक्शन का इस्तेमाल करते समय बेहद सावधानी बरतें. भले ही, एजेंट उन्हें ट्रिगर कर सकता है, लेकिन आपके ऐप्लिकेशन में पुष्टि करने का अपना चरण शामिल होना चाहिए. साथ ही, इसमें साफ़ और आसान भाषा का इस्तेमाल किया जाना चाहिए, ताकि उपयोगकर्ता को साफ़ तौर पर समझ आ सके कि उसे क्या करना है. पुष्टि करने के लिए एक से ज़्यादा चरण जोड़ना भी मददगार होता है. इससे यह पक्का किया जा सकता है कि उपयोगकर्ता को पता है कि उससे क्या करने के लिए कहा जा रहा है.

AppFunction इंटिग्रेशन की पुष्टि करना

आपने AppFunctions को सही तरीके से इंटिग्रेट किया है या नहीं, इसकी पुष्टि करने के लिए adb shell cmd app_function का इस्तेमाल किया जा सकता है.

अपने ऐप्लिकेशन में मौजूद AppFunctions की जानकारी देखने के लिए, adb shell cmd app_function list-app-functions --package $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 एमसीपी की सुविधाओं को आज़माने और बिना किसी प्रॉम्प्ट के एंड-टू-एंड वर्कफ़्लो की पुष्टि करने के लिए, अपने डिवाइस पर 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.
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 एट्रिब्यूट का नाम बदलकर appfn:instructions कर दिया गया है.

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