Vertex AI और LangChain4j की मदद से, Java में Gemini की सुविधा

1. परिचय

यह कोडलैब, Gemini के लार्ज लैंग्वेज मॉडल (एलएलएम) पर फ़ोकस करता है. इसे Google Cloud पर Vertex AI पर होस्ट किया जाता है. Vertex AI एक ऐसा प्लैटफ़ॉर्म है जिसमें Google Cloud पर मशीन लर्निंग के सभी प्रॉडक्ट, सेवाएं, और मॉडल शामिल होते हैं.

LangChain4j फ़्रेमवर्क की मदद से, Gemini API के साथ इंटरैक्ट करने के लिए Java का इस्तेमाल करना होगा. आपको एलएलएम का इस्तेमाल करके, सवालों के जवाब देने, आइडिया जनरेट करने, इकाई और स्ट्रक्चर्ड कॉन्टेंट निकालने, वापस पाने के लिए ऑगमेंटेड जनरेशन, और फ़ंक्शन कॉलिंग जैसी सुविधाओं के बारे में बेहतर जानकारी मिलेगी.

जनरेटिव एआई क्या है?

जनरेटिव एआई का मतलब है, आर्टिफ़िशियल इंटेलिजेंस का इस्तेमाल करके नया कॉन्टेंट बनाना. जैसे, टेक्स्ट, इमेज, संगीत, ऑडियो, और वीडियो.

जनरेटिव एआई, लार्ज लैंग्वेज मॉडल (एलएलएम) की मदद से काम करता है. इसकी मदद से, एक साथ कई काम किए जा सकते हैं. साथ ही, यह कई तरह के काम कर सकता है. जैसे, ख़ास जानकारी देना, सवाल-जवाब करना, कैटगरी तय करना वगैरह. कम से कम ट्रेनिंग के साथ, बुनियादी मॉडल को टारगेट किए गए इस्तेमाल के मामलों में अपनाया जा सकता है. साथ ही, मॉडल को बहुत कम डेटा के साथ भी तैयार किया जा सकता है.

जनरेटिव एआई कैसे काम करता है?

जनरेटिव एआई, मशीन लर्निंग (एमएल) मॉडल का इस्तेमाल करके, लोगों के बनाए गए कॉन्टेंट के डेटासेट के पैटर्न और संबंधों के बारे में बताता है. इसके बाद, वह सीखे गए पैटर्न का इस्तेमाल करके नया कॉन्टेंट जनरेट करता है.

जनरेटिव एआई मॉडल को ट्रेनिंग देने का सबसे सामान्य तरीका है, निगरानी में रखे गए लर्निंग का इस्तेमाल करना. मॉडल को इंसानों के बनाए गए कॉन्टेंट और उससे जुड़े लेबल का एक सेट दिया जाता है. इसके बाद, वह ऐसा कॉन्टेंट जनरेट करना सीख जाता है जो किसी व्यक्ति के बनाए गए कॉन्टेंट से मिलता-जुलता हो.

जनरेटिव एआई का इस्तेमाल करने वाले आम ऐप्लिकेशन क्या हैं?

जनरेटिव एआई का इस्तेमाल इन कामों के लिए किया जा सकता है:

  • बेहतर चैट और खोज के अनुभव की मदद से, ग्राहकों के इंटरैक्शन को बेहतर बनाएं.
  • बातचीत वाले इंटरफ़ेस और खास जानकारी की मदद से, कई तरह के अनस्ट्रक्चर्ड डेटा को एक्सप्लोर करें.
  • प्रपोज़ल के अनुरोधों का जवाब देने, मार्केटिंग कॉन्टेंट को अलग-अलग भाषाओं में स्थानीय भाषा में लिखने, ग्राहक से जुड़े समझौतों की जांच वगैरह जैसे कामों को बार-बार करने में मदद करें.

Google Cloud में जनरेटिव एआई की कौनसी सुविधाएं उपलब्ध हैं?

Vertex AI की मदद से, मशीन लर्निंग के बारे में बहुत कम या पूरी जानकारी न होने पर भी, फ़ाउंडेशन मॉडल के साथ इंटरैक्ट किया जा सकता है, उन्हें अपनी पसंद के मुताबिक बनाया जा सकता है, और अपने ऐप्लिकेशन में एम्बेड किया जा सकता है. मॉडल गार्डन पर फ़ाउंडेशन के मॉडल ऐक्सेस किए जा सकते हैं. इसके अलावा, Vertex AI Studio पर आसान यूआई से मॉडल को ट्यून किया जा सकता है या डेटा साइंस नोटबुक में मॉडल का इस्तेमाल किया जा सकता है.

Vertex AI Search and Conversation की मदद से डेवलपर, जनरेटिव एआई की मदद से काम करने वाले सर्च इंजन और चैटबॉट तेज़ी से बना सकते हैं.

Gemini for Google Cloud, एआई की मदद से काम करने वाला Gemini और Google Cloud का एक सहयोगी है. यह Google Cloud और IDEs में उपलब्ध है. इससे आपको अपने काम तेज़ी से करने में मदद मिलती है. Gemini Code Assist पर आपको कोड पूरा करने, कोड जनरेट करने, और कोड के बारे में जानकारी पाने की सुविधा मिलती है. साथ ही, Gemini के साथ चैट करके तकनीकी सवाल पूछे जा सकते हैं.

Gemini क्या है?

Gemini, ऐसे जनरेटिव एआई मॉडल का एक फ़ैमिली ग्रुप है जिसे Google DeepMind ने बनाया है. इसे मल्टीमॉडल इस्तेमाल के लिए डिज़ाइन किया गया है. मल्टीमोडल का मतलब है कि यह अलग-अलग तरह के कॉन्टेंट को प्रोसेस और जनरेट कर सकता है. जैसे, टेक्स्ट, कोड, इमेज, और ऑडियो.

b9913d011999e7c7.png

Gemini अलग-अलग साइज़ और वैरिएंट में उपलब्ध है:

  • Gemini Ultra: यह मुश्किल कामों को पूरा करने के लिए सबसे बड़ा और बेहतरीन वर्शन है.
  • Gemini Flash: यह सबसे तेज़ और किफ़ायती विकल्प है. इसे ज़्यादा वॉल्यूम वाले कामों के लिए ऑप्टिमाइज़ किया गया है.
  • Gemini Pro: मीडियम साइज़ में उपलब्ध, इसे अलग-अलग तरह के कामों के लिए ऑप्टिमाइज़ किया गया है.
  • Gemini Nano: यह सबसे बेहतर सुविधा है. इसे डिवाइस पर किए जाने वाले कामों को करने के लिए डिज़ाइन किया गया है.

मुख्य सुविधाएं:

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

Java के Vertex AI में Gemini को इस्तेमाल करने का तरीका क्या है?

आपके पास दो विकल्प हैं:

  1. Vertex AI Java API for Gemini की आधिकारिक लाइब्रेरी.
  2. LangChain4j फ़्रेमवर्क.

इस कोडलैब में, LangChain4j फ़्रेमवर्क का इस्तेमाल किया जा सकता है.

LangChain4j फ़्रेमवर्क क्या है?

LangChain4j फ़्रेमवर्क एक ओपन सोर्स लाइब्रेरी है. इसकी मदद से, Java ऐप्लिकेशन में एलएलएम इंटिग्रेट किए जा सकते हैं. इसके लिए, एलएलएम जैसे कई कॉम्पोनेंट को व्यवस्थित किया जाता है. साथ ही, वेक्टर डेटाबेस (सिमेंटिक खोजों के लिए), दस्तावेज़ लोडर और स्प्लिटर (दस्तावेज़ों का विश्लेषण करने और उनसे सीखने के लिए), आउटपुट पार्सर वगैरह को व्यवस्थित किया जाता है.

यह प्रोजेक्ट LangChain Python प्रोजेक्ट से प्रेरित था. हालांकि, इसका मकसद Java डेवलपर की मदद करना था.

bb908ea1e6c96ac2.png

आपको इनके बारे में जानकारी मिलेगी

  • Gemini और LangChain4j का इस्तेमाल करने के लिए Java प्रोजेक्ट कैसे सेट अप करें
  • Gemini को अपना पहला प्रॉम्प्ट प्रोग्राम के हिसाब से भेजने का तरीका
  • Gemini से मिले जवाबों को स्ट्रीम करने का तरीका
  • उपयोगकर्ता और Gemini के बीच बातचीत शुरू करने का तरीका
  • टेक्स्ट और इमेज, दोनों भेजकर Gemini को मल्टीमोडल कॉन्टेक्स्ट में इस्तेमाल करने का तरीका
  • बिना स्ट्रक्चर वाले कॉन्टेंट से काम के स्ट्रक्चर्ड जानकारी निकालने का तरीका
  • प्रॉम्प्ट टेंप्लेट में बदलाव करने का तरीका
  • टेक्स्ट की कैटगरी तय करने का तरीका, जैसे कि भावनाओं का विश्लेषण करना
  • अपने दस्तावेज़ों के साथ चैट करने का तरीका (रिकवरी ऑगमेंटेड जनरेशन)
  • फ़ंक्शन कॉलिंग की मदद से, चैटबॉट की अवधि बढ़ाने का तरीका
  • Ollama और TestContainers के साथ Gemma को स्थानीय तौर पर इस्तेमाल करने का तरीका

आपको इन चीज़ों की ज़रूरत होगी

  • Java प्रोग्रामिंग भाषा की जानकारी
  • Google Cloud प्रोजेक्ट
  • Chrome या Firefox जैसा कोई ब्राउज़र

2. सेटअप और ज़रूरी शर्तें

अपने हिसाब से एनवायरमेंट सेटअप करना

  1. Google Cloud Console में साइन इन करें और नया प्रोजेक्ट बनाएं या किसी मौजूदा प्रोजेक्ट का फिर से इस्तेमाल करें. अगर आपके पास पहले से Gmail या Google Workspace खाता नहीं है, तो आपको नया खाता बनाना होगा.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • प्रोजेक्ट का नाम, इस प्रोजेक्ट में हिस्सा लेने वाले लोगों का डिसप्ले नेम होता है. यह एक वर्ण स्ट्रिंग है, जिसका इस्तेमाल Google API नहीं करता. इसे कभी भी अपडेट किया जा सकता है.
  • प्रोजेक्ट आईडी, Google Cloud के सभी प्रोजेक्ट के लिए यूनीक होता है. साथ ही, इसे बदला नहीं जा सकता. इसे सेट करने के बाद बदला नहीं जा सकता. Cloud Console, एक यूनीक स्ट्रिंग अपने-आप जनरेट करता है; आम तौर पर, आपको उसके होने की कोई परवाह नहीं होती. ज़्यादातर कोडलैब में, आपको अपना प्रोजेक्ट आईडी बताना होगा. आम तौर पर, इसकी पहचान PROJECT_ID के रूप में की जाती है. अगर आपको जनरेट किया गया आईडी पसंद नहीं है, तो किसी भी क्रम में एक और आईडी जनरेट किया जा सकता है. दूसरा तरीका यह है कि आप खुद भी आज़माकर देखें कि वह उपलब्ध है या नहीं. इस चरण के बाद, इसे बदला नहीं जा सकता. साथ ही, यह प्रोजेक्ट के खत्म होने तक बना रहता है.
  • आपकी जानकारी के लिए, प्रोजेक्ट नंबर नाम की एक तीसरी वैल्यू दी गई है. इसका इस्तेमाल कुछ एपीआई करते हैं. दस्तावेज़ में इन तीनों वैल्यू के बारे में ज़्यादा जानें.
  1. इसके बाद, आपको क्लाउड संसाधनों/एपीआई का इस्तेमाल करने के लिए, Cloud Console में बिलिंग चालू करनी होगी. इस कोडलैब का इस्तेमाल करने पर, आपको ज़्यादा पैसे नहीं चुकाने होंगे. इस ट्यूटोरियल के अलावा, बिलिंग से बचने के लिए संसाधनों को बंद करें. इसके लिए, अपने बनाए गए संसाधनों को मिटाएं या प्रोजेक्ट को मिटाएं. Google Cloud के नए उपयोगकर्ता, 300 डॉलर के मुफ़्त ट्रायल वाले प्रोग्राम में हिस्सा ले सकते हैं.

