रीट्राइवल-एग्मेंटेड जनरेशन (आरएजी)

Firebase Genkit आपको ऐसी ऐब्स्ट्रैक्ट जानकारी देता है जो फिर से हासिल करने में मदद करने वाली जनरेशन में आपकी मदद करती हैं (RAG) फ़्लो के साथ-साथ ऐसे प्लगिन भी जो मिलते-जुलते टूल के साथ इंटिग्रेशन करते हैं.

आरएजी क्या है?

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

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

वहीं दूसरी ओर, RAG एक प्रॉम्प्ट में बाहरी डेटा सोर्स को शामिल करके काम करता है मॉडल को पास किया गया समय होता है. उदाहरण के लिए, आपके पास प्रॉम्प्ट, "लीसा के साथ बार्ट का क्या संबंध है?" को इससे विस्तृत ("संवर्धित") किया जा सकता है कुछ प्रासंगिक जानकारी शामिल करके, "होमर और मीना के बच्चों के नाम बार्ट, लिसा, और मैगी हैं. बार्ट का रिश्ता क्या है को लीसा को?"

इस तरीके के कई फ़ायदे हैं:

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

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

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

  • इंडेक्स करने वाले लोग: "इंडेक्स" में दस्तावेज़ जोड़ें.
  • एम्बेडर: यह दस्तावेज़ों को वेक्टर रिप्रज़ेंटेशन में बदलता है
  • रिट्रीवर: किसी क्वेरी के नतीजे के तौर पर "इंडेक्स" से दस्तावेज़ फिर से हासिल करते हैं.

इन परिभाषाओं को अलग-अलग मकसद से बनाया गया है. ऐसा इसलिए है, क्योंकि Genkit किसी प्रॉडक्ट के बारे में कोई राय नहीं देता "इंडेक्स" क्या होता है है या इसमें से कोई दस्तावेज़ कितने सटीक तरीके से लिया जाता है. सिर्फ़ Genkit के लिए Document फ़ॉर्मैट उपलब्ध कराता है और बाकी सब कुछ रिट्रीवर की ओर से तय किया जाता है या इंडेक्स करने की सुविधा देने वाली कंपनी.

इंडेक्स करने वाले लोग

यह इंडेक्स की ज़िम्मेदारी है कि आप अपने दस्तावेज़ों को इस तरह ट्रैक करें कि की मदद से किसी खास क्वेरी के लिए, काम के दस्तावेज़ों को तुरंत वापस पाया जा सकता है. यह ज़्यादातर अक्सर वेक्टर डेटाबेस का इस्तेमाल करके पूरा किया जाता है. यह डेटाबेस का इस्तेमाल करके, आपके दस्तावेज़ों को इंडेक्स करता है बहु-आयामी वेक्टर होते हैं, जिन्हें एम्बेडिंग कहा जाता है. एम्बेड किया गया टेक्स्ट (ओपेक) टेक्स्ट के किसी पैसेज से बताए गए कॉन्सेप्ट को दिखाता है; ये जनरेट होते हैं एमएल मॉडल इस्तेमाल किए जा सकते हैं. टेक्स्ट को इंडेक्स करने के लिए, उसे एम्बेड करने की सुविधा से, एक वेक्टर इस डेटाबेस में, सैद्धांतिक तौर पर जुड़े हुए टेक्स्ट को इकट्ठा करने और दस्तावेज़ों को वापस पाने की सुविधा है टेक्स्ट (क्वेरी) की नई स्ट्रिंग से जुड़ा हो.

