paint-brush
Dewy এবং LangChain.js এর সাথে একটি প্রশ্ন-উত্তরকারী CLI তৈরি করাদ্বারা@kerinin
494 পড়া
494 পড়া

Dewy এবং LangChain.js এর সাথে একটি প্রশ্ন-উত্তরকারী CLI তৈরি করা

দ্বারা Ryan11m2024/02/17
Read on Terminal Reader

অতিদীর্ঘ; পড়তে

এই টিউটোরিয়ালে, আমরা কীভাবে Dewy এবং LangChain.js ব্যবহার করে একটি প্রশ্ন-উত্তরকারী CLI টুল তৈরি করতে হয় তার উপর ফোকাস করছি।
featured image - Dewy এবং LangChain.js এর সাথে একটি প্রশ্ন-উত্তরকারী CLI তৈরি করা
Ryan HackerNoon profile picture
0-item
1-item
2-item

এই টিউটোরিয়ালে, আমরা কীভাবে Dewy এবং LangChain.js ব্যবহার করে একটি প্রশ্ন-উত্তরকারী CLI টুল তৈরি করতে হয় তার উপর ফোকাস করছি। Dewy হল একটি মুক্ত-উৎস জ্ঞানের ভিত্তি যা বিকাশকারীদের দক্ষতার সাথে তথ্য সংগঠিত করতে এবং পুনরুদ্ধার করতে সহায়তা করে। LangChain.js হল একটি ফ্রেমওয়ার্ক যা অ্যাপ্লিকেশানগুলিতে বৃহৎ ভাষা মডেলের (LLM) একীকরণকে সহজ করে। LangChain.js এর LLM ইন্টিগ্রেশনের সাথে জ্ঞান পরিচালনার জন্য Dewy-এর ক্ষমতার সমন্বয় করে, আপনি এমন সরঞ্জাম তৈরি করতে পারেন যা সুনির্দিষ্ট এবং প্রাসঙ্গিক তথ্য সহ জটিল প্রশ্নের উত্তর দেয়।


এই নির্দেশিকা আপনাকে আপনার পরিবেশ সেট আপ করার, Dewy-এ নথি লোড করার এবং সঞ্চিত ডেটার উপর ভিত্তি করে প্রশ্নের উত্তর দেওয়ার জন্য LangChain.js এর মাধ্যমে একটি LLM ব্যবহার করার মাধ্যমে নিয়ে যায়। এটি ইঞ্জিনিয়ারদের জন্য ডিজাইন করা হয়েছে যারা তাদের প্রজেক্টগুলিকে উন্নত প্রশ্ন-উত্তর কার্যকারিতা সহ উন্নত করতে চাইছেন৷

কেন Dewy এবং LangChain.js?

Dewy হল একটি OSS নলেজ বেস যা ডেভেলপারদের তথ্য সঞ্চয়, সংগঠিত এবং পুনরুদ্ধার করার পদ্ধতিকে প্রবাহিত করার জন্য ডিজাইন করা হয়েছে। এর নমনীয়তা এবং ব্যবহারের সহজতা এটিকে জ্ঞান-চালিত অ্যাপ্লিকেশন তৈরি করার লক্ষ্যে বিকাশকারীদের জন্য একটি চমৎকার পছন্দ করে তোলে।


অন্যদিকে, LangChain.js হল একটি শক্তিশালী ফ্রেমওয়ার্ক যা ডেভেলপারদের তাদের অ্যাপ্লিকেশানগুলিতে নির্বিঘ্নে LLM একীভূত করতে সক্ষম করে৷ Dewy-এর কাঠামোগত জ্ঞান ব্যবস্থাপনাকে LangChain.js-এর LLM ক্ষমতার সাথে একত্রিত করে, বিকাশকারীরা পরিশীলিত প্রশ্ন-উত্তর সিস্টেম তৈরি করতে পারে যা জটিল প্রশ্নগুলি বুঝতে এবং প্রক্রিয়া করতে পারে, সুনির্দিষ্ট এবং প্রাসঙ্গিকভাবে প্রাসঙ্গিক উত্তর প্রদান করে।

লক্ষ

