إنشاء تكنولوجيا استرداد البيانات المعززة (RAG)

ويوفّر Firebase Genkit اقتراحات تساعدك في إنشاء تدفقات الجيل المعزز (RAG) باسترداد البيانات، بالإضافة إلى مكونات إضافية توفّر عمليات دمج مع الأدوات ذات الصلة.

ما المقصود بطريقة RAG؟

إنّ الإنشاء المعزز الاسترجاعي هو أسلوب يُستخدم لدمج مصادر المعلومات الخارجية في ردود النموذج اللغوي الكبير. ومن المهم أن تكون قادرًا على إجراء ذلك، لأنّه يتم تدريب النماذج اللغوية الكبيرة عادةً على مجموعة كبيرة من المواد، إلا أنّ الاستخدام العملي للنماذج اللغوية الكبيرة غالبًا ما يتطلّب معرفة مجال معيّن (على سبيل المثال، قد تحتاج إلى استخدام النموذج اللغوي الكبير للإجابة عن أسئلة العملاء حول منتجات شركتك).

أحد الحلول هو تحسين النموذج باستخدام بيانات أكثر تحديدًا. ومع ذلك، قد يكون ذلك مكلفًا من حيث التكلفة الحوسبية والجهد المطلوب لإعداد بيانات التدريب الكافية.

في المقابل، تعمل طريقة RAG من خلال دمج مصادر البيانات الخارجية في مطالبة في وقت تمريرها إلى النموذج. على سبيل المثال، يمكنك أن تتخيل المطالبة، "ما هي علاقة بارت بليزا؟" يمكن توسيعها ("معززة") من خلال إضافة بعض المعلومات ذات الصلة، مما يؤدي إلى ظهور المطالبة، "أطفال هوميروس ومارج تُسمّى بارت وليزا وماجي. ما علاقة بارت بليزا؟"

ويتميز هذا الأسلوب بعدة مزايا:

  • قد يكون أكثر فعالية من حيث التكلفة لأنك لن تحتاج إلى إعادة تدريب النموذج.
  • يمكنك تعديل مصدر بياناتك باستمرار، وسيستفيد النموذج اللغوي الكبير على الفور من المعلومات المعدَّلة.
  • يمكنك الآن ذِكر المراجع في ردود النموذج اللغوي الكبير.

من ناحية أخرى، يعني استخدام طريقة RAG بشكل طبيعي طلبات أطول، كما تحصّل بعض خدمات واجهة برمجة التطبيقات اللغوية الكبيرة رسومًا مقابل كل رمز إدخال ترسله. في النهاية، يجب عليك تقييم مقايضات التكلفة لتطبيقاتك.

RAG هي منطقة واسعة للغاية وهناك العديد من الأساليب المختلفة المستخدمة لتحقيق أفضل جودة RAG. يقدّم إطار عمل Genkit الأساسي تجريدين رئيسيين لمساعدتك على تنفيذ RAG:

  • المفهرسة: يمكنك إضافة المستندات إلى "فهرس".
  • أدوات التضمين: تحول المستندات إلى تمثيل متجه
  • المستردون: استرداد المستندات من "فهرس" معين، وفقًا للاستعلام.

هذه التعريفات هي عامة عن قصد لأنّ Genkit ليس لديه رأي بشأن ماهية "الفهرس" أو كيفية استرداد المستندات منه بالضبط. توفّر Genkit فقط تنسيق Document ويتم تحديد كل شيء آخر من خلال موفّر تنفيذ برنامج المسترد أو أداة الفهرسة.

أدوات الفهرسة

ويكون الفهرس مسؤولاً عن تتبع مستنداتك بطريقة تتيح لك استرداد المستندات ذات الصلة بسرعة عند استخدام استعلام محدد. يتم تحقيق ذلك في أغلب الأحيان باستخدام قاعدة بيانات متجهات، والتي تفهرس مستنداتك باستخدام متجهات متعددة الأبعاد تسمى التضمينات. يمثل تضمين النص (بشكل غير شفاف) المفاهيم التي يتم التعبير عنها بفقرة من النص، ويتم إنشاؤها باستخدام نماذج تعلُّم الآلة لغرض خاص. من خلال فهرسة النص باستخدام تضمينه، يمكن لقاعدة بيانات المتجه تجميع النص المرتبط من الناحية النظرية واسترداد المستندات المتعلقة بسلسلة نص جديدة (الاستعلام).