Cloud Shell शुरू करना

Google Cloud को आपके लैपटॉप से, कहीं से भी ऑपरेट किया जा सकता है. हालांकि, इस कोडलैब में Cloud Shell का इस्तेमाल किया जा रहा है. यह क्लाउड में चलने वाला कमांड लाइन एनवायरमेंट है.

Cloud Shell चालू करें

  1. Cloud Console में, Cloud Shell चालू करें 853e55310c205094.png पर क्लिक करें.

3c1dabeca90e44e5.png

अगर आपने Cloud Shell का इस्तेमाल पहली बार किया है, तो आपको बीच में आने वाली स्क्रीन दिखेगी. इसमें यह बताया जाएगा कि यह क्या है. अगर आपको बीच के लेवल पर मिलने वाली स्क्रीन दिखती है, तो जारी रखें पर क्लिक करें.

9c92662c6a846a5c.png

प्रावधान करने और Cloud Shell से कनेक्ट होने में कुछ ही समय लगेगा.

9f0e51b578fecce5.png

इस वर्चुअल मशीन में डेवलपमेंट के सभी ज़रूरी टूल मौजूद हैं. इसमें लगातार पांच जीबी की होम डायरेक्ट्री मिलती है और यह Google Cloud में काम करती है. यह नेटवर्क की परफ़ॉर्मेंस और ऑथेंटिकेशन को बेहतर बनाने में मदद करती है. अगर सभी नहीं, तो इस कोडलैब में आपका बहुत सारा काम ब्राउज़र से किया जा सकता है.

Cloud Shell से कनेक्ट करने के बाद, आपको दिखेगा कि आपकी पुष्टि हो चुकी है और प्रोजेक्ट आपके प्रोजेक्ट आईडी पर सेट है.

  1. यह पुष्टि करने के लिए Cloud Shell में नीचे दिया गया कमांड चलाएं कि आपकी पुष्टि हो गई है:
gcloud auth list

कमांड आउटपुट

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell में यह कमांड चलाएं, ताकि यह पुष्टि की जा सके कि gcloud के लिए कमांड को आपके प्रोजेक्ट के बारे में जानकारी है:
gcloud config list project

कमांड आउटपुट

[core]
project = <PROJECT_ID>

अगर ऐसा नहीं है, तो आप इसे इस निर्देश की मदद से सेट कर सकते हैं:

gcloud config set project <PROJECT_ID>

कमांड आउटपुट

Updated property [core/project].

3. डेवलपमेंट एनवायरमेंट को तैयार किया जा रहा है

इस कोडलैब में, Java प्रोग्राम डेवलप करने के लिए, Cloud Shell टर्मिनल और Cloud Shell एडिटर का इस्तेमाल किया जा सकता है.

Vertex AI API को चालू करें

पक्का करें कि Google Cloud Console में, आपके प्रोजेक्ट का नाम Google Cloud Console में सबसे ऊपर दिख रहा हो. अगर ऐसा नहीं है, तो प्रोजेक्ट सिलेक्टर खोलने के लिए, कोई प्रोजेक्ट चुनें पर क्लिक करें और अपनी पसंद का प्रोजेक्ट चुनें.

Vertex AI API को Google Cloud Console के Vertex AI सेक्शन या Cloud Shell टर्मिनल से चालू किया जा सकता है.

Google Cloud Console से चालू करने के लिए, सबसे पहले Google Cloud Console के मेन्यू के Vertex AI सेक्शन पर जाएं:

451976f1c8652341.png

Vertex AI डैशबोर्ड में, सुझाए गए सभी एपीआई चालू करें पर क्लिक करें.

ऐसा करने से कई एपीआई चालू हो जाएंगे, लेकिन कोडलैब के लिए सबसे ज़रूरी aiplatform.googleapis.com है.

इसके अलावा, इस एपीआई को Cloud Shell टर्मिनल से इस कमांड का इस्तेमाल करके भी चालू किया जा सकता है:

gcloud services enable aiplatform.googleapis.com

GitHub डेटा स्टोर करने की जगह का क्लोन करें

Cloud Shell टर्मिनल में, इस कोडलैब के लिए डेटा स्टोर करने की जगह का क्लोन बनाएं:

git clone https://github.com/glaforge/gemini-workshop-for-java-developers.git

यह देखने के लिए कि प्रोजेक्ट चलने के लिए तैयार है या नहीं, "Hey World" चलाकर देखें कार्यक्रम.

पक्का करें कि आप टॉप लेवल फ़ोल्डर पर हों:

cd gemini-workshop-for-java-developers/ 

Gradle रैपर बनाना:

gradle wrapper

gradlew के साथ चलाएं:

./gradlew run

आपको यह आउटपुट दिखेगा:

..
> Task :app:run
Hello World!

Cloud Editor खोलना और उसे सेट अप करना

Cloud Shell से, Cloud Code Editor की मदद से कोड खोलें:

42908e11b28f4383.png

Cloud Code Editor में, File -> को चुनकर कोडलैब का सोर्स फ़ोल्डर खोलें Open Folder और कोडलैब के सोर्स फ़ोल्डर पर जाएं (उदाहरण के लिए, /home/username/gemini-workshop-for-java-developers/).

Java के लिए Gradle इंस्टॉल करना

Gradle के साथ क्लाउड कोड एडिटर ठीक से काम करे, इसके लिए Gradle for Java का एक्सटेंशन इंस्टॉल करें.

सबसे पहले, Java प्रोजेक्ट सेक्शन में जाएं और प्लस का निशान दबाएं:

84d15639ac61c197.png

Gradle for Java चुनें:

34d6c4136a3cc9ff.png

Install Pre-Release वर्शन चुनें:

3b044fb450ccb7.png

इंस्टॉल हो जाने के बाद, आपको Disable और Uninstall बटन दिखेंगे:

46410fe86d777f9c.png

आखिर में, नई सेटिंग लागू करने के लिए फ़ाइल फ़ोल्डर को साफ़ करें:

31e27e9bb61d975d.png

ऐसा करने पर, आपको वर्कशॉप को फिर से लोड करने और मिटाने के लिए कहा जाएगा. आगे बढ़ें और Reload and delete को चुनें:

d6303bc49e391dc.png

यदि आप कोई फ़ाइल खोलते हैं, उदाहरण के लिए App.java, तो अब आपको सिंटैक्स हाइलाइटिंग के साथ संपादक को ठीक से काम करते हुए देखना चाहिए:

fed1b1b5de0dff58.png

अब आप Gemini के साथ कुछ सैंपल चलाने के लिए तैयार हैं!

एनवायरमेंट वैरिएबल सेटअप करें

Terminal -> को चुनकर Cloud Code Editor में नया टर्मिनल खोलें New Terminal. कोड के उदाहरण चलाने के लिए ज़रूरी दो एनवायरमेंट वैरिएबल सेट अप करें:

  • PROJECT_ID — आपका Google Cloud प्रोजेक्ट आईडी
  • LOCATION — वह देश या इलाका जहां Gemini मॉडल को डिप्लॉय किया गया है

वैरिएबल को इस तरह एक्सपोर्ट करें:

export PROJECT_ID=$(gcloud config get-value project)
export LOCATION=us-central1

4. Gemini मॉडल के लिए पहला कॉल

प्रोजेक्ट सही तरीके से सेट अप हो गया है. इसलिए, अब Gemini API को कॉल किया जाता है.

app/src/main/java/gemini/workshop डायरेक्ट्री में QA.java पर एक नज़र डालें:

package gemini.workshop;

import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.model.chat.ChatLanguageModel;

public class QA {
    public static void main(String[] args) {
        ChatLanguageModel model = VertexAiGeminiChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .build();

        System.out.println(model.generate("Why is the sky blue?"));
    }
}

इस पहले उदाहरण में, आपको VertexAiGeminiChatModel क्लास इंपोर्ट करनी होगी, जो ChatModel इंटरफ़ेस को लागू करती है.

main तरीके में, VertexAiGeminiChatModel के लिए बिल्डर का इस्तेमाल करके, चैट के भाषा मॉडल को कॉन्फ़िगर किया जा सकता है और यह जानकारी दी जा सकती है:

  • प्रोजेक्ट
  • जगह
  • मॉडल का नाम (gemini-1.5-flash-001).

भाषा का मॉडल तैयार है. इसके बाद, generate() तरीके को कॉल करके, अपना प्रॉम्प्ट, अपना सवाल या निर्देश एलएलएम को भेजे जा सकते हैं. यहां, तुम इस बारे में एक आसान से सवाल पूछते हो कि आसमान को नीला कैसे बनाता है.

इस प्रॉम्प्ट में बदलाव करके, अलग-अलग सवाल पूछे जा सकते हैं या टास्क पूरे किए जा सकते हैं.

सैंपल को सोर्स कोड के रूट फ़ोल्डर में चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.QA

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

The sky appears blue because of a phenomenon called Rayleigh scattering.
When sunlight enters the atmosphere, it is made up of a mixture of
different wavelengths of light, each with a different color. The
different wavelengths of light interact with the molecules and particles
in the atmosphere in different ways.

The shorter wavelengths of light, such as those corresponding to blue
and violet light, are more likely to be scattered in all directions by
these particles than the longer wavelengths of light, such as those
corresponding to red and orange light. This is because the shorter
wavelengths of light have a smaller wavelength and are able to bend
around the particles more easily.

As a result of Rayleigh scattering, the blue light from the sun is
scattered in all directions, and it is this scattered blue light that we
see when we look up at the sky. The blue light from the sun is not
actually scattered in a single direction, so the color of the sky can
vary depending on the position of the sun in the sky and the amount of
dust and water droplets in the atmosphere.

बधाई हो, आपने Gemini पर पहला कॉल किया!

जवाब स्ट्रीम किया जा रहा है

क्या आपने देखा कि कुछ सेकंड के बाद, एक बार में जवाब दिया गया? स्ट्रीमिंग रिस्पॉन्स वैरिएंट की वजह से, अलग-अलग रिस्पॉन्स मिल भी सकते हैं. स्ट्रीमिंग रिस्पॉन्स, मॉडल उपलब्ध होने पर यह रिस्पॉन्स देता है.

इस कोडलैब में, हम नॉन-स्ट्रीमिंग जवाब का इस्तेमाल करेंगे. हालांकि, आइए अब स्ट्रीमिंग के जवाब पर नज़र डालते हैं, ताकि यह समझ सकें कि यह कैसे किया जा सकता है.

StreamQA.java में, app/src/main/java/gemini/workshop डायरेक्ट्री में जाकर स्ट्रीमिंग के रिस्पॉन्स को देखा जा सकता है:

package gemini.workshop;

import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiStreamingChatModel;
import dev.langchain4j.model.StreamingResponseHandler;

public class StreamQA {
    public static void main(String[] args) {
        StreamingChatLanguageModel model = VertexAiGeminiStreamingChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .build();
        
        model.generate("Why is the sky blue?", new StreamingResponseHandler<>() {
            @Override
            public void onNext(String text) {
                System.out.println(text);
            }

            @Override
            public void onError(Throwable error) {
                error.printStackTrace();
            }
        });
    }
}