जनरेट करने के लिए दस्तावेज़ों को वापस पाने से पहले, आपको ये काम करने होंगे उन्हें अपने दस्तावेज़ के इंडेक्स में डाला जा सकता है. डेटा डालने का सामान्य फ़्लो, फ़ॉलो किया जा रहा है:

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

    Genkit में, पहले से मौजूद चंकिंग लाइब्रेरी उपलब्ध नहीं हैं; हालांकि, हमारे पास ऐसी सोर्स लाइब्रेरी उपलब्ध हैं जो Genkit के साथ काम करती हैं.

  2. हर हिस्से के लिए एम्बेडिंग जनरेट करें. आपके इस्तेमाल किए जा रहे डेटाबेस के हिसाब से, आप खास तौर पर एम्बेड करने वाले जनरेशन मॉडल के साथ ऐसा कर सकते हैं या डेटाबेस से मिले एम्बेडिंग जनरेटर का इस्तेमाल कर सकता है.

  3. डेटाबेस में, टेक्स्ट का हिस्सा और उसका इंडेक्स जोड़ें.

अगर आप काम कर रहे हैं, तो डेटा डालने का फ़्लो कम या सिर्फ़ एक बार चल सकता है जिसकी मदद से डेटा का एक स्थायी स्रोत चुना जा सकता है. दूसरी ओर, अगर डेटा का इस्तेमाल किया जा रहा है, तो जो बार-बार बदलता रहता है, तो शायद आप डेटा डालने का फ़्लो लगातार चलाना चाहें ( उदाहरण के लिए, जब भी कोई दस्तावेज़ अपडेट किया जाता है, तब Cloud Firestore ट्रिगर में).

एम्बेडर

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

रिट्रीवर

रिट्रीवर एक ऐसा सिद्धांत है जो किसी भी तरह के दस्तावेज़ से जुड़े लॉजिक को एन्क्रिप्ट (सुरक्षित) करता है वापस पाना. आमतौर पर धनवापसी के सबसे लोकप्रिय मामलों में हालांकि, वेक्टर स्टोर, Genkit में एक रिट्रीवर ऐसा कोई भी फ़ंक्शन हो सकता है जो डेटा दिखाता है.

रिट्रीवर बनाने के लिए, यहां दिए गए तरीकों में से किसी एक का इस्तेमाल करें या अपना खुद का बनाएं.

इस्तेमाल किए जा सकने वाले इंडेक्स, रिट्रीवर, और एम्बेडर

Genkit अपने प्लगिन सिस्टम की मदद से, इंडेक्स करने वाले और रिट्रीवर की सहायता करता है. कॉन्टेंट बनाने निम्न प्लगिन आधिकारिक रूप से समर्थित हैं:

  • Pinecone क्लाउड वेक्टर डेटाबेस

इसके अलावा, Genkit पहले से तय की गई सेवाओं के ज़रिए, इन वेक्टर स्टोर के साथ काम करता है कोड टेंप्लेट, जिन्हें डेटाबेस कॉन्फ़िगरेशन के हिसाब से कस्टमाइज़ किया जा सकता है और स्कीमा:

एम्बेडिंग मॉडल की सहायता इन प्लगिन के ज़रिए दी जाती है:

प्लग इन मॉडल
Google का जनरेटिव एआई गेको टेक्स्ट एम्बेड करना
Google Vertex AI गेको टेक्स्ट एम्बेड करना

आरएजी फ़्लो तय करना

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

डिपेंडेंसी इंस्टॉल करें

इस उदाहरण में, हम langchaingo की textsplitter लाइब्रेरी और ledongthuc/pdf की PDF पार्सिंग लाइब्रेरी:

go get github.com/tmc/langchaingo/textsplitter
go get github.com/ledongthuc/pdf

इंडेक्सर तय करना

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

यह लोकल फ़ाइल-आधारित वेक्टर समानता रिट्रीवर का इस्तेमाल करता है जेनकिट आसान टेस्टिंग और प्रोटोटाइपिंग के लिए एक नया प्रॉडक्ट उपलब्ध कराता है (ऐसा न करें प्रोडक्शन में इस्तेमाल किया जा सकता है)

इंडेक्सर बनाना

// Import Genkit's file-based vector retriever, (Don't use in production.)
import "github.com/firebase/genkit/go/plugins/localvec"

