نسل افزوده بازیابی (RAG)، نسل افزوده بازیابی (RAG)، نسل افزوده بازیابی (RAG)

Firebase Genkit انتزاعی‌هایی را ارائه می‌کند که به شما کمک می‌کند جریان‌های تولید تقویت‌شده بازیابی (RAG) بسازید، و همچنین پلاگین‌هایی که با ابزارهای مرتبط ادغام می‌شوند.

RAG چیست؟

تولید افزوده بازیابی تکنیکی است که برای ترکیب منابع خارجی اطلاعات در پاسخ‌های LLM استفاده می‌شود. مهم است که بتوانیم این کار را انجام دهیم، زیرا در حالی که LLM ها معمولاً بر روی مجموعه وسیعی از مطالب آموزش می بینند، استفاده عملی از LLM اغلب به دانش حوزه خاصی نیاز دارد (به عنوان مثال، ممکن است بخواهید از یک LLM برای پاسخ به سوالات مشتریان در مورد شرکت خود استفاده کنید. محصولات).

یک راه حل این است که مدل را با استفاده از داده های خاص تر تنظیم کنید. با این حال، این می تواند هم از نظر هزینه محاسباتی و هم از نظر تلاش مورد نیاز برای تهیه داده های آموزشی کافی گران باشد.

در مقابل، RAG با ترکیب منابع داده خارجی در یک اعلان در زمان ارسال به مدل کار می کند. به عنوان مثال، می توانید این اعلان را تصور کنید: "رابطه بارت با لیزا چیست؟" ممکن است با اضافه کردن برخی اطلاعات مرتبط، گسترش ("افزایش") شود، و در نتیجه این دستور، "فرزندان هومر و مارج بارت، لیزا، و مگی نامیده می شوند. رابطه بارت با لیزا چیست؟"

این رویکرد چندین مزیت دارد:

  • این می تواند مقرون به صرفه تر باشد زیرا نیازی به آموزش مجدد مدل ندارید.
  • شما می توانید به طور مداوم منبع داده خود را به روز کنید و LLM می تواند بلافاصله از اطلاعات به روز شده استفاده کند.
  • شما اکنون این پتانسیل را دارید که در پاسخ های LLM خود به منابع استناد کنید.

از طرف دیگر، استفاده از RAG به طور طبیعی به معنای درخواست های طولانی تر است و برخی از خدمات LLM API برای هر توکن ورودی که ارسال می کنید، هزینه دریافت می کنند. در نهایت، شما باید مبادلات هزینه را برای برنامه های خود ارزیابی کنید.

RAG یک منطقه بسیار گسترده است و تکنیک های مختلفی برای دستیابی به بهترین کیفیت RAG استفاده می شود. چارچوب اصلی Genkit سه انتزاع اصلی را برای کمک به شما در انجام RAG ارائه می دهد:

  • نمایه سازها: اسناد را به یک "نمایه" اضافه کنید.
  • Embedders: اسناد را به یک نمایش برداری تبدیل می کند
  • Retrievers: بازیابی اسناد از یک "شاخص"، با یک پرس و جو.

این تعاریف عمداً گسترده هستند، زیرا Genkit درباره چیستی «نمایه» یا نحوه دقیق بازیابی اسناد از آن نظری ندارد. Genkit فقط یک فرمت Document را ارائه می دهد و بقیه موارد توسط ارائه دهنده پیاده سازی بازیابی یا نمایه ساز تعریف می شود.

نمایه سازها

ایندکس وظیفه ردیابی اسناد شما را دارد به گونه ای که بتوانید به سرعت اسناد مربوطه را با توجه به یک پرس و جو خاص بازیابی کنید. این اغلب با استفاده از یک پایگاه داده برداری انجام می شود، که اسناد شما را با استفاده از بردارهای چند بعدی به نام embeddings فهرست می کند. تعبیه متن (به صورت غیر شفاف) مفاهیمی را نشان می دهد که توسط یک متن بیان شده است. اینها با استفاده از مدل‌های ML با هدف خاص تولید می‌شوند. با نمایه سازی متن با استفاده از جاسازی آن، یک پایگاه داده برداری می تواند متن مرتبط مفهومی را خوشه بندی کند و اسناد مربوط به یک رشته متن جدید (پرس و جو) را بازیابی کند.