इस बार, हम स्ट्रीमिंग क्लास के VertexAiGeminiStreamingChatModel वैरिएंट इंपोर्ट करते हैं. इससे StreamingChatLanguageModel इंटरफ़ेस लागू होता है. आपको StreamingResponseHandler की भी ज़रूरत होगी.

इस बार, generate() तरीके का हस्ताक्षर थोड़ा अलग है. स्ट्रिंग लौटाने के बजाय, रिटर्न टाइप अमान्य होता है. प्रॉम्प्ट के अलावा, आपको एक स्ट्रीमिंग रिस्पॉन्स हैंडलर भी पास करना होगा. यहां, पहचान छिपाने वाला इनर क्लास बनाकर इंटरफ़ेस लागू किया जाता है. इसमें दो तरीकों onNext(String text) और onError(Throwable error) का इस्तेमाल किया जाता है. हर बार जवाब का कोई नया हिस्सा उपलब्ध होने पर, पुराने नियम का इस्तेमाल किया जाता है. हालांकि, कोई गड़बड़ी होने पर ही बाद वाले लेबल को कॉल किया जाता है.

चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.StreamQA

आपको पिछली क्लास जैसा ही जवाब मिलेगा. हालांकि, इस बार पूरा जवाब दिखने का इंतज़ार करने के बजाय, आपके शेल में जवाब एक-एक करके दिखेगा.

अतिरिक्त कॉन्फ़िगरेशन

कॉन्फ़िगरेशन के लिए, हमने सिर्फ़ प्रोजेक्ट, जगह, और मॉडल का नाम तय किया है. हालांकि, ऐसे अन्य पैरामीटर भी हैं जिन्हें मॉडल के लिए तय किया जा सकता है:

  • temperature(Float temp) — इससे यह पता चलता है कि जवाब कितना क्रिएटिव होना चाहिए (0 का मतलब है कम क्रिएटिव होना और ज़्यादातर मामलों में तथ्य भी, जबकि 1 का मतलब ज़्यादा क्रिएटिव आउटपुट के लिए होना चाहिए)
  • topP(Float topP) — ऐसे संभावित शब्दों को चुनने के लिए जिनकी कुल संभावना को उस फ़्लोटिंग पॉइंट नंबर (0 और 1 के बीच) में जोड़ा जाता है
  • topK(Integer topK) — टेक्स्ट पूरा होने के लिए, संभावित शब्दों की ज़्यादा से ज़्यादा संख्या में से किसी भी क्रम में कोई शब्द चुना जा सकता है. यह संख्या 1 से 40 तक हो सकती है
  • maxOutputTokens(Integer max) — मॉडल से दिए गए जवाब की ज़्यादा से ज़्यादा लंबाई तय करने के लिए (आम तौर पर, चार टोकन करीब तीन शब्दों को दिखाते हैं)
  • maxRetries(Integer retries) — अगर आपने हर बार अनुरोध करने के कोटा से ज़्यादा अनुरोध कर लिया है या प्लैटफ़ॉर्म में कोई तकनीकी समस्या आ रही है, तो मॉडल को तीन बार फिर से कॉल करने की कोशिश करें

आपने अब तक Gemini से सिर्फ़ एक सवाल पूछा है. हालाँकि, आपके पास बहु-स्थितियों वाली बातचीत भी करने का विकल्प है. अगले सेक्शन में, इसके बारे में ज़्यादा जानकारी मिलेगी.

5. Gemini के साथ चैट करें

पिछले चरण में, आपने एक सवाल पूछा था. अब उपयोगकर्ता और एलएलएम के बीच असली बातचीत करने का समय है. प्रत्येक प्रश्न और उत्तर पिछले प्रश्न और उत्तर के आधार पर वास्तविक चर्चा का निर्माण कर सकता है.

app/src/main/java/gemini/workshop फ़ोल्डर में मौजूद Conversation.java को देखें:

package gemini.workshop;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.service.AiServices;

import java.util.List;

public class Conversation {
    public static void main(String[] args) {
        ChatLanguageModel model = VertexAiGeminiChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .build();

        MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder()
            .maxMessages(20)
            .build();

        interface ConversationService {
            String chat(String message);
        }

        ConversationService conversation =
            AiServices.builder(ConversationService.class)
                .chatLanguageModel(model)
                .chatMemory(chatMemory)
                .build();

        List.of(
            "Hello!",
            "What is the country where the Eiffel tower is situated?",
            "How many inhabitants are there in that country?"
        ).forEach( message -> {
            System.out.println("\nUser: " + message);
            System.out.println("Gemini: " + conversation.chat(message));
        });
    }
}

इस क्लास में कुछ नए दिलचस्प इंपोर्ट:

  • MessageWindowChatMemory — ऐसी क्लास जो बार-बार होने वाले बातचीत के पहलू को मैनेज करने में मदद करती है. साथ ही, इससे पिछले सवालों और जवाबों को अपने डिवाइस की मेमोरी में सेव किया जाता है
  • AiServices — ऐसी क्लास जो चैट मॉडल और चैट मेमोरी को एक साथ जोड़े रखती है

मुख्य तरीके में, आपको मॉडल, चैट मेमोरी, और एआई सेवा को सेट अप करना होगा. मॉडल को प्रोजेक्ट, स्थान और मॉडल के नाम की जानकारी के साथ सामान्य रूप से कॉन्फ़िगर किया जाता है.

चैट मेमोरी के लिए, हम MessageWindowChatMemory के बिल्डर का इस्तेमाल करके एक मेमोरी बनाते हैं. इसमें शेयर किए गए आखिरी 20 मैसेज सेव रहते हैं. यह बातचीत पर स्लाइड करने वाली विंडो होती है, जिसका कॉन्टेक्स्ट हमारे Java क्लास क्लाइंट में स्थानीय रूप से रखा जाता है.

इसके बाद, एक AI service बनाएं, जो चैट मॉडल को चैट मेमोरी से जोड़ता हो.

ध्यान दें कि एआई सेवा, हमारे बताए गए कस्टम ConversationService इंटरफ़ेस का इस्तेमाल कैसे करती है. साथ ही, यह भी देखें कि LangChain4j लागू होता है या नहीं, जो String क्वेरी लेता है और String रिस्पॉन्स देता है.

अब Gemini से बातचीत करने का समय आ गया है. सबसे पहले, एफ़िल टावर के बारे में एक सवाल भेजा जाता है. उससे पहले यह पूछा जाता है कि यह किस देश में है. ध्यान दें कि आखिरी वाक्य पहले सवाल के जवाब से जुड़ा है. ऐसा इसलिए, क्योंकि आपको है कि जिस देश में एफ़िल टावर मौजूद है वहां कितने लोग हैं. पिछले जवाब में उस देश का नाम नहीं बताया गया था जिसके बारे में पहले बताया गया था. इससे पता चलता है कि हर सवाल के साथ पिछले सवाल और जवाब भेजे जाते हैं.

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.Conversation

आपको इनसे मिलते-जुलते तीन जवाब दिखेंगे:

User: Hello!
Gemini: Hi there! How can I assist you today?

User: What is the country where the Eiffel tower is situated?
Gemini: France

User: How many inhabitants are there in that country?
Gemini: As of 2023, the population of France is estimated to be around 67.8 million.

Gemini से एक बार में सवाल पूछे जा सकते हैं या उसके साथ कई बार बातचीत की जा सकती है. हालाँकि, अब तक इनपुट में सिर्फ़ टेक्स्ट शामिल किया गया है. इमेज कैसी होती हैं? आइए, अगले चरण में इमेज को एक्सप्लोर करें.

6. Gemini की मदद से मल्टीमोडैलिटी

Gemini एक मल्टीमॉडल मॉडल है. यह टेक्स्ट को इनपुट के तौर पर स्वीकार करने के साथ-साथ इमेज और वीडियो को भी इनपुट के तौर पर स्वीकार करता है. इस सेक्शन में, आपको टेक्स्ट और इमेज को एक साथ इस्तेमाल करने के लिए, इस्तेमाल का उदाहरण दिखेगा.

क्या आपको लगता है कि Gemini इस बिल्ली की आवाज़ पहचान पाएगा?

af00516493ec9ade.png

बर्फ़ में पड़ी एक बिल्ली की तस्वीर, जो Wikipedia से ली गई हैhttps://upload.wikimedia.org/wikipedia/commons/b/b6/Felis_catus-cat_on_snow.jpg

app/src/main/java/gemini/workshop डायरेक्ट्री में Multimodal.java पर एक नज़र डालें:

package gemini.workshop;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.output.Response;
import dev.langchain4j.data.message.ImageContent;
import dev.langchain4j.data.message.TextContent;

public class Multimodal {

    static final String CAT_IMAGE_URL =
        "https://upload.wikimedia.org/wikipedia/" +
        "commons/b/b6/Felis_catus-cat_on_snow.jpg";


    public static void main(String[] args) {
        ChatLanguageModel model = VertexAiGeminiChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .build();

        UserMessage userMessage = UserMessage.from(
            ImageContent.from(CAT_IMAGE_URL),
            TextContent.from("Describe the picture")
        );

        Response<AiMessage> response = model.generate(userMessage);

        System.out.println(response.content().text());
    }
}

इंपोर्ट करते समय, ध्यान दें कि हम अलग-अलग तरह के मैसेज और कॉन्टेंट के बीच फ़र्क़ करते हैं. UserMessage में TextContent और ImageContent, दोनों ऑब्जेक्ट हो सकते हैं. यह एक मल्टीमोडैलिटी है, जिसमें टेक्स्ट और इमेज को एक साथ इस्तेमाल किया जाता है. मॉडल, एक Response भेजता है, जिसमें AiMessage होता है.

इसके बाद, content() की मदद से रिस्पॉन्स से AiMessage और फिर text() की मदद से मैसेज का टेक्स्ट.

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.Multimodal

तस्वीर के नाम से साफ़ तौर पर पता चलता है कि उसमें कौनसी जानकारी मौजूद है. हालाँकि, Gemini का आउटपुट कुछ ऐसा ही है:

A cat with brown fur is walking in the snow. The cat has a white patch of fur on its chest and white paws. The cat is looking at the camera.

इमेज और टेक्स्ट प्रॉम्प्ट को एक साथ इस्तेमाल करने से, इस्तेमाल के दिलचस्प उदाहरण मिलते हैं. आपके पास ऐसे ऐप्लिकेशन बनाने का विकल्प होता है जो:

  • तस्वीरों में दिख रहे टेक्स्ट की पहचान करें.
  • देखें कि इमेज दिखाने के लिए सुरक्षित है या नहीं.
  • इमेज के कैप्शन बनाएं.
  • सादे टेक्स्ट की जानकारी वाली इमेज के डेटाबेस में से खोजें.

इमेज से जानकारी निकालने के अलावा, आपके पास बिना स्ट्रक्चर वाले टेक्स्ट से भी जानकारी निकालने का विकल्प होता है. आपको अगले सेक्शन में यही जानकारी मिलेगी.

7. बिना स्ट्रक्चर वाले टेक्स्ट से स्ट्रक्चर्ड जानकारी एक्सट्रैक्ट करना

कई मामलों में, रिपोर्ट के दस्तावेज़ों, ईमेल या लंबी अवधि के अन्य टेक्स्ट में, ज़रूरी जानकारी बिना किसी व्यवस्थित तरीके से दी जाती है. आम तौर पर, आपको स्ट्रक्चर्ड ऑब्जेक्ट के रूप में, बिना स्ट्रक्चर वाले टेक्स्ट में शामिल मुख्य जानकारी को एक्सट्रैक्ट करना हो. आइए, जानते हैं कि इसे कैसे किया जा सकता है.