// Vertex AI provides the text-embedding-004 embedder model.
import "github.com/firebase/genkit/go/plugins/vertexai"
ctx := context.Background()

err := vertexai.Init(ctx, &vertexai.Config{})
if err != nil {
    log.Fatal(err)
}
err = localvec.Init()
if err != nil {
    log.Fatal(err)
}

menuPDFIndexer, _, err := localvec.DefineIndexerAndRetriever(
    "menuQA",
    localvec.Config{
        Embedder: vertexai.Embedder("text-embedding-004"),
    },
)
if err != nil {
    log.Fatal(err)
}

चंकिंग कॉन्फ़िगरेशन बनाएं

इस उदाहरण में, textsplitter लाइब्रेरी का इस्तेमाल किया गया है, जिसमें आसान टेक्स्ट स्प्लिटर की मदद से दस्तावेज़ों को सेगमेंट में बांटा जा सकता है, जिन्हें वेक्टराइज़ किया जा सकता है.

नीचे दी गई परिभाषा, चंकिंग फ़ंक्शन को कॉन्फ़िगर करती है, ताकि दस्तावेज़ दिखाया जा सके 200 वर्णों के सेगमेंट, जिनमें 20 वर्णों के हिस्से ओवरलैप हों.

splitter := textsplitter.NewRecursiveCharacter(
    textsplitter.WithChunkSize(200),
    textsplitter.WithChunkOverlap(20),
)

इस लाइब्रेरी के लिए चकराने वाले और विकल्प यहां मिल सकते हैं langchaingo दस्तावेज़.

इंडेक्स करने वाला फ़्लो तय करना

genkit.DefineFlow(
    "indexMenu",
    func(ctx context.Context, path string) (any, error) {
        // Extract plain text from the PDF. Wrap the logic in Run so it
        // appears as a step in your traces.
        pdfText, err := genkit.Run(ctx, "extract", func() (string, error) {
            return readPDF(path)
        })
        if err != nil {
            return nil, err
        }

        // Split the text into chunks. Wrap the logic in Run so it
        // appears as a step in your traces.
        docs, err := genkit.Run(ctx, "chunk", func() ([]*ai.Document, error) {
            chunks, err := splitter.SplitText(pdfText)
            if err != nil {
                return nil, err
            }

            var docs []*ai.Document
            for _, chunk := range chunks {
                docs = append(docs, ai.DocumentFromText(chunk, nil))
            }
            return docs, nil
        })
        if err != nil {
            return nil, err
        }

        // Add chunks to the index.
        err = menuPDFIndexer.Index(ctx, &ai.IndexerRequest{Documents: docs})
        return nil, err
    },
)
// Helper function to extract plain text from a PDF. Excerpted from
// https://github.com/ledongthuc/pdf
func readPDF(path string) (string, error) {
    f, r, err := pdf.Open(path)
    if f != nil {
        defer f.Close()
    }
    if err != nil {
        return "", err
    }

    reader, err := r.GetPlainText()
    if err != nil {
        return "", err
    }

    bytes, err := io.ReadAll(reader)
    if err != nil {
        return "", err
    }
    return string(bytes), nil
}

इंडेक्सर फ़्लो चलाएं

genkit flow:run indexMenu "'menu.pdf'"

indexMenu फ़्लो चलने के बाद, वेक्टर डेटाबेस को इसके साथ जोड़ा जाएगा इसमें ऐसे दस्तावेज़ शामिल होते हैं जो Genkit फ़्लो में इस्तेमाल करने के लिए तैयार होते हैं. साथ ही, डेटा वापस पाने के चरणों के बारे में भी बताते हैं.

वापस पाने की प्रक्रिया के साथ फ़्लो तय करना