আমাদের লক্ষ্য হল একটি সহজ অথচ শক্তিশালী প্রশ্ন-উত্তর CLI স্ক্রিপ্ট তৈরি করা। এই স্ক্রিপ্ট ব্যবহারকারীদের Dewy নলেজ বেসে ডকুমেন্ট লোড করতে এবং তারপর Dewy-এ সঞ্চিত তথ্যের উপর ভিত্তি করে প্রশ্নের উত্তর দিতে LangChain.js-এর মাধ্যমে একটি LLM ব্যবহার করার অনুমতি দেবে। এই টিউটোরিয়ালটি আপনার পরিবেশ সেট আপ করা থেকে শুরু করে CLI স্ক্রিপ্ট বাস্তবায়ন পর্যন্ত প্রক্রিয়ার মাধ্যমে আপনাকে গাইড করবে।


আপনি শিখবেন কীভাবে ল্যাংচেইন ব্যবহার করে একটি সহজ প্রশ্ন-উত্তর অ্যাপ্লিকেশন তৈরি করতে হয়, এবং কীভাবে Dewy কে জ্ঞানের উৎস হিসাবে একীভূত করতে হয়, আপনার আবেদনকারীকে আপনার দেওয়া নির্দিষ্ট নথির উপর ভিত্তি করে প্রশ্নের উত্তর দেওয়ার অনুমতি দেয়।

পূর্বশর্ত

টিউটোরিয়ালে ডুব দেওয়ার আগে, নিশ্চিত করুন যে আপনি নিম্নলিখিত পূর্বশর্তগুলি কভার করেছেন:

  • টাইপস্ক্রিপ্ট প্রোগ্রামিং এর প্রাথমিক জ্ঞান
  • CLI টুলস ডেভেলপমেন্টের সাথে পরিচিতি
  • আপনার স্থানীয় মেশিনে চলমান Dewy-এর একটি অনুলিপি (যদি আপনার এখানে সাহায্যের প্রয়োজন হয় Dewy-এর ইনস্টলেশন নির্দেশাবলী দেখুন)।

ধাপ 1: আপনার প্রকল্প সেট আপ করুন

আপনি যদি এগিয়ে যেতে চান তবে এই উদাহরণের জন্য চূড়ান্ত কোডটি ডিউই রেপোতে উপলব্ধ।

প্রথমে, TypeScript CLI প্রকল্পের জন্য একটি ডিরেক্টরি তৈরি করুন এবং ডিরেক্টরিতে পরিবর্তন করুন

 mkdir dewy_qa cd dewy_qa

ডিরেক্টরি সেট আপ করে, আপনি টাইপস্ক্রিপ্ট ইনস্টল করতে পারেন এবং প্রকল্পটি শুরু করতে পারেন:

 npm init -y npm i typescript --save-dev npx tsc --init