मान लें कि आपको किसी व्यक्ति की जीवनी या विवरण के आधार पर, उसका नाम और उम्र निकालना है. एलएलएम को, बड़े पैमाने पर बदलाव किए गए प्रॉम्प्ट की मदद से, बिना स्ट्रक्चर वाले टेक्स्ट से JSON को एक्सट्रैक्ट करने के लिए कहा जा सकता है. इसे आम तौर पर, "प्रॉम्प्ट इंजीनियरिंग" कहा जाता है.

app/src/main/java/gemini/workshop में ExtractData.java को देखें:

package gemini.workshop;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.UserMessage;

public class ExtractData {

    static record Person(String name, int age) {}

    interface PersonExtractor {
        @UserMessage("""
            Extract the name and age of the person described below.
            Return a JSON document with a "name" and an "age" property, \
            following this structure: {"name": "John Doe", "age": 34}
            Return only JSON, without any markdown markup surrounding it.
            Here is the document describing the person:
            ---
            {{it}}
            ---
            JSON:
            """)
        Person extractPerson(String text);
    }

    public static void main(String[] args) {
        ChatLanguageModel model = VertexAiGeminiChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .temperature(0f)
            .topK(1)
            .build();

        PersonExtractor extractor = AiServices.create(PersonExtractor.class, model);

        Person person = extractor.extractPerson("""
            Anna is a 23 year old artist based in Brooklyn, New York. She was born and 
            raised in the suburbs of Chicago, where she developed a love for art at a 
            young age. She attended the School of the Art Institute of Chicago, where 
            she studied painting and drawing. After graduating, she moved to New York 
            City to pursue her art career. Anna's work is inspired by her personal 
            experiences and observations of the world around her. She often uses bright 
            colors and bold lines to create vibrant and energetic paintings. Her work 
            has been exhibited in galleries and museums in New York City and Chicago.    
            """
        );

        System.out.println(person.name());  // Anna
        System.out.println(person.age());   // 23
    }
}

चलिए, इस फ़ाइल के अलग-अलग चरणों पर नज़र डालते हैं:

  • Person रिकॉर्ड, किसी व्यक्ति के बारे में जानकारी देने के लिए तय किया जाता है. जैसे, नाम और उम्र.
  • PersonExtractor इंटरफ़ेस को ऐसे तरीके से तय किया जाता है जो बिना स्ट्रक्चर वाली टेक्स्ट स्ट्रिंग उपलब्ध कराता है. इससे, Person इंस्टेंस दिखाता है.
  • extractPerson() के साथ @UserMessage एनोटेशन दिखाया जाता है, जो किसी प्रॉम्प्ट को इसके साथ जोड़ता है. इस प्रॉम्प्ट का इस्तेमाल करके, मॉडल जानकारी एक्सट्रैक्ट करेगा और जानकारी को JSON दस्तावेज़ के तौर पर दिखाएगा. इसे आपके लिए पार्स किया जाएगा और Person इंस्टेंस में अनमर्श किया जाएगा.

अब main() तरीके के कॉन्टेंट पर नज़र डालते हैं:

  • चैट मॉडल इंस्टैंशिएट किया जाता है. ध्यान दें कि हम एक सटीक जवाब पक्का करने के लिए, शून्य के बहुत कम temperature और सिर्फ़ एक के topK का इस्तेमाल करते हैं. इससे मॉडल को निर्देशों का बेहतर तरीके से पालन करने में भी मदद मिलती है. खास तौर पर, हम नहीं चाहते कि Gemini, JSON के जवाब में ज़्यादा Markdown मार्कअप का इस्तेमाल करे.
  • PersonExtractor ऑब्जेक्ट, LangChain4j की AiServices क्लास की मदद से बनाया गया है.
  • इसके बाद, बिना स्ट्रक्चर वाले टेक्स्ट से व्यक्ति की जानकारी निकालने के लिए, Person person = extractor.extractPerson(...) को कॉल करें. साथ ही, नाम और उम्र वाला Person इंस्टेंस वापस पाएं.

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.ExtractData

आपको यह आउटपुट दिखेगा:

Anna
23

हां, ये ऐना हैं और उनकी उम्र 23 साल है!

इस AiServices तरीके से, ज़ोर से टाइप किए गए ऑब्जेक्ट के साथ काम किया जाता है. आपने सीधे एलएलएम से इंटरैक्ट नहीं किया है. इसके बजाय, एक्सट्रैक्ट की गई निजी जानकारी को दिखाने के लिए Person रिकॉर्ड जैसी कंक्रीट क्लास का इस्तेमाल किया जा रहा है. साथ ही, आपके पास extractPerson() तरीके वाला PersonExtractor ऑब्जेक्ट है, जो Person इंस्टेंस दिखाता है. एलएलएम का सिद्धांत हटा दिया गया है. Java डेवलपर के तौर पर, यह सिर्फ़ सामान्य क्लास और ऑब्जेक्ट में बदलाव कर रहा है.

8. प्रॉम्प्ट टेंप्लेट से स्ट्रक्चर के प्रॉम्प्ट

जब किसी एलएलएम से इंटरैक्ट करने के लिए, एक ही तरह के निर्देशों या सवालों का इस्तेमाल किया जाता है, तो प्रॉम्प्ट का एक हिस्सा कभी नहीं बदलता. अन्य हिस्सों में डेटा मौजूद होता है. उदाहरण के लिए, अगर आपको रेसिपी बनानी है, तो आप "आप एक हुनरमंद शेफ़ हैं, तो कृपया यहां दी गई सामग्री की मदद से एक रेसिपी बनाएं: ..." जैसे प्रॉम्प्ट का इस्तेमाल करें. इसके बाद, आपको उस टेक्स्ट के आखिर में सामग्री जोड़नी होगी. यह प्रॉम्प्ट टेंप्लेट के लिए काम करता है — यह प्रोग्रामिंग भाषाओं में इंटरपोलेट की गई स्ट्रिंग की तरह ही होता है. प्रॉम्प्ट टेंप्लेट में प्लेसहोल्डर होते हैं. इन्हें एलएलएम को किए गए किसी कॉल के लिए, सही डेटा से बदला जा सकता है.

आइए, app/src/main/java/gemini/workshop डायरेक्ट्री में मौजूद TemplatePrompt.java के बारे में ज़्यादा विस्तार से जानते हैं:

package gemini.workshop;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.output.Response;

import java.util.HashMap;
import java.util.Map;

public class TemplatePrompt {
    public static void main(String[] args) {
        ChatLanguageModel model = VertexAiGeminiChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .maxOutputTokens(500)
            .temperature(0.8f)
            .topK(40)
            .topP(0.95f)
            .maxRetries(3)
            .build();

        PromptTemplate promptTemplate = PromptTemplate.from("""
            You're a friendly chef with a lot of cooking experience.
            Create a recipe for a {{dish}} with the following ingredients: \
            {{ingredients}}, and give it a name.
            """
        );

        Map<String, Object> variables = new HashMap<>();
        variables.put("dish", "dessert");
        variables.put("ingredients", "strawberries, chocolate, and whipped cream");

        Prompt prompt = promptTemplate.apply(variables);

        Response<AiMessage> response = model.generate(prompt.toUserMessage());

        System.out.println(response.content().text());
    }
}

हमेशा की तरह, VertexAiGeminiChatModel मॉडल को कॉन्फ़िगर किया जाता है. इसमें, हाई लेवल की क्रिएटिविटी के साथ-साथ, टॉपपी और टॉपके की वैल्यू भी ज़्यादा होती हैं. इसके बाद, हमारे प्रॉम्प्ट की स्ट्रिंग पास करके, PromptTemplate को उसके from() स्टैटिक तरीके से पास करें. इसके बाद, डबल कर्ली-ब्रेसेस प्लेसहोल्डर वैरिएबल {{dish}} और {{ingredients}} का इस्तेमाल करें.

apply() को कॉल करके फ़ाइनल प्रॉम्प्ट बनाया जाता है. यह प्रॉम्प्ट, की/वैल्यू पेयर का एक मैप लेता है. इसमें प्लेसहोल्डर का नाम और उसकी जगह डाली जाने वाली स्ट्रिंग वैल्यू दिखती है.

आख़िर में, Gemini मॉडल के generate() तरीके को कॉल किया जा सकता है. इसके लिए, उस प्रॉम्प्ट से उपयोगकर्ता के लिए मैसेज बनाया जाएगा. साथ ही, prompt.toUserMessage() निर्देश का इस्तेमाल किया जाएगा.

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.TemplatePrompt

आपको इसके जैसा एक जनरेट किया गया आउटपुट दिखेगा:

**Strawberry Shortcake**

Ingredients:

* 1 pint strawberries, hulled and sliced
* 1/2 cup sugar
* 1/4 cup cornstarch
* 1/4 cup water
* 1 tablespoon lemon juice
* 1/2 cup heavy cream, whipped
* 1/4 cup confectioners' sugar
* 1/4 teaspoon vanilla extract
* 6 graham cracker squares, crushed

Instructions:

1. In a medium saucepan, combine the strawberries, sugar, cornstarch, 
water, and lemon juice. Bring to a boil over medium heat, stirring 
constantly. Reduce heat and simmer for 5 minutes, or until the sauce has 
thickened.
2. Remove from heat and let cool slightly.
3. In a large bowl, combine the whipped cream, confectioners' sugar, and 
vanilla extract. Beat until soft peaks form.
4. To assemble the shortcakes, place a graham cracker square on each of 
6 dessert plates. Top with a scoop of whipped cream, then a spoonful of 
strawberry sauce. Repeat layers, ending with a graham cracker square.
5. Serve immediately.

**Tips:**

* For a more elegant presentation, you can use fresh strawberries 
instead of sliced strawberries.
* If you don't have time to make your own whipped cream, you can use 
store-bought whipped cream.

मैप में dish और ingredients की वैल्यू बदलें और तापमान, topK, और tokP में बदलाव करें. इसके बाद, कोड को फिर से चलाएं. इससे आपको यह पता चल पाएगा कि एलएलएम पर इन पैरामीटर में बदलाव करने का क्या असर होता है.

एलएलएम कॉल के लिए, प्रॉम्प्ट टेंप्लेट एक अच्छा तरीका है. इनमें ऐसे निर्देश होते हैं जिन्हें फिर से इस्तेमाल किया जा सकता है और जिन्हें पैरामीटर के तौर पर इस्तेमाल किया जा सकता है. आपके पास डेटा पास करने और उपयोगकर्ताओं की अलग-अलग वैल्यू के लिए प्रॉम्प्ट को पसंद के मुताबिक बनाने का विकल्प होता है.

9. कुछ शॉट के साथ टेक्स्ट की कैटगरी तय करने की सुविधा

एलएलएम, टेक्स्ट को अलग-अलग कैटगरी में बांट सकते हैं. इस टास्क में एलएलएम की मदद करने के लिए, टेक्स्ट और उनसे जुड़ी कैटगरी के कुछ उदाहरण दिए जा सकते हैं. इस तरीके को अक्सर कुछ शॉट प्रॉम्प्ट कहा जाता है.

किसी खास तरह के टेक्स्ट की कैटगरी तय करने के लिए, app/src/main/java/gemini/workshop डायरेक्ट्री में मौजूद TextClassification.java पर एक नज़र डालें: भावनाओं का विश्लेषण.

package gemini.workshop;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;

package gemini.workshop;

