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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

एम्बेडर

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

रिट्रीवर

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

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

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

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

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

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

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

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

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

PDF प्रोसेस करने के लिए डिपेंडेंसी इंस्टॉल करें

npm install llm-chunk pdf-parse
npm i -D --save @types/pdf-parse

अपने कॉन्फ़िगरेशन में कोई लोकल वेक्टर स्टोर जोड़ें

import {
  devLocalIndexerRef,
  devLocalVectorstore,
} from '@genkit-ai/dev-local-vectorstore';
import { textEmbeddingGecko, vertexAI } from '@genkit-ai/vertexai';

configureGenkit({
  plugins: [
    // vertexAI provides the textEmbeddingGecko embedder
    vertexAI(),

    // the local vector store requires an embedder to translate from text to vector
    devLocalVectorstore([
      {
        indexName: 'menuQA',
        embedder: textEmbeddingGecko,
      },
    ]),
  ],
});

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

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

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

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

import { devLocalIndexerRef } from '@genkit-ai/dev-local-vectorstore';

export const menuPdfIndexer = devLocalIndexerRef('menuQA');

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

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

नीचे दी गई परिभाषा, चंकिंग फ़ंक्शन को कॉन्फ़िगर करती है, ताकि 100 वर्णों के बीच ओवरलैप के साथ 1,000 से 2,000 वर्णों के बीच वाले दस्तावेज़ के सेगमेंट की सुरक्षा की जा सके.

const chunkingConfig = {
  minLength: 1000,
  maxLength: 2000,
  splitter: 'sentence',
  overlap: 100,
  delimiters: '',
} as any;

इस लाइब्रेरी के लिए छोटे-छोटे हिस्सों को हटाने के और विकल्प, llm-chunk दस्तावेज़ में मिल सकते हैं.

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

import { index } from '@genkit-ai/ai';
import { Document } from '@genkit-ai/ai/retriever';
import { defineFlow, run } from '@genkit-ai/flow';
import { readFile } from 'fs/promises';
import { chunk } from 'llm-chunk';
import path from 'path';
import pdf from 'pdf-parse';
import * as z from 'zod';

export const indexMenu = defineFlow(
  {
    name: 'indexMenu',
    inputSchema: z.string().describe('PDF file path'),
    outputSchema: z.void(),
  },
  async (filePath: string) => {
    filePath = path.resolve(filePath);

    // Read the pdf.
    const pdfTxt = await run('extract-text', () =>
      extractTextFromPdf(filePath)
    );

    // Divide the pdf text into segments.
    const chunks = await run('chunk-it', async () =>
      chunk(pdfTxt, chunkingConfig)
    );

    // Convert chunks of text into documents to store in the index.
    const documents = chunks.map((text) => {
      return Document.fromText(text, { filePath });
    });

    // Add documents to the index.
    await index({
      indexer: menuPdfIndexer,
      documents,
    });
  }
);

async function extractTextFromPdf(filePath: string) {
  const pdfFile = path.resolve(filePath);
  const dataBuffer = await readFile(pdfFile);
  const data = await pdf(dataBuffer);
  return data.text;
}

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

genkit flow:run indexMenu "'../pdfs'"

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

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

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

import { generate } from '@genkit-ai/ai';
import { retrieve } from '@genkit-ai/ai/retriever';
import { devLocalRetrieverRef } from '@genkit-ai/dev-local-vectorstore';
import { defineFlow } from '@genkit-ai/flow';
import { geminiPro } from '@genkit-ai/vertexai';
import * as z from 'zod';

// Define the retriever reference
export const menuRetriever = devLocalRetrieverRef('menuQA');

export const menuQAFlow = defineFlow(
  { name: 'menuQA', inputSchema: z.string(), outputSchema: z.string() },
  async (input: string) => {
    // retrieve relevant documents
    const docs = await retrieve({
      retriever: menuRetriever,
      query: input,
      options: { k: 3 },
    });

    // generate a response
    const llmResponse = await generate({
      model: geminiPro,
      prompt: `
    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.

    Question: ${input}
    `,
      context: docs,
    });

    const output = llmResponse.text();
    return output;
  }
);

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

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

सिंपल रिट्रीवर

सिंपल रिट्रीवर की मदद से, मौजूदा कोड को आसानी से रिकवर करने वालों में बदला जा सकता है:

import {
  defineSimpleRetriever,
  retrieve
} from '@genkit-ai/ai/retriever';
import { searchEmails } from './db';
import { z } from 'zod';

defineSimpleRetriever({
  name: 'myDatabase',
  configSchema: z.object({
    limit: z.number().optional()
  }).optional(),
  // we'll extract "message" from the returned email item
  content: 'message',
  // and several keys to use as metadata
  metadata: ['from', 'to', 'subject'],
} async (query, config) => {
  const result = await searchEmails(query.text(), {limit: config.limit});
  return result.data.emails;
});

कस्टम रिट्रीवर

import {
  CommonRetrieverOptionsSchema,
  defineRetriever,
  retrieve,
} from '@genkit-ai/ai/retriever';
import * as z from 'zod';

export const menuRetriever = devLocalRetrieverRef('menuQA');

const advancedMenuRetrieverOptionsSchema = CommonRetrieverOptionsSchema.extend({
  preRerankK: z.number().max(1000),
});

const advancedMenuRetriever = defineRetriever(
  {
    name: `custom/advancedMenuRetriever`,
    configSchema: advancedMenuRetrieverOptionsSchema,
  },
  async (input, options) => {
    const extendedPrompt = await extendPrompt(input);
    const docs = await retrieve({
      retriever: menuRetriever,
      query: extendedPrompt,
      options: { k: options.preRerankK || 10 },
    });
    const rerankedDocs = await rerank(docs);
    return rerankedDocs.slice(0, options.k || 3);
  }
);

(extendPrompt और rerank कुछ ऐसा है जिसे आपको खुद लागू करना होगा, फ़्रेमवर्क उपलब्ध नहीं कराता)

इसके बाद, अपने रिट्रीवर को बदला जा सकता है:

const docs = await retrieve({
  retriever: advancedRetriever,
  query: input,
  options: { preRerankK: 7, k: 3 },
});