স্কেলে প্রোডাকশন RAG: প্রতিদিন ১০,০০০+ লিস্টিং থেকে প্রাপ্ত শিক্ষা
আমি একটি জব বোর্ডের জন্য একটি RAG পাইপলাইন তৈরি করেছি। এটি স্টেজিং-এ কাজ করলেও রিয়েল লোডের ক্ষেত্রে হিমশিম খাচ্ছিল। প্রতিদিন হাজার হাজার লিস্টিং প্রসেস করার জন্য শুধুমাত্র একটি ভালো ভেক্টর স্টোর থাকলেই চলে না। সিস্টেমটি কোথায় ভেঙে পড়ছে তা আপনাকে বুঝতে হবে।
চাঙ্কিং (chunking), এমবেডিং (embeddings), খরচ এবং অবজারভেবিলিটি (observability) সম্পর্কে আমার শিক্ষাগুলো নিচে দেওয়া হলো।
১. আপনার চাঙ্কিং স্ট্র্যাটেজি নিয়ে আন্দাজে সিদ্ধান্ত নেবেন না
বেশিরভাগ টিউটোরিয়ালে চাঙ্কিংকে একটি সাধারণ সেটিংস হিসেবে দেখা হয়। কিন্তু প্রোডাকশনে, আপনার স্ট্র্যাটেজিই নির্ভুলতা এবং খরচ নির্ধারণ করে দেয়।
আমি জব লিস্টিংয়ের জন্য তিনটি পদ্ধতি পরীক্ষা করেছি:
- Fixed-size chunks: এগুলো ব্যর্থ হয়েছে। এগুলো "requirements" এবং "benefits"-এর মতো বিভাগগুলোকে এলোমেলোভাবে ভেঙে ফেলেছিল। এর ফলে রিট্রিভাল (retrieval) প্রক্রিয়ায় ভুল তথ্য আসার সম্ভাবনা তৈরি হয়।
- Semantic chunking: এটি কিছুটা ভালো ছিল কিন্তু অসামঞ্জস্যপূর্ণ ছিল। কিছু চাঙ্ক ছিল অনেক বড় এবং কিছু ছিল অনেক ছোট।
- Recursive character splitting with overlap: এটি সবচেয়ে ভালো কাজ করেছে। আমি নিউলাইন (newline) এবং সেন্টেন্স (sentence) অনুযায়ী ভাগ করেছি। আমি ৫০ টোকেন ওভারল্যাপসহ ৪০০ টোকেন সাইজ ব্যবহার করেছি। এটি নিশ্চিত করে যে দুটি চাঙ্কের মধ্যে থাকা বাক্যগুলো যেন বিচ্ছিন্ন না হয়ে সংযুক্ত থাকে।
প্রো টিপ: চাঙ্কিং করার আগে আপনার ডেটা নরমালাইজ (normalize) করে নিন। Greenhouse বা Lever-এর মতো বিভিন্ন সোর্স থেকে ভিন্ন ভিন্ন ফরম্যাটে ডেটা আসে। প্রথমে টেক্সটটি ক্লিন করে নিন যাতে আপনার চাঙ্কার একটি সামঞ্জস্যপূর্ণ স্ট্রাকচার দেখতে পায়।
২. এমবেডিং: খরচ বনাম নির্ভুলতা
আমি Ollama-এর মাধ্যমে Llama 3.1-এর সাথে OpenAI text-embedding-3-small-এর তুলনা করেছি। লোকাল মডেলটি ফ্রি ছিল কিন্তু "equity compensation"-এর মতো ডোমেইন-স্পেসিফিক টার্মগুলোর ক্ষেত্রে এটি হিমশিম খাচ্ছিল। এটি ভুল বা অস্পষ্ট ফলাফল দিচ্ছিল। OpenAI-এর খরচ বেশি হলেও এটি নির্ভুল ম্যাচ প্রদান করছিল। আমি OpenAI বেছে নিয়েছি কারণ ভুল রিট্রিভালের কারণে পরবর্তীতে LLM কল করার ক্ষেত্রে আরও বেশি খরচ হতে পারে।
সময় বাঁচাতে আমি রিকোয়েস্টগুলো ব্যাচ (batch) আকারে পাঠাই। আমি এক কলেই ১০০টি চাঙ্ক পর্যন্ত পাঠাই। এটি ল্যাটেন্সি (latency) কমায় এবং পাইপলাইনকে দ্রুত রাখে।
৩. ভেক্টর স্টোরের ট্রেড-অফ (Trade-off)
প্রোটোটাইপিংয়ের জন্য আমি Pinecone ব্যবহার করেছিলাম কারণ এটি সেটআপ করা খুব দ্রুত। তবে স্কেল বাড়ার সাথে সাথে এর খরচ অনেক বেড়ে গিয়েছিল।
আমি PostgreSQL-এর ভেতরে pgvector ব্যবহার শুরু করি।
- এটি সেটআপ করতে কিছুটা বেশি পরিশ্রম ছিল।
- এটি প্রচুর টাকা সাশ্রয় করেছে।
- এটি ট্রানজ্যাকশনাল কনসিস্টেন্সি (transactional consistency) প্রদান করে।
যেহেতু এমবেডিংগুলো জব ডেটার সাথেই একই ডেটাবেসে থাকে, তাই আপনার কাছে একটি মাত্র 'সোর্স অফ ট্রুথ' (source of truth) থাকে। আপনাকে দুটি আলাদা সিস্টেম সিঙ্ক (sync) করার প্রয়োজন হয় না।
৪. LLM খরচ নিয়ন্ত্রণ করা
প্রতিটি লিস্টিংকে GPT-4o দিয়ে স্কোর করা বেশ ব্যয়বহুল। খরচ কমাতে আমি তিনটি কৌশল ব্যবহার করেছি:
- OpenAI Batch API: আমি স্কোরিংয়ের কাজগুলো রাতারাতি প্রসেস করি। এতে বড় ধরনের ডিসকাউন্ট পাওয়া যায়।
- Caching: বারবার আসা ক্যান্ডিডেট প্রোফাইলের জন্য আমি রেজাল্ট ক্যাশ (cache) করে রাখি।
- Model tiering: "Sales Representative"-এর মতো সাধারণ রোলের জন্য আমি GPT-4o-mini ব্যবহার করি। শুধুমাত্র নিস (niche) রোলের ক্ষেত্রে যেখানে নির্ভুলতা অত্যন্ত গুরুত্বপূর্ণ, সেখানে আমি GPT-4o ব্যবহার করি।
৫. প্রথমে অবজারভেবিলিটি (Observability) তৈরি করুন
আমার পাইপলাইন একবার নিঃশব্দে ব্যর্থ হয়েছিল। ভুল ফরম্যাটের ডেটার কারণে কিছু চাঙ্ক খালি ছিল, যা সিস্টেমটি কোনো এরর (error) ছাড়াই স্কিপ করে দিয়েছিল।
আমি একটি কোরিলেশন আইডি (correlation ID)-সহ স্ট্রাকচার্ড লগিং (structured logging) যোগ করে এটি ঠিক করেছি। এর ফলে ইনজেশন (ingestion) থেকে স্কোরিং পর্যন্ত একটি লিস্টিংকে ট্র্যাক করা সম্ভব হয়েছে। অবশেষে আমি দেখতে পেরেছি কোন ডেটা সোর্সগুলোর কারণে ব্যর্থতা ঘটছে।
সবচেয়ে বড় শিক্ষা: বেশিরভাগ সমস্যা আসে অগোছালো ডেটা থেকে, AI থেকে নয়। তাই প্রথমে আপনার ডেটা পাইপলাইন বা 'ডেটা প্লাম্বিং' ঠিক করুন।
Optional learning community: https://t.me/GyaanSetuAi
