Google में प्रॉडक्ट मैनेजर
Android, एक इंटेलिजेंस सिस्टम के तौर पर विकसित हो रहा है. इसमें Gemini जैसे एजेंट, अलग-अलग ऐप्लिकेशन पर टास्क मैनेज करते हैं. नए उपयोगकर्ता अनुभव देने और एजेंटिक वर्कफ़्लो में हिस्सा लेने के लिए, आपके ऐप्लिकेशन को अपनी मुख्य सुविधाएं उपलब्ध करानी होंगी.
इस सेशन में, अपने ऐप्लिकेशन को Android की इंटेलिजेंस लेयर के साथ इंटिग्रेट करने के बारे में बताया गया है. हम इंटिग्रेशन के मुख्य पाथ का सर्वे करेंगे. इनमें उपयोगकर्ता के डिवाइस पर Android एमसीपी, सर्वर-साइड एमसीपी, और टास्क ऑटोमेशन शामिल हैं. साथ ही, हम आपके इस्तेमाल के उदाहरण के लिए सही पाथ चुनने के लिए, फ़ैसला लेने का फ़्रेमवर्क उपलब्ध कराएंगे. हम निजी संदर्भ की मदद से, आपको बेहतर अनुभव देने की कोशिश करेंगे.
आपको इनके बारे में जानकारी मिलेगी:
AppFunctionDeclaration का इस्तेमाल करके टूल के बारे में बताएं, KDoc ब्यौरे दें, और Kotlin की डिफ़ॉल्ट वैल्यू पर भरोसा करने से बचें.ट्रांसक्रिप्ट
ठीक है।
सभी को नमस्ते।
मैं डैन गैलपिन हूं और मैं डेवलपर रिलेशंस टीम से हूं.
मैं सारा शम्सी हूं. मैं एक प्रॉडक्ट मैनेजर हूं और Android के डेवलपर इंटेलिजेंस फ़्रेमवर्क पर काम करती हूं.
आज के सेशन में आपका स्वागत है.
हम देख रहे हैं कि उपयोगकर्ता अपने डिवाइसों के साथ किस तरह इंटरैक्ट कर रहे हैं. इसमें काफ़ी बदलाव आया है.
वेब के दौर में, उपयोगकर्ता आम तौर पर वेब ब्राउज़र पर जाकर और फिर खोज फ़ंक्शन का इस्तेमाल करके अपनी यात्रा शुरू करते थे.
इसके बाद, मोबाइल के दौर में हमने देखा कि उपयोगकर्ता, एक बार की जाने वाली कार्रवाइयों को पूरा करने के लिए, ज़्यादा खास और नेटिव ऐप्लिकेशन का इस्तेमाल करने लगे.
यहां इसका एक उदाहरण दिया गया है.
मेरे दोस्त का मैसेज आता है, जिसमें उसने मुझे डिनर पर बुलाया है.
इसका जवाब देने के लिए, मुझे अपना कैलेंडर देखना होगा कि मैं उस दिन खाली हूँ या नहीं. इसके बाद, मुझे अपने कैलेंडर में इवेंट जोड़ना होगा. अगर मुझे वहाँ जाने के लिए गाड़ी चाहिए, तो मुझे गाड़ी बुक करनी होगी. इसके बाद, मुझे अपने दोस्त को मैसेज करके बताना होगा कि मैं आ सकता हूँ.
पहले, इसके लिए मुझे एक ऐप्लिकेशन से दूसरे ऐप्लिकेशन पर जाना पड़ता था. इसका मतलब है कि मुझे अपने डिवाइस पर अलग-अलग ऐप्लिकेशन के बीच स्विच करना पड़ता था, ताकि मैं असल में कार्रवाइयां पूरी कर सकूं और अपनी यात्रा पूरी कर सकूं.
हालांकि, अब हम इंटेलिजेंस के दौर में हैं.
इंटेलिजेंस के इस दौर में, उपयोगकर्ता अपनी यात्रा की शुरुआत, किसी खास कार्रवाई के बजाय, अपने मकसद और लक्ष्यों को बताकर करते हैं.
उपभोक्ताओं के व्यवहार में एक बुनियादी बदलाव आया है. वे अपने डिवाइस को सिर्फ़ काम करने के लिए इस्तेमाल होने वाली चीज़ के तौर पर नहीं देखते. इसके बजाय, वे इसे एक ऐसे डिवाइस के तौर पर देखते हैं जो उनकी ज़रूरतों का पहले से अनुमान लगाता है और उनके लक्ष्यों को पूरा करने में मदद करता है.
हमने यह भी देखा कि लोग इस इंटेलिजेंस फ़्रेमवर्क को सिर्फ़ अपने फ़ोन पर ही नहीं, बल्कि अलग-अलग तरह के डिवाइसों पर भी इस्तेमाल करना चाहते हैं. जैसे, स्मार्टवॉच, कार, टीवी, लैपटॉप, एक्सआर, और स्मार्ट ग्लास.
इंटेलिजेंस के दौर में, एक ही क्रॉस-ऐप्लिकेशन प्रॉम्प्ट से, डिनर के न्योते वाले उदाहरण को आसानी से समझा जा सकता है.
मेरे डिवाइस पर मौजूद एजेंट, सिर्फ़ एक प्रॉम्प्ट से मेरी ज़रूरत के सभी काम कर सकता है. जैसे, कैलेंडर देखना, इवेंट जोड़ना, राइड बुक करना, और मैसेज भेजना.
आज हम इस बारे में ज़्यादा जानकारी शेयर करेंगे कि Android के क्या प्लान हैं. साथ ही, यह भी बताएंगे कि डेवलपर के तौर पर, बेहतरीन अनुभव कैसे दिया जा सकता है.
डैन.
धन्यवाद, सारा.
इसलिए, Android अपने कोर में इंटेलिजेंस जोड़ना जारी रखेगा. इससे आपके ऐप्लिकेशन को इस इंटेलिजेंस के दौर में, नए तरीके से काम करने में मदद मिलेगी.
डेवलपर इंटेलिजेंस के इन दो स्तंभों में से पहला है, इन-ऐप्लिकेशन इंटेलिजेंस. इसका मतलब है कि आपके ऐप्लिकेशन को ज़्यादा स्मार्ट बनाना और उसमें इंटेलिजेंस को शामिल करना. इसके लिए, ML Kit के ज़रिए Gemini Nano 4 जैसे उपयोगकर्ता के डिवाइस पर चलने वाले मॉडल इस्तेमाल किए जा सकते हैं. इससे मैसेज थ्रेड की खास जानकारी पाने, जवाबों को फिर से लिखने, और इमेज समझने में मदद मिलती है. इसके अलावा, अपने ऐप्लिकेशन के फ़ंक्शन को कॉल करने के लिए, नैचुरल लैंग्वेज का इस्तेमाल किया जा सकता है.
Firebase AI Logic की मदद से, क्लाउड में मौजूद Gemini मॉडल को सीधे अपने ऐप्लिकेशन में इस्तेमाल किया जा सकता है.
हालांकि, आज हमारा फ़ोकस दूसरे पिलर पर है. इसमें यह पक्का किया जाता है कि आपके ऐप्लिकेशन की सुविधाएं, इंटेलिजेंस सिस्टम में उपलब्ध हों.
इंटेलिजेंस सिस्टम क्या है?
इसलिए, सारा ने जिन प्रॉम्प्ट के बारे में पहले बताया था उनके बारे में बात करने और उन्हें होस्ट करने के अलावा, यह एक ऐसा सिस्टम है जो ऐप्लिकेशन नेटवर्क में उपयोगकर्ताओं के अनुभव को बेहतर बनाने के लिए, डीप इंटिग्रेशन की सुविधा देता है.
इसलिए, Gemini Intelligence के लिए डिज़ाइन किए गए डिवाइस, इन इंटिग्रेशन पॉइंट का इस्तेमाल करके सुझाव, खास जानकारी वाली सूचनाएं, बेहतर वॉइस इनपुट, और फ़ॉर्म अपने-आप भरने जैसी सुविधाएं देते हैं.
अब सुझाव, 'अपने-आप मिलने वाली मदद' सुविधा का हिस्सा हैं.
अब तक, हमने प्रॉम्प्ट के ज़रिए मिलने वाली मदद के बारे में बात की है.
हम सभी को इसकी आदत है. इसमें उपयोगकर्ता, एजेंट को अपनी ओर से एक या उससे ज़्यादा टास्क पूरे करने के लिए कहता है.
हालांकि, अपने-आप मिलने वाली मदद का असल मकसद, उपयोगकर्ता की अगली कार्रवाई या कार्रवाइयों का अनुमान लगाना है.
डिनर वाले उदाहरण पर वापस आते हैं. एजेंट, उपयोगकर्ता को मैसेज के जवाब में सुझाव दे सकता है. जैसे, न्योते को कैलेंडर में जोड़ना या राइडशेयर बुक करना.
लेकिन यह उपयोगकर्ता को असल में कैसे प्रॉम्प्ट करता है?
अपने-आप मिलने वाली मदद की सुविधा, लॉक स्क्रीन, सूचनाओं, वर्चुअल कीबोर्ड, और चुने गए ऐप्लिकेशन जैसी जगहों पर कार्रवाइयां दिखा सकती है.
तो, ऐसा क्या किया जाए कि आपका ऐप्लिकेशन इन चिप में दिखे और आपको ट्रैफ़िक मिले?
आप पहले से ही सबसे नए SDK वर्शन के हिसाब से ऐप्लिकेशन बना रहे हैं और हमारे सबसे आधुनिक टूल का इस्तेमाल कर रहे हैं. हालांकि, अब सबसे अच्छा काम यह किया जा सकता है कि आप यह पक्का करें कि आपका ऐप्लिकेशन, प्रॉम्प्ट की मदद से मिलने वाली सहायता के लिए भी तैयार हो. इसके बारे में सारा आगे बताएंगी.
धन्यवाद, डैन.
ठीक है, तो प्रॉम्प्टेड असिस्टेंस में उपयोगकर्ता सीधे किसी एजेंट के पास जाते हैं और उससे टास्क शुरू करने और उन्हें मैनेज करने के लिए कहते हैं. भले ही, वे टास्क अलग-अलग ऐप्लिकेशन में हों.
इससे, पहले से मौजूद मैन्युअल और ज़्यादा समय लेने वाले वर्कफ़्लो को ऑटोमेट किया जा सकता है.
Android पर हमारे इंटेलिजेंस सिस्टम के लिए, हम मुख्य रूप से Gemini जैसे सिस्टम एजेंट पर फ़ोकस कर रहे हैं. ये एजेंट, उपयोगकर्ता के डिवाइस पर मौजूद ऐप्लिकेशन को ऐक्सेस कर सकते हैं.
इस सुविधा में आपका ऐप्लिकेशन कुछ अलग-अलग तरीकों से दिख सकता है. साथ ही, सिस्टम एजेंट के साथ इंटरैक्ट कर सकता है.
इसके लिए, हमारे पास प्लैटफ़ॉर्म पर आधारित टास्क ऑटोमेशन की सुविधा है. इसके अलावा, सिस्टम एजेंट में सीधे एपीआई इंटिग्रेशन की सुविधा भी है.
हमारा सुझाव है कि जहां भी संभव हो, सीधे एपीआई इंटिग्रेशन का इस्तेमाल करके ऐप्लिकेशन बनाएं.
इन्हें डेवलपर कंट्रोल करते हैं. इसलिए, आपके पास यह तय करने और कंट्रोल करने का विकल्प होता है कि आपका ऐप्लिकेशन और ब्रैंड असल में कैसा दिखे.
ये ज़्यादा तेज़ और भरोसेमंद होते हैं. साथ ही, ये आपके उपयोगकर्ताओं को बेहतर अनुभव देते हैं.
एजेंट के साथ इंटिग्रेट करने के लिए, दो एपीआई पाथ इस्तेमाल किए जा सकते हैं.
मॉडल कॉन्टेक्स्ट प्रोटोकॉल या एमसीपी, सर्वर-साइड फ़ंक्शन कॉलिंग के लिए इंडस्ट्री का सबसे सही तरीका है.
हालांकि, उपयोगकर्ता के डिवाइस पर टूल कॉल करने के लिए, हम Android MCP को लॉन्च कर रहे हैं. यह Android पर फ़ंक्शन कॉलिंग का यूनिफ़ाइड फ़्रेमवर्क है.
Android एमसीपी, Jetpack AppFunctions लाइब्रेरी की मदद से काम करता है. यह लाइब्रेरी, तकनीकी और सैद्धांतिक तौर पर एमसीपी के साथ काम करती है. साथ ही, यह Kotlin के जाने-पहचाने और आसान तरीके से लागू होने वाले वर्शन के साथ काम करती है. इस वर्शन के बारे में, Android के कई डेवलपर जानते हैं.
इससे एपीआई को लागू करना और इस्तेमाल करना आसान हो जाता है. साथ ही, पहले से मौजूद सर्वर-साइड एमसीपी इंटिग्रेशन से मिली जानकारी का फ़ायदा लिया जा सकता है.
कई एमसीपी प्रिमिटिव, Jetpack AppFunctions कंस्ट्रक्ट के साथ वन-टू-वन मैप होते हैं.
आइए, इसे कुछ उदाहरणों की मदद से समझते हैं.
एमसीपी टूल का एंडपॉइंट, AppFunctionDeclaration के साथ एनोटेट किए गए Kotlin फ़ंक्शन से अच्छी तरह मैप होता है.
आपके pydantic मॉडल, Kotlin डेटा क्लास से मैप होते हैं. इन क्लास में AppFunctionSerializable के साथ एनोटेशन किया जाता है.
हालांकि, Android एमसीपी, स्ट्रीम किए जा सकने वाले एचटीटीपी और/या स्टैंडर्ड I/O ट्रांसपोर्ट के बजाय, ऑप्टिमाइज़ किए गए Android बाइंडर IPC पर काम करता है.
आज हमें यह बताते हुए बेहद खुशी हो रही है कि Jetpack लाइब्रेरी, AppFunctions अब बीटा वर्शन में उपलब्ध है.
ठीक है, तो मुझे यकीन है कि आप शायद सोच रहे होंगे कि एक एपीआई पाथ का इस्तेमाल कब करना चाहिए और खास तौर पर, Android MCP का इस्तेमाल कब करना चाहिए.
Android एमसीपी का इस्तेमाल, उपयोगकर्ता की गतिविधि को पूरा करने के लिए, उपयोगकर्ता के डिवाइस पर मौजूद या लोकल कॉन्टेक्स्ट का फ़ायदा पाने के लिए किया जाता है.
आइए, कुछ ऐसे उदाहरणों के बारे में जानते हैं जहां Android एमसीपी काफ़ी मददगार साबित होता है.
खास तौर पर, आपको Android MCP का इस्तेमाल तब करना चाहिए, जब आपको डिवाइस पर मौजूद एन्क्रिप्ट किए गए डेटा को ऐक्सेस करना हो.
यह खास तौर पर मैसेज सेवा और कम्यूनिकेशन ऐप्लिकेशन के लिए ज़रूरी है. इनमें पूरी तरह सुरक्षित (E2EE) की वजह से, मैसेज का इतिहास उपयोगकर्ता के डिवाइस पर सेव रखना ज़रूरी होता है.
Android MCP, सिर्फ़ उपयोगकर्ता के डिवाइस पर और ऑफ़लाइन की जाने वाली कार्रवाइयों के लिए भी काम का है. हालांकि, इसके लिए ज़रूरी है कि एजेंट इन कार्रवाइयों के साथ काम करते हों.
हमारे Wear OS पार्टनर, Samsung Health ने रन और एक्सरसाइज़ शुरू करने के लिए एक फ़ंक्शन लॉन्च किया है. इसे पूरी तरह से उपयोगकर्ता के डिवाइस पर ही मैनेज किया जाता है.
उपयोगकर्ता अक्सर चाहते हैं कि इस तरह की कार्रवाइयां बहुत तेज़ी से, ज़्यादा भरोसेमंद तरीके से, और कम इंतज़ार के समय में पूरी हों. कभी-कभी, वे चाहते हैं कि ये कार्रवाइयां कम कनेक्टिविटी वाले माहौल में भी पूरी हों. टूल कॉलिंग और रूटिंग के साथ-साथ क्वेरी रूटिंग की पूरी प्रोसेस को उपयोगकर्ता के डिवाइस पर ही पूरा करके, यह पक्का किया जा सकता है कि उपयोगकर्ताओं की ये ज़रूरतें पूरी हों.
अगर आपको डिवाइस के सेंसर ऐक्सेस करने हैं या डिवाइस के हार्डवेयर से सीधे तौर पर इंटरैक्ट करना है, तो Android एमसीपी भी एपीआई का पसंदीदा पाथ है.
उदाहरण के लिए, कई ऑटोमोटिव ऐप्लिकेशन, उपयोगकर्ताओं को यूडब्ल्यूबी कनेक्टिविटी की मदद से, कार के दरवाज़े तुरंत अनलॉक करने की सुविधा देना चाहते हैं. इसके लिए, अक्सर बोलकर फ़ोन इस्तेमाल करने की सुविधा का इस्तेमाल किया जाता है.
इसलिए, Android MCP इसमें मदद कर सकता है.
आखिर में, अगर आपके ऐप्लिकेशन या एपीआई के लिए कोई क्लाउड एंडपॉइंट उपलब्ध नहीं है, तो यह आपके लिए एक समाधान है.
डिवाइस बनाने वाली कंपनियों के कई पहले पक्ष के ऐप्लिकेशन, सिर्फ़ क्लाइंट-साइड पर काम करते हैं. इसलिए, वे Android एमसीपी का इस्तेमाल कर रहे हैं.
उदाहरण के लिए, Gemini का इस्तेमाल करके, Xiaomi Gallery जैसे क्लाइंट-साइड गैलरी ऐप्लिकेशन से फ़ोटो खोजी और वापस पाई जा सकती हैं.
यहाँ, मैं Gemini को खोलकर कह सकती हूँ, "Xiaomi Gallery में, पिछले हफ़्ते ली गई Android बॉट की फ़ोटो ढूँढो."
Xiaomi ने फ़ोटो खोजने के लिए, ऐप्लिकेशन के एक फ़ंक्शन को एक्सपोज़ किया है. Gemini इस फ़ंक्शन को कॉल करता है और काम की फ़ोटो सीधे Gemini ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में दिखती हैं. इससे उपयोगकर्ता को ऐप्लिकेशन से बाहर जाने की ज़रूरत नहीं पड़ती.
हम Android OS में, उपयोगकर्ता के डिवाइस पर की जाने वाली कार्रवाइयों के लिए भी इसी आर्किटेक्चर का इस्तेमाल कर रहे हैं.
Android एमसीपी, डिवाइस से जुड़ी मदद की सुविधा को बेहतर बनाता है. इसे हाल ही में लॉन्च किया गया है. यह Android OS को सीधे Gemini ऐप्लिकेशन में इंटिग्रेट करता है.
यह सुविधा, Pixel और Lenovo डिवाइसों पर पहले से ही उपलब्ध है. फ़िलहाल, इसे Android ओईएम नेटवर्क के अन्य डिवाइसों पर भी उपलब्ध कराया जा रहा है.
उपयोगकर्ता, Gemini से डिवाइस की सेटिंग और सिस्टम की सेटिंग को ऑप्टिमाइज़ करने का तरीक़ा पूछ सकते हैं. इससे उन्हें डिवाइस इस्तेमाल करने का बेहतर अनुभव मिलेगा.
उदाहरण के लिए, कलरब्लाइंडनेस से पीड़ित कोई व्यक्ति, अलग-अलग मेन्यू में जाकर सेटिंग ढूंढने के बजाय, Gemini से कह सकता है, "मुझे कलरब्लाइंडनेस है.
मैं इस फ़ोन को अपनी ज़रूरतों के मुताबिक़ बेहतर कैसे बनाऊँ?"
Gemini, डिवाइस पर मौजूद ऐप्लिकेशन के फ़ंक्शन को कॉल और ट्रिगर करेगा. साथ ही, अनुरोध को समझकर, उपयोगकर्ता को सिस्टम की सेटिंग बदलने के असल तरीक़े सुझाएगा. इसके अलावा, वह डिवाइस की सेटिंग बदलने के ख़ास तरीक़े भी सुझाएगा, ताकि उपयोगकर्ता को बेहतर अनुभव मिल सके.
इसके बाद, Gemini ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में, ऐप्लिकेशन में मौजूद कंट्रोल दिखते हैं. इससे उपयोगकर्ता तुरंत कंट्रोल कर सकते हैं.
उदाहरण के लिए, कलरब्लाइंडनेस से जुड़ी इस ऑप्टिमाइज़ेशन रिक्वेस्ट को पूरा करने के लिए, Gemini ने कलर-करेक्शन टॉगल दिखाया है. इससे उपयोगकर्ता तुरंत बदलाव कर सकते हैं.
Android एमसीपी की सुविधाएं, चुनिंदा डिवाइसों पर पहले से उपलब्ध हैं. हम कई पार्टनर के साथ मिलकर काम कर रहे हैं, ताकि Android ईकोसिस्टम पर और भी इस्तेमाल के उदाहरण उपलब्ध कराए जा सकें.
Android एमसीपी टूल के बारे में साफ़ तौर पर एलान करना हमारा गोल्ड स्टैंडर्ड है. हालांकि, हमें पता है कि ऐसे मामले भी होंगे जहां उपयोगकर्ता, एजेंट से जटिल या कम इस्तेमाल होने वाले टास्क पूरे करने के लिए कहेंगे. ऐसे में, प्लैटफ़ॉर्म पर आधारित टास्क ऑटोमेशन की सुविधा काम आती है.
इस मॉडल के तहत, ओईएम का पहले से लोड किया गया एजेंट, Android के कंप्यूटर-कंट्रोल एपीआई का इस्तेमाल करके, किसी ऐप्लिकेशन की स्क्रीन पर टच इवेंट को सिम्युलेट कर सकता है.
इसके लिए, डेवलपर को कोड में कोई बदलाव करने की ज़रूरत नहीं होती. साथ ही, एजेंट को ऐप्लिकेशन की स्क्रीन का विश्लेषण करके, यूज़र इंटरफ़ेस (यूआई) पर टैप करना होता है. ठीक वैसे ही जैसे कोई उपयोगकर्ता करता है.
उपयोगकर्ता की सुरक्षा और कंट्रोल को पक्का करने के लिए, यह ज़रूरी है कि एजेंट, पेमेंट फ़्लो जैसी संवेदनशील कार्रवाइयों के दौरान रुकें और आगे बढ़ने के लिए उपयोगकर्ता से पुष्टि करने का अनुरोध करें.
आइए, एक उदाहरण से इसे समझते हैं.
यहाँ एक उपयोगकर्ता ने कॉफ़ी और चॉकलेट टूर की फ़ोटो ली है. वह Expedia से पूछना चाहता है या Gemini से Expedia पर जाकर यह देखने के लिए कहना चाहता है कि क्या उसे ऐसा ही कोई टूर मिल सकता है. हालाँकि, उसे लोगों की संख्या को छह मेहमानों के हिसाब से अडजस्ट करना है.
Gemini, Expedia के यूज़र इंटरफ़ेस (यूआई) को बैकग्राउंड में उसी तरह नेविगेट करता है जिस तरह कोई उपयोगकर्ता करता है. इसके लिए, उसे सीधे एपीआई इंटिग्रेशन की ज़रूरत नहीं होती.
इसके बाद, यह खोजे गए काम के विकल्प दिखाता है और बुकिंग की पुष्टि करने के लिए, उपयोगकर्ता को आसानी से रीडायरेक्ट करता है.
यह समझना ज़रूरी है कि ये तीनों तरीके एक-दूसरे से अलग नहीं हैं. असल में, ये साथ मिलकर बेहतर तरीके से काम करते हैं.
उपयोगकर्ताओं और डेवलपर को यह उम्मीद करनी चाहिए कि एजेंट, सर्वर के सभी इंप्लिमेंटेशन में टूल को कोऑर्डिनेट कर सकें. साथ ही, वे लोकल Android ऐप्लिकेशन को भी कोऑर्डिनेट कर सकें, ताकि उपयोगकर्ताओं को सबसे बेहतर और बेहतरीन अनुभव मिल सके.
ठीक है।
धन्यवाद, सारा.
हम Android एमसीपी के बारे में ज़्यादा जानकारी हासिल करेंगे.
साथ ही, जब भी संभव हो, मैं चाहता हूं कि आपको पता हो कि मैं Jetpacker से एमसीपी कोड स्निपेट ले रहा हूं. यह यात्रा से जुड़ा हमारा सैंपल ऐप्लिकेशन है. इसे Antigravity की मदद से बनाया गया है.
आपने इस बारे में कीनोट में देखा होगा.
इसमें कई तरह की शानदार और इंटेलिजेंट सुविधाएं मौजूद हैं. इनमें, ML Kit जेन एआई और ML Kit के बोली की पहचान एपीआई के साथ, उपयोगकर्ता के डिवाइस पर इंटिग्रेशन की सुविधा भी शामिल है.
अब Android एमसीपी और क्लाउड-आधारित एमसीपी का स्ट्रक्चर एक जैसा है.
दोनों में क्लाइंट और सर्वर होते हैं.
एमसीपी की दुनिया में, सर्वर टूल उपलब्ध कराते हैं. क्लाइंट इन टूल को ढूंढ सकते हैं और उन्हें कॉल कर सकते हैं.
Android एमसीपी की मदद से, अब आपका ऐप्लिकेशन सर्वर के तौर पर और एजेंट क्लाइंट के तौर पर काम करता है.
Android प्लैटफ़ॉर्म, टूल रजिस्ट्री की भूमिका निभाता है.
अब ऐप्लिकेशन, सर्वर उपलब्ध कराने के लिए किसी सेवा को रजिस्टर करते हैं. हम यहां अपने सैंपल में, AppFunctions Jetpack लाइब्रेरी की डिपेंडेंसी जोड़ना शुरू करेंगे.
implementation(libs.androidx.appfunctions)
ksp(libs.androidx.appfunctions.compiler)इसके बाद, हम केएसपी एनोटेशन का इस्तेमाल करके AppFunctionService बनाएंगे.
इस सेवा का मतलब है कि आपके ऐप्लिकेशन के फ़ंक्शन, Android के लाइफ़साइकल का सही तरीके से पालन कर सकते हैं.
हम Hilt के साथ डिपेंडेंसी इंजेक्शन का इस्तेमाल करके, डेटा रिपॉज़िटरी इंजेक्ट कर सकते हैं. इनका इस्तेमाल हमारे AppFunctions कर सकते हैं.
@RequiresApi(36)
@AndroidEntryPoint
@AppFunctionServiceEntryPoint(
serviceName = "JetPackerAppFunctionService",
appFunctionXmlFileName = "jetpacker_app_function_service"
)
abstract class BaseJetPackerAppFunctionService : AppFunctionService() {
@Inject internal lateinit var tripDao: TripDao
@Inject internal lateinit var expenseDao: ExpenseDao
...माफ़ करें.
चलिए, अपने डायग्राम पर वापस चलते हैं.
हमारे Android एमसीपी सर्वर के लिए सेवा सेट अप करने के बाद, ऐप्लिकेशन AppFunctions के तौर पर टूल रजिस्टर करता है. साथ ही, उनके इस्तेमाल किए गए किसी भी डेटा मॉडल को भी रजिस्टर करता है.
अब AppFunctionSerializable एनोटेशन का इस्तेमाल करके, डेटा मॉडल को रजिस्टर किया जा सकता है. यहां दिया गया KDoc बहुत अच्छा है.
/** Represents an event in a trip itinerary. */
@AppFunctionSerializable(isDescribedByKDoc = true)
data class TimelineEventSerializable(
/** The event's unique identifier. */
val id: String,
/** The trip ID this event belongs to. */
val tripId: String,
/** The type of event (e.g., TRANSPORTATION, ACCOMMODATION, FOOD_AND_DRINK). */
val type: String,
/** The timestamp of the event. */
val timestamp: Long,
/** The title of the event. */
val title: String,
/** The location of the event. */
val location: String,
/** A description of the event. */
val description: String?,
/** The title of the trip this event belongs to. */
val tripTitle: String? = null,
/** Optional title of a matched itinerary event when added as a reminder. */
val matchedEventTitle: String? = null,
/** Execution status of the tool call (e.g., SUCCESS). */
val status: String? = "SUCCESS",
/** Human-readable completion message instructing the agent not to repeat the call. */
val message: String? = null,
)यह सिर्फ़ मेटाडेटा बन जाएगा. इसका इस्तेमाल एजेंट यह समझने के लिए करेगा कि मॉडल का इस्तेमाल कैसे करना है.
अपने फ़ंक्शन को टूल के तौर पर रजिस्टर करने के लिए, AppFunctionDeclaration एनोटेशन का इस्तेमाल किया जा सकता है.
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
val trip = resolveTrip(tripId = tripId)
val targetTripId = trip?.id ?: ActiveTripSession.activeTripId ?: tripId?.takeIf { it.isNotBlank() } ?: "2026-1"
val dbEvents = eventDao.getEventsForTrip(targetTripId).first()
val events =
if (dbEvents.isNotEmpty()) { dbEvents } else {
val fallback = DummyData.events.filter { it.tripId == targetTripId }
if (fallback.isNotEmpty()) { eventDao.insertEvents(fallback) }
fallback
}
events.map { it.toSerializable(tripTitle = trip?.title) }
}
}isDescribedByKDoc को true पर सेट करने से, KDoc एजेंट को फ़ंक्शन के बारे में जानकारी देता है, ताकि वह समझ सके कि आपका फ़ंक्शन क्या करता है. इससे इंसानों को भी इसे समझने में मदद मिलती है.
/**
* Retrieves the itinerary events (with their eventId, title, type, and location) for a specific
* trip. Note: getItinerary returns event summaries only; call getEventDetails(eventId) with a
* specific event's ID to retrieve ticket prices, hotel nightly rates, baggage allowance, or
* visitor tips.
*
* @param tripId Optional ID of the trip (e.g., '2026-1'). Defaults to the active trip if omitted.
* @return A list of events in the itinerary.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
val trip = resolveTrip(tripId = tripId)
val targetTripId = trip?.id ?: ActiveTripSession.activeTripId ?: tripId?.takeIf { it.isNotBlank() } ?: "2026-1"
val dbEvents = eventDao.getEventsForTrip(targetTripId).first()
...पैरामीटर डिज़ाइन करने के कुछ सबसे सही तरीके यहां दिए गए हैं.
/**
* @param tripId Optional ID of the trip (e.g., '2026-1').
* Defaults to the active trip if omitted.
* @return A list of events in the itinerary.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
// resolveTrip defaults to the active trip if tripId == null
val trip = resolveTrip(tripId = tripId)सबसे पहले, आपको साफ़ और अलग-अलग नामों वाले पैरामीटर के सबसे छोटे सबसेट पर फ़ोकस करना चाहिए.
ग़ैर-ज़रूरी पैरामीटर को शून्य पर सेट करें और फ़ंक्शन बॉडी में उनकी डिफ़ॉल्ट वैल्यू को रिज़ॉल्व करें.
यह न सिर्फ़ सबसे सही तरीका है.
यह काम नहीं करेगा, क्योंकि Kotlin की डिफ़ॉल्ट वैल्यू को अनदेखा कर दिया जाएगा. ऐसा इसलिए, क्योंकि AppFunctions लाइब्रेरी, छोड़े गए नलेबल पैरामीटर के लिए शून्य वैल्यू पास करती है.
हम पहले वाले का उदाहरण भी नहीं दिखाएंगे. हालांकि, कैच-ऑल पैरामीटर का इस्तेमाल न करें.
पूरा कॉन्टेक्स्ट या बातचीत का इतिहास जैसी चीज़ें, बहुत खराब हैं.
इनकी वजह से, एजेंट बड़ी मात्रा में डेटा पास कर सकते हैं और डेटा को बाहर भेज सकते हैं. यह एक गलत तरीका है.
AppFunction, डिफ़ॉल्ट रूप से यूज़र इंटरफ़ेस (यूआई) थ्रेड पर चलता है. इसलिए, आपको यह पक्का करना होगा कि लंबे समय तक चलने वाली कोई भी कार्रवाई बैकग्राउंड में हो. इसके लिए, सही कोरूटीन डिस्पैचर चुनें.
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
val trip = resolveTrip(tripId = tripId)
val targetTripId = trip?.id ?: ActiveTripSession.activeTripId ?:
tripId?.takeIf { it.isNotBlank() } ?: "2026-1"
val dbEvents = eventDao.getEventsForTrip(targetTripId).first()
val events =
if (dbEvents.isNotEmpty()) { dbEvents } else {
val fallback = DummyData.events.filter { it.tripId == targetTripId }
...आपको ज़रूरत पड़ने पर, पूरी जानकारी के साथ AppFunction अपवादों को थ्रो करना है, ताकि एजेंट को गड़बड़ी ठीक करने का तरीका समझ आ सके.
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun send(
message: Message
): Result {
...
throw AppFunctionException(
errorCode = AppFunctionException.ErrorCode.ERROR_INVALID_ARGUMENT,
errorMessage = "To address has invalid syntax!"
)
}ठीक है, अब हमारे पास एक AppFunction है. चलिए, यहां हमारे आर्किटेक्चर डायग्राम पर वापस चलते हैं.
एजेंट क्लाइंट ऐप्लिकेशन, AppFunctions को कॉल करने के लिए प्लैटफ़ॉर्म पर रजिस्टर करता है.
उदाहरण के लिए, Gemini.
इसके लिए, उनके पास खास अनुमति होनी चाहिए. इसके बाद, रजिस्टर किए गए एजेंट, Android प्लैटफ़ॉर्म से AppFunction का मेटाडेटा देख सकते हैं. साथ ही, यह तय कर सकते हैं कि उपयोगकर्ता के अनुरोध को पूरा करने के लिए, उन्हें रजिस्टर किए गए एक या उससे ज़्यादा AppFunction को लागू करना चाहिए या नहीं.
एक या उससे ज़्यादा AppFunctions के बारे में बात करते हुए, एजेंट असल में कैसे फ़ैसला लेता है?
आखिर में, आपको ऐप्लिकेशन की मेटाडेटा फ़ाइल में, अपने ऐप्लिकेशन की सुविधाओं के बारे में बताना होगा.
<AppFunctionAppMetadata
xmlns:appfn="http://schemas.android.com/apk/androidx.appfunctions"
appfn:description="This app manages trips and travel expenses.
Operational Patterns:
- Use 'getAllTrips' to find the correct 'tripId' before
adding expenses or managing trip details.
Constraints:
- Expenses must be associated with a valid trip ID."
appfn:displayDescription="@string/user_visible_description" />एजेंट, टूल चुनने के लिए सबसे पहले आपके AppFunctionAppMetadata में मौजूद ब्यौरे को पढ़ते हैं. इसके बाद, वे हर AppFunction के ब्यौरे को पढ़ते हैं.
इसलिए, आपको ब्यौरे की शुरुआत में अपने ऐप्लिकेशन या टूल के मुख्य डोमेन के बारे में कम शब्दों में जानकारी देनी चाहिए.
इसके बाद, ऑपरेशनल पैटर्न का इस्तेमाल किया जा सकता है. इससे एजेंट को ऑपरेशन के लॉजिकल फ़्लो को समझने में मदद मिलती है. यह ज़रूरी शर्तों को बताने या एजेंट को टास्क पूरा करने के लिए सबसे सही पाथ की ओर ले जाने के लिए अच्छा है.
इसके अलावा, पाबंदियों के बारे में बताने वाला सेक्शन जोड़ा जा सकता है. इसमें इनपुट डेटा टाइप की लॉजिकल सीमाओं के बारे में बताया जा सकता है या ऑपरेशनल पैटर्न को बेहतर बनाया जा सकता है.
ये Android एमसीपी की बुनियादी जानकारी थी. हालांकि, इसमें और भी कई काम किए जा सकते हैं.
एक और काम किया जा सकता है, जो काफ़ी शानदार है. वह यह है कि AppFunctions को डाइनैमिक तौर पर चालू और बंद किया जा सकता है.
viewModelScope.launch {
val newState =
if (enabled) {
AppFunctionManager.APP_FUNCTION_STATE_ENABLED
} else {
AppFunctionManager.APP_FUNCTION_STATE_DISABLED
}
appFunctionManager?.setAppFunctionEnabled(id, newState)
}यह ज़रूरी है. उदाहरण के लिए, अगर आपको AppFunction को सिर्फ़ तब उपलब्ध कराना है, जब उपयोगकर्ता ने लॉग इन किया हो.
इसके अलावा, आपके पास ऐसे AppFunctions बनाने का विकल्प भी है जो आपके ऐप्लिकेशन की किसी खास स्क्रीन या गतिविधि के लिए उपलब्ध हों.
@AppFunctionSignature(
scope = AppFunctionMetadata.SCOPE_ACTIVITY,
appFunctionXmlFileName = "cart_functions"
)
fun interface AddCurrentItemToCart {
/** Adds the item currently shown on screen to the cart. Returns the new cart size. */
suspend fun addToCart(quantity: Int): Int
}इसलिए, AppFunction का एलान करने के बजाय, AppFunctionSignature का एलान करें.
इसके बाद, अपने यूज़र इंटरफ़ेस (यूआई) में AppFunction के लिए हैंडलर लागू किया जा सकता है.
val addToCartAppFunctionAdapter =
appFunctionManager.getHandleAppFunctionRequestAdapter<AddCurrentItemToCart>()
coroutineScope.launch {
appFunctionManager.handleAppFunction(
addToCartAppFunctionAdapter.adapt { quantity ->
val newCartSize = cart.addItem(quantity)
newCartSize
}
)
}इसकी सबसे अच्छी बात यह है कि हैंडलर का लाइफ़साइकल, आपके इस्तेमाल किए जाने वाले कोरूटीन स्कोप के लाइफ़साइकल से अपने-आप जुड़ जाता है.
अब आपको इस AppFunctions अनुरोध अडैप्टर को कैश मेमोरी में सेव करना होगा. माफ़ करें, AppFunctions अनुरोध अडैप्टर को हैंडल करना होगा. ऐसा इसलिए, क्योंकि लाइब्रेरी इसे इंस्टैंशिएट करने के लिए, हुड के तहत रिफ़्लेक्शन का इस्तेमाल करती है.
साथ ही, इसमें स्कोप वाले AppFunctions बनाने की सुविधा भी है. इनका रजिस्ट्रेशन, Android लाइफ़साइकल से सीधे तौर पर नहीं जुड़ा होता.
अब एक आखिरी चीज़ है, जिसे हम ऐक्सेस लेवल कहते हैं.
@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(
level = AppFunctionMetadata.ACCESS_LEVEL_SELF,
isCompatEnforcementEnabled = true,
)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
...साथ ही, आपके पास खुद के, सिस्टम के या भरोसेमंद ऐक्सेस लेवल को पाने और इस्तेमाल करने का विकल्प होता है. इससे यह पता चलता है कि कौनसे फ़ंक्शन सिर्फ़ आपके इन-ऐप्लिकेशन एजेंट के लिए इस्तेमाल किए जा सकते हैं. जैसे, ML Kit GenAI की मदद से बनाया गया एजेंट. साथ ही, यह भी पता चलता है कि कौनसे फ़ंक्शन सिर्फ़ सिस्टम के लिए उपलब्ध हैं और कौनसे फ़ंक्शन भरोसेमंद सिस्टम एजेंट के लिए उपलब्ध हैं.
हमने पहले ही बताया था कि ML Kit, अपने यूनिफ़ाइड टूल कॉलिंग फ़्रेमवर्क के तहत, AppFunctions के साथ काम करता है. इससे आपको अपने ऐप्लिकेशन में एजेंट बनाने में मदद मिलती है.
इन्हें एक बार तय करने पर, सिस्टम-लेवल के एजेंट और लोकल इन-ऐप्लिकेशन मॉडल, दोनों में कवरेज मिलता है. इससे, आपको अलग-अलग टूल के एलान करने से बचने में मदद मिलती है.
ज़्यादा जानने के लिए, यह टॉक देखें: ML Kit GenAI की मदद से, अपने ऐप्लिकेशन में एजेंटिक एआई की सुविधाएं अनलॉक करें. इसके अलावा, Jetpack में मौजूद नई सुविधाएं भी देखी जा सकती हैं.
यह काफ़ी शानदार है.
ठीक है, अब सारा के पास चलते हैं.
ठीक है।
तो डैन ने आपको बताया कि Android MCP कैसे काम करता है.
आइए, इसे व्यवहार में लाएं.
याद रखें कि आपका ऐप्लिकेशन एक सर्वर है और एजेंट क्लाइंट हैं. इसलिए, आपका लक्ष्य यह होना चाहिए कि आपका ऐप्लिकेशन उनके लिए एक अच्छा सर्वर हो.
हम Jetpacker का इस्तेमाल करेंगे. यह यात्रा से जुड़ा हमारा सैंपल के तौर पर मिला ऐप्लिकेशन है. हम Gemini के ज़रिए, टूल के पहले एलान से लेकर असल फ़ंक्शन कॉल तक की प्रोसेस को फ़ॉलो करेंगे.
AppFunctions को खुद लिखा जा सकता है या कोडिंग एजेंट का इस्तेमाल करके शुरुआत की जा सकती है.
हमने AppFunctions स्किल रिलीज़ की है. इसकी मदद से, फ़ंक्शन लिखना शुरू किया जा सकता है. इन्हें Android का सीएलआई के ज़रिए Android Studio से जोड़ा जा सकता है या अपनी पसंद के कोडिंग एजेंट के साथ इस्तेमाल किया जा सकता है.
android skills add appfunctionsयह स्किल तीन चरणों में काम करती है.
सबसे पहले, यह आपके कोड बेस का विश्लेषण करता है, ताकि उपयोगकर्ता की उन मुख्य गतिविधियों की पहचान की जा सके जो काम के टूल बना सकती हैं. जैसे, मीडिया चलाना, नोट बनाना या मैसेज भेजना.
इसके बाद, यह AppFunction के एलान जनरेट करता है. इनसे उन गतिविधियों के बारे में पता चलता है जिन्हें लागू करने और प्लैटफ़ॉर्म की मदद की ज़रूरत होती है.
उदाहरण के लिए, Gradle और केएसपी, ऐप्लिकेशन मेटाडेटा एक्सएमएल, और डिपेंडेंसी इंजेक्शन.
आखिर में, यह आपके KDoc ब्यौरों को बेहतर बनाएगा. ऐसा इसलिए, क्योंकि एजेंट इसी जानकारी का इस्तेमाल करके यह तय करता है कि आपके फ़ंक्शन को कब और कैसे कॉल करना है.
हमें इस तरह के नतीजे चाहिए.
यह Jetpacker के लिए एक addExpense फ़ंक्शन है.
/**
* Adds an expense to a trip.
*
* @param tripId ID of the trip. If null, the active trip is used.
* @param category Expense category. If null, it's inferred from the title.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun addExpense(
title: String,
amount: Double,
currency: String,
tripId: String? = null,
category: String? = null,
): ExpenseSerializable = withContext(Dispatchers.IO) {
if (amount <= 0.0) {
throw AppFunctionInvalidArgumentException("amount must be greater than zero")
}
val trip = resolveTrip(tripId)
?: throw AppFunctionElementNotFoundException("No trip with ID $tripId")
val expense = Expense(trip.id, title, amount, currency,
category = category ?: inferExpenseCategory(title))
expenseDao.insertExpense(expense)
expense.toSerializable(remainingBudgetUsd(trip.id))
}ब्यौरा एक लाइन का वाक्य होता है, जो क्रिया और संसाधन से शुरू होता है: यात्रा में खर्च जोड़ता है.
यात्रा और कैटगरी फ़ील्ड को खाली छोड़ा जा सकता है, क्योंकि हो सकता है कि उपयोगकर्ता हमेशा यह जानकारी न दें. हालांकि, फ़ंक्शन उन्हें मुख्य हिस्से में हल कर देगा.
जब कोई अनुरोध पूरा नहीं हो पाता, तो यह AppFunctionException दिखाता है, ताकि एजेंट को पता चल सके कि क्या गड़बड़ी हुई है.
चलिए, अब टेस्टिंग के बारे में जानते हैं.
आपके फ़ंक्शन अब ऐसी सेवाएं हैं जिनका इस्तेमाल एजेंट कर सकते हैं.
इसलिए, आपको दो चीज़ों की जांच करनी होगी: फ़ंक्शन के काम करने का तरीका और एजेंट के काम करने का तरीका.
आपके फ़ंक्शन के लिए, हम उस यूनिट टेस्ट से शुरुआत करेंगे जो आपने शायद पहले ही लिख लिया है. हालांकि, जब एजेंट फ़ंक्शन कॉल करता है, तो कुछ बातों का ध्यान रखना ज़रूरी है.
सबसे पहले, Kotlin की डिफ़ॉल्ट वैल्यू यहां लागू नहीं होती हैं.
हटाया गया पैरामीटर, शून्य, गलत या 0 के तौर पर दिखता है.
इसलिए, पहले जो सीमा डिफ़ॉल्ट रूप से 10 पर सेट थी वह अब 0 पर सेट होगी.
कॉल भी मुख्य थ्रेड पर शुरू होता है. साथ ही, टेस्ट से अपने फ़ंक्शन को कॉल करने पर, हमेशा यह साफ़ तौर पर नहीं दिखता. इसलिए, आपको अपने स्टैंडर्ड IO को बैकग्राउंड डिस्पैचर पर चलाना चाहिए.
जब कोई गड़बड़ी हो, तो AppFunctionException थ्रो करें.
इसके अलावा, कोई भी गड़बड़ी होने पर आपके एजेंट को सामान्य गड़बड़ी का मैसेज मिलेगा. इसमें गड़बड़ी का रॉ मैसेज भी अटैच होगा.
आपके इनपुट किसी मॉडल से आएंगे. इसलिए, आपको खाली जगहें, डॉलर के समानार्थी शब्द जैसे कि USD, और रेंज से बाहर की वैल्यू जैसी स्थितियों के लिए ज़रूर टेस्ट करना चाहिए.
एजेंट के लिए, आपको यह जानना होगा कि एजेंट आपके टूल को सही समय पर चुनता है या नहीं और वह फ़ंक्शन को सही तरीके से लागू करता है या नहीं.
हर एजेंट का व्यवहार थोड़ा अलग होता है. इसलिए, अपने फ़ंक्शन को किसी ऐसे एजेंट के साथ टेस्ट करना ज़रूरी है जो उनका इस्तेमाल कर सकता है.
इंटिग्रेशन की जांच करने के लिए, हमारे ओपन-सोर्स सैंपल एजेंट का इस्तेमाल किया जा सकता है. इसके अलावा, Gemini का इस्तेमाल भी किया जा सकता है.
अगर आपको यह देखना है कि Gemini आपके AppFunctions के साथ कैसे इंटरैक्ट करता है या आपको Gemini ऐप्लिकेशन पर Android एमसीपी लॉन्च करना है, तो Connected Apps Platform का इस्तेमाल करें. हमने इसे आज ही लॉन्च किया है और फ़िलहाल यह झलक के तौर पर उपलब्ध है.
Connected Apps Platform के ज़रिए, अपने ऐप्लिकेशन को Gemini और Search के साथ इंटिग्रेट करने के बारे में ज़्यादा जानने के लिए, हमारी पिछली बातचीत देखें.
झलक दिखाने वाले प्रोग्राम में शामिल होने के बाद, Gemini के साथ टेस्टिंग करने की प्रोसेस में दो चरण होते हैं.
सबसे पहले, Google Connected Console में कनेक्ट किए गए ऐप्लिकेशन में, हम कनेक्ट किया गया एक ऐसा ऐप्लिकेशन बनाएंगे जिसे Gemini इस्तेमाल कर सके.
इसके बाद, हमें Play Console में आपका ऐप्लिकेशन बंडल अपलोड करना होगा, ताकि हम AppFunctions को ऐक्सेस कर सकें. साथ ही, ऐप्लिकेशन का इंटरनल टेस्ट ट्रैक शुरू किया जा सके, ताकि टेस्टर फ़ंक्शन की पुष्टि कर सकें.
चलिए, Google Connected Console से शुरुआत करते हैं.
यहां, सबसे ऊपर दाईं ओर मौजूद "ऐप्लिकेशन बनाएं" बटन का इस्तेमाल करके, कनेक्ट किया गया ऐप्लिकेशन बनाया जा सकता है.
कनेक्ट किया गया ऐप्लिकेशन बनाते समय, इस स्क्रीन पर Android MCP चुनें.
अगर आपको सर्वर-साइड एमसीपी लागू करना है, तो आपको अपना सर्वर कोड यहां अपलोड करना होगा.
"सेट अप करें" पर क्लिक करने पर, आपके डेवलपर खाते से जुड़े Google Play के सभी ऐप्लिकेशन यहां दिखेंगे. इन्हें कनेक्ट किए गए ऐप्लिकेशन से लिंक किया जा सकता है.
अगर आपके पास पहले से Google Play ऐप्लिकेशन नहीं है, तो आपको पहले Play Console में एक ऐप्लिकेशन बनाना होगा.
यहां हमारे पास पहले से ही Jetpacker है. इसलिए, हम आगे बढ़ेंगे और उसे लिंक करेंगे.
इसके बाद, वह जानकारी जोड़ें जो उपयोगकर्ताओं को Gemini के फ़्रंट एंड में दिखेगी. ऐसा तब होगा, जब वे आपके ऐप्लिकेशन से इंटरैक्ट करने की कोशिश करेंगे.
इसमें ऐप्लिकेशन का आइकॉन, डिसप्ले नेम, कनेक्ट किए गए ऐप्लिकेशन की क्षमताएं, और प्रॉम्प्ट के उदाहरण शामिल होते हैं. उपयोगकर्ता इन प्रॉम्प्ट का इस्तेमाल करके, ऐप्लिकेशन से इंटरैक्ट कर सकते हैं.
इनमें पूरी जानकारी होनी चाहिए, ताकि उपयोगकर्ताओं को बेहतर तरीके से गाइड किया जा सके.
ठीक है, कनेक्ट किए गए ऐप्लिकेशन का सेटअप पूरा हो गया है. हालांकि, आपको यह जानने की उत्सुकता हो सकती है कि कनेक्ट किए गए ऐप्लिकेशन को फ़ंक्शन का ऐक्सेस असल में कैसे मिलता है.
ये फ़ंक्शन आपके ऐप्लिकेशन के कोड में मौजूद होते हैं. इसलिए, अब हमें Play Console पर जाकर, आपका ऐप्लिकेशन बंडल अपलोड करना होगा और एक टेस्ट ट्रैक बनाना होगा. इससे हम असल उपयोगकर्ताओं के साथ इसकी जांच कर पाएंगे.
इसलिए, इस स्क्रीन पर हम ऐप्लिकेशन बंडल अपलोड करेंगे. इसके बाद, हमें यहां दिखेगा कि Google Play, AppFunctions का पता लगा सकता है. साथ ही, उन्हें सीधे तौर पर कनेक्ट किए गए ऐप्लिकेशन से लिंक किया जा सकता है.
अब Jetpack से कनेक्ट किया गया ऐप्लिकेशन, इंटिग्रेट किए गए फ़ंक्शन के साथ देखा जा सकता है.
इसके बाद, हमें टेस्टर जोड़ने होंगे, ताकि कोई व्यक्ति आपके फ़ंक्शन की पुष्टि कर सके और यह पक्का कर सके कि वे ठीक से काम कर रहे हैं.
इसलिए, कनेक्ट किए गए ऐप्लिकेशन के वर्शन पर क्लिक करें.
इस स्क्रीन पर, "टेस्टर मैनेज करें" को चुनें. इसके बाद, टेस्टर की एक सीमित सूची कॉन्फ़िगर करें. इस सूची में शामिल लोग, Gemini में कनेक्ट किए गए आपके ऐप्लिकेशन को ऐक्सेस कर पाएंगे. हालांकि, ऐसा ऐप्लिकेशन उपलब्ध होने के बाद ही किया जा सकेगा.
इस टेस्टिंग ट्रैक में टेस्टर जोड़ने का तरीका वही है जो किसी अन्य रिलीज़ के लिए होता है.
पक्का करें कि कनेक्ट किए गए ऐप्लिकेशन के लिए, टेस्टर की वही सूची हो जो आपके Google Play ऐप्लिकेशन के लिए है.
ठीक है, अब इंटिग्रेशन की जांच करते हैं.
यह उपयोगकर्ता के फ़ोन पर Jetpacker ऐप्लिकेशन है. इसमें इस समिट के लिए पहले से ही एक यात्रा लॉग की गई है.
मैं ऐप्लिकेशन को कनेक्ट करने के लिए, Gemini का इस्तेमाल करूँगी.
मुझे यह पक्का करना होगा कि मेरे डिवाइस पर ऐप्लिकेशन का वही वर्शन इंस्टॉल हो जो हमने पहले Play पर अपलोड किया था. साथ ही, हमें Gemini के लिए उस खाते का इस्तेमाल करना चाहिए जिसे टेस्टर की सूची में जोड़ा गया था.
इसलिए, मैं यहां Jetpacker का ज़िक्र करूंगा और माउंटेन व्यू में अपने होटल के कमरे की बुकिंग की पुष्टि करने वाला नंबर मांगूंगा.
Gemini, डिवाइस पर Jetpacker के AppFunctions को कॉल करता है और जवाब देता है, "रेडवुड ग्रोव होटल में RED11223."
इसके बाद, मैं पूछूँगा, "मेरे पास विज्ञापनों के लिए कितना बजट बचा है?"
फ़ंक्शन कॉल से 1,850 डॉलर की वैल्यू मिलेगी.
अब मुझे इसमें कुछ बदलाव करना है. इसलिए, मैं Gemini से कहूँगी कि वह मेरी एडीएस ट्रिप में, एयरपोर्ट जाने के लिए Uber के किराये के तौर पर 45 डॉलर जोड़ दे.
Gemini, पहले से मौजूद addExpense फ़ंक्शन को कॉल करता है, ताकि इसे सीधे मेरी यात्रा की योजना में जोड़ा जा सके.
जब मैं Jetpacker खोलता हूं, तो खर्च पहले से ही वहां मौजूद होता है और बजट उसके हिसाब से कम हो जाता है.
अगर आपको Gemini के लिए कनेक्ट किए गए ऐप्लिकेशन बनाने हैं, तो Connected Apps Platform के ऐक्सेस के लिए आवेदन करें. फ़िलहाल, यह प्लैटफ़ॉर्म झलक के तौर पर उपलब्ध है.
ठीक है।
चलिए, अब इस वीडियो को खत्म करते हैं.
इसलिए, हम मोबाइल कंप्यूटिंग में एक बड़े बदलाव की शुरुआत में हैं. अब यह पक्का करने का समय आ गया है कि आपका ऐप्लिकेशन तैयार है.
इसलिए, सबसे पहले अपने ऐप्लिकेशन के लिए, उपयोगकर्ता की उन गतिविधियों की पहचान करें जो आपके कारोबार के लिए सबसे अहम हैं.
सर्वर-साइड एमसीपी का इस्तेमाल करके, अपने क्लाउड के बैक एंड को एक्सपोज़ करें. साथ ही, पूरी तरह सुरक्षित डेटा, लोकल ऑपरेशन, डिवाइस सेंसर, और उपयोगकर्ता के डिवाइस पर मौजूद अन्य चीज़ों के साथ काम करने के लिए, Android एमसीपी का इस्तेमाल करें.
प्रोटोटाइप बनाने के लिए, Android Studio और Android का सीएलआई को डाउनलोड किया जा सकता है.
अपनी बिल्ड फ़ाइलों में AndroidX AppFunctions बीटा डिपेंडेंसी जोड़ी जा सकती है. साथ ही, उपयोगकर्ता के डिवाइस पर मौजूद अपने पहले फ़ंक्शन को स्थानीय तौर पर टेस्ट किया जा सकता है.
Gemini के साथ प्रोडक्शन शुरू करने के लिए, इस क्यूआर कोड को स्कैन करके साइन अप करें.
साथ ही, Connected Apps Platform के साथ काम करने के बारे में ये बातचीत ज़रूर देखें. इसके अलावा, यह भी जानें कि एमएल किट या अपने एजेंट के साथ इंटरनल AppFunctions का इस्तेमाल करके, अपने इन-ऐप्लिकेशन एजेंट को कैसे चलाया जा सकता है.
आखिर में, अपना समय देने के लिए बहुत-बहुत धन्यवाद.
हमें यह देखने का बेसब्री से इंतज़ार है कि Android के लिए, किस तरह के शानदार अनुभव तैयार किए जा रहे हैं.
implementation(libs.androidx.appfunctions)
ksp(libs.androidx.appfunctions.compiler)@RequiresApi(36)
@AndroidEntryPoint
@AppFunctionServiceEntryPoint(
serviceName = "JetPackerAppFunctionService",
appFunctionXmlFileName = "jetpacker_app_function_service"
)
abstract class BaseJetPackerAppFunctionService : AppFunctionService() {
@Inject internal lateinit var tripDao: TripDao
@Inject internal lateinit var expenseDao: ExpenseDao
.../** Represents an event in a trip itinerary. */
@AppFunctionSerializable(isDescribedByKDoc = true)
data class TimelineEventSerializable(
/** The event's unique identifier. */
val id: String,
/** The trip ID this event belongs to. */
val tripId: String,
/** The type of event (e.g., TRANSPORTATION, ACCOMMODATION, FOOD_AND_DRINK). */
val type: String,
/** The timestamp of the event. */
val timestamp: Long,
/** The title of the event. */
val title: String,
/** The location of the event. */
val location: String,
/** A description of the event. */
val description: String?,
/** The title of the trip this event belongs to. */
val tripTitle: String? = null,
/** Optional title of a matched itinerary event when added as a reminder. */
val matchedEventTitle: String? = null,
/** Execution status of the tool call (e.g., SUCCESS). */
val status: String? = "SUCCESS",
/** Human-readable completion message instructing the agent not to repeat the call. */
val message: String? = null,
)@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
val trip = resolveTrip(tripId = tripId)
val targetTripId = trip?.id ?: ActiveTripSession.activeTripId ?: tripId?.takeIf { it.isNotBlank() } ?: "2026-1"
val dbEvents = eventDao.getEventsForTrip(targetTripId).first()
val events =
if (dbEvents.isNotEmpty()) { dbEvents } else {
val fallback = DummyData.events.filter { it.tripId == targetTripId }
if (fallback.isNotEmpty()) { eventDao.insertEvents(fallback) }
fallback
}
events.map { it.toSerializable(tripTitle = trip?.title) }
}
}/**
* Retrieves the itinerary events (with their eventId, title, type, and location) for a specific
* trip. Note: getItinerary returns event summaries only; call getEventDetails(eventId) with a
* specific event's ID to retrieve ticket prices, hotel nightly rates, baggage allowance, or
* visitor tips.
*
* @param tripId Optional ID of the trip (e.g., '2026-1'). Defaults to the active trip if omitted.
* @return A list of events in the itinerary.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
val trip = resolveTrip(tripId = tripId)
val targetTripId = trip?.id ?: ActiveTripSession.activeTripId ?: tripId?.takeIf { it.isNotBlank() } ?: "2026-1"
val dbEvents = eventDao.getEventsForTrip(targetTripId).first()
.../**
* @param tripId Optional ID of the trip (e.g., '2026-1').
* Defaults to the active trip if omitted.
* @return A list of events in the itinerary.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
// resolveTrip defaults to the active trip if tripId == null
val trip = resolveTrip(tripId = tripId)@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
val trip = resolveTrip(tripId = tripId)
val targetTripId = trip?.id ?: ActiveTripSession.activeTripId ?:
tripId?.takeIf { it.isNotBlank() } ?: "2026-1"
val dbEvents = eventDao.getEventsForTrip(targetTripId).first()
val events =
if (dbEvents.isNotEmpty()) { dbEvents } else {
val fallback = DummyData.events.filter { it.tripId == targetTripId }
...@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun send(
message: Message
): Result {
...
throw AppFunctionException(
errorCode = AppFunctionException.ErrorCode.ERROR_INVALID_ARGUMENT,
errorMessage = "To address has invalid syntax!"
)
}<AppFunctionAppMetadata
xmlns:appfn="http://schemas.android.com/apk/androidx.appfunctions"
appfn:description="This app manages trips and travel expenses.
Operational Patterns:
- Use 'getAllTrips' to find the correct 'tripId' before
adding expenses or managing trip details.
Constraints:
- Expenses must be associated with a valid trip ID."
appfn:displayDescription="@string/user_visible_description" />viewModelScope.launch {
val newState =
if (enabled) {
AppFunctionManager.APP_FUNCTION_STATE_ENABLED
} else {
AppFunctionManager.APP_FUNCTION_STATE_DISABLED
}
appFunctionManager?.setAppFunctionEnabled(id, newState)
}@AppFunctionSignature(
scope = AppFunctionMetadata.SCOPE_ACTIVITY,
appFunctionXmlFileName = "cart_functions"
)
fun interface AddCurrentItemToCart {
/** Adds the item currently shown on screen to the cart. Returns the new cart size. */
suspend fun addToCart(quantity: Int): Int
}val addToCartAppFunctionAdapter =
appFunctionManager.getHandleAppFunctionRequestAdapter<AddCurrentItemToCart>()
coroutineScope.launch {
appFunctionManager.handleAppFunction(
addToCartAppFunctionAdapter.adapt { quantity ->
val newCartSize = cart.addItem(quantity)
newCartSize
}
)
}@AppFunctionDeclaration(isDescribedByKDoc = true)
@AppFunctionAccessLevel(
level = AppFunctionMetadata.ACCESS_LEVEL_SELF,
isCompatEnforcementEnabled = true,
)
suspend fun getItinerary(tripId: String? = null): List<TimelineEventSerializable> {
return withContext(Dispatchers.IO) {
...android skills add appfunctions/**
* Adds an expense to a trip.
*
* @param tripId ID of the trip. If null, the active trip is used.
* @param category Expense category. If null, it's inferred from the title.
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun addExpense(
title: String,
amount: Double,
currency: String,
tripId: String? = null,
category: String? = null,
): ExpenseSerializable = withContext(Dispatchers.IO) {
if (amount <= 0.0) {
throw AppFunctionInvalidArgumentException("amount must be greater than zero")
}
val trip = resolveTrip(tripId)
?: throw AppFunctionElementNotFoundException("No trip with ID $tripId")
val expense = Expense(trip.id, title, amount, currency,
category = category ?: inferExpenseCategory(title))
expenseDao.insertExpense(expense)
expense.toSerializable(remainingBudgetUsd(trip.id))
}