قبل از اینکه بتوانید اسناد را به منظور تولید بازیابی کنید، باید آنها را در فهرست سند خود وارد کنید. یک جریان بلع معمولی موارد زیر را انجام می دهد:

  1. اسناد بزرگ را به اسناد کوچکتر تقسیم کنید تا فقط از بخش های مربوطه برای تقویت درخواست های شما استفاده شود - "قطع کردن". این امر ضروری است زیرا بسیاری از LLM ها دارای یک پنجره زمینه محدود هستند که گنجاندن کل اسناد با یک اعلان را غیرعملی می کند.

    Genkit کتابخانه های تکه ای داخلی را ارائه نمی دهد. با این حال، کتابخانه های منبع باز در دسترس هستند که با Genkit سازگار هستند.

  2. برای هر تکه جاسازی ایجاد کنید. بسته به پایگاه داده ای که استفاده می کنید، ممکن است این کار را به صراحت با یک مدل تولید جاسازی انجام دهید، یا ممکن است از ژنراتور جاسازی ارائه شده توسط پایگاه داده استفاده کنید.

  3. تکه متن و فهرست آن را به پایگاه داده اضافه کنید.

اگر با یک منبع داده پایدار کار می کنید، ممکن است جریان دریافت خود را به ندرت یا فقط یک بار اجرا کنید. از طرف دیگر، اگر با داده‌هایی کار می‌کنید که مرتباً تغییر می‌کنند، ممکن است به طور مداوم جریان انتقال را اجرا کنید (به عنوان مثال، در یک راه‌انداز Cloud Firestore، هر زمان که یک سند به‌روزرسانی می‌شود).

تعبیه کننده ها

Embedder تابعی است که محتوا (متن، تصاویر، صدا و غیره) را می گیرد و یک بردار عددی ایجاد می کند که معنای معنایی محتوای اصلی را رمزگذاری می کند. همانطور که در بالا ذکر شد، embedder ها به عنوان بخشی از فرآیند نمایه سازی مورد استفاده قرار می گیرند، با این حال، آنها همچنین می توانند به طور مستقل برای ایجاد جاسازی های بدون شاخص استفاده شوند.

رتریورها

بازیابی مفهومی است که منطق مربوط به هر نوع بازیابی سند را در بر می گیرد. محبوب ترین موارد بازیابی معمولاً شامل بازیابی از فروشگاه های برداری است، با این حال، در Genkit یک بازیابی می تواند هر تابعی باشد که داده را برمی گرداند.

برای ایجاد یک Retriever، می توانید از یکی از پیاده سازی های ارائه شده استفاده کنید یا خود را ایجاد کنید.

نمایه سازها، رتریورها و embedderها پشتیبانی می شود

Genkit از طریق سیستم پلاگین خود، از فهرست‌کننده و بازیابی پشتیبانی می‌کند. پلاگین های زیر به طور رسمی پشتیبانی می شوند:

علاوه بر این، Genkit از ذخیره‌های برداری زیر از طریق قالب‌های کد از پیش تعریف‌شده پشتیبانی می‌کند، که می‌توانید آن‌ها را برای پیکربندی و طرح پایگاه داده خود سفارشی کنید:

پشتیبانی از مدل جاسازی از طریق پلاگین های زیر ارائه می شود:

پلاگین مدل ها
هوش مصنوعی Google Generative جاسازی متن gecko
Google Vertex AI جاسازی متن gecko

تعریف جریان RAG

مثال‌های زیر نشان می‌دهند که چگونه می‌توانید مجموعه‌ای از اسناد PDF منوی رستوران را در یک پایگاه داده برداری وارد کنید و آنها را برای استفاده در جریانی بازیابی کنید که تعیین می‌کند چه اقلام غذایی در دسترس هستند.

برای پردازش فایل های PDF وابستگی ها را نصب کنید

npm install llm-chunk pdf-parse @genkit-ai/dev-local-vectorstore
npm i -D --save @types/pdf-parse

یک فروشگاه برداری محلی به پیکربندی خود اضافه کنید

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

const ai = genkit({
  plugins: [
    // vertexAI provides the textEmbedding004 embedder
    vertexAI(),

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

یک Indexer را تعریف کنید

مثال زیر نشان می دهد که چگونه می توان یک نمایه ساز ایجاد کرد تا مجموعه ای از اسناد PDF را جذب کند و آنها را در یک پایگاه داده برداری محلی ذخیره کند.

از بازیابی شباهت برداری مبتنی بر فایل محلی استفاده می کند که Genkit به صورت خارج از جعبه برای آزمایش و نمونه سازی ساده ارائه می کند ( در تولید استفاده نکنید )

نمایه ساز را ایجاد کنید

export const menuPdfIndexer = devLocalIndexerRef('menuQA');

ایجاد پیکربندی chunking

این مثال از کتابخانه llm-chunk استفاده می‌کند که یک تقسیم‌کننده متن ساده را برای تقسیم اسناد به بخش‌هایی که می‌توانند بردار شوند، فراهم می‌کند.

تعریف زیر تابع chunking را برای تضمین یک بخش سند بین 1000 تا 2000 کاراکتر، شکسته در انتهای جمله، با همپوشانی بین تکه های 100 کاراکتری، پیکربندی می کند.

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

گزینه های تکه تکه بیشتر برای این کتابخانه را می توان در مستندات llm-chunk یافت.

جریان نمایه ساز خود را تعریف کنید

import { Document } from 'genkit/retriever';
import { chunk } from 'llm-chunk';
import { readFile } from 'fs/promises';
import path from 'path';
import pdf from 'pdf-parse';

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

export const indexMenu = ai.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 ai.index({
      indexer: menuPdfIndexer,
      documents,
    });
  }
);

جریان نمایه ساز را اجرا کنید

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

پس از اجرای جریان indexMenu ، پایگاه داده برداری با اسناد و مدارک آماده می شود تا در جریان های Genkit با مراحل بازیابی استفاده شود.

یک جریان را با بازیابی تعریف کنید

مثال زیر نشان می دهد که چگونه می توانید از یک رتریور در یک جریان RAG استفاده کنید. مانند مثال نمایه ساز، این مثال از بازیابی برداری مبتنی بر فایل Genkit استفاده می کند که نباید در تولید از آن استفاده کنید.

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

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

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

    // generate a response
   const { text } = await ai.generate({
      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}`,
      docs,
    });

    return text;
  }
);

ایندکسرها و رتریورهای خود را بنویسید

همچنین این امکان وجود دارد که بازیابی خود را ایجاد کنید. اگر اسناد شما در یک فروشگاه اسنادی که در Genkit پشتیبانی نمی شود (به عنوان مثال: MySQL، Google Drive و غیره) مدیریت شوند، مفید است. Genkit SDK روش های انعطاف پذیری را ارائه می دهد که به شما امکان می دهد کد سفارشی برای واکشی اسناد ارائه دهید. همچنین می‌توانید رتریورهای سفارشی را تعریف کنید که بر روی رتریورهای موجود در Genkit ساخته می‌شوند و تکنیک‌های پیشرفته RAG (مانند رتبه‌بندی مجدد یا پسوندهای سریع) را در بالا اعمال می‌کنند.

رتریورهای ساده

رتریورهای ساده به شما امکان می دهند کدهای موجود را به راحتی به رتریور تبدیل کنید:

import { z } from "genkit";
import { searchEmails } from "./db";

ai.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,
} from 'genkit/retriever';
import { z } from 'genkit';

export const menuRetriever = devLocalRetrieverRef('menuQA');

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

const advancedMenuRetriever = ai.defineRetriever(
  {
    name: `custom/advancedMenuRetriever`,
    configSchema: advancedMenuRetrieverOptionsSchema,
  },
  async (input, options) => {
    const extendedPrompt = await extendPrompt(input);
    const docs = await ai.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 ai.retrieve({
  retriever: advancedRetriever,
  query: input,
  options: { preRerankK: 7, k: 3 },
});

رتبه بندی مجدد و بازیابی دو مرحله ای

مدل رتبه بندی مجدد - همچنین به عنوان رمزگذار متقاطع شناخته می شود - نوعی مدل است که با توجه به یک پرس و جو و سند، یک امتیاز شباهت به دست می دهد. ما از این امتیاز برای مرتب سازی مجدد اسناد بر اساس ارتباط با درخواست خود استفاده می کنیم. API های Reranker لیستی از اسناد (به عنوان مثال خروجی یک بازیابی) را می گیرند و اسناد را بر اساس ارتباط آنها با پرس و جو مرتب می کنند. این مرحله می تواند برای تنظیم دقیق نتایج و حصول اطمینان از استفاده از مناسب ترین اطلاعات در اعلان ارائه شده به یک مدل تولیدی مفید باشد.

مثال Reranker

یک رتبه‌بندی مجدد در Genkit با نحوی مشابه به بازیابی‌کننده‌ها و نمایه‌سازها تعریف می‌شود. در اینجا یک مثال با استفاده از رتبه بندی مجدد در Genkit آورده شده است. این جریان مجموعه ای از اسناد را بر اساس ارتباط آنها با پرس و جو ارائه شده با استفاده از یک رتبه بندی مجدد Vertex AI از پیش تعریف شده رتبه بندی می کند.

const FAKE_DOCUMENT_CONTENT = [
  'pythagorean theorem',
  'e=mc^2',
  'pi',
  'dinosaurs',
  'quantum mechanics',
  'pizza',
  'harry potter',
];

export const rerankFlow = ai.defineFlow(
  {
    name: 'rerankFlow',
    inputSchema: z.object({ query: z.string() }),
    outputSchema: z.array(
      z.object({
        text: z.string(),
        score: z.number(),
      })
    ),
  },
  async ({ query }) => {
    const documents = FAKE_DOCUMENT_CONTENT.map((text) =>
       ({ content: text })
    );

    const rerankedDocuments = await ai.rerank({
      reranker: 'vertexai/semantic-ranker-512',
      query:  ({ content: query }),
      documents,
    });

    return rerankedDocuments.map((doc) => ({
      text: doc.content,
      score: doc.metadata.score,
    }));
  }
);

این رتبه‌بندی مجدد از پلاگین Vertex AI genkit با semantic-ranker-512 برای امتیازدهی و رتبه‌بندی اسناد استفاده می‌کند. هرچه امتیاز بالاتر باشد، سند با پرس و جو مرتبط تر است.

رتبه بندی مجدد سفارشی

همچنین می‌توانید رتبه‌بندی‌های سفارشی را متناسب با مورد خاص خود تعریف کنید. این زمانی مفید است که بخواهید اسناد را با استفاده از منطق سفارشی خود یا یک مدل سفارشی رتبه بندی کنید. در اینجا یک مثال ساده از تعریف مجدد رتبه بندی سفارشی آورده شده است:

export const customReranker = ai.defineReranker(
  {
    name: 'custom/reranker',
    configSchema: z.object({
      k: z.number().optional(),
    }),
  },
  async (query, documents, options) => {
    // Your custom reranking logic here
    const rerankedDocs = documents.map((doc) => {
      const score = Math.random(); // Assign random scores for demonstration
      return {
        ...doc,
        metadata: { ...doc.metadata, score },
      };
    });

    return rerankedDocs.sort((a, b) => b.metadata.score - a.metadata.score).slice(0, options.k || 3);
  }
);

پس از تعریف، این رتبه‌بندی مجدد سفارشی می‌تواند مانند هر رتبه‌بندی مجدد دیگری در جریان‌های RAG شما مورد استفاده قرار گیرد و به شما انعطاف‌پذیری برای اجرای استراتژی‌های رتبه‌بندی پیشرفته می‌دهد.