Firebase Genkit বিমূর্ততা প্রদান করে যা আপনাকে পুনরুদ্ধার-অগমেন্টেড জেনারেশন (RAG) ফ্লো তৈরি করতে সাহায্য করে, সেইসাথে প্লাগইনগুলি যা সম্পর্কিত সরঞ্জামগুলির সাথে একীকরণ প্রদান করে।
RAG কি?
পুনরুদ্ধার-বর্ধিত প্রজন্ম হল একটি কৌশল যা LLM-এর প্রতিক্রিয়াগুলিতে তথ্যের বাহ্যিক উত্সগুলিকে অন্তর্ভুক্ত করতে ব্যবহৃত হয়। এটি করতে সক্ষম হওয়া গুরুত্বপূর্ণ কারণ, এলএলএমগুলি সাধারণত বিস্তৃত উপাদানের উপর প্রশিক্ষিত হয়, এলএলএমগুলির ব্যবহারিক ব্যবহারের জন্য প্রায়শই নির্দিষ্ট ডোমেন জ্ঞানের প্রয়োজন হয় (উদাহরণস্বরূপ, আপনি আপনার কোম্পানির সম্পর্কে গ্রাহকদের প্রশ্নের উত্তর দেওয়ার জন্য একটি এলএলএম ব্যবহার করতে চাইতে পারেন পণ্য)।
একটি সমাধান হল আরও নির্দিষ্ট ডেটা ব্যবহার করে মডেলটিকে সূক্ষ্ম-টিউন করা। যাইহোক, এটি গণনা খরচ এবং পর্যাপ্ত প্রশিক্ষণ ডেটা প্রস্তুত করার জন্য প্রয়োজনীয় প্রচেষ্টার পরিপ্রেক্ষিতে উভয়ই ব্যয়বহুল হতে পারে।
বিপরীতে, RAG মডেলে পাস করার সময় একটি প্রম্পটে বাহ্যিক ডেটা উত্সগুলিকে অন্তর্ভুক্ত করে কাজ করে। উদাহরণস্বরূপ, আপনি প্রম্পটটি কল্পনা করতে পারেন, "লিসার সাথে বার্টের সম্পর্ক কী?" কিছু প্রাসঙ্গিক তথ্য প্রিপেন্ড করে প্রসারিত ("বর্ধিত") হতে পারে, যার ফলে প্রম্পট হয়, "হোমার এবং মার্জের সন্তানদের নাম বার্ট, লিসা এবং ম্যাগি। লিসার সাথে বার্টের সম্পর্ক কী?"
এই পদ্ধতির বেশ কয়েকটি সুবিধা রয়েছে:
- এটি আরও সাশ্রয়ী হতে পারে কারণ আপনাকে মডেলটিকে পুনরায় প্রশিক্ষণ দিতে হবে না।
- আপনি ক্রমাগত আপনার ডেটা উত্স আপডেট করতে পারেন এবং LLM অবিলম্বে আপডেট করা তথ্য ব্যবহার করতে পারে।
- আপনার কাছে এখন আপনার এলএলএম-এর প্রতিক্রিয়াগুলিতে রেফারেন্স উদ্ধৃত করার সম্ভাবনা রয়েছে।
অন্যদিকে, RAG ব্যবহার করার অর্থ স্বাভাবিকভাবেই দীর্ঘ প্রম্পট, এবং কিছু LLM API পরিষেবা আপনার পাঠানো প্রতিটি ইনপুট টোকেনের জন্য চার্জ করে। শেষ পর্যন্ত, আপনাকে অবশ্যই আপনার অ্যাপ্লিকেশনগুলির জন্য খরচ ট্রেডঅফ মূল্যায়ন করতে হবে।
RAG একটি খুব বিস্তৃত এলাকা এবং সেরা মানের RAG অর্জনের জন্য বিভিন্ন কৌশল ব্যবহার করা হয়। মূল জেনকিট ফ্রেমওয়ার্ক আপনাকে RAG করতে সাহায্য করার জন্য তিনটি প্রধান বিমূর্ততা প্রদান করে:
- ইনডেক্সার: একটি "সূচীতে" নথি যোগ করুন।
- এমবেডার: নথিগুলিকে ভেক্টর উপস্থাপনায় রূপান্তরিত করে
- পুনরুদ্ধারকারী: একটি "সূচী" থেকে নথি পুনরুদ্ধার করুন, একটি প্রশ্ন দেওয়া হয়েছে।
এই সংজ্ঞাগুলি উদ্দেশ্যমূলকভাবে বিস্তৃত কারণ জেনকিট একটি "সূচী" কী বা এটি থেকে ঠিক কীভাবে নথিগুলি পুনরুদ্ধার করা হয় সে সম্পর্কে অ-মতামত। Genkit শুধুমাত্র একটি Document
বিন্যাস প্রদান করে এবং বাকি সবকিছু পুনরুদ্ধারকারী বা সূচক বাস্তবায়ন প্রদানকারী দ্বারা সংজ্ঞায়িত করা হয়।
সূচক
সূচী এমনভাবে আপনার নথিগুলির ট্র্যাক রাখার জন্য দায়ী যাতে আপনি একটি নির্দিষ্ট প্রশ্নে প্রাসঙ্গিক নথিগুলি দ্রুত পুনরুদ্ধার করতে পারেন। এটি প্রায়শই একটি ভেক্টর ডাটাবেস ব্যবহার করে সম্পন্ন করা হয়, যা এম্বেডিং নামক বহুমাত্রিক ভেক্টর ব্যবহার করে আপনার নথিগুলিকে সূচী করে। একটি পাঠ্য এমবেডিং (অস্বচ্ছভাবে) পাঠ্যের একটি উত্তরণ দ্বারা প্রকাশিত ধারণাগুলিকে উপস্থাপন করে; এগুলি বিশেষ-উদ্দেশ্য ML মডেল ব্যবহার করে তৈরি করা হয়। টেক্সট এর এমবেডিং ব্যবহার করে ইন্ডেক্স করার মাধ্যমে, একটি ভেক্টর ডাটাবেস ধারণাগতভাবে সম্পর্কিত পাঠ্যকে ক্লাস্টার করতে এবং পাঠ্যের একটি অভিনব স্ট্রিং (কোয়েরি) সম্পর্কিত নথিগুলি পুনরুদ্ধার করতে সক্ষম হয়।
আপনি প্রজন্মের উদ্দেশ্যে নথিগুলি পুনরুদ্ধার করতে পারার আগে, আপনাকে সেগুলিকে আপনার নথির সূচীতে অন্তর্ভুক্ত করতে হবে৷ একটি সাধারণ ইনজেশন প্রবাহ নিম্নলিখিত কাজ করে:
বড় নথিগুলিকে ছোট নথিতে বিভক্ত করুন যাতে শুধুমাত্র প্রাসঙ্গিক অংশগুলি আপনার প্রম্পটগুলিকে বাড়ানোর জন্য ব্যবহার করা হয় - "চঙ্কিং"৷ এটি প্রয়োজনীয় কারণ অনেক এলএলএম-এর একটি সীমিত প্রসঙ্গ উইন্ডো থাকে, যা প্রম্পটের সাথে সম্পূর্ণ নথি অন্তর্ভুক্ত করা অব্যবহারিক করে তোলে।
জেনকিট বিল্ট-ইন চঙ্কিং লাইব্রেরি প্রদান করে না; যাইহোক, জেনকিটের সাথে সামঞ্জস্যপূর্ণ ওপেন সোর্স লাইব্রেরি পাওয়া যায়।
প্রতিটি খণ্ডের জন্য এম্বেডিং তৈরি করুন। আপনি যে ডাটাবেসটি ব্যবহার করছেন তার উপর নির্ভর করে, আপনি স্পষ্টভাবে একটি এমবেডিং জেনারেশন মডেলের সাথে এটি করতে পারেন, অথবা আপনি ডাটাবেস দ্বারা প্রদত্ত এমবেডিং জেনারেটর ব্যবহার করতে পারেন।
ডাটাবেসে পাঠ্য খণ্ড এবং এর সূচক যুক্ত করুন।
যদি আপনি ডেটার একটি স্থিতিশীল উৎসের সাথে কাজ করেন তবে আপনি আপনার ইনজেশন ফ্লো খুব কম সময়ে বা শুধুমাত্র একবার চালাতে পারেন। অন্যদিকে, আপনি যদি ঘন ঘন পরিবর্তিত ডেটা নিয়ে কাজ করেন, তাহলে আপনি ক্রমাগত ইনজেশন ফ্লো চালাতে পারেন (উদাহরণস্বরূপ, একটি ক্লাউড ফায়ারস্টোর ট্রিগারে, যখনই একটি নথি আপডেট করা হয়)৷
এমবেডার্স
একটি এমবেডার হল একটি ফাংশন যা বিষয়বস্তু (টেক্সট, ছবি, অডিও, ইত্যাদি) নেয় এবং একটি সংখ্যাসূচক ভেক্টর তৈরি করে যা মূল বিষয়বস্তুর শব্দার্থিক অর্থকে এনকোড করে। উপরে উল্লিখিত হিসাবে, সূচীকরণের প্রক্রিয়ার অংশ হিসাবে এমবেডারগুলিকে লিভারেজ করা হয়, তবে, এগুলিকে সূচক ছাড়াই এমবেডিং তৈরি করতে স্বাধীনভাবে ব্যবহার করা যেতে পারে।
উদ্ধারকারী
একটি পুনরুদ্ধারকারী এমন একটি ধারণা যা যেকোনো ধরনের নথি পুনরুদ্ধারের সাথে সম্পর্কিত যুক্তিকে অন্তর্ভুক্ত করে। সর্বাধিক জনপ্রিয় পুনরুদ্ধারের ক্ষেত্রে সাধারণত ভেক্টর স্টোর থেকে পুনরুদ্ধার অন্তর্ভুক্ত থাকে, তবে, জেনকিটে একটি পুনরুদ্ধার এমন যে কোনও ফাংশন হতে পারে যা ডেটা ফেরত দেয়।
একটি পুনরুদ্ধার তৈরি করতে, আপনি প্রদত্ত বাস্তবায়নের একটি ব্যবহার করতে পারেন বা নিজের তৈরি করতে পারেন।
সমর্থিত সূচক, পুনরুদ্ধারকারী এবং এমবেডার
Genkit তার প্লাগইন সিস্টেমের মাধ্যমে সূচক এবং পুনরুদ্ধার সমর্থন প্রদান করে। নিম্নলিখিত প্লাগইনগুলি আনুষ্ঠানিকভাবে সমর্থিত:
- ক্লাউড ফায়ারস্টোর ভেক্টর স্টোর
- ভার্টেক্স এআই ভেক্টর অনুসন্ধান
- ক্রোমা ডিবি ভেক্টর ডাটাবেস
- পাইনকোন ক্লাউড ভেক্টর ডাটাবেস
এছাড়াও, জেনকিট পূর্বনির্ধারিত কোড টেমপ্লেটগুলির মাধ্যমে নিম্নলিখিত ভেক্টর স্টোরগুলিকে সমর্থন করে, যা আপনি আপনার ডাটাবেস কনফিগারেশন এবং স্কিমার জন্য কাস্টমাইজ করতে পারেন:
-
pgvector
সহ PostgreSQL
এমবেডিং মডেল সমর্থন নিম্নলিখিত প্লাগইনগুলির মাধ্যমে প্রদান করা হয়:
প্লাগইন | মডেল |
---|---|
গুগল জেনারেটিভ এআই | গেকো টেক্সট এম্বেডিং |
গুগল ভার্টেক্স এআই | গেকো টেক্সট এম্বেডিং |
একটি RAG ফ্লো সংজ্ঞায়িত করা
নিম্নলিখিত উদাহরণগুলি দেখায় যে আপনি কীভাবে একটি ভেক্টর ডাটাবেসে রেস্তোরাঁর মেনু পিডিএফ নথিগুলির একটি সংগ্রহ গ্রহণ করতে পারেন এবং একটি প্রবাহে ব্যবহারের জন্য সেগুলি পুনরুদ্ধার করতে পারেন যা নির্ধারণ করে যে কোন খাবারের আইটেমগুলি উপলব্ধ।
পিডিএফ প্রক্রিয়াকরণের জন্য নির্ভরতা ইনস্টল করুন
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,
},
]),
],
});
একটি সূচক সংজ্ঞায়িত করুন
নিচের উদাহরণে দেখানো হয়েছে কিভাবে পিডিএফ ডকুমেন্টের একটি সংগ্রহ ইনজেস্ট করার জন্য একটি ইনডেক্সার তৈরি করতে হয় এবং সেগুলিকে স্থানীয় ভেক্টর ডাটাবেসে সংরক্ষণ করতে হয়।
এটি স্থানীয় ফাইল-ভিত্তিক ভেক্টর সাদৃশ্য পুনরুদ্ধার ব্যবহার করে যা Genkit সাধারণ পরীক্ষা এবং প্রোটোটাইপিংয়ের জন্য আউট-অফ-দ্য-বক্স সরবরাহ করে ( উৎপাদনে ব্যবহার করবেন না )
সূচক তৈরি করুন
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 { 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 প্রবাহে একটি পুনরুদ্ধার ব্যবহার করতে পারেন। ইনডেক্সার উদাহরণের মতো, এই উদাহরণটি জেনকিটের ফাইল-ভিত্তিক ভেক্টর পুনরুদ্ধার ব্যবহার করে, যা আপনার উত্পাদনে ব্যবহার করা উচিত নয়।
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 SDK নমনীয় পদ্ধতি প্রদান করে যা আপনাকে নথি আনার জন্য কাস্টম কোড প্রদান করতে দেয়। এছাড়াও আপনি কাস্টম পুনরুদ্ধারকারীদের সংজ্ঞায়িত করতে পারেন যা জেনকিটে বিদ্যমান পুনরুদ্ধারগুলির উপরে তৈরি করে এবং উপরে উন্নত 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 },
});
Rerankers এবং দুই পর্যায় পুনরুদ্ধার
একটি পুনঃর্যাঙ্কিং মডেল - এটি ক্রস-এনকোডার নামেও পরিচিত - এমন একটি মডেল যা একটি প্রশ্ন এবং নথি দেওয়া হলে, একটি মিল স্কোর আউটপুট করবে। আমরা এই স্কোরটি ব্যবহার করি আমাদের প্রশ্নের সাথে প্রাসঙ্গিকতার ভিত্তিতে নথিগুলিকে পুনরায় সাজাতে। Reranker API গুলি নথিগুলির একটি তালিকা নেয় (উদাহরণস্বরূপ একটি পুনরুদ্ধারের আউটপুট) এবং নথিগুলিকে অনুসন্ধানের সাথে তাদের প্রাসঙ্গিকতার উপর ভিত্তি করে পুনরায় সাজায়৷ এই পদক্ষেপটি ফলাফলগুলিকে সূক্ষ্ম-টিউন করার জন্য এবং একটি জেনারেটিভ মডেলে প্রদত্ত প্রম্পটে সর্বাধিক প্রাসঙ্গিক তথ্য ব্যবহার করা হয়েছে তা নিশ্চিত করার জন্য কার্যকর হতে পারে।
Reranker উদাহরণ
Genkit-এ একটি reranker retrievers এবং indexers-এর অনুরূপ সিনট্যাক্সে সংজ্ঞায়িত করা হয়েছে। এখানে একটি উদাহরণ Genkit একটি reranker ব্যবহার করে. এই প্রবাহটি একটি পূর্বনির্ধারিত 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,
}));
}
);
এই রির্যাঙ্কারটি নথি স্কোর এবং র্যাঙ্ক করার জন্য semantic-ranker-512
সহ Vertex AI genkit প্লাগইন ব্যবহার করে। স্কোর যত বেশি হবে, ডকুমেন্টটি প্রশ্নের সাথে তত বেশি প্রাসঙ্গিক হবে।
কাস্টম Rerankers
আপনি আপনার নির্দিষ্ট ব্যবহারের ক্ষেত্রে অনুসারে কাস্টম rerankers সংজ্ঞায়িত করতে পারেন। আপনার নিজের কাস্টম লজিক বা একটি কাস্টম মডেল ব্যবহার করে ডকুমেন্টগুলিকে পুনরায় র্যাঙ্ক করার প্রয়োজন হলে এটি সহায়ক। এখানে একটি কাস্টম রিরেঙ্কার সংজ্ঞায়িত করার একটি সহজ উদাহরণ:
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 ফ্লোতে অন্য যেকোনো রির্যাঙ্কারের মতোই ব্যবহার করা যেতে পারে, যা আপনাকে উন্নত পুনঃর্যাঙ্কিং কৌশলগুলি বাস্তবায়নের জন্য নমনীয়তা প্রদান করে।