import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.vertexai.VertexAiGeminiChatModel;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.model.input.Prompt;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.output.Response;

import java.util.Map;

public class TextClassification {
    public static void main(String[] args) {
        ChatLanguageModel model = VertexAiGeminiChatModel.builder()
            .project(System.getenv("PROJECT_ID"))
            .location(System.getenv("LOCATION"))
            .modelName("gemini-1.5-flash-001")
            .maxOutputTokens(10)
            .maxRetries(3)
            .build();

        PromptTemplate promptTemplate = PromptTemplate.from("""
            Analyze the sentiment of the text below. Respond only with one word to describe the sentiment.

            INPUT: This is fantastic news!
            OUTPUT: POSITIVE

            INPUT: Pi is roughly equal to 3.14
            OUTPUT: NEUTRAL

            INPUT: I really disliked the pizza. Who would use pineapples as a pizza topping?
            OUTPUT: NEGATIVE

            INPUT: {{text}}
            OUTPUT: 
            """);

        Prompt prompt = promptTemplate.apply(
            Map.of("text", "I love strawberries!"));

        Response<AiMessage> response = model.generate(prompt.toUserMessage());

        System.out.println(response.content().text());
    }
}

main() तरीके में, Gemini चैट मॉडल को हमेशा की तरह बनाया जाता है. हालाँकि, इसमें सबसे छोटे आउटपुट टोकन नंबर का इस्तेमाल किया जाता है. ऐसा इसलिए, क्योंकि आपको कम जवाब की ज़रूरत होती है: टेक्स्ट POSITIVE, NEGATIVE या NEUTRAL है.

इसके बाद, कुछ शॉट के साथ प्रॉम्प्ट देने की तकनीक की मदद से, फिर से इस्तेमाल किया जा सकने वाला प्रॉम्प्ट टेंप्लेट बनाया जाता है. इसके लिए, मॉडल को इनपुट और आउटपुट के कुछ उदाहरणों के बारे में निर्देश दिए जाते हैं. इससे मॉडल को वास्तविक आउटपुट का पालन करने में भी मदद मिलती है. Gemini, पूरे वाक्य में जवाब नहीं देगा. इसके बजाय, Gemini को सिर्फ़ एक शब्द में जवाब देने के लिए कहा जाता है.

{{text}} प्लेसहोल्डर को असली पैरामीटर ("I love strawberries") से बदलने और उस टेंप्लेट को toUserMessage() की मदद से उपयोगकर्ता मैसेज में बदलने के लिए, apply() तरीके से वैरिएबल लागू करें.

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.TextClassification

आपको एक शब्द दिखना चाहिए:

POSITIVE

ऐसा लगता है कि स्ट्रॉबेरी से प्यार करने जैसा एहसास होता है!

10. रिकवर करने में मदद करने वाली ऑगमेंटेड जनरेशन

एलएलएम को बहुत ज़्यादा टेक्स्ट का इस्तेमाल करके ट्रेनिंग दी जाती है. हालांकि, डेवलपर को सिर्फ़ वह जानकारी मिलती है जो ट्रेनिंग के दौरान मिली है. अगर मॉडल ट्रेनिंग की कट-ऑफ़ तारीख के बाद कोई नई जानकारी जारी की जाती है, तो मॉडल के लिए यह जानकारी उपलब्ध नहीं होगी. इसलिए, यह मॉडल उस जानकारी से जुड़े सवालों के जवाब नहीं दे पाएगा जो उसने अब तक नहीं देखी है.

यही वजह है कि Retrieval ऑगमेंटेड जनरेशन (RAG) जैसे तरीकों से ऐसी अतिरिक्त जानकारी देने में मदद मिलती है जो एलएलएम को अपने उपयोगकर्ताओं के अनुरोधों को पूरा करने के लिए ज़रूरी हो. इससे, किसी ऐसी निजी जानकारी के साथ जवाब दिया जा सकता है जो ट्रेनिंग के समय ऐक्सेस न की जा सकती हो.

चलिए, बातचीत पर वापस चलते हैं. इस दौरान, आपके पास अपने दस्तावेज़ों के बारे में सवाल पूछने का विकल्प होगा. आपको एक ऐसा चैटबॉट बनाना होगा जो उस डेटाबेस से काम की जानकारी हासिल कर सके जिसमें आपके दस्तावेज़ छोटे-छोटे हिस्सों ("छोटे-छोटे हिस्सों") में बंटे होते हैं. साथ ही, मॉडल इस जानकारी का इस्तेमाल अपने जवाबों को आधार बनाने के लिए करेगा, न कि सिर्फ़ ट्रेनिंग में शामिल जानकारी पर.

आरएजी में दो चरण हैं:

  1. डेटा डालने का चरण — दस्तावेज़, मेमोरी में लोड किए जाते हैं और उन्हें छोटे-छोटे हिस्सों में बांट दिया जाता है. साथ ही, वेक्टर एम्बेडिंग (अलग-अलग हिस्सों को एक साथ कई डाइमेंशन में दिखाने वाला वेक्टर) का हिसाब लगाया जाता है और उसे ऐसे वेक्टर डेटाबेस में सेव किया जाता है जिसमें सिमैंटिक सर्च किया जा सकता है. डेटा डालने का यह चरण, आम तौर पर एक बार किया जाता है. ऐसा तब होता है, जब नए दस्तावेज़ों को दस्तावेज़ के संग्रह में जोड़ने की ज़रूरत होती है.

cd07d33d20ffa1c8.png

  1. क्वेरी फ़ेज़ — उपयोगकर्ता अब दस्तावेज़ों के बारे में सवाल पूछ सकते हैं. सवाल को भी वेक्टर में बदल दिया जाएगा और उसकी तुलना, डेटाबेस में मौजूद अन्य सभी वेक्टर से की जाएगी. सबसे मिलते-जुलते वेक्टर आम तौर पर शब्दार्थ रूप से जुड़े होते हैं और वेक्टर डेटाबेस के ज़रिए दिखाए जाते हैं. इसके बाद, एलएलएम को बातचीत का संदर्भ दिया जाता है. इसमें टेक्स्ट के वे हिस्से दिए जाते हैं जो डेटाबेस से लौटाए गए वेक्टर से मेल खाते हैं. इसके बाद, एलएलएम को इन हिस्सों को देखकर अपना जवाब देने के लिए कहा जाता है.

a1d2e2deb83c6d27.png

अपने दस्तावेज़ तैयार करना

इस नए डेमो के लिए, आपसे "सिर्फ़ ज़रूरी सूचनाएं पाने की ज़रूरत है" के बारे में सवाल पूछे जाएंगे रिसर्च पेपर इसमें ट्रांसफ़ॉर्मर न्यूरल नेटवर्क आर्किटेक्चर की जानकारी दी गई है, जिसकी शुरुआत Google ने की थी. आज-कल सभी मॉडर्न लार्ज लैंग्वेज मॉडल इसी तरह से लागू किए जाते हैं.

यह पेपर, डेटा स्टोर करने की जगह में पहले से ही attention-is-all-you-need.pdf में डाउनलोड हो चुका होता है.

चैटबॉट का इस्तेमाल करना

आइए, दो चरणों वाला तरीका बनाने का तरीका जानें: सबसे पहले, दस्तावेज़ का डेटा डालने की सुविधा के साथ और फिर क्वेरी के समय की मदद से, जब उपयोगकर्ता दस्तावेज़ के बारे में सवाल पूछते हैं.

इस उदाहरण में, दोनों चरण एक ही क्लास में लागू किए जाते हैं. आम तौर पर, आपके पास एक ऐप्लिकेशन होता है जो डेटा डालने का काम करता है. दूसरा, जो आपके उपयोगकर्ताओं को चैटबॉट इंटरफ़ेस उपलब्ध कराता है.

साथ ही, इस उदाहरण में हम इन-मेमोरी वेक्टर डेटाबेस का इस्तेमाल करेंगे. प्रोडक्शन की असल स्थिति में, डेटा डालने और क्वेरी करने के चरणों को दो अलग-अलग ऐप्लिकेशन में अलग-अलग किया जाएगा और वेक्टर एक स्टैंडअलोन डेटाबेस में रहते हैं.

दस्तावेज़ का डेटा डालना

दस्तावेज़ डालने के चरण का सबसे पहला चरण, डाउनलोड की गई PDF फ़ाइल ढूंढना और उसे पढ़ने के लिए PdfParser तैयार करना है:

URL url = new URI("https://github.com/glaforge/gemini-workshop-for-java-developers/raw/main/attention-is-all-you-need.pdf").toURL();
ApachePdfBoxDocumentParser pdfParser = new ApachePdfBoxDocumentParser();
Document document = pdfParser.parse(url.openStream());

चैट के लिए सामान्य भाषा वाला मॉडल बनाने के बजाय, एम्बेड करने वाले मॉडल का इंस्टेंस बनाएं. यह एक विशेष मॉडल है, जिसकी भूमिका टेक्स्ट के हिस्सों (शब्द, वाक्य या यहां तक कि पैराग्राफ़) का वेक्टर निरूपण करने में होती है. यह टेक्स्ट रिस्पॉन्स दिखाने के बजाय, फ़्लोटिंग पॉइंट नंबर के वेक्टर दिखाता है.

VertexAiEmbeddingModel embeddingModel = VertexAiEmbeddingModel.builder()
    .endpoint(System.getenv("LOCATION") + "-aiplatform.googleapis.com:443")
    .project(System.getenv("PROJECT_ID"))
    .location(System.getenv("LOCATION"))
    .publisher("google")
    .modelName("textembedding-gecko@003")
    .maxRetries(3)
    .build();

इसके बाद, आपको कुछ क्लास की ज़रूरत होगी, ताकि आप साथ मिलकर ये काम कर पाएं:

  • PDF दस्तावेज़ को लोड करें और अलग-अलग हिस्सों में बांटें.
  • इन सभी हिस्सों के लिए वेक्टर एम्बेडिंग बनाएं.
InMemoryEmbeddingStore<TextSegment> embeddingStore = 
    new InMemoryEmbeddingStore<>();

EmbeddingStoreIngestor storeIngestor = EmbeddingStoreIngestor.builder()
    .documentSplitter(DocumentSplitters.recursive(500, 100))
    .embeddingModel(embeddingModel)
    .embeddingStore(embeddingStore)
    .build();
storeIngestor.ingest(document);

वेक्टर एम्बेडिंग को सेव करने के लिए, इन-मेमोरी वेक्टर डेटाबेस InMemoryEmbeddingStore का एक इंस्टेंस बनाया गया है.

DocumentSplitters क्लास की वजह से दस्तावेज़ को अलग-अलग हिस्सों में बांटा गया है. यह PDF फ़ाइल के टेक्स्ट को 500 वर्णों के स्निपेट में बांट देगा. यह 100 वर्णों के ओवरलैप के साथ होगा. शब्दों या वाक्यों को बिट और पीस में काटने से बचने के लिए, नीचे दिए गए हिस्से के साथ ऐसा होगा.

स्टोर इंजेस्टर, वेक्टर का हिसाब लगाने के लिए दस्तावेज़ स्प्लिटर, एम्बेड करने वाले मॉडल, और इन-मेमोरी वेक्टर डेटाबेस को लिंक करता है. इसके बाद, डेटा डालने का काम ingest() तरीके से किया जाएगा.

अब पहला चरण खत्म हो गया है. दस्तावेज़ को वेक्टर एम्बेड करने की सुविधा के साथ, टेक्स्ट के छोटे हिस्सों में बदल दिया गया है और वेक्टर डेटाबेस में सेव कर दिया गया है.

सवाल पूछना