इस उदाहरण में दिखाया गया है कि आरएजी फ़्लो में रिट्रीवर का इस्तेमाल कैसे किया जा सकता है. किसी ने भी पसंद नहीं किया इंडेक्सर उदाहरण में, इस उदाहरण में Genkit के फ़ाइल-आधारित वेक्टर रिट्रीवर का इस्तेमाल किया गया है, जिसका इस्तेमाल आपको प्रोडक्शन में नहीं करना चाहिए.

    ctx := context.Background()

    err := vertexai.Init(ctx, &vertexai.Config{})
    if err != nil {
        log.Fatal(err)
    }
    err = localvec.Init()
    if err != nil {
        log.Fatal(err)
    }

    model := vertexai.Model("gemini-1.5-pro")

    _, menuPdfRetriever, err := localvec.DefineIndexerAndRetriever(
        "menuQA",
        localvec.Config{
            Embedder: vertexai.Embedder("text-embedding-004"),
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    genkit.DefineFlow(
        "menuQA",
        func(ctx context.Context, question string) (string, error) {
            // Retrieve text relevant to the user's question.
            docs, err := menuPdfRetriever.Retrieve(ctx, &ai.RetrieverRequest{
                Document: ai.DocumentFromText(question, nil),
            })
            if err != nil {
                return "", err
            }

            // Construct a system message containing the menu excerpts you just
            // retrieved.
            menuInfo := ai.NewSystemTextMessage("Here's the menu context:")
            for _, doc := range docs.Documents {
                menuInfo.Content = append(menuInfo.Content, doc.Content...)
            }

            // Call Generate, including the menu information in your prompt.
            resp, err := model.Generate(ctx, &ai.GenerateRequest{
                Messages: []*ai.Message{
                    ai.NewSystemTextMessage(`
You are acting as a helpful AI assistant that can answer questions about the
food available on the menu at Genkit Grub Pub.
Use only the context provided to answer the question. If you don't know, do not
make up an answer. Do not add or change items on the menu.`),
                    menuInfo,
                    ai.NewUserTextMessage(question),
                },
            }, nil)
            if err != nil {
                return "", err
            }

            return resp.Text()
        })

अपने खुद के इंडेक्सर और रिट्रीवर तैयार करें

अपना खुद का रिट्रीवर बनाना भी संभव है. यह तब फ़ायदेमंद होता है, जब दस्तावेज़ों को ऐसे डॉक्यूमेंट स्टोर में मैनेज किया जाता है जो Genkit में काम नहीं करता (उदाहरण: MySQL, Google Drive वगैरह). Genkit SDK टूल की मदद से, आसान तरीके से तो आपको दस्तावेज़ फ़ेच करने के लिए कस्टम कोड देना होगा.

आपके पास कस्टम रिट्रीवर को तय करने का विकल्प भी होता है. ये ऐसे उपयोगकर्ता होते हैं जो मौजूदा रिट्रीवर के ऊपर बनते हैं और आरएजी की बेहतर तकनीकों का इस्तेमाल करें, जैसे कि रीरैंकिंग या प्रॉम्प्ट एक्सटेंशन).

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

type CustomMenuRetrieverOptions struct {
    K          int
    PreRerankK int
}
advancedMenuRetriever := ai.DefineRetriever(
    "custom",
    "advancedMenuRetriever",
    func(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) {
        // Handle options passed using our custom type.
        opts, _ := req.Options.(CustomMenuRetrieverOptions)
        // Set fields to default values when either the field was undefined
        // or when req.Options is not a CustomMenuRetrieverOptions.
        if opts.K == 0 {
            opts.K = 3
        }
        if opts.PreRerankK == 0 {
            opts.PreRerankK = 10
        }

        // Call the retriever as in the simple case.
        response, err := menuPDFRetriever.Retrieve(ctx, &ai.RetrieverRequest{
            Document: req.Document,
            Options:  localvec.RetrieverOptions{K: opts.PreRerankK},
        })
        if err != nil {
            return nil, err
        }

        // Re-rank the returned documents using your custom function.
        rerankedDocs := rerank(response.Documents)
        response.Documents = rerankedDocs[:opts.K]

        return response, nil
    },
)