আপনার পরিবেশের উপর নির্ভর করে, আপনাকে আপনার TypeScript কনফিগারেশনে কিছু পরিবর্তন করতে হতে পারে। নিশ্চিত করুন যে আপনার tsconfig.json নিম্নলিখিত মত কিছু দেখায়:

 { "compilerOptions": { "target": "ES6", "module": "CommonJS", "moduleResolution": "node", "declaration": true, "outDir": "./dist", "esModuleInterop": true, "strict": true, }

এখন আপনি CLI অ্যাপ্লিকেশন তৈরি করতে প্রস্তুত। কোডটিকে খুব অগোছালো না করতে, নিম্নলিখিত লেআউট সহ এটিকে কয়েকটি ডিরেক্টরিতে সংগঠিত করুন

 dewy_qa/ ├── commands/ │ └── ... ├── utils/ │ └── ... ├── index.ts ├── package.json └── tsconfig.ts

প্রতিটি কমান্ড commands ডিরেক্টরিতে প্রয়োগ করা হবে, এবং ভাগ করা কোড utils ডিরেক্টরিতে যাবে। CLI অ্যাপ্লিকেশনের এন্ট্রিপয়েন্ট হল index.ts ফাইল।

index.ts এর একটি সাধারণ "হ্যালো ওয়ার্ল্ড" সংস্করণ দিয়ে শুরু করুন - আপনি পরবর্তী বিভাগে এটি পূরণ করতে শুরু করবেন

 #!/usr/bin/env ts-node-script console.log("hello world");

পরিবেশটি সঠিকভাবে সেটআপ করা হয়েছে তা যাচাই করতে, নিম্নলিখিত কমান্ডটি চালানোর চেষ্টা করুন - আপনি কনসোলে "হ্যালো ওয়ার্ল্ড" মুদ্রিত দেখতে পাবেন:

 npx ts-node index.ts

প্রতিবার এই খুব দীর্ঘ কমান্ডটি টাইপ করার পরিবর্তে, কমান্ডের জন্য package.json এ একটি এন্ট্রি তৈরি করা যাক। এটি আমাদের মনে রাখতে সাহায্য করবে কিভাবে CLI চালু করতে হয় এবং কমান্ড হিসাবে ইনস্টল করা সহজ করে তোলে:

 { ... "bin": { "dewy_qa": "./index.ts" } ... }

এখন আপনি npm exec dewy_qa বা npm link দিয়ে আপনার স্ক্রিপ্ট চালাতে পারেন এবং এটিকে শুধু dewy_qa হিসাবে চালাতে পারেন

ধাপ 2: ডকুমেন্ট লোডিং বাস্তবায়ন করুন

Dewy ক্লায়েন্ট সেট আপ করে নথি লোড করুন। প্রথম ধাপ হল প্রকল্পে কিছু নির্ভরতা যোগ করা। প্রথমটি হল dewy-ts , Dewy-এর জন্য ক্লায়েন্ট লাইব্রেরি। দ্বিতীয়টি হল commander , যা আর্গুমেন্ট পার্সিং, সাবকমান্ড এবং আরও অনেক কিছু সহ একটি CLI অ্যাপ্লিকেশন তৈরি করতে সাহায্য করবে। অবশেষে, প্রম্পটগুলিকে আরও রঙিন করতে chalk

 npm install dewy-ts commander chalk

এর পরে, লোড কমান্ডের যুক্তি প্রয়োগ করুন। আপনি commands/load.ts নামে একটি পৃথক ফাইলে এটি করবেন। এই ফাইলটি load নামের একটি ফাংশন প্রয়োগ করে, যা একটি URL এবং কিছু অতিরিক্ত বিকল্পের প্রত্যাশা করে - এটি পরবর্তী বিভাগে CLI এর সাথে সংযুক্ত করা হবে।


Dewy দস্তাবেজ লোড করা খুব সহজ করে তোলে - শুধু ক্লায়েন্ট সেটআপ করুন এবং আপনি যে ফাইলটি লোড করতে চান তার URL দিয়ে addDocument কল করুন। ডিউই পিডিএফ-এর বিষয়বস্তু বের করার যত্ন নেয়, এলএলএম-এ পাঠানোর জন্য সঠিক মাপের খণ্ডে বিভক্ত করে এবং শব্দার্থিক অনুসন্ধানের জন্য সেগুলিকে সূচী করে।

 import { Dewy } from 'dewy-ts'; import { success, error } from '../utils/colors'; export async function load(url: string, options: { collection: string, dewy_endpoint: string }): Promise<void> { console.log(success(`Loading ${url} into collection: ${options.collection}`)); try { const dewy = new Dewy({ BASE: options.dewy_endpoint }) const result = await dewy.kb.addDocument({ collection: options.collection, url }); console.log(success(`File loaded successfully`)); console.log(JSON.stringify(result, null, 2)); } catch (err: any) { console.error(error(`Failed to load file: ${err.message}`)); } }

আপনি হয়তো লক্ষ্য করেছেন যে কিছু ফাংশন ../utils/colors থেকে আমদানি করা হয়েছে। এই ফাইলটি শুধুমাত্র রঙিন কনসোল আউটপুট করার জন্য কিছু সাহায্যকারী সেট আপ করে - এটিকে utils রাখুন যাতে এটি অন্য কোথাও ব্যবহার করা যায়:

 import chalk from 'chalk'; export const success = (message: string) => chalk.green(message); export const info = (message: string) => chalk.blue(message); export const error = (message: string) => chalk.red(message);

ধাপ 3: প্রশ্ন-উত্তর প্রয়োগ করুন

Dewy-এ নথি লোড করার ক্ষমতার সাথে, এখন LangChain.js-কে সংহত করার সময় প্রশ্নের উত্তর দেওয়ার জন্য LLM ব্যবহার করার জন্য। এই ধাপে Dewy নলেজ বেস জিজ্ঞাসা করার জন্য LangChain.js সেট আপ করা এবং উত্তর তৈরি করতে LLM ব্যবহার করে ফলাফলগুলি প্রক্রিয়া করা জড়িত।


শুরু করতে, LLM হিসাবে OpenAI API ব্যবহার করার জন্য কিছু অতিরিক্ত প্যাকেজ - langchain এবং openai ইনস্টল করুন:

 npm install dewy-langchain langchain @langchain/openai openai

এই কমান্ডটি বেশ দীর্ঘ, তাই শেষ পর্যন্ত একত্রিত করার আগে আমরা এর কয়েকটি টুকরো দিয়ে হাঁটব

OpenAI এবং Dewy-এর জন্য ক্লায়েন্ট তৈরি করুন

সেটআপ করার প্রথম জিনিসটি হল ডিউই (আগের মতো) এবং একটি এলএলএম। আগের থেকে একটি পার্থক্য হল যে dewy একটি DewyRetriever তৈরি করতে ব্যবহৃত হয়: এটি একটি বিশেষ প্রকার যা ল্যাংচেইন দ্বারা একটি চেইনের অংশ হিসাবে তথ্য পুনরুদ্ধারের জন্য ব্যবহৃত হয়। আপনি দেখতে পাবেন কিভাবে পুনরুদ্ধার শুধুমাত্র এক মিনিটের মধ্যে ব্যবহার করা হয়.

 const model = new ChatOpenAI({ openAIApiKey: options.openai_api_key, }); const dewy = new Dewy({ BASE: options.dewy_endpoint }) const retriever = new DewyRetriever({ dewy, collection });

একটি LangChain প্রম্পট তৈরি করুন

এটি একটি স্ট্রিং টেমপ্লেট যা এলএলএমকে নির্দেশ দেয় যে এটি কীভাবে আচরণ করা উচিত, অতিরিক্ত প্রসঙ্গের জন্য স্থানধারক সহ যা "চেইন" তৈরি করার সময় প্রদান করা হবে। এই ক্ষেত্রে, LLM কে প্রশ্নের উত্তর দেওয়ার জন্য নির্দেশ দেওয়া হয়, কিন্তু শুধুমাত্র এটি দেওয়া তথ্য ব্যবহার করে। এটি মডেলের "হ্যালুসিনেট" করার প্রবণতাকে হ্রাস করে, বা এমন একটি উত্তর তৈরি করে যা যুক্তিসঙ্গত কিন্তু ভুল। context এবং question মান পরবর্তী ধাপে প্রদান করা হয়েছে:

 const prompt = PromptTemplate.fromTemplate(`Answer the question based only on the following context: {context} Question: {question}`);

চেইন তৈরি করুন

ল্যাংচেইন আচরণের "চেইন" তৈরি করে কাজ করে যা LLM এবং অন্যান্য ডেটা উত্সগুলি কীভাবে অনুসন্ধান করতে হয় তা নিয়ন্ত্রণ করে। এই উদাহরণটি LCEL ব্যবহার করে, যা LangChain-এর মূল ইন্টারফেসের তুলনায় আরও নমনীয় প্রোগ্রামিং অভিজ্ঞতা প্রদান করে।


একটি LCEL চেইন তৈরি করতে একটি RunnableSequence ব্যবহার করুন। এই চেইনটি বর্ণনা করে যে কীভাবে context এবং question মান তৈরি করতে হয়: পূর্বে তৈরি করা পুনরুদ্ধার ব্যবহার করে প্রসঙ্গ তৈরি করা হয় এবং ধাপের ইনপুটটি অতিক্রম করে প্রশ্ন তৈরি করা হয়। Dewy retrieves ফলাফল formatDocumentsAsString ফাংশনে পাইপ করে একটি স্ট্রিং হিসাবে ফর্ম্যাট করা হয়।


এই চেইন নিম্নলিখিত কাজ করে:

  1. এটি DewyRetriever ব্যবহার করে নথিগুলি পুনরুদ্ধার করে এবং সেগুলিকে context বরাদ্দ করে এবং question চেইনের ইনপুট মান নির্ধারণ করে।
  2. এটি প্রম্পট স্ট্রিংকে context এবং question ভেরিয়েবল ব্যবহার করে ফর্ম্যাট করে।
  3. এটি একটি প্রতিক্রিয়া তৈরি করতে LLM-এ বিন্যাসিত প্রম্পট পাস করে।
  4. এটি একটি স্ট্রিং হিসাবে LLM এর প্রতিক্রিয়া ফর্ম্যাট করে।
 const chain = RunnableSequence.from([ { context: retriever.pipe(formatDocumentsAsString), question: new RunnablePassthrough(), }, prompt, model, new StringOutputParser(), ]);

চেইন চালান

এখন যেহেতু চেইনটি তৈরি করা হয়েছে, এটি চালান এবং ফলাফলগুলি কনসোলে আউটপুট করুন। আপনি দেখতে পাবেন, question হল ফাংশনের কলার দ্বারা প্রদত্ত একটি ইনপুট আর্গুমেন্ট।


chain.streamLog() ব্যবহার করে চেইন চালানোর ফলে আপনি প্রতিটি প্রতিক্রিয়া খণ্ড দেখতে পারবেন কারণ এটি LLM থেকে ফিরে এসেছে। স্ট্রীম হ্যান্ডলার লুপটি বেশ কুৎসিত, কিন্তু এটি কেবলমাত্র উপযুক্ত স্ট্রিম ফলাফলগুলি ফিল্টার করে এবং STDOUT এ লিখছে ( console.log ব্যবহার করে এটি প্রতিটি খণ্ডের পরে নতুন লাইন যুক্ত করবে)।

 const stream = await chain.streamLog(question); // Write chunks of the response to STDOUT as they're received console.log("Answer:"); for await (const chunk of stream) { if (chunk.ops?.length > 0 && chunk.ops[0].op === "add") { const addOp = chunk.ops[0]; if ( addOp.path.startsWith("/logs/ChatOpenAI") && typeof addOp.value === "string" && addOp.value.length ) { process.stdout.write(addOp.value); } } }

আদেশ হিসাবে এটি সব একসাথে টানুন

এখন আপনি সমস্ত টুকরা দেখেছেন, আপনি query কমান্ড তৈরি করতে প্রস্তুত। এটি কিছু অতিরিক্ত আমদানি সহ আগের থেকে load কমান্ডের অনুরূপ হওয়া উচিত।

 import { StringOutputParser } from "@langchain/core/output_parsers"; import { PromptTemplate } from "@langchain/core/prompts"; import { formatDocumentsAsString } from "langchain/util/document"; import { RunnablePassthrough, RunnableSequence } from "@langchain/core/runnables"; import { ChatOpenAI } from "@langchain/openai"; import { Dewy } from 'dewy-ts'; import { DewyRetriever } from 'dewy-langchain'; import { success, error } from '../utils/colors'; export async function query(question: string, options: { collection: string, dewy_endpoint: string, openai_api_key: string }): Promise<void> { console.log(success(`Querying ${options.collection} collection for: "${question}"`)); try { const model = new ChatOpenAI({ openAIApiKey: options.openai_api_key, }); const dewy = new Dewy({ BASE: options.dewy_endpoint }) const retriever = new DewyRetriever({ dewy, collection: options.collection }); const prompt = PromptTemplate.fromTemplate(`Answer the question based only on the following context: {context} Question: {question}`); const chain = RunnableSequence.from([ { context: retriever.pipe(formatDocumentsAsString), question: new RunnablePassthrough(), }, prompt, model, new StringOutputParser(), ]); const stream = await chain.streamLog(question); // Write chunks of the response to STDOUT as they're received console.log("Answer:"); for await (const chunk of stream) { if (chunk.ops?.length > 0 && chunk.ops[0].op === "add") { const addOp = chunk.ops[0]; if ( addOp.path.startsWith("/logs/ChatOpenAI") && typeof addOp.value === "string" && addOp.value.length ) { process.stdout.write(addOp.value); } } } } catch (err: any) { console.error(error(`Failed to query: ${err.message}`)); } }

ধাপ 4: CLI তৈরি করা

Dewy এবং LangChain.js সমন্বিত করে, পরবর্তী ধাপ হল CLI ইন্টারফেস তৈরি করা। একটি ব্যবহারকারী-বান্ধব কমান্ড-লাইন ইন্টারফেস তৈরি করতে commander মতো একটি লাইব্রেরি ব্যবহার করুন যা Dewy-এ নথি লোড করার জন্য কমান্ড সমর্থন করে এবং LangChain.js ব্যবহার করে জ্ঞানের ভিত্তি অনুসন্ধান করে।


প্রথমে, সাবকমান্ড load এবং query তৈরি করতে index.ts পুনরায় লিখুন। --collection আর্গুমেন্ট নির্ধারণ করে যে কোন Dewy সংগ্রহে নথিটি লোড করা উচিত (Dewy আপনাকে ফাইল ফোল্ডারের মতো বিভিন্ন সংগ্রহে নথিগুলি সংগঠিত করতে দেয়)। --dewy-endpoint আর্গুমেন্ট আপনাকে Dewy-এর সাথে কীভাবে সংযোগ করতে হবে তা নির্দিষ্ট করতে দেয় - ডিফল্টরূপে পোর্ট 8000 এ স্থানীয়ভাবে চলমান একটি উদাহরণ অনুমান করা হয়। অবশেষে, --openai_api_key আর্গুমেন্ট (যা একটি এনভায়রনমেন্ট ভেরিয়েবলে ডিফল্ট) OpenAI API কনফিগার করে:

 #!/usr/bin/env ts-node-script import { Command } from 'commander'; import { load } from './commands/load'; import { query } from './commands/query'; const program = new Command(); program.name('dewy-qa').description('CLI tool for interacting with a knowledge base API').version('1.0.0'); const defaultOpenAIKey = process.env.OPENAI_API_KEY; program .command('load') .description("Load documents into Dewy from a URL") .option('--collection <collection>', 'Specify the collection name', 'main') .option('--dewy-endpoint <endpoint>', 'Specify the collection name', 'http://localhost:8000') .argument('<url>', 'URL to load into the knowledge base') .action(load); program .command('query') .description('Ask questions using an LLM and the loaded documents for answers') .option('--collection <collection>', 'Specify the collection name', 'main') .option('--dewy-endpoint <endpoint>', 'Specify the collection name', 'http://localhost:8000') .option('--openai-api-key <key>', 'Specify the collection name', defaultOpenAIKey) .argument('<question>', 'Question to ask the knowledge base') .action(query); program.parse(process.argv);

ঠিক আছে, সব সম্পন্ন - এটা সহজ ছিল না? আপনি কমান্ড চালানোর মাধ্যমে এটি চেষ্টা করতে পারেন:

 dewy_qa load https://arxiv.org/pdf/2009.08553.pdf

আপনি মত কিছু দেখতে হবে

 Loading https://arxiv.org/pdf/2009.08553.pdf into collection: main File loaded successfully { "id": 18, "collection": "main", "extracted_text": null, "url": "https://arxiv.org/pdf/2009.08553.pdf", "ingest_state": "pending", "ingest_error": null }

একটি বড় PDF এর বিষয়বস্তু বের করতে এক বা দুই মিনিট সময় লাগতে পারে, তাই আপনি যখন প্রথমবার একটি নতুন নথি লোড করবেন তখন আপনি প্রায়ই "ingest_state": "pending" দেখতে পাবেন।

পরবর্তী, কিছু প্রশ্ন জিজ্ঞাসা করার চেষ্টা করুন:

 dewy_qa query "tell me about RAG

আপনি মত কিছু দেখতে হবে

 Querying main collection for: "tell me about RAG" Answer: Based on the given context, RAG refers to the RAG proteins, which are involved in DNA binding and V(D)J recombination. The RAG1 and RAG2 proteins work together to bind specific DNA sequences known as RSS (recombination signal sequences) and facilitate the cutting and rearrangement of DNA segments during the process of V(D)J recombination...

উপসংহার

এই নির্দেশিকা অনুসরণ করে, আপনি শিখেছেন কীভাবে একটি CLI তৈরি করতে হয় যা জ্ঞান পরিচালনার জন্য Dewy এবং LangChain.js প্রশ্ন প্রক্রিয়াকরণ এবং উত্তর তৈরি করতে ব্যবহার করে। এই টুলটি LLM-এর বিশ্লেষণাত্মক শক্তির সাথে একটি কাঠামোগত জ্ঞানের ভিত্তিকে একত্রিত করার ব্যবহারিক প্রয়োগ প্রদর্শন করে, যা ডেভেলপারদের আরও বুদ্ধিমান এবং প্রতিক্রিয়াশীল অ্যাপ্লিকেশন তৈরি করতে সক্ষম করে।

আরও পড়া এবং সম্পদ