अब सवाल पूछने के लिए तैयार हो जाएं! बातचीत शुरू करने के लिए, कोई चैट मॉडल बनाएं:

ChatLanguageModel model = VertexAiGeminiChatModel.builder()
        .project(System.getenv("PROJECT_ID"))
        .location(System.getenv("LOCATION"))
        .modelName("gemini-1.5-flash-001")
        .maxOutputTokens(1000)
        .build();

आपको एम्बेड करने वाले मॉडल के साथ वेक्टर डेटाबेस (embeddingStore वैरिएबल में) को लिंक करने के लिए, रिट्रीवर क्लास की भी ज़रूरत होगी. इसका काम, उपयोगकर्ता की क्वेरी के लिए वेक्टर एम्बेडिंग का हिसाब लगाकर वेक्टर डेटाबेस से क्वेरी करना है, ताकि डेटाबेस में मिलते-जुलते वेक्टर खोजा जा सके:

EmbeddingStoreContentRetriever retriever =
    new EmbeddingStoreContentRetriever(embeddingStore, embeddingModel);

मुख्य तरीके के अलावा, एलएलएम विशेषज्ञ असिस्टेंट को दिखाने वाला इंटरफ़ेस बनाएं. यह एक ऐसा इंटरफ़ेस है जिसे AiServices क्लास आपके मॉडल से इंटरैक्ट करने के लिए इस्तेमाल करेगी:

interface LlmExpert {
    String ask(String question);
}

अब आपके पास एआई की नई सेवा को कॉन्फ़िगर करने का विकल्प है:

LlmExpert expert = AiServices.builder(LlmExpert.class)
    .chatLanguageModel(model)
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
    .contentRetriever(retriever)
    .build();

यह सेवा एक साथ बंधती है:

  • चैट की भाषा का वह मॉडल जिसे आपने पहले कॉन्फ़िगर किया है.
  • बातचीत का ट्रैक रखने के लिए, चैट की यादें.
  • रीट्रीवर, वेक्टर एम्बेड करने वाली क्वेरी की तुलना डेटाबेस के वेक्टर से करता है.
  • प्रॉम्प्ट टेंप्लेट में साफ़ तौर पर कहा गया हो कि चैट मॉडल को अपनी दी गई जानकारी के आधार पर जवाब देना चाहिए.यह जवाब, दी गई जानकारी के हिसाब से होना चाहिए. उदाहरण के लिए, दस्तावेज़ के ऐसे हिस्से जिनके वेक्टर एम्बेड करने की सुविधा, व्यक्ति के सवाल के वेक्टर से मिलती-जुलती है.
.retrievalAugmentor(DefaultRetrievalAugmentor.builder()
    .contentInjector(DefaultContentInjector.builder()
        .promptTemplate(PromptTemplate.from("""
            You are an expert in large language models,\s
            you excel at explaining simply and clearly questions about LLMs.

            Here is the question: {{userMessage}}

            Answer using the following information:
            {{contents}}
            """))
        .build())
    .contentRetriever(retriever)
    .build())

अब आप अपने सवाल पूछने के लिए तैयार हैं!

List.of(
    "What neural network architecture can be used for language models?",
    "What are the different components of a transformer neural network?",
    "What is attention in large language models?",
    "What is the name of the process that transforms text into vectors?"
).forEach(query ->
    System.out.printf("%n=== %s === %n%n %s %n%n", query, expert.ask(query)));
);

पूरा सोर्स कोड app/src/main/java/gemini/workshop डायरेक्ट्री के RAG.java में है:

सैंपल चलाएं:

./gradlew -q run -DjavaMainClass=gemini.workshop.RAG

आउटपुट में आपको अपने सवालों के जवाब दिखेंगे:

=== What neural network architecture can be used for language models? === 

 Transformer architecture 


=== What are the different components of a transformer neural network? === 

 The different components of a transformer neural network are:

1. Encoder: The encoder takes the input sequence and converts it into a 
sequence of hidden states. Each hidden state represents the context of 
the corresponding input token.
2. Decoder: The decoder takes the hidden states from the encoder and 
uses them to generate the output sequence. Each output token is 
generated by attending to the hidden states and then using a 
feed-forward network to predict the token's probability distribution.
3. Attention mechanism: The attention mechanism allows the decoder to 
attend to the hidden states from the encoder when generating each output 
token. This allows the decoder to take into account the context of the 
input sequence when generating the output sequence.
4. Positional encoding: Positional encoding is a technique used to 
inject positional information into the input sequence. This is important 
because the transformer neural network does not have any inherent sense 
of the order of the tokens in the input sequence.
5. Feed-forward network: The feed-forward network is a type of neural 
network that is used to predict the probability distribution of each 
output token. The feed-forward network takes the hidden state from the 
decoder as input and outputs a vector of probabilities. 


=== What is attention in large language models? === 

Attention in large language models is a mechanism that allows the model 
to focus on specific parts of the input sequence when generating the 
output sequence. This is important because it allows the model to take 
into account the context of the input sequence when generating each output token.

Attention is implemented using a function that takes two sequences as 
input: a query sequence and a key-value sequence. The query sequence is 
typically the hidden state from the previous decoder layer, and the 
key-value sequence is typically the sequence of hidden states from the 
encoder. The attention function computes a weighted sum of the values in 
the key-value sequence, where the weights are determined by the 
similarity between the query and the keys.

The output of the attention function is a vector of context vectors, 
which are then used as input to the feed-forward network in the decoder. 
The feed-forward network then predicts the probability distribution of 
the next output token.

Attention is a powerful mechanism that allows large language models to 
generate text that is both coherent and informative. It is one of the 
key factors that has contributed to the recent success of large language 
models in a wide range of natural language processing tasks. 


=== What is the name of the process that transforms text into vectors? === 

The process of transforming text into vectors is called **word embedding**.

Word embedding is a technique used in natural language processing (NLP) 
to represent words as vectors of real numbers. Each word is assigned a 
unique vector, which captures its meaning and semantic relationships 
with other words. Word embeddings are used in a variety of NLP tasks, 
such as machine translation, text classification, and question 
answering.

There are a number of different word embedding techniques, but one of 
the most common is the **skip-gram** model. The skip-gram model is a 
neural network that is trained to predict the surrounding words of a 
given word. By learning to predict the surrounding words, the skip-gram 
model learns to capture the meaning and semantic relationships of words.

Once a word embedding model has been trained, it can be used to 
transform text into vectors. To do this, each word in the text is 
converted to its corresponding vector. The vectors for all of the words 
in the text are then concatenated to form a single vector, which 
represents the entire text.

Text vectors can be used in a variety of NLP tasks. For example, text 
vectors can be used to train machine translation models, text 
classification models, and question answering models. Text vectors can 
also be used to perform tasks such as text summarization and text 
clustering. 

11. फ़ंक्शन कॉलिंग

कुछ मामलों में, आपको एलएलएम को बाहरी सिस्टम का ऐक्सेस देना चाहिए. जैसे, ऐसा रिमोट वेब एपीआई जो जानकारी हासिल करे, कोई कार्रवाई करे या किसी तरह का कंप्यूटेशन करे. उदाहरण के लिए:

रिमोट वेब एपीआई:

  • ग्राहक के ऑर्डर ट्रैक करना और उन्हें अपडेट करना.
  • समस्या को ट्रैक करने वाले टूल में टिकट ढूंढें या बनाएं.
  • रीयल-टाइम डेटा फ़ेच करें, जैसे कि स्टॉक के भाव या IoT सेंसर माप.
  • ईमेल भेजें.

कंप्यूटेशन टूल:

  • गणित के ऐडवांस सवालों के लिए कैलकुलेटर.
  • एलएलएम को रीज़निंग लॉजिक की ज़रूरत होने पर, कोड चलाने के लिए कोड की व्याख्या.
  • नैचुरल लैंग्वेज के अनुरोधों को एसक्यूएल क्वेरी में बदलें, ताकि एलएलएम किसी डेटाबेस के लिए क्वेरी कर सके.

फ़ंक्शन कॉलिंग, मॉडल के लिए एक या उससे ज़्यादा फ़ंक्शन कॉल का अनुरोध करने की क्षमता है, ताकि वह ज़्यादा नए डेटा के साथ उपयोगकर्ता के सवाल का सही जवाब दे सके.

किसी व्यक्ति के किसी खास प्रॉम्प्ट और उस कॉन्टेक्स्ट के हिसाब से काम के मौजूदा फ़ंक्शन की जानकारी होने पर, एलएलएम फ़ंक्शन कॉल का अनुरोध करके जवाब दे सकता है. एलएलएम को इंटिग्रेट करने वाला ऐप्लिकेशन, फ़ंक्शन को कॉल कर सकता है और फिर जवाब के साथ एलएलएम का जवाब दे सकता है. इसके बाद, एलएलएम टेक्स्ट के ज़रिए जवाब देकर जवाब देता है.

फ़ंक्शन कॉल करने के चार चरण

आइए, फ़ंक्शन कॉल करने के उदाहरण पर नज़र डालते हैं: मौसम के पूर्वानुमान के बारे में जानकारी पाना.

अगर Gemini या किसी अन्य एलएलएम से पेरिस के मौसम के बारे में पूछा जाता है, तो वह जवाब देगा कि उसमें मौसम के पूर्वानुमान की कोई जानकारी नहीं है. अगर आपको एलएलएम को मौसम के डेटा को रीयल-टाइम में ऐक्सेस करने की सुविधा देनी है, तो आपको कुछ ऐसे फ़ंक्शन तय करने होंगे जिनका इस्तेमाल किया जा सके.

इस डायग्राम को देखें:

31e0c2AB5e6f21c.png

1️⃣ सबसे पहले कोई उपयोगकर्ता पेरिस के मौसम के बारे में पूछता है. चैटबॉट ऐप्लिकेशन को एक या एक से ज़्यादा ऐसे फ़ंक्शन के बारे में पता होता है जो एलएलएम से क्वेरी को पूरा करने में मदद करते हैं. चैटबॉट, दोनों शुरुआती प्रॉम्प्ट और कॉल किए जा सकने वाले फ़ंक्शन की सूची भेजता है. इसमें getWeather() नाम का एक फ़ंक्शन है, जो जगह के लिए स्ट्रिंग पैरामीटर लेता है.

8863be53a73c4a70.png

एलएलएम को मौसम के पूर्वानुमान की जानकारी नहीं होती. इसलिए, यह मैसेज का जवाब देने के बजाय, फ़ंक्शन को लागू करने का अनुरोध भेजता है. चैटबॉट को "Paris" वाले getWeather() फ़ंक्शन को जगह की जानकारी के पैरामीटर के तौर पर कॉल करना होगा.

d1367cc69c07b14d.png

2️⃣ चैटबॉट, एलएलएम की ओर से उस फ़ंक्शन को शुरू करता है और फ़ंक्शन के रिस्पॉन्स को वापस लाता है. हमारा मानना है कि जवाब {"forecast": "sunny"} है.

73a5f2ed19f47d8.png

3️⃣ चैटबॉट ऐप्लिकेशन JSON के जवाब को वापस एलएलएम पर भेज देगा.

20832cb1ee6fbfeb.png

4️⃣ एलएलएम JSON से मिले जवाब को देखता है और उस जानकारी को समझता है. इसके बाद, वह यह लिखकर जवाब देता है कि पेरिस में मौसम कैसा है.

हर चरण को कोड के तौर पर

सबसे पहले, Gemini मॉडल को सामान्य तरीके से कॉन्फ़िगर करना होगा:

ChatLanguageModel model = VertexAiGeminiChatModel.builder()
    .project(System.getenv("PROJECT_ID"))
    .location(System.getenv("LOCATION"))
    .modelName("gemini-1.5-flash-001")
    .maxOutputTokens(100)
    .build();

आप टूल की खास बातें तय करते हैं. इससे यह जानकारी मिलती है कि किस तरह के फ़ंक्शन को कॉल किया जा सकता है:

ToolSpecification weatherToolSpec = ToolSpecification.builder()
    .name("getWeatherForecast")
    .description("Get the weather forecast for a location")
    .addParameter("location", JsonSchemaProperty.STRING,
        JsonSchemaProperty.description("the location to get the weather forecast for"))
    .build();

फ़ंक्शन का नाम, पैरामीटर का नाम और टाइप तय किया जाता है, लेकिन यह ध्यान रखें कि फ़ंक्शन और पैरामीटर, दोनों के बारे में जानकारी दी गई है. ब्यौरे बहुत अहम होते हैं. इनसे एलएलएम को यह समझने में मदद मिलती है कि फ़ंक्शन क्या कर सकता है. इससे यह तय किया जा सकेगा कि इस फ़ंक्शन को बातचीत के संदर्भ में कॉल करने की ज़रूरत है या नहीं.

आइए, पेरिस के मौसम के बारे में शुरुआती सवाल भेजकर, चरण #1 शुरू करें:

List<ChatMessage> allMessages = new ArrayList<>();

// 1) Ask the question about the weather
UserMessage weatherQuestion = UserMessage.from("What is the weather in Paris?");
allMessages.add(weatherQuestion);

दूसरे चरण में, हम मॉडल को जिस टूल का इस्तेमाल करने के लिए भेजना चाहते हैं उसे पास करते हैं. इसके बाद, मॉडल बहुत ज़्यादा एक्ज़ीक्यूशन के अनुरोध के साथ जवाब देता है:

// 2) The model replies with a function call request
Response<AiMessage> messageResponse = model.generate(allMessages, weatherToolSpec);
ToolExecutionRequest toolExecutionRequest = messageResponse.content().toolExecutionRequests().getFirst();
System.out.println("Tool execution request: " + toolExecutionRequest);
allMessages.add(messageResponse.content());

चरण #3. अब हमें पता है कि एलएलएम हमें किस फ़ंक्शन के लिए कॉल करेगा. कोड में, हम किसी बाहरी एपीआई को असली कॉल नहीं करते. हम सिर्फ़ एक काल्पनिक मौसम का पूर्वानुमान बताते हैं:

// 3) We send back the result of the function call
ToolExecutionResultMessage toolExecResMsg = ToolExecutionResultMessage.from(toolExecutionRequest,
    "{\"location\":\"Paris\",\"forecast\":\"sunny\", \"temperature\": 20}");
allMessages.add(toolExecResMsg);

चौथे चरण में, एलएलएम को फ़ंक्शन के एक्ज़ीक्यूट होने के नतीजे के बारे में पता चलता है. इसके बाद, वह टेक्स्ट वाले जवाब को सिंथेसाइज़ कर सकता है:

// 4) The model answers with a sentence describing the weather
Response<AiMessage> weatherResponse = model.generate(allMessages);
System.out.println("Answer: " + weatherResponse.content().text());

आउटपुट है:

Tool execution request: ToolExecutionRequest { id = null, name = "getWeatherForecast", arguments = "{"location":"Paris"}" }
Answer:  The weather in Paris is sunny with a temperature of 20 degrees Celsius.

टूल को लागू करने के अनुरोध के ऊपर मौजूद आउटपुट में, जवाब भी देखा जा सकता है.

पूरा सोर्स कोड app/src/main/java/gemini/workshop डायरेक्ट्री के FunctionCalling.java में है:

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.FunctionCalling

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

Tool execution request: ToolExecutionRequest { id = null, name = "getWeatherForecast", arguments = "{"location":"Paris"}" }
Answer:  The weather in Paris is sunny with a temperature of 20 degrees Celsius.

12. LangChain4j फ़ंक्शन कॉलिंग को हैंडल करता है

पिछले चरण में, आपने देखा कि सामान्य टेक्स्ट वाले सवाल/जवाब और फ़ंक्शन के अनुरोध/रिस्पॉन्स के इंटरैक्शन को कैसे इंटरलीव किया जाता है. साथ ही, आपने अनुरोध किए गए फ़ंक्शन का जवाब सीधे तौर पर दिया है, बिना किसी रीयल फ़ंक्शन को कॉल किए.

हालांकि, LangChain4j एक हाई-लेवल ऐब्स्ट्रैक्ट भी है. यह बातचीत को आपके लिए सामान्य तरीके से हैंडल करते हुए, फ़ंक्शन कॉल को आपके लिए पारदर्शी तरीके से हैंडल कर सकता है.

सिंगल फ़ंक्शन कॉल

आइए, FunctionCallingAssistant.java के बारे में जानें.

सबसे पहले, एक रिकॉर्ड बनाएं, जो फ़ंक्शन के रिस्पॉन्स डेटा के स्ट्रक्चर को दिखाएगा:

record WeatherForecast(String location, String forecast, int temperature) {}

जवाब में जगह, पूर्वानुमान, और तापमान के बारे में जानकारी शामिल होती है.

इसके बाद, एक ऐसी क्लास बनाएं जिसमें वह असल फ़ंक्शन शामिल हो जिसे आपको मॉडल में उपलब्ध कराना है:

static class WeatherForecastService {
    @Tool("Get the weather forecast for a location")
    WeatherForecast getForecast(@P("Location to get the forecast for") String location) {
        if (location.equals("Paris")) {
            return new WeatherForecast("Paris", "Sunny", 20);
        } else if (location.equals("London")) {
            return new WeatherForecast("London", "Rainy", 15);
        } else {
            return new WeatherForecast("Unknown", "Unknown", 0);
        }
    }
}

ध्यान दें कि इस क्लास में सिर्फ़ एक फ़ंक्शन है, लेकिन इसकी व्याख्या @Tool एनोटेशन के साथ की जाती है. यह जानकारी, उस फ़ंक्शन की जानकारी से जुड़ी होती है जिसे मॉडल कॉल करने का अनुरोध कर सकता है.

फ़ंक्शन के पैरामीटर (यहां एक है) के बारे में भी बताया गया है, लेकिन इस छोटे @P एनोटेशन के साथ पैरामीटर की जानकारी भी दी गई है. ज़्यादा मुश्किल स्थितियों के लिए, मॉडल में जितने चाहें उतने फ़ंक्शन जोड़े जा सकते हैं.

इस क्लास में, पहले से तैयार कुछ जवाब दिए जाते हैं. हालांकि, अगर आपको मौसम के पूर्वानुमान वाली किसी बाहरी सेवा को कॉल करना है, तो इस तरीके के हिसाब से आपको उस सेवा को कॉल करना होगा.

जैसा कि हमने पिछले तरीके में ToolSpecification बनाया था, इसलिए यह जानना ज़रूरी है कि फ़ंक्शन क्या करता है. साथ ही, यह बताना ज़रूरी है कि पैरामीटर किससे जुड़े हैं. इससे मॉडल को यह समझने में मदद मिलती है कि इस फ़ंक्शन का इस्तेमाल कब और कैसे किया जा सकता है.

इसके बाद, LangChain4j आपको एक ऐसा इंटरफ़ेस उपलब्ध कराने की अनुमति देता है जो उस कॉन्ट्रैक्ट से जुड़ा होता है जिसका इस्तेमाल आपको मॉडल के साथ इंटरैक्ट करने के लिए करना है. यहां, यह एक आसान इंटरफ़ेस दिया गया है. यह उपयोगकर्ता के मैसेज को दिखाने वाली स्ट्रिंग लेता है और मॉडल के रिस्पॉन्स के हिसाब से स्ट्रिंग दिखाता है:

interface WeatherAssistant {
    String chat(String userMessage);
}

अगर आपको ज़्यादा ऐडवांस लेवल की स्थितियों को हैंडल करना है, तो LangChain4j के UserMessage (उपयोगकर्ता मैसेज के लिए) या AiMessage (मॉडल से जवाब देने के लिए) TokenStream या TokenStream वाले सिग्नेचर का भी इस्तेमाल किया जा सकता है. ऐसा इसलिए, क्योंकि इन मुश्किल ऑब्जेक्ट में इस्तेमाल किए गए टोकन की संख्या जैसी अतिरिक्त जानकारी भी होती है. आसान शब्दों में कहें, तो हम सिर्फ़ इनपुट में स्ट्रिंग और आउटपुट में स्ट्रिंग लेंगे.

चलिए, अब सभी हिस्सों को एक साथ जोड़ने वाले main() तरीके की मदद लेते हैं:

public static void main(String[] args) {
    ChatLanguageModel model = VertexAiGeminiChatModel.builder()
        .project(System.getenv("PROJECT_ID"))
        .location(System.getenv("LOCATION"))
        .modelName("gemini-1.5-flash-001")
        .maxOutputTokens(100)
        .build();

    WeatherForecastService weatherForecastService = new WeatherForecastService();

    WeatherAssistant assistant = AiServices.builder(WeatherAssistant.class)
        .chatLanguageModel(model)
        .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
        .tools(weatherForecastService)
        .build();

    System.out.println(assistant.chat("What is the weather in Paris?"));
}

हमेशा की तरह, Gemini के चैट मॉडल को कॉन्फ़िगर किया जाता है. इसके बाद, मौसम के पूर्वानुमान वाली सेवा को इंस्टैंशिएट किया जाता है, जिसमें "फ़ंक्शन" होता है कि मॉडल हमसे कॉल करने का अनुरोध करेगा.

अब चैट मॉडल, चैट मेमोरी, और टूल (जैसे, मौसम के पूर्वानुमान वाली सेवा को इसके फ़ंक्शन से) बाइंड करने के लिए, AiServices क्लास का फिर से इस्तेमाल किया जाता है. AiServices एक ऐसा ऑब्जेक्ट दिखाता है जो आपके तय किए गए WeatherAssistant इंटरफ़ेस को लागू करता है. उस Assistant के chat() तरीके को कॉल करना ही बचा है. इसे लागू करते समय, आपको सिर्फ़ टेक्स्ट रिस्पॉन्स दिखेंगे. हालांकि, फ़ंक्शन कॉल के अनुरोध और फ़ंक्शन कॉल के रिस्पॉन्स, डेवलपर को नहीं दिखेंगे. इन अनुरोधों को अपने-आप और पारदर्शिता के साथ मैनेज किया जाएगा. अगर Gemini को लगता है कि किसी फ़ंक्शन को कॉल करना चाहिए, तो वह फ़ंक्शन कॉल का अनुरोध करेगा. इसके बाद, LangChain4j आपकी ओर से लोकल फ़ंक्शन को कॉल करेगा.

सैंपल चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.FunctionCallingAssistant

आपको इससे मिलता-जुलता आउटपुट दिखेगा:

OK. The weather in Paris is sunny with a temperature of 20 degrees.

यह एक फ़ंक्शन का उदाहरण था.

एक से ज़्यादा फ़ंक्शन कॉल

आपके पास एक से ज़्यादा फ़ंक्शन हो सकते हैं और LangChain4j को आपकी ओर से एक से ज़्यादा फ़ंक्शन कॉल हैंडल करने दें. एक से ज़्यादा फ़ंक्शन के उदाहरण के लिए, MultiFunctionCallingAssistant.java पर एक नज़र डालें.

इसमें मुद्राओं को बदलने का फ़ंक्शन मौजूद है:

@Tool("Convert amounts between two currencies")
double convertCurrency(
    @P("Currency to convert from") String fromCurrency,
    @P("Currency to convert to") String toCurrency,
    @P("Amount to convert") double amount) {

    double result = amount;

    if (fromCurrency.equals("USD") && toCurrency.equals("EUR")) {
        result = amount * 0.93;
    } else if (fromCurrency.equals("USD") && toCurrency.equals("GBP")) {
        result = amount * 0.79;
    }

    System.out.println(
        "convertCurrency(fromCurrency = " + fromCurrency +
            ", toCurrency = " + toCurrency +
            ", amount = " + amount + ") == " + result);

    return result;
}

किसी स्टॉक की कीमत जानने के लिए एक और फ़ंक्शन:

@Tool("Get the current value of a stock in US dollars")
double getStockPrice(@P("Stock symbol") String symbol) {
    double result = 170.0 + 10 * new Random().nextDouble();

    System.out.println("getStockPrice(symbol = " + symbol + ") == " + result);

    return result;
}

किसी दी गई रकम पर प्रतिशत लागू करने के लिए दूसरा फ़ंक्शन:

@Tool("Apply a percentage to a given amount")
double applyPercentage(@P("Initial amount") double amount, @P("Percentage between 0-100 to apply") double percentage) {
    double result = amount * (percentage / 100);

    System.out.println("applyPercentage(amount = " + amount + ", percentage = " + percentage + ") == " + result);

    return result;
}

इसके बाद, इन सभी फ़ंक्शन और MultiTools क्लास को एक साथ जोड़ा जा सकता है. साथ ही, इस तरह का सवाल पूछा जा सकता है, "डॉलर से यूरो में बदली गई AAPL स्टॉक की 10% कीमत कितनी है?"

public static void main(String[] args) {
    ChatLanguageModel model = VertexAiGeminiChatModel.builder()
        .project(System.getenv("PROJECT_ID"))
        .location(System.getenv("LOCATION"))
        .modelName("gemini-1.5-flash-001")
        .maxOutputTokens(100)
        .build();

    MultiTools multiTools = new MultiTools();

    MultiToolsAssistant assistant = AiServices.builder(MultiToolsAssistant.class)
        .chatLanguageModel(model)
        .chatMemory(withMaxMessages(10))
        .tools(multiTools)
        .build();

    System.out.println(assistant.chat(
        "What is 10% of the AAPL stock price converted from USD to EUR?"));
}

इसे इस तरह से चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.MultiFunctionCallingAssistant

साथ ही, आपको कई फ़ंक्शन दिखेंगे, जिन्हें कहा जाता है:

getStockPrice(symbol = AAPL) == 172.8022224055534
convertCurrency(fromCurrency = USD, toCurrency = EUR, amount = 172.8022224055534) == 160.70606683716468
applyPercentage(amount = 160.70606683716468, percentage = 10.0) == 16.07060668371647
10% of the AAPL stock price converted from USD to EUR is 16.07060668371647 EUR.

एजेंटों की ओर

Gemini जैसे लार्ज लैंग्वेज मॉडल के लिए, फ़ंक्शन कॉलिंग एक बेहतरीन एक्सटेंशन है. इसकी मदद से हम ज़्यादा जटिल सिस्टम बना सकते हैं, जिन्हें अक्सर "एजेंट" कहा जाता है या "एआई असिस्टेंट" शामिल हैं. ये एजेंट, बाहरी एपीआई और ऐसी सेवाओं के साथ बाहरी दुनिया से इंटरैक्ट कर सकते हैं जिनका बाहरी पर्यावरण पर खराब असर पड़ सकता है. जैसे, ईमेल भेजना, टिकट बनाना वगैरह.

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

13. ओलामा और TestContainers के साथ जेमा चलाना

अब तक हम Gemini का इस्तेमाल कर रहे हैं. हालाँकि, Gemma नाम की इसकी छोटी सिस्टर मॉडल भी है.

Gemma एक लाइटवेट और बेहतरीन ओपन मॉडल है. इसे Gemini मॉडल में इस्तेमाल की गई रिसर्च और टेक्नोलॉजी का इस्तेमाल करके बनाया गया है. Gemma, Gemma1 और Gemma2 के दो वैरिएंट में उपलब्ध है. हर वैरिएंट अलग-अलग साइज़ में उपलब्ध है. Gemma1 दो साइज़ में उपलब्ध है: 2B और 7B. Gemma2 दो साइज़ में उपलब्ध है: 9B और 27B. ये Chromebook डिवाइसों पर आसानी से उपलब्ध हैं. इन्हें छोटे साइज़ में इस्तेमाल करने का मतलब है कि इन्हें अपने लैपटॉप या Cloud Shell पर खुद चलाया जा सकता है.

Gemma कैसे चलाया जाता है?

Gemma को चलाने के कई तरीके हैं: क्लाउड में, एक बटन पर क्लिक करके Vertex AI के ज़रिए या कुछ जीपीयू पर GKE (जीकेई) के ज़रिए, लेकिन इसे स्थानीय तौर पर भी चलाया जा सकता है.

Gemma को स्थानीय तौर पर चलाने का एक अच्छा विकल्प Ollama है. यह ऐसा टूल है जिससे आपको अपनी लोकल मशीन पर Llama 2, Mistral और कई अन्य छोटे मॉडल चलाने में मदद मिलती है. यह Docker की तरह ही है. हालांकि, यह एलएलएम के लिए भी काम करता है.

अपने ऑपरेटिंग सिस्टम के निर्देश का पालन करके, Ollama इंस्टॉल करें.

अगर आप Linux वातावरण का इस्तेमाल कर रहे हैं, तो आपको ओलामा को इंस्टॉल करने के बाद पहले उसे चालू करना होगा.

ollama serve > /dev/null 2>&1 & 

डिवाइस पर इंस्टॉल होने के बाद, मॉडल बनाने के लिए निर्देश दिए जा सकते हैं:

ollama pull gemma:2b

मॉडल निकाले जाने तक इंतज़ार करें. इसमें कुछ समय लग सकता है.

मॉडल चलाएं:

ollama run gemma:2b

अब मॉडल का इस्तेमाल किया जा सकता है:

>>> Hello!
Hello! It's nice to hear from you. What can I do for you today?

प्रॉम्प्ट से बाहर निकलने के लिए, Ctrl+D दबाएं

TestContainers पर ओलामा में जेमा चलाना

Ollama को स्थानीय तौर पर इंस्टॉल और चलाने के बजाय, आप TestContainers से मैनेज किए जाने वाले कंटेनर के अंदर Ollama का इस्तेमाल कर सकते हैं.

TestContainers का इस्तेमाल सिर्फ़ टेस्टिंग के लिए नहीं होता, बल्कि इसका इस्तेमाल कंटेनर को एक्ज़ीक्यूट करने के लिए भी किया जा सकता है. यहां तक कि एक खास OllamaContainer भी है, जिसका आप फ़ायदा ले सकते हैं!

यहां पूरी जानकारी दी गई है:

2382c05a48708dfd.png

लागू करना

आइए, GemmaWithOllamaContainer.java के बारे में जानें.

सबसे पहले, आपको एक ऐसा ओलामा कंटेनर बनाना होगा जो जेमा मॉडल के साथ आता हो. यह इमेज या तो पहले की गई प्रोसेस से पहले से मौजूद है या इसे बना दिया जाएगा. अगर इमेज पहले से मौजूद है, तो आपको TestContainers को यह बताना होगा कि डिफ़ॉल्ट Ollama इमेज को Gemma के साथ काम करने वाले वैरिएंट से बदल दिया जाए:

private static final String TC_OLLAMA_GEMMA_2_B = "tc-ollama-gemma-2b";

// Creating an Ollama container with Gemma 2B if it doesn't exist.
private static OllamaContainer createGemmaOllamaContainer() throws IOException, InterruptedException {

    // Check if the custom Gemma Ollama image exists already
    List<Image> listImagesCmd = DockerClientFactory.lazyClient()
        .listImagesCmd()
        .withImageNameFilter(TC_OLLAMA_GEMMA_2_B)
        .exec();

    if (listImagesCmd.isEmpty()) {
        System.out.println("Creating a new Ollama container with Gemma 2B image...");
        OllamaContainer ollama = new OllamaContainer("ollama/ollama:0.1.26");
        ollama.start();
        ollama.execInContainer("ollama", "pull", "gemma:2b");
        ollama.commitToImage(TC_OLLAMA_GEMMA_2_B);
        return ollama;
    } else {
        System.out.println("Using existing Ollama container with Gemma 2B image...");
        // Substitute the default Ollama image with our Gemma variant
        return new OllamaContainer(
            DockerImageName.parse(TC_OLLAMA_GEMMA_2_B)
                .asCompatibleSubstituteFor("ollama/ollama"));
    }
}

इसके बाद, एक Ollama टेस्ट कंटेनर बनाया और शुरू किया जाता है. इसके बाद, जिस मॉडल का इस्तेमाल करना है उसके पते और पोर्ट की ओर जाकर, एक Ollama चैट मॉडल बनाया जाता है. आखिर में, आप हमेशा की तरह model.generate(yourPrompt) शुरू करते हैं:

public static void main(String[] args) throws IOException, InterruptedException {
    OllamaContainer ollama = createGemmaOllamaContainer();
    ollama.start();

    ChatLanguageModel model = OllamaChatModel.builder()
        .baseUrl(String.format("http://%s:%d", ollama.getHost(), ollama.getFirstMappedPort()))
        .modelName("gemma:2b")
        .build();

    String response = model.generate("Why is the sky blue?");

    System.out.println(response);
}

इसे इस तरह से चलाएं:

./gradlew run -q -DjavaMainClass=gemini.workshop.GemmaWithOllamaContainer

पहली बार कंटेनर बनाने और चलाने में थोड़ा समय लगेगा, लेकिन एक बार पूरा हो जाने पर, आपको Gemma यह जवाब देगा:

INFO: Container ollama/ollama:0.1.26 started in PT2.827064047S
The sky appears blue due to Rayleigh scattering. Rayleigh scattering is a phenomenon that occurs when sunlight interacts with molecules in the Earth's atmosphere.

* **Scattering particles:** The main scattering particles in the atmosphere are molecules of nitrogen (N2) and oxygen (O2).
* **Wavelength of light:** Blue light has a shorter wavelength than other colors of light, such as red and yellow.
* **Scattering process:** When blue light interacts with these molecules, it is scattered in all directions.
* **Human eyes:** Our eyes are more sensitive to blue light than other colors, so we perceive the sky as blue.

This scattering process results in a blue appearance for the sky, even though the sun is actually emitting light of all colors.

In addition to Rayleigh scattering, other atmospheric factors can also influence the color of the sky, such as dust particles, aerosols, and clouds.

आपका Gemma Cloud Shell में चल रहा है!

14. बधाई हो

बधाई हो, आपने LangChain4j और Gemini API का इस्तेमाल करके, Java में अपना पहला जनरेटिव एआई चैट ऐप्लिकेशन बना लिया है! आपको इस बारे में पता चला है कि मल्टीमोडल लार्ज लैंग्वेज मॉडल काफ़ी दमदार होते हैं. साथ ही, इनमें सवालों/जवाबों जैसे कई कामों को आसानी से पूरा किया जा सकता है. इनमें अपने दस्तावेज़, डेटा निकालने, बाहरी एपीआई के साथ इंटरैक्ट करने जैसे कई काम भी किए जा सकते हैं.

आगे क्या होगा?

अब शानदार एलएलएम इंटिग्रेशन की मदद से, अपने ऐप्लिकेशन को और बेहतर बनाने की बारी है!

आगे पढ़ें

पहचान फ़ाइलें