इस सेशन में, हम ML Kit एपीआई का इस्तेमाल करके, जेन एआई के इस्तेमाल के अलग-अलग उदाहरणों के बारे में जानेंगे. इससे यह समझने में मदद मिलेगी कि किसी ऐप्लिकेशन के लिए, उपयोगकर्ता के डिवाइस पर मौजूद जेन एआई का इस्तेमाल कब करना चाहिए. साथ ही, जेन एआई के इस्तेमाल के मौजूदा उदाहरणों का आकलन करने का तरीका भी जानेंगे, ताकि यह देखा जा सके कि उपयोगकर्ता के डिवाइस पर मौजूद मॉडल, क्वालिटी के स्टैंडर्ड को पूरा कर सकते हैं या नहीं.
ट्रांसक्रिप्ट
डेविड चाउ
नमस्ते.
गुड आफ़्टरनून,
मेरा नाम डेविड चाउ है.
मैं Android AICore में प्रॉडक्ट लीड हूं.
हमें आपको उपयोगकर्ता के डिवाइस पर मौजूद एआई की सुविधाओं के बारे में बताते हुए बेहद खुशी हो रही है.
आज हम कुछ अहम विषयों पर बात करेंगे.
पहली बात यह है कि आपको उपयोगकर्ता के डिवाइस पर ही काम करने वाले एआई का इस्तेमाल क्यों करना चाहिए. खास तौर पर, तब जब क्लाउड पर एआई की सुविधा तेज़ी से बेहतर हो रही है.
दूसरा, पिछले साल हमने उपयोगकर्ता के डिवाइस पर इन एआई मॉडल को और बेहतर बनाने के लिए कुछ सुधार किए हैं ताकि वे बेहद शक्तिशाली बन सकें.
आखिर में, हम एक उदाहरण के ज़रिए यह समझेंगे कि उपयोगकर्ता के डिवाइस पर एआई का इस्तेमाल करके, इन सभी फ़ायदों का लाभ कैसे उठाया जा सकता है.
सबसे पहले, उपयोगकर्ता के डिवाइस पर काम करने वाला एआई क्यों इस्तेमाल करें?
इसकी चार मुख्य वजहें हैं.
पहला है स्पीड, दूसरा है कीमत, तीसरा है उपलब्धता, और चौथा है निजता.
इसलिए, स्पीड के मामले में, अब यह देखा जा सकता है कि डिवाइस पर मौजूद एआई का अनुमान, क्लाउड पर चलने वाले अनुमान से ज़्यादा तेज़ हो सकता है. ऐसा इसलिए, क्योंकि आपको उपयोगकर्ता के डिवाइस और क्लाउड के बीच जानकारी को आगे-पीछे भेजने की ज़रूरत नहीं पड़ती.
इसलिए, हमने जिन इस्तेमाल के उदाहरणों में टेस्ट किया है उनमें से ज़्यादातर में हमें पता चला है कि क्लाउड की तुलना में, उपयोगकर्ता के डिवाइस पर एआई की मदद से काम करने की स्पीड दोगुनी हो जाती है.
दूसरा है, लागत के बारे में सोचना.
मान लें कि आपको एक दिन में दस लाख अनुमान लगाने हैं.
अगर क्लाउड के बजाय उपयोगकर्ता के डिवाइस पर एआई का इस्तेमाल किया जाए, तो लाखों डॉलर बचाए जा सकते हैं.
तीसरा, ऑफ़लाइन मोड में उपलब्ध होना.
इसलिए, हमने इस साल की शुरुआत में एक सर्वे किया. इसमें हमने जिन डेवलपर से बात की उनमें से 62% ने बताया कि उपयोगकर्ता के डिवाइस पर मौजूद एआई का इस्तेमाल करने की तीन मुख्य वजहों में से एक, ऑफ़लाइन उपलब्धता है.
चाहे आपका उपयोगकर्ता 35,000 फ़ीट की ऊंचाई पर हो या टोक्यो स्टेशन के सब-बेसमेंट में हो और उसके पास इंटरनेट का ऐक्सेस न हो, फिर भी वह इन सभी सुविधाओं का फ़ायदा ले सकता है.
आखिर में, जब हमने डेवलपर का सर्वे किया, तो हमें पता चला कि निजता सबसे बड़ी वजहों में से एक थी.
इसलिए, 72% लोगों ने इसे तीन सबसे अहम वजहों में से एक माना.
40% लोगों ने इसे पहली रैंक दी.
इसलिए, जब बात आपके मैसेज, वॉइस नोट, फ़ोटो या वित्तीय या चिकित्सा से जुड़ी जानकारी की आती है, तो उपयोगकर्ता इसे निजी रखना चाहते हैं. इसलिए, उपयोगकर्ता के डिवाइस पर अनुमान लगाने की सुविधा उनके लिए काफ़ी मददगार साबित होगी.
इन सभी फ़ायदों को ध्यान में रखते हुए, हमने ML Kit को बनाया है, ताकि आप इन सभी सुविधाओं का इस्तेमाल कर सकें और इन्हें मैनेज कर सकें.
हम इसे बेहद आसान बनाने की कोशिश कर रहे हैं.
इसलिए, अगर आपको कोई शिकायत करनी है, तो कृपया बेझिझक मुझसे संपर्क करें.
मैं इस बातचीत के आखिर में अपनी संपर्क जानकारी शेयर करूँगा.
हमें यह जानने में खुशी होगी कि हम आपके इस्तेमाल के उदाहरणों को कैसे चालू कर सकते हैं.
इसलिए, हमने शुरुआत में ही इसमें कई सुविधाएं जोड़ी हैं. जैसे, स्पीच और क्रिएटिव इस्तेमाल के उदाहरण.
जैसे, प्रूफ़रीड करना, फिर से लिखना, और टेक्स्ट, इमेज वग़ैरह का एक साथ इस्तेमाल करके प्रॉम्प्ट डालने की सुविधा चालू करना.
इसलिए, हमारा मकसद इसे ज़्यादा से ज़्यादा इस्तेमाल करने लायक बनाना है.
इसलिए, हमारे कई उपयोगकर्ताओं और डेवलपर ने इसे पहले ही अपनाना शुरू कर दिया है.
फ़िलहाल, हमारे पास तीसरे पक्ष के नौ ऐसे ऐप्लिकेशन हैं जिन्हें एक करोड़ बार डाउनलोड किया गया है. ये ऐप्लिकेशन, प्रोडक्शन में इन जेन एआई सुविधाओं का इस्तेमाल कर रहे हैं.
साथ ही, हम अपने पहले पक्ष के ऐप्लिकेशन और तीसरे पक्ष के ऐप्लिकेशन, दोनों में काफ़ी बढ़ोतरी देख रहे हैं. एआई प्रोडक्शन की सुविधाओं के लिए, ऐप्लिकेशन लॉन्च में तिमाही दर तिमाही 110% की बढ़ोतरी हुई है.
इसलिए, हम आपको मैसेजिंग, क्रिएटिव, और फ़ाइनेंस से जुड़े हमारे एपीआई आज़माने का सुझाव देते हैं.
हमें लगता है कि इस टेक्नोलॉजी का इस्तेमाल कई तरीकों से किया जा सकता है. साथ ही, इनमें से कई मामलों में आपकी कल्पना हमसे बेहतर हो सकती है. इसलिए, हम आपके साथ मिलकर काम करना चाहते हैं और यह देखना चाहते हैं कि हम साथ मिलकर कैसे काम कर सकते हैं.
अब आपको इसके मुख्य फ़ायदों के बारे में पता चल गया है.
इसलिए, आपके मन में यह सवाल हो सकता है कि असल में रीच कैसी है?
डिवाइस पर परफ़ॉर्मेंस कैसी है?
जब हम स्पीड की बात करते हैं, तो यह असल में कितनी अच्छी है?
या फिर मॉडल की क्वालिटी कैसी है, है न?
इसलिए, हम इनके बारे में एक-एक करके जानेंगे.
पहुंच की बात करें, तो आज हम 17.5 करोड़ AICore डिवाइसों पर पहले से ही उपलब्ध हैं.
हमें उम्मीद है कि आने वाले साल में, इस सुविधा का दायरा काफ़ी बढ़ जाएगा. इससे करोड़ों अन्य डिवाइसों को भी इस सुविधा का फ़ायदा मिलेगा.
यह इसलिए मुमकिन हो पाया है, क्योंकि हमने एक बहुत ही आसान हार्नेस बनाया है.
इसलिए, आपको सिर्फ़ ML Kit API को इंटिग्रेट करना होगा.
AICore में इनपुट और आउटपुट के सुरक्षा सिग्नल को मैनेज करने, मॉडल को वेट के साथ डाउनलोड करने, और रनटाइम में इसे सेट अप करने का तरीका तय करने के लिए, कई जटिलताएं मौजूद हैं.
इसलिए, हमारा मकसद ML Kit को इस्तेमाल करने में आसान बनाना है, ताकि आप सिर्फ़ इस बात पर ध्यान दे सकें कि उपयोगकर्ताओं को बेहतरीन अनुभव कैसे दिया जाए. बाकी की चिंता हम पर छोड़ दें.
डेविड चाउ
हमें यह बताते हुए भी खुशी हो रही है कि हमने Gemini Nano को चलाने के लिए ज़रूरी रैम को काफ़ी कम कर दिया है. यह मुख्य मॉडल है, जो इनमें से कई जेन एआई एपीआई को बेहतर बनाता है.
इसलिए, हमने रैम के इस्तेमाल को 3.6 जीबी से घटाकर 1.1 जीबी कर दिया है.
इसलिए, हमने इसे मुमकिन बनाने के लिए चार मुख्य सुधार किए हैं.
पहला तरीका है, 4-बिट के बजाय 2-बिट क्वांटाइज़ेशन का इस्तेमाल करना.
इसलिए, आधे बिट का मतलब है कि आपको मॉडल के ज़रिए आधे बाइट ही भेजने होंगे.
दूसरा तरीका है, शेयर किए गए KV कैश का इस्तेमाल करना.
इसलिए, हमने इसे काफ़ी कम कर दिया है. इसके लिए, हमने मॉडल की शुरुआती 20 लेयर को 35 एमबी के पैकेज में शेयर करने की अनुमति दी है. इसका मतलब है कि आपको इस छोटे पैकेज साइज़ में भी 16K टोकन का कॉन्टेक्स्ट साइज़ ऐक्सेस करने की सुविधा मिलती है.
तीसरा है, लेज़ी विज़न और ऑडियो एन्कोडर चालू करना.
इसलिए, टेक्स्ट के इस्तेमाल के जिन मामलों को हम देख रहे हैं उनमें से ज़्यादातर के लिए, आपको शुरुआत में इन्हें लोड करने की ज़रूरत नहीं है.
इससे आपको आधा गीगाबाइट रैम की बचत होगी.
इसके अलावा, बैटरी लाइफ़ को बेहतर बनाने के लिए, हमने एनपीयू क्लॉक स्केलिंग की सुविधा भी चालू की है.
इसका मतलब है कि एनपीयू, बिजली बचाने के लिए ज़रूरत के हिसाब से तेज़ी से काम कर सकता है. साथ ही, यह पक्का करता है कि आपको अपने इस्तेमाल के उदाहरणों के लिए अच्छी क्वालिटी मिले.
हमने स्पीडअप की सुविधा में कुछ और सुधार किए हैं.
पहला तरीका है, प्रीफ़िक्स कैश मेमोरी को चालू करना.
इस उदाहरण में देखा जा सकता है कि सिस्टम प्रॉम्प्ट का इस्तेमाल कई बार किया गया है, है न?
इसलिए, इसे प्रोसेस करना बहुत महंगा हो जाता है.
हालांकि, प्रीफ़िक्स कैशिंग की मदद से, हम यह कह रहे हैं कि आपको सिस्टम प्रॉम्प्ट को बार-बार प्रोसेस करने की ज़रूरत नहीं है, है ना?
इसे प्रीफ़िक्स कैश मेमोरी में सेव किया जा सकता है. इसका मतलब है कि इस उदाहरण के लिए, आपको पास किए जाने वाले 60% टोकन सेव करने होंगे. इसका मतलब यह भी है कि आपके डिवाइस पर, 60% तेज़ी से काम होगा.
हमने दूसरा सुधार, स्पेक्युलेटिव डिकोडिंग में किया है.
इस उदाहरण में, आपको दिख रहा होगा कि Gemini Nano हर पास के लिए एक टोकन जनरेट कर रहा है.
इसकी वजह यह है कि एलएलएम ऑटोरिग्रेसिव होते हैं. इसका मतलब है कि उन्हें अगले टोकन का पता लगाने के लिए, जनरेट किए गए पिछले टोकन को देखते रहना पड़ता है.
इसलिए, हमने इस समस्या को हल करने के लिए एक ड्राफ़्टर मॉडल बनाया. यह मॉडल, बहुत तेज़ी से कई टोकन जनरेट करता है.
यह ड्राफ़्टर मॉडल, Gemini Nano से काफ़ी छोटा है. इसलिए, यह एक बार में कई टोकन जनरेट कर सकता है. इसके बाद, यह उन्हें Gemini को भेज सकता है, ताकि यह देखा जा सके कि कौनसे टोकन Gemini Nano के अनुमानों से मेल खाते हैं.
इस उदाहरण में देखा जा सकता है कि पहले पास में, यह छह टोकन जनरेट करता है. इसके बाद, अगले पास में पांच टोकन जनरेट करता है. तीसरे पास में, यह अपना काम पूरा कर लेता है.
हमने देखा है कि इंटरनल इस्तेमाल के कई मामलों में, इससे औसतन दो गुना ज़्यादा तेज़ी से काम हो रहा है.
आपके हार्डवेयर के हिसाब से, आपको तेज़ी से सुधार देखने को मिल सकते हैं.
ठीक है।
इसलिए, स्पीड की बात करें, तो आपको डिवाइस पर और क्लाउड पर, 5G नेटवर्क पर भी स्पीड में काफ़ी अंतर देखने को मिल सकता है.
आपने देखा कि क्लाउड पर डेटा अपलोड करने में सिर्फ़ एक सेकंड लगता है. यह 1,000 से 1,300 इनपुट टोकन के लिए है. इसलिए, भले ही क्लाउड पर प्रीफ़िल और डिकोड करने की प्रोसेस तेज़ हो, लेकिन डिवाइस पर मॉडल चलाने पर, पूरी प्रोसेस काफ़ी तेज़ होती है.
क्वालिटी की बात करें, तो हमें यह बताते हुए खुशी हो रही है कि यह 2B मॉडल, पिछले साल के 27B मॉडल से बेहतर परफ़ॉर्म कर रहा है.
हालांकि, यह आज के हमारे फ़्रंटियर मॉडल जितना बेहतर नहीं है, लेकिन हम यहां यह दिखा रहे हैं कि उपयोगकर्ता के डिवाइस पर मौजूद इन एआई मॉडल में काफ़ी सुधार हुआ है, है न?
MMLU-Pro, नॉलेज बेंचमार्क का एक स्टैंडर्ड है. इसमें 2B मॉडल की परफ़ॉर्मेंस, पिछले साल के 27B पैरामीटर मॉडल जितनी ही अच्छी है.
इसके अलावा, जब बात ग्रेजुएट लेवल की साइंस, रीज़निंग, और एजेंटिक टूल के इस्तेमाल से जुड़े बेंचमार्क की आती है, तो यह 2B मॉडल पिछले साल के मॉडल से बेहतर परफ़ॉर्म करता है. भले ही, इसका साइज़ पिछले साल के मॉडल के साइज़ के दसवें हिस्से से भी कम है.
हमें उम्मीद है कि ये ट्रेंड आगे भी जारी रहेंगे.
हम GDM टीम के साथ मिलकर काम कर रहे हैं, ताकि हम मॉडल में ये सुधार करते रहें और आपको इनका ज़्यादा से ज़्यादा फ़ायदा मिल सके.
डेविड चाउ
ठीक है।
मैंने आपको कई फ़ायदे और नए सुधार बताए हैं. शायद आप सोच रहे हों, "हम इन मॉडल का इस्तेमाल करके असल में क्या कर सकते हैं?"
इसलिए, हम एपीआई में भी ये सभी सुधार कर रहे हैं.
इसलिए, सिस्टम के निर्देशों की मदद से, पर्सोना सेट किया जा सकता है.
एक से ज़्यादा इमेज अपलोड करने की सुविधा की मदद से, एक बार में कई इमेज अपलोड की जा सकती हैं.
हमने इस साल की शुरुआत में थिंकिंग मोड भी चालू किया था, ताकि आप तर्क करने से जुड़ी समस्याओं के साथ-साथ टूल कॉलिंग में ज़्यादा समय बिता सकें.
आखिर में, स्ट्रक्चर्ड आउटपुट की मदद से, हमने यह भी मुमकिन बनाया है कि Kotlin क्लास को एनोटेट किया जा सके और आपके स्कीमा से मैच करने वाला ऑब्जेक्ट वापस पाया जा सके.
इसके अलावा, हम डेवलपर से यह भी पूछते हैं कि वे अपने ऐप्लिकेशन में क्या सुधार करना चाहते हैं.
हमने सर्वे के दौरान जिन डेवलपर से बात की उनमें से 91% ने कहा कि अगर इनमें से कोई भी सुविधा उपलब्ध होती, तो वे अपना अनुभव लॉन्च करते.
इसलिए, इस हफ़्ते की शुरुआत में हमने ये चार सुविधाएं लॉन्च कीं: बोली का अनुवाद, एंबेडिंग, लिखे गए शब्दों को सुनने की सुविधा, और फ़ंक्शन कॉलिंग.
बोली का अनुवाद करने की सुविधा की मदद से, 20 से ज़्यादा भाषाओं में ऑडियो को ट्रांसक्राइब किया जा सकता है.
एम्बेडिंग की मदद से, अपने ऐप्लिकेशन के कॉन्टेंट को सिर्फ़ कीवर्ड के बजाय, सिमैंटिक मीनिंग के हिसाब से खोजा जा सकता है.
लिखे गए शब्दों को सुनने की सुविधा की मदद से, अब अपने अनुभवों के लिए इंसानों जैसी आवाज़ें उपलब्ध कराई जा सकती हैं. इससे आपको रोबोट जैसी आवाज़ों का इस्तेमाल नहीं करना पड़ेगा.
फ़ंक्शन कॉलिंग की मदद से, अपने ऐप्लिकेशन को उपयोगकर्ताओं की ओर से कार्रवाइयां करने की अनुमति दी जा सकती है.
इसलिए, हम पहले से ही कुछ डेवलपर के साथ पार्टनरशिप कर रहे हैं, ताकि यह देखा जा सके कि वे अपने ऐप्लिकेशन में एजेंट कैसे बना सकते हैं. हमें उम्मीद है कि हम अगले साल के आखिर तक इसे लॉन्च कर देंगे.
डेविड चाउ
इन सभी सुविधाओं को ध्यान में रखते हुए, आइए एक उदाहरण के ज़रिए समझते हैं कि इन सभी सुविधाओं का इस्तेमाल करके, हम किस तरह से एक एंड-टू-एंड ऐप्लिकेशन बना सकते हैं.
मान लीजिए कि अमेरिका का कोई फ़ोटोग्राफ़र जापान की यात्रा कर रहा है.
जैसा कि फ़ोटो में दिख रहा है, उनके पास ज़रूरत के मुताबिक कैमरे या लेंस नहीं हैं.
इसलिए, वे एक नया लेंस ऑर्डर करते हैं और उसे अपने होटल में डिलीवर करवाते हैं.
लेकिन एक समस्या है: दिन भर के काम के दौरान, उन्हें एक वॉइसमेल मिलता है. इसमें उन्हें बताया जाता है कि दिन के आखिर तक अपना पैकेज पाने के लिए, उन्हें 6,800 येन का क्लीयरेंस शुल्क देना होगा.
उन्हें एक मैसेज भी मिला है, जिसमें रोके गए पार्सल की फ़ोटो है.
इसलिए, इस मामले में उपयोगकर्ता को धोखाधड़ी का पता लगाने की सुविधा चाहिए, है न?
वे यह जानना चाहते हैं कि असली और धोखाधड़ी वाले वॉइसमेल की पहचान कैसे की जा सकती है.
इसलिए, हम सारी जानकारी को निजी रखना चाहते हैं. हम यह पक्का करना चाहते हैं कि जानकारी सही हो और उसे आसानी से समझा जा सके.
इसलिए, हम यह पक्का करेंगे कि वे जापानी टेक्स्ट को समझ सकें. साथ ही, अगर उन्हें सुलभता से जुड़ी ज़रूरतें हैं, तो वे भाषा से जुड़ी उन सभी सुविधाओं का इस्तेमाल कर सकें जो उपलब्ध हैं.
तो यहां चार चरण होंगे, है न?
सबसे पहले, उस वॉइसमेल को टेक्स्ट में प्रोसेस किया जाता है. इसका मतलब है कि उस वॉइसमेल का अनुवाद किया जाएगा.
इसके लिए, हम Speech Translation API का इस्तेमाल करेंगे.
इसके बाद, अगर वे चाहें, तो सवाल पूछ सकते हैं. इसके लिए, हम बोली पहचानने वाले एपीआई का इस्तेमाल करेंगे.
इसके बाद, हम ऐसे फ़ंक्शन तय करना चाहते हैं जो उपयोगकर्ता की ज़रूरत के हिसाब से जानकारी हासिल कर सकें.
इसलिए, हम Prompt API और फ़ंक्शन कॉलिंग का इस्तेमाल करेंगे.
अब यह बैकग्राउंड में चलेगा और उपयोगकर्ता को कुछ भी नहीं दिखेगा.
तीसरा, हम एम्बेडिंग एपीआई और AppSearch का इस्तेमाल करके, उस जानकारी को सेव करने और वापस पाने का तरीका जानेंगे.
यह बैकग्राउंड में भी चलेगा.
इसके बाद, हम इकट्ठा की गई जानकारी के आधार पर फ़ैसला लेंगे. इसके लिए, हम Prompt API, स्ट्रक्चर्ड आउटपुट, और स्पीच सिंथेसिस का इस्तेमाल करेंगे.
ठीक है।
तो चलिए शुरू करते हैं.
हम बातचीत के अनुवाद और बोली पहचानने की सुविधा का इस्तेमाल करेंगे.
इसलिए, पहला कदम यह है कि वॉइसमेल और टेक्स्ट को काम की जानकारी में बदला जाए, है न?
इसलिए, उपयोगकर्ता को यह मैसेज मिला है, "आपको 6,800 येन का शुल्क देना होगा.
हो सकता है कि ड्यूटी का पेमेंट पहले से न किया गया हो. इसलिए, आपको इसका पेमेंट करना होगा."
इसलिए, वे चाहते हैं कि उन्हें अनुवाद की सुविधा मिले, है न?
इसलिए, हम यहां एक बात बताना चाहेंगे कि अनुवाद और जानकारी के बारे में उपयोगकर्ता का सवाल, दोनों ही एक ही ऑडियो एन्कोडर का इस्तेमाल करके Gemini Nano 4 पर जाते हैं. ऐसा इसलिए, क्योंकि ऑडियो एन्कोडर को 100 से ज़्यादा भाषाओं में ट्रेनिंग दी गई है.
इसलिए, आपको उस अनुवाद के साथ-साथ उपयोगकर्ता के सवाल के टेक्स्ट के लिए एएसआर भी मिल सकता है.
इसलिए, अनुवाद करने के लिए, हमें सिर्फ़ सोर्स और डेस्टिनेशन भाषा सेट अप करनी होगी.
val translator = SpeechTranslation.getClient(
SpeechTranslatorOptions.Builder(
Locale.JAPANESE, Locale.US
).build()
)इस मामले में, हम जैपनीज़ के साथ-साथ अमेरिकन इंग्लिश का इस्तेमाल करेंगे.
हम एक Kotlin फ़्लो सेट अप करेंगे, जो बाइट को सीधे स्पीच ट्रांसलेटर में पास करेगा.
val translator = SpeechTranslation.getClient(
SpeechTranslatorOptions.Builder(
Locale.JAPANESE, Locale.US
).build()
)
val audioFlow = flow {
emit(TranslationAudio(Locale.JAPANESE, courierVoicemailBytes))
}
val request = SpeechTranslatorRequest.Builder(audioFlow).build()
translator.startTranslation(request).collect { response ->
if (response is SpeechTranslatorResponse.Text) {
translatedCourierClaim = response.translatedText
}
}इसके बाद, अनुवाद किए गए टेक्स्ट का इस्तेमाल करके जवाब इकट्ठा किया जा सकता है.
ठीक है।
बहुत बढ़िया.
अब हमें समझ आ गया है कि वॉइसमेल में असल में क्या कहा जा रहा है, है न?
कैमरे के लेंस का एक पार्सल है.
इसे कस्टम विभाग ने रोक लिया है.
उन्हें 6,800 येन का शुल्क देना होगा.
अब उपयोगकर्ता को इस बारे में सवाल पूछना है.
ठीक है।
तो क्या कस्टम विभाग ने वाकई आपका पार्सल रोका है?
इस मामले में, हम एएसआर एपीआई का इस्तेमाल करेंगे.
val recognizer = SpeechRecognition.getClient(
speechRecognizerOptions {
locale = Locale.US
preferredMode = SpeechRecognizerOptions.Mode.MODE_ADVANCED
}
)हम अब भी अमेरिकन इंग्लिश को स्थानीय भाषा के तौर पर सेट कर रहे हैं.
इसके बाद, हम इसे ऐडवांस मोड पर सेट कर सकते हैं. यह मोड, ऑडियो एन्कोडर का इस्तेमाल करता है.
इसके बाद, हम उस जानकारी को भी पास कर सकते हैं, फ़ाइनल जवाब इकट्ठा कर सकते हैं, और फिर उसे ऐसी जानकारी में बदल सकते हैं जिसे हम Gemini Nano को पास करेंगे.
val recognizer = SpeechRecognition.getClient(
speechRecognizerOptions {
locale = Locale.US
preferredMode = SpeechRecognizerOptions.Mode.MODE_ADVANCED
}
)
val request = speechRecognizerRequest {
audioSource = AudioSource.fromMic()
recognitionOptions { enableWordTiming = true } // optional
}
recognizer.startRecognition(request).collect { response ->
when (response) {
is SpeechRecognizerResponse.PartialTextResponse ->
liveCaption = response.text
is SpeechRecognizerResponse.FinalTextResponse ->
planAndCallVaultTool(translatedCourierClaim, response.text)
}
}ठीक है।
अब हमने इंटेक का पहला हिस्सा पूरा कर लिया है.
जैसा कि आपने देखा, इसके लिए कोड की ज़्यादा लाइनों की ज़रूरत नहीं पड़ी.
डेविड चाउ
दूसरा हिस्सा है, ट्राइएज. इसमें यह तय किया जाता है कि Prompt API और फ़ंक्शन कॉलिंग का इस्तेमाल करके, किस तरह की जानकारी की ज़रूरत है.
ठीक है।
इसलिए, इसका आर्किटेक्चर बनाने के लिए, हम सबसे पहले कुछ सिस्टम निर्देश सेट अप करेंगे.
इसके बाद, हम वॉइसमेल, टेक्स्ट, और फ़ोटो के साथ-साथ उपयोगकर्ता के सवाल को भी Gemini Nano में इनपुट के तौर पर पास करेंगे.
इसके बाद, हम ऐसे फ़ंक्शन तय करेंगे जो हमारी ज़रूरत के मुताबिक जानकारी को फ़ेच करेंगे.
retrieveOrderAndDeliveryPhotos(
searchQuery = "camera lens customs",
claimedFeeUsd = 45,
comparePackagePhotos = true
)सबसे पहले, हम सिस्टम के निर्देशों को सेट अप करेंगे.
// Keep SystemInstruction static across Turn 1 & Turn 2:
val securitySystemInstruction = SystemInstruction(
"""
You are an on-device parcel & phishing security auditor.
1. Cross-check packaging, carrier labels and tracking across all images.
2. Verify customs fee claims against local DDP (Delivered Duty Paid) order records.
3. Flag any payment demand when the order record shows duties prepaid.
""".trimIndent()
)यहां आपको एक बहुत ही आसान उदाहरण दिखेगा कि सुरक्षा ऑडिटर सेट अप करने पर, यह कैसा दिखेगा. इसमें बताया गया है कि हम पैकेजिंग और लेबल की दोबारा जांच करेंगे, कस्टम शुल्क देखेंगे, और ज़रूरी पेमेंट की मांग को फ़्लैग करेंगे.
आपको यह पता चल सकता है कि प्रॉम्प्ट ऑप्टिमाइज़ेशन की सुविधा, आपको काफ़ी बेहतर क्वालिटी पाने में मदद करेगी.
सिस्टम के लिए निर्देशों का सेट मिलने के बाद, हम पिछले चरण से मिला सारा कॉन्टेंट ले सकते हैं. इसमें वॉइसमेल और टेक्स्ट का अनुवाद, उपयोगकर्ता का सवाल, और सिस्टम के लिए निर्देश शामिल हैं. इसके बाद, हम इस जानकारी को TextPart फ़ील्ड में पास कर सकते हैं.
// Pull in the Speech Translation (AST) & Speech Recognition (ASR) outputs:
val turn1Prompt = """
Translated voicemail: $translatedCourierClaim
Incoming text message: $callerText
User's spoken question: $userQuestion
""".trimIndent()
val turn1Request = generateContentRequest(
securitySystemInstruction,
TextPart(turn1Prompt)
) { /* enableThinking & setTools */ }इसके बाद, हम एक ऐसा फ़ंक्शन तय करना चाहते हैं जिसका इस्तेमाल किया जा सके.
इसलिए, इस उदाहरण के लिए, हमें यह बताते हुए खुशी हो रही है कि हमने ऐप्लिकेशन के फ़ंक्शन को इंटिग्रेट कर लिया है.
इसलिए, अगर आपमें से किसी को इसके बारे में जानकारी है, तो शायद आपको पहले से ही पता होगा कि यह कैसे काम करता है.
इसलिए, यहां हम एक AppFunctionService एंट्री पॉइंट सेट अप करते हैं. इसके बाद, हम ऐप्लिकेशन फ़ंक्शन तय करते हैं.
// Registered in AndroidManifest.xml
@AppFunctionServiceEntryPoint("ScamInvestigationService", "scam_tools")
abstract class ScamInvestigationTools : AppFunctionService() {
}आपको दिखेगा कि यह बिलकुल वैसा ही है जैसा किसी सिस्टम एजेंट के लिए ऐप्लिकेशन के फ़ंक्शन सेट अप करते समय होता है.
आपने searchQuery के पैरामीटर में, दावे का शुल्क सेट किया है. साथ ही, यह भी सेट किया है कि हमें पैकेज की फ़ोटो की तुलना करनी चाहिए या नहीं. ये कुछ उदाहरण पैरामीटर हैं, है न?
// Registered in AndroidManifest.xml
@AppFunctionServiceEntryPoint("ScamInvestigationService", "scam_tools")
abstract class ScamInvestigationTools : AppFunctionService() {
/**
* Searches local orders and order images in AppSearch.
* @param searchQuery Semantic query to match the order
* @param claimedFeeUsd Customs fee claimed in USD
* @param comparePackagePhotos Whether to fetch order images to compare
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun retrieveOrderAndDeliveryPhotos(
searchQuery: String,
claimedFeeUsd: Int,
comparePackagePhotos: Boolean
): VaultMatch = withContext(Dispatchers.IO) { executeVaultSearch(searchQuery) }
}आपके पास अलग-अलग तरह के लक्ष्य तय करने का विकल्प होता है.
इसके बाद, हमारे पास उस फ़ंक्शन के लिए एक executeVaultSearch फ़ंक्शन है, जिसे हम बाद में तय करेंगे.
मैं आपको बताना चाहूँगा कि हमारे पास थिंकिंग मोड उपलब्ध है.
val vaultTools = AppFunctionTools.getClient().getAppFunctions()
val turn1Request = generateContentRequest(securitySystemInstruction, TextPart(turn1Prompt)) {
enableThinking = true
setTools(vaultTools)
}
val call = generativeModel.generateContent(turn1Request)
.candidates.first().functionCalls.first()हमारा सुझाव है कि टूल कॉलिंग की सुविधा का इस्तेमाल करते समय, इस मॉडल का इस्तेमाल करें.
इसकी वजह यह है कि हमने कई टेस्ट किए हैं. इनसे पता चलता है कि Gemini Nano 4 पर टूल कॉलिंग की सुविधा इस्तेमाल करने पर, 100 में से 13 पॉइंट की बढ़ोतरी हुई है.
कुछ मामलों में, यह संख्या काफ़ी ज़्यादा होती है. इसलिए, इससे आपको बेहतर नतीजे मिल सकते हैं.
अगर आपको फ़ंक्शन कॉलिंग के बारे में ज़्यादा जानना है, तो कृपया मेरी कलीग जोलांडा और विक्टोरिया के सेशन में शामिल हों. यह सेशन बाद में होगा.
यह आपके ऐप्लिकेशन में एजेंटिक एआई की सुविधाओं के बारे में है.
मुझे लगता है कि विक्टोरिया भी भीड़ में कहीं बैठी होंगी. इसलिए, उन्हें भी नमस्ते कहें.
डेविड चाउ
ठीक है.
अब हमने पहले दो चरण पूरे कर लिए हैं.
आइए, अब बात करते हैं कि जानकारी को वापस पाने की प्रोसेस कैसे काम करती है.
इसलिए, हम एंबेडिंग और AppSearch का इस्तेमाल करेंगे.
इसलिए, उपयोगकर्ता की यात्रा में कुछ कदम पीछे जाकर, उस पॉइंट पर जहां उन्होंने लेंस का ऑर्डर दिया था, हम उस जानकारी को इंडेक्स करना चाहेंगे, ताकि वह उपयोगकर्ता के साथ-साथ डेवलपर के लिए भी ऐक्सेस की जा सके, है न?
इसलिए, इसमें हमारे पास ऑर्डर पैकेज नंबर की जानकारी होती है.
इसमें एक ट्रैकिंग नंबर भी होता है.
इसमें इस बारे में कुछ जानकारी होती है कि इसे कहां भेजा गया है.
यहां देखा जा सकता है कि इसमें पहले ही बताया गया है कि शुल्क का पेमेंट पहले ही कर दिया गया है.
इसलिए, हम मल्टीमॉडल एंबेडिंग जनरेट करेंगे, ताकि उस जानकारी को इंडेक्स किया जा सके. साथ ही, ऐसी अन्य जानकारी को भी इंडेक्स किया जा सके जो हमारे लिए अहम हो सकती है.
यह कुछ ऐसा दिखता है.
val embedModel = EmbeddingGeneration.getEmbeddingModels().first()
val docVector = embedModel.generateEmbedding(
EmbeddingRequest(
Content(listOf(
ImagePart(orderEmailLensBmp), // the lens photo in the email
TextPart(orderEmailBody) // the order details
))
)
).embedding.vectorहम ऑर्डर की जानकारी वाला ईमेल लेते हैं.
यह भी बहुत आसान है. हम सिर्फ़ एक इमेज और एक टेक्स्ट पास करते हैं. हालांकि, ऐसा हो सकता है कि आपके पास ज़्यादा कॉन्टेंट हो, जिसे आपको यहां इंडेक्स करना हो.
इसके बाद, हम एंबेडिंग वेक्टर जनरेट करेंगे.
ऐसा करने के बाद, हम AppSearch का इस्तेमाल करके एक दस्तावेज़ बनाएंगे. इसके बाद, हम वे सभी प्रॉपर्टी सेट करेंगे जो हमें उस दस्तावेज़ को जनरेट करने के लिए ज़रूरी लगती हैं.
val embedModel = EmbeddingGeneration.getEmbeddingModels().first()
val docVector = embedModel.generateEmbedding(
EmbeddingRequest(
Content(listOf(
ImagePart(orderEmailLensBmp), // the lens photo in the email
TextPart(orderEmailBody) // the order details
))
)
).embedding.vector
val doc = GenericDocument.Builder<GenericDocument.Builder<*>>(
"orders", "order_jp_904", "OrderRecord"
)
.setPropertyString("summary", "Shipped DDP, duties prepaid ($0 owed). In transit.")
.setPropertyString("photoUris", "order_email_lens.png")
.setPropertyEmbedding(
"embedding",
EmbeddingVector(docVector, embedModel.getModelSignature())
).build()एक और बात, हम मल्टीमॉडल एंबेडिंग इसलिए करना चाहते हैं, क्योंकि हो सकता है कि यहां कोई भी शब्द शेयर न किया गया हो. हालांकि, इमेज और टेक्स्ट का कॉन्टेंट एक जैसा होने की वजह से, हमें सिमैंटिक समानता का पता लगाने में मदद मिलती है.
इसलिए, हम कोसाइन सिमिलैरिटी का इस्तेमाल करके ऐसे आइटम ढूंढ सकते हैं जिनमें कोई भी शब्द कॉमन न हो.
ठीक है, तो यह executeVaultSearch फ़ंक्शन है, जिसके बारे में हमने पहले बात की थी.
suspend fun executeVaultSearch(searchQuery: String): VaultMatch {
val qVec = embedModel.generateEmbedding(
EmbeddingRequest(
content = Content(listOf(TextPart(searchQuery)))
)
).embedding.vector
}यह वही है जिसे हमने ऐप्लिकेशन फ़ंक्शन के तौर पर तय किया था.
इसलिए, इस उदाहरण में हम Gemini Nano से जनरेट की गई सर्च क्वेरी को पास कर रहे हैं.
इसके बाद, हम रैंकिंग की रणनीति, कोसाइन सिमिलैरिटी का इस्तेमाल करेंगे.
suspend fun executeVaultSearch(searchQuery: String): VaultMatch {
val qVec = embedModel.generateEmbedding(
EmbeddingRequest(
content = Content(listOf(TextPart(searchQuery)))
)
).embedding.vector
val spec = SearchSpec.Builder()
.addEmbeddingParameters(EmbeddingVector(qVec, embedModel.getModelSignature()))
.setDefaultEmbeddingSearchMetricType(SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
.setRankingStrategy("sum(this.matchedSemanticScores(getEmbeddingParameter(0)))")
.setListFilterQueryLanguageEnabled(true).build()
}इसके बाद, this.matchedSemanticScores हमें वह जानकारी पाने में मदद करता है.
इसके बाद, हम 0.68 की थ्रेशोल्ड वैल्यू तय करेंगे, ताकि यह पता चल सके कि जानकारी फ़ेच करने के लिए, कोसाइन सिमिलैरिटी की कम से कम वैल्यू क्या है.
suspend fun executeVaultSearch(searchQuery: String): VaultMatch {
val qVec = embedModel.generateEmbedding(
EmbeddingRequest(
content = Content(listOf(TextPart(searchQuery)))
)
).embedding.vector
val spec = SearchSpec.Builder()
.addEmbeddingParameters(EmbeddingVector(qVec, embedModel.getModelSignature()))
.setDefaultEmbeddingSearchMetricType(SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
.setRankingStrategy("sum(this.matchedSemanticScores(getEmbeddingParameter(0)))")
.setListFilterQueryLanguageEnabled(true).build()
val results = appSearchSession.search(
"semanticSearch(getEmbeddingParameter(0), 0.68)", spec
)
// Cleanly close the cursor.
return results.use { it.nextPageAsync.await().first().genericDocument.toVaultMatch() }
}इसलिए, हो सकता है कि आप अपने इस्तेमाल के उदाहरणों के साथ एक्सपेरिमेंट करना चाहें, ताकि यह देखा जा सके कि आपके लिए असल में क्या काम करता है.
एक और बात, इस मॉडल की सुरक्षा के बारे में बात करें, तो Gemini Nano कोई कोड नहीं चलाता, है न?
इसलिए, Gemini Nano सिर्फ़ एक सवाल पूछ रहा है. हालांकि, आपका ऐप्लिकेशन कोड तय करता है और फिर वह कोड को खुद चलाता है. इसके बाद, वह जानकारी को वापस लाता है और फिर यह तय करता है कि Gemini Nano को क्या पास करना है.
इससे आपको इस बात पर ज़्यादा कंट्रोल मिलता है कि Gemini Nano को कौनसा डेटा ऐक्सेस करने की अनुमति देनी है. साथ ही, इससे आपके उपयोगकर्ताओं को ज़्यादा निजता और सुरक्षा मिलती है.
डेविड चाउ
ठीक है, अब यह आखिरी चरण है.
इसलिए, हमारे पास ज़रूरत के मुताबिक जानकारी है.
हम तुलना करने के लिए, Prompt API, स्ट्रक्चर्ड आउटपुट, और स्पीच सिंथेसिस का इस्तेमाल करेंगे. इसके बाद, उपयोगकर्ता को वह जानकारी आसान तरीके से उपलब्ध कराएंगे.
ठीक है, तो बुनियादी तौर पर, हमें यह जानना है कि क्या यह पार्सल असल में उपयोगकर्ता की जानकारी से मेल खाता है, जो कि असली ऑर्डर है.
ऐसा करने के लिए, हम वॉइसमेल और टेक्स्ट से मिली जानकारी के आधार पर एंबेडिंग भी जनरेट करेंगे. साथ ही, उसे उसी एंबेडिंग वेक्टर में बदलेंगे जो हमने पहले किया था.
val callerVector = embedModel.generateEmbedding(
EmbeddingRequest(
Content(listOf(
ImagePart(callerParcelPhotoBmp), // brown box w/ shipping label
TextPart(callerText) // "Clearance fee ¥6,800 due today."
))
)
).embedding.vector
val matchScore = cosineSimilarity(callerVector, vaultMatch.embedding)इसके बाद, हम matchScore का इस्तेमाल करेंगे. कोसाइन सिमिलैरिटी का इस्तेमाल करके, हम वॉल्ट से मिले मैच और कॉलर से मिली जानकारी के बीच तुलना कर सकते हैं.
इसलिए, यहां देखा जा सकता है कि भले ही इमेज मैच न हों, लेकिन टेक्स्ट अब भी काफ़ी मददगार है.
इसलिए, मल्टीमोडल एंबेडिंग काफ़ी काम की होती हैं.
इसमें अलग-अलग तरह की जानकारी डाली जा सकती है और फिर उससे एक ही तरह का मतलब निकाला जा सकता है, है न?
इसलिए, मल्टीमोडल एंबेडिंग की मदद से, इन सभी को एक ही वेक्टर स्पेस पर मैप किया जा सकता है.
हम यहां फिर से थिंकिंग मोड का इस्तेमाल करेंगे, ताकि हम बेहतर तरीके से सोच सकें.
val forensicRequest = generateContentRequest(
securitySystemInstruction,
content {
image(callerParcelPhotoBmp)
image(orderEmailLensBmp)
text(forensicAuditPrompt)
}
) {
// Enable Gemini Nano 4 reasoning before it answers
enableThinking = true
}इसके बाद, हम आउटपुट क्लास भी तय करेंगे, है ना?
@Generable(description = "Details of a visual contradiction found during an audit")
data class VisualContradiction(
@Guide(
description = "The type of contradiction",
enumValues = ["PACKAGING_MISMATCH", "STATUS_CONFLICT", "DUTY_FRAUD"],
)
val contradictionType: String,
@Guide(description = "Detailed explanation of the visual contradiction")
val explanation: String,
)इसलिए, हम यहां सिर्फ़ एक VisualContradiction सेट अप कर रहे हैं. साथ ही, अलग-अलग तरह के संभावित विरोधाभासों के लिए कुछ अलग-अलग enum सेट अप कर रहे हैं, है न?
चाहे वह DUTY_FRAUD हो, PACKAGING_MISMATCH हो या STATUS_CONFLICT हो.
इसके बाद, हम यहां इसके बारे में जानकारी देंगे.
इसके बाद, हम उस पूरी जानकारी को ऑडिट के फ़ैसले में पास करेंगे, है न?
@Generable(description = "Verdict and details of a phishing audit")
data class PhishingAuditVerdict(
@Guide(
description = "The audit verdict",
enumValues = ["CONFIRMED_PHISHING_SCAM", "LEGITIMATE_HOLD"],
)
val verdict: String,
@Guide(description = "The fraud risk score from 0 to 100", minimum = 0.0, maximum = 100.0)
val fraudRiskScore: Int,
@Guide(description = "Estimated amount of money saved in USD", minimum = 0.0)
val moneySavedUsd: Int,
@Guide(description = "List of visual contradictions identified during audit", minItems = 0)
val contradictions: List<VisualContradiction>,
// @Guide(description = "Summary of the warning to be spoken to the user")
// `@Guide` annotations are optional.
val spokenWarningSummary: String,
)यहां देखा जा सकता है कि यह बहुत आसान है.
हमारा सवाल यह है कि क्या यह कोई धोखाधड़ी है?
हमारे पास एक fraudRiskScore हो सकता है, जहां 0 से 100 तक की कम से कम और ज़्यादा से ज़्यादा वैल्यू सेट की जा सकती है.
अगर आपको समय के साथ इस बात का ट्रैक रखना है कि आपने उपयोगकर्ता के कितने पैसे बचाए हैं, तो उसे यह जानकारी भी दी जा सकती है.
इसके बाद, हमने जिन विज़ुअल विरोधाभासों के बारे में बताया है उन्हें भी लिस्ट किया जा सकता है.
ध्यान देने वाली एक और बात यह है कि @Guide एनोटेशन का इस्तेमाल करना ज़रूरी नहीं है.
आपको इसे सेट करने और इस तरह की जानकारी देने की ज़रूरत नहीं है.
यह इसलिए मददगार है, क्योंकि हो सकता है कि आप सिर्फ़ वैरिएबल की परिभाषा से यह न बता पाएं कि क्या हो रहा है.
इसलिए, ब्यौरा होने से Gemini Nano को यह समझने में मदद मिल सकती है कि कौनसे फ़ंक्शन को कॉल करना है और उसे कैसे सेट अप करना है, ताकि वह सही फ़ील्ड में सही जानकारी डाल सके.
इसे सेट अप करने के बाद, इस्तेमाल करना बहुत आसान है.
आपको यहां कोड की सिर्फ़ तीन लाइनें चलानी होंगी.
val typedRequest = generateTypedContentRequest(forensicRequest, PhishingAuditVerdict::class)
val response = generativeModel.generateContent(typedRequest)
val verdict: PhishingAuditVerdict? = response.candidates.first().responsegenerateTypedContentRequest, आपको वह कॉन्टेंट जनरेट करना होगा. इसके बाद, आपको वह जवाब मिल सकता है.
इसके बाद, यहां कुछ ऐसा है जिससे पता चलता है कि यह कैसा दिखेगा, है न?
{
"verdict": "CONFIRMED_PHISHING_SCAM",
"fraudRiskScore": 98,
"moneySavedUsd": 45,
"contradictions": [
{ "contradictionType": "DUTY_FRAUD", "explanation": "Fee demanded, but duties were prepaid." },
{ "contradictionType": "PACKAGING_MISMATCH", "explanation": "Carton has no carrier label or tracking." },
],
"spokenWarningSummary": "Scam blocked. Do not pay the 45 dollar fee. Your lens shipped duties prepaid."
}इससे हमें पता चलता है कि यह एक धोखाधड़ी है. हमें धोखाधड़ी का जोखिम स्कोर बहुत ज़्यादा मिलता है. इससे, कॉलर की जानकारी की तुलना में, आपके वापस पाए गए डेटा में साफ़ तौर पर दिखने वाले विरोधाभासों के बारे में पता चलता है.
हमने इस उपयोगकर्ता के लिए कुछ पैसे भी बचाए हैं, ताकि हम वहां ट्रैकर रख सकें.
हाँ।
इस तरह, हमें वह सारी जानकारी मिल जाती है जिसकी हमें ज़रूरत होती है.
इसके बाद, आखिरी चरण में इस जानकारी को उपयोगकर्ता को इस तरह से दिखाना होगा कि वह इसे आसानी से ऐक्सेस कर सके.
उदाहरण के लिए, अगर वे कहीं जा रहे हैं या उन्हें देखने में परेशानी हो रही है, तो हो सकता है कि आप उन्हें टेक्स्ट पढ़ने के लिए कहने के बजाय, उस जानकारी को ज़ोर से पढ़कर सुनाना चाहें.
इसलिए, हम लिखे गए शब्दों को सुनने की सुविधा लागू करने जा रहे हैं, ताकि यह दिखाया जा सके कि यह सुविधा कैसी दिखेगी.
val synthesizer = SpeechSynthesis.getClient(SpeechSynthesizerOptions.builder(Locale.US).build())
try {
val voice = synthesizer.getSupportedVoices(Locale.US).firstOrNull()
if (voice != null) {
val ttsRequest = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text(verdict.spokenWarningSummary, voice),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
/* more code here */
}
} finally {
synthesizer.close() // safe here: the flow completes after DONE, STOP, or Error
}इसलिए, यह बहुत आसान है. हम स्थान-भाषा और भाषा भी तय करते हैं. इसके बाद, हम उस फ़ैसले को पास करते हैं जिसे हमने अभी-अभी @Generable और @Guide का इस्तेमाल करके जनरेट किया था.
इसके बाद, हमें तुरंत ही जवाब मिल जाता है.
val synthesizer = SpeechSynthesis.getClient(SpeechSynthesizerOptions.builder(Locale.US).build())
try {
val voice = synthesizer.getSupportedVoices(Locale.US).firstOrNull()
if (voice != null) {
val ttsRequest = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text(verdict.spokenWarningSummary, voice),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
synthesizer.synthesize(ttsRequest).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate ->
/* playback finished */
if (response.status == SpeechSynthesizerResponse.Status.DONE) { }
/* response.errorCode is a TextToSpeech.ERROR_* code */
is SpeechSynthesizerResponse.Error -> { }
else -> {}
}
}
}
} finally {
synthesizer.close() // safe here: the flow completes after DONE, STOP, or Error
}ज़्यादातर डिवाइसों पर, जवाब जनरेट करने में 120 मिलीसेकंड से भी कम समय लगेगा.
हम ज़रूरी मेमोरी को खाली कर देंगे.
इसके बाद, अगर ज़रूरत हो, तो हम प्लैटफ़ॉर्म एपीआई का इस्तेमाल करके फ़ॉलबैक भी सेट कर सकते हैं.
val synthesizer = SpeechSynthesis.getClient(SpeechSynthesizerOptions.builder(Locale.US).build())
try {
val voice = synthesizer.getSupportedVoices(Locale.US).firstOrNull()
if (voice != null) {
val ttsRequest = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text(verdict.spokenWarningSummary, voice),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
synthesizer.synthesize(ttsRequest).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate ->
/* playback finished */
if (response.status == SpeechSynthesizerResponse.Status.DONE) { }
/* response.errorCode is a TextToSpeech.ERROR_* code */
is SpeechSynthesizerResponse.Error -> {
/* handle fallback to platform API */
}
else -> {}
}
}
}
// more code here ...इसमें अब भी रोबोटिक आवाज़ होगी. हालांकि, इससे उपयोगकर्ता को इंटरैक्ट करने के लिए कुछ मिल जाता है, भले ही वह पढ़ न पाए.
डेविड चाउ
ठीक है।
हमने बहुत कुछ किया. हमने इनटेक, ट्राइएज, डेटा वापस पाने, और ऑडिट की प्रोसेस में इन सभी अलग-अलग एपीआई का इस्तेमाल किया.
जैसे, बोली पहचानने और अनुवाद करने वाले एपीआई, Prompt API, फ़ंक्शन कॉलिंग, एंबेडिंग, और AppSearch. साथ ही, स्ट्रक्चर्ड आउटपुट और बोली को लिखाई में बदलने की सुविधा.
यह सिर्फ़ एक उदाहरण है कि इन सभी सुविधाओं का इस्तेमाल, एक साथ और पूरी तरह से डिवाइस पर कैसे किया जा सकता है.
इसके बाद, हमने शुरुआत में जो बात की थी उस पर वापस आते हैं. इसे चलाने के काफ़ी फ़ायदे हैं, है न?
अगर आज इसे अपने डिवाइस पर चलाकर देखा जाए, तो आपको पता चलेगा कि यह ज़्यादातर हार्डवेयर पर काफ़ी तेज़ी से काम करता है.
इसके लिए आपको कोई शुल्क नहीं देना पड़ता. साथ ही, अगर इसे हमेशा चालू रखा जाए, तो लाखों डॉलर बचाए जा सकते हैं.
यह सुविधा तब भी उपलब्ध होती है, जब उपयोगकर्ता पहाड़ों पर हो या किसी बेसमेंट में हो और उसके पास इंटरनेट का ऐक्सेस न हो.
साथ ही, यह पूरी तरह से निजी है, क्योंकि आपको क्लाउड पर कोई जानकारी भेजने की ज़रूरत नहीं होती.
इन सभी सुविधाओं के बारे में जानने के लिए, कृपया दस्तावेज़ देखें, क्यूआर कोड देखें या लिंक पर जाएं.
ये सभी सुविधाएं, हमने पिछले छह महीनों में लॉन्च की हैं.
इस बातचीत में शामिल होने के लिए, आप सभी का बहुत-बहुत धन्यवाद.
हमारे साथ समय बिताने के लिए धन्यवाद.
इसलिए, आपका बहुत-बहुत धन्यवाद.
val translator = SpeechTranslation.getClient(
SpeechTranslatorOptions.Builder(
Locale.JAPANESE, Locale.US
).build()
)val translator = SpeechTranslation.getClient(
SpeechTranslatorOptions.Builder(
Locale.JAPANESE, Locale.US
).build()
)
val audioFlow = flow {
emit(TranslationAudio(Locale.JAPANESE, courierVoicemailBytes))
}
val request = SpeechTranslatorRequest.Builder(audioFlow).build()
translator.startTranslation(request).collect { response ->
if (response is SpeechTranslatorResponse.Text) {
translatedCourierClaim = response.translatedText
}
}val recognizer = SpeechRecognition.getClient(
speechRecognizerOptions {
locale = Locale.US
preferredMode = SpeechRecognizerOptions.Mode.MODE_ADVANCED
}
)val recognizer = SpeechRecognition.getClient(
speechRecognizerOptions {
locale = Locale.US
preferredMode = SpeechRecognizerOptions.Mode.MODE_ADVANCED
}
)
val request = speechRecognizerRequest {
audioSource = AudioSource.fromMic()
recognitionOptions { enableWordTiming = true } // optional
}
recognizer.startRecognition(request).collect { response ->
when (response) {
is SpeechRecognizerResponse.PartialTextResponse ->
liveCaption = response.text
is SpeechRecognizerResponse.FinalTextResponse ->
planAndCallVaultTool(translatedCourierClaim, response.text)
}
}retrieveOrderAndDeliveryPhotos(
searchQuery = "camera lens customs",
claimedFeeUsd = 45,
comparePackagePhotos = true
)// Keep SystemInstruction static across Turn 1 & Turn 2:
val securitySystemInstruction = SystemInstruction(
"""
You are an on-device parcel & phishing security auditor.
1. Cross-check packaging, carrier labels and tracking across all images.
2. Verify customs fee claims against local DDP (Delivered Duty Paid) order records.
3. Flag any payment demand when the order record shows duties prepaid.
""".trimIndent()
)// Pull in the Speech Translation (AST) & Speech Recognition (ASR) outputs:
val turn1Prompt = """
Translated voicemail: $translatedCourierClaim
Incoming text message: $callerText
User's spoken question: $userQuestion
""".trimIndent()
val turn1Request = generateContentRequest(
securitySystemInstruction,
TextPart(turn1Prompt)
) { /* enableThinking & setTools */ }// Registered in AndroidManifest.xml
@AppFunctionServiceEntryPoint("ScamInvestigationService", "scam_tools")
abstract class ScamInvestigationTools : AppFunctionService() {
}// Registered in AndroidManifest.xml
@AppFunctionServiceEntryPoint("ScamInvestigationService", "scam_tools")
abstract class ScamInvestigationTools : AppFunctionService() {
/**
* Searches local orders and order images in AppSearch.
* @param searchQuery Semantic query to match the order
* @param claimedFeeUsd Customs fee claimed in USD
* @param comparePackagePhotos Whether to fetch order images to compare
*/
@AppFunctionDeclaration(isDescribedByKDoc = true)
suspend fun retrieveOrderAndDeliveryPhotos(
searchQuery: String,
claimedFeeUsd: Int,
comparePackagePhotos: Boolean
): VaultMatch = withContext(Dispatchers.IO) { executeVaultSearch(searchQuery) }
}val vaultTools = AppFunctionTools.getClient().getAppFunctions()
val turn1Request = generateContentRequest(securitySystemInstruction, TextPart(turn1Prompt)) {
enableThinking = true
setTools(vaultTools)
}
val call = generativeModel.generateContent(turn1Request)
.candidates.first().functionCalls.first()val embedModel = EmbeddingGeneration.getEmbeddingModels().first()
val docVector = embedModel.generateEmbedding(
EmbeddingRequest(
Content(listOf(
ImagePart(orderEmailLensBmp), // the lens photo in the email
TextPart(orderEmailBody) // the order details
))
)
).embedding.vectorval embedModel = EmbeddingGeneration.getEmbeddingModels().first()
val docVector = embedModel.generateEmbedding(
EmbeddingRequest(
Content(listOf(
ImagePart(orderEmailLensBmp), // the lens photo in the email
TextPart(orderEmailBody) // the order details
))
)
).embedding.vector
val doc = GenericDocument.Builder<GenericDocument.Builder<*>>(
"orders", "order_jp_904", "OrderRecord"
)
.setPropertyString("summary", "Shipped DDP, duties prepaid ($0 owed). In transit.")
.setPropertyString("photoUris", "order_email_lens.png")
.setPropertyEmbedding(
"embedding",
EmbeddingVector(docVector, embedModel.getModelSignature())
).build()suspend fun executeVaultSearch(searchQuery: String): VaultMatch {
val qVec = embedModel.generateEmbedding(
EmbeddingRequest(
content = Content(listOf(TextPart(searchQuery)))
)
).embedding.vector
}suspend fun executeVaultSearch(searchQuery: String): VaultMatch {
val qVec = embedModel.generateEmbedding(
EmbeddingRequest(
content = Content(listOf(TextPart(searchQuery)))
)
).embedding.vector
val spec = SearchSpec.Builder()
.addEmbeddingParameters(EmbeddingVector(qVec, embedModel.getModelSignature()))
.setDefaultEmbeddingSearchMetricType(SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
.setRankingStrategy("sum(this.matchedSemanticScores(getEmbeddingParameter(0)))")
.setListFilterQueryLanguageEnabled(true).build()
}suspend fun executeVaultSearch(searchQuery: String): VaultMatch {
val qVec = embedModel.generateEmbedding(
EmbeddingRequest(
content = Content(listOf(TextPart(searchQuery)))
)
).embedding.vector
val spec = SearchSpec.Builder()
.addEmbeddingParameters(EmbeddingVector(qVec, embedModel.getModelSignature()))
.setDefaultEmbeddingSearchMetricType(SearchSpec.EMBEDDING_SEARCH_METRIC_TYPE_COSINE)
.setRankingStrategy("sum(this.matchedSemanticScores(getEmbeddingParameter(0)))")
.setListFilterQueryLanguageEnabled(true).build()
val results = appSearchSession.search(
"semanticSearch(getEmbeddingParameter(0), 0.68)", spec
)
// Cleanly close the cursor.
return results.use { it.nextPageAsync.await().first().genericDocument.toVaultMatch() }
}val callerVector = embedModel.generateEmbedding(
EmbeddingRequest(
Content(listOf(
ImagePart(callerParcelPhotoBmp), // brown box w/ shipping label
TextPart(callerText) // "Clearance fee ¥6,800 due today."
))
)
).embedding.vector
val matchScore = cosineSimilarity(callerVector, vaultMatch.embedding)val forensicRequest = generateContentRequest(
securitySystemInstruction,
content {
image(callerParcelPhotoBmp)
image(orderEmailLensBmp)
text(forensicAuditPrompt)
}
) {
// Enable Gemini Nano 4 reasoning before it answers
enableThinking = true
}@Generable(description = "Details of a visual contradiction found during an audit")
data class VisualContradiction(
@Guide(
description = "The type of contradiction",
enumValues = ["PACKAGING_MISMATCH", "STATUS_CONFLICT", "DUTY_FRAUD"],
)
val contradictionType: String,
@Guide(description = "Detailed explanation of the visual contradiction")
val explanation: String,
)@Generable(description = "Verdict and details of a phishing audit")
data class PhishingAuditVerdict(
@Guide(
description = "The audit verdict",
enumValues = ["CONFIRMED_PHISHING_SCAM", "LEGITIMATE_HOLD"],
)
val verdict: String,
@Guide(description = "The fraud risk score from 0 to 100", minimum = 0.0, maximum = 100.0)
val fraudRiskScore: Int,
@Guide(description = "Estimated amount of money saved in USD", minimum = 0.0)
val moneySavedUsd: Int,
@Guide(description = "List of visual contradictions identified during audit", minItems = 0)
val contradictions: List<VisualContradiction>,
// @Guide(description = "Summary of the warning to be spoken to the user")
// `@Guide` annotations are optional.
val spokenWarningSummary: String,
)val typedRequest = generateTypedContentRequest(forensicRequest, PhishingAuditVerdict::class)
val response = generativeModel.generateContent(typedRequest)
val verdict: PhishingAuditVerdict? = response.candidates.first().response{
"verdict": "CONFIRMED_PHISHING_SCAM",
"fraudRiskScore": 98,
"moneySavedUsd": 45,
"contradictions": [
{ "contradictionType": "DUTY_FRAUD", "explanation": "Fee demanded, but duties were prepaid." },
{ "contradictionType": "PACKAGING_MISMATCH", "explanation": "Carton has no carrier label or tracking." },
],
"spokenWarningSummary": "Scam blocked. Do not pay the 45 dollar fee. Your lens shipped duties prepaid."
}val synthesizer = SpeechSynthesis.getClient(SpeechSynthesizerOptions.builder(Locale.US).build())
try {
val voice = synthesizer.getSupportedVoices(Locale.US).firstOrNull()
if (voice != null) {
val ttsRequest = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text(verdict.spokenWarningSummary, voice),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
/* more code here */
}
} finally {
synthesizer.close() // safe here: the flow completes after DONE, STOP, or Error
}val synthesizer = SpeechSynthesis.getClient(SpeechSynthesizerOptions.builder(Locale.US).build())
try {
val voice = synthesizer.getSupportedVoices(Locale.US).firstOrNull()
if (voice != null) {
val ttsRequest = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text(verdict.spokenWarningSummary, voice),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
synthesizer.synthesize(ttsRequest).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate ->
/* playback finished */
if (response.status == SpeechSynthesizerResponse.Status.DONE) { }
/* response.errorCode is a TextToSpeech.ERROR_* code */
is SpeechSynthesizerResponse.Error -> { }
else -> {}
}
}
}
} finally {
synthesizer.close() // safe here: the flow completes after DONE, STOP, or Error
}val synthesizer = SpeechSynthesis.getClient(SpeechSynthesizerOptions.builder(Locale.US).build())
try {
val voice = synthesizer.getSupportedVoices(Locale.US).firstOrNull()
if (voice != null) {
val ttsRequest = SpeechSynthesizerRequest(
input = SpeechSynthesizerRequest.Input.Text(verdict.spokenWarningSummary, voice),
audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.build(),
)
synthesizer.synthesize(ttsRequest).collect { response ->
when (response) {
is SpeechSynthesizerResponse.StatusUpdate ->
/* playback finished */
if (response.status == SpeechSynthesizerResponse.Status.DONE) { }
/* response.errorCode is a TextToSpeech.ERROR_* code */
is SpeechSynthesizerResponse.Error -> {
/* handle fallback to platform API */
}
else -> {}
}
}
}
// more code here ...