قبل أن تتمكن من استرداد المستندات لغرض إنشائها، يجب نقلها إلى فهرس المستندات الخاص بك. وينفّذ تدفق العرض النموذجي ما يلي:

  1. قسِّم المستندات الكبيرة إلى مستندات أصغر حجمًا بحيث يتم استخدام الأجزاء ذات الصلة فقط لزيادة طلباتك، وهي "التقسيم إلى أجزاء". يُعدّ ذلك ضروريًا لأنّ العديد من النماذج اللغوية الكبيرة لها نافذة سياق محدودة، ما يجعل من غير العملي تضمين مستندات كاملة بدون طلب.

    لا توفر Genkit مكتبات التقسيم المضمنة. ومع ذلك، تتوفر مكتبات مفتوحة المصدر متوافقة مع Genkit.

  2. إنشاء تضمينات لكل مقطع. واعتمادًا على قاعدة البيانات التي تستخدمها، يمكنك إجراء ذلك صراحةً باستخدام نموذج لإنشاء التضمين أو قد تستخدم أداة إنشاء التضمين التي توفرها قاعدة البيانات.

  3. أضف مقطع النص والفهرس الخاص به إلى قاعدة البيانات.

يمكنك إجراء عملية النقل بشكل غير متكرّر أو مرّة واحدة فقط إذا كنت تعمل باستخدام مصدر ثابت للبيانات. في المقابل، إذا كنت تعمل على بيانات تتغير بشكل متكرر، يمكنك تنفيذ خطوات النقل بشكل مستمر (على سبيل المثال، في مشغِّل Cloud Firestore كلما تم تعديل المستند).

أدوات التضمين

أداة التضمين هي دالة تأخذ المحتوى (النص والصور والصوت وما إلى ذلك) وتُنشئ متجهًا رقميًا يشفِّر المعنى الدلالي للمحتوى الأصلي. كما ذكرنا أعلاه، تتم الاستفادة من أدوات التضمين كجزء من عملية الفهرسة، ولكن يمكن أيضًا استخدامها بشكل مستقل لإنشاء عمليات تضمين بدون فهرس.

ريتريفر

المسترد هو مفهوم يغلف المنطق المرتبط بأي نوع من استرجاع المستندات. تتضمن حالات الاسترجاع الأكثر شيوعًا عادةً الاسترجاع من متاجر المتجهات، ومع ذلك، في Genkit يمكن أن يكون جهاز الاسترجاع أي دالة تقوم بإرجاع البيانات.

لإنشاء جهاز استرداد، يمكنك استخدام أحد عمليات التنفيذ المتوفرة أو إنشاء تطبيقك الخاص.

برامج الفهرسة وبرامج استرداد البيانات وتضمينات المتوافقة

توفر Genkit إمكانية عمل المفهرس والاسترداد من خلال نظام المكونات الإضافية. المكونات الإضافية التالية معتمدة رسميًا:

بالإضافة إلى ذلك، تدعم Genkit متاجر المتجه التالية من خلال قوالب التعليمات البرمجية المحددة مسبقًا، والتي يمكنك تخصيصها لإعداد قاعدة البيانات ومخططها:

ويتم توفير إمكانية تضمين النموذج من خلال المكوّنات الإضافية التالية:

المكوّن الإضافي الطرُز
الذكاء الاصطناعي التوليدي من Google تضمين نص أبو بريص
الذكاء الاصطناعي من Google Vertex تضمين نص أبو بريص

تحديد تدفق RAG

توضح الأمثلة التالية كيف يمكنك نقل مجموعة من مستندات 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 وتخزينها في قاعدة بيانات متجهات محلية.

وهي تستخدم أداة استرداد التشابه المتشابه المتجهي المستندة إلى الملفات المحلية، والتي يوفّرها Genkit عمليات اختبار ونماذج أولية بسيطة (لا تستخدمه في الإنتاج).

إنشاء أداة الفهرسة

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

export const menuPdfIndexer = devLocalIndexerRef('menuQA');

إنشاء تهيئة التجميع

يستخدم هذا المثال مكتبة llm-chunk التي توفّر أداة تقسيم نص بسيطة لتقسيم المستندات إلى أجزاء يمكن توجيهها باتجاهه.

يهيئ التعريف التالي دالة التقسيم لضمان مقطع مستند مكون من 1000 و2000 حرف، مكسور في نهاية الجملة، مع تداخل بين الأجزاء المكونة من 100 حرف.

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 مع خطوات الاسترداد.

تحديد التدفق مع الاسترجاع

يوضح المثال التالي كيف يمكنك استخدام جهاز استرداد في تدفق RAG. مثل مثال برنامج الفهرسة، يستخدم هذا المثال برنامج استرداد المتجه المستند إلى الملف من 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 وتطبيق أساليب RAG المتقدمة (مثل إضافات إعادة الترتيب أو الطلبات الإضافية).

المستخدمين البسيطين ريتريفر

تتيح لك برامج الاسترداد البسيطة تحويل التعليمة البرمجية الحالية إلى برامج استرداد:

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 },
});