ChatGPT কি আপনার কোম্পানির ৫ লাখ PDF মুখস্থ করতে পারবে? এখানেই RAG-এর গল্প

Documents → Chunking → Embeddings → Vector DB → Retrieval → LLM → Answer (Series 08, Episode 04)

🟡 INTERMEDIATE Series 08 — Generative AI Engineering Episode 04 / 09

📑 এই পর্বে যা যা আছে

🧩 ১. গল্প: ৫ লাখ PDF আর একটা প্রশ্ন

Rahim-এর কোম্পানির HR head এসে বললেন: "আমাদের ৫ লাখ পুরনো job posting, policy document আর candidate report আছে। আমি চাই একটা assistant — যাকে বাংলায় প্রশ্ন করলে এই document গুলো থেকে সঠিক উত্তর দেবে।" Rahim ভাবল — সহজ! সব document ChatGPT-কে পাঠিয়ে দিই। কিন্তু প্রথম চেষ্টাতেই দেয়াল।

Maya: "তুমি কি সব ৫ লাখ document একটা prompt-এ পাঠাতে চাও? প্রতিটা LLM-এর একটা context window আছে — একবারে সীমিত পরিমাণ text-ই নিতে পারে। আর পাঠাতে পারলেও খরচ আকাশছোঁয়া, আর ৯৯% document তো প্রশ্নের সাথে সম্পর্কহীন। সমাধান হলো — আগে relevant অংশ খুঁজে বের করো, তারপর শুধু সেটুকু LLM-কে দাও। এই ধারণারই নাম RAG।"

২. সমস্যা: কেন সব document সরাসরি LLM-কে দেওয়া যায় না

কারণ ১ — Context window সীমিত। LLM একবারে নির্দিষ্ট token-ই পড়তে পারে; ৫ লাখ document একটা request-এ অসম্ভব।

কারণ ২ — খরচ ও গতি। প্রতিটা token processing-এ খরচ ও সময়; পুরো corpus প্রতি প্রশ্নে পাঠানো অবাস্তব।

কারণ ৩ — Relevance। একটা প্রশ্নের জন্য মাত্র কয়েকটা অংশ প্রাসঙ্গিক; বাকিটা শুধু noise বাড়ায় ও উত্তর খারাপ করে।
User Question ↓ Relevant অংশ খুঁজে বের করো (Retrieval) ↓ সেটুকু দিয়ে prompt সমৃদ্ধ করো (Augment) ↓ LLM উত্তর তৈরি করে (Generation)

🎯 ৩. RAG কী (Retrieval + Augmented + Generation)

শব্দমানে
Retrievalknowledge base থেকে প্রশ্ন-সম্পর্কিত অংশ খুঁজে আনা
Augmentedসেই অংশ দিয়ে LLM-এর prompt/context সমৃদ্ধ করা
Generationসেই সমৃদ্ধ context দিয়ে LLM natural-language উত্তর লেখে
RAG = আগে relevant তথ্য খোঁজা → তারপর সেটা LLM-কে context হিসেবে দেওয়া → তারপর উত্তর generate করা। LLM-এর নিজের "মুখস্থ" জ্ঞানের উপর নির্ভর না করে, আমরা তাকে সঠিক তথ্য হাতে ধরিয়ে উত্তর লেখাই।

🪜 ৪. তিন স্তরে বোঝা

Level 1 — সহজ intuition

একটা open-book পরীক্ষা ভাবুন। প্রশ্ন এলে আপনি প্রথমে বইয়ের সঠিক পৃষ্ঠা খুঁজে বের করেন (retrieval), তারপর সেই পৃষ্ঠা দেখে নিজের ভাষায় উত্তর লেখেন (generation)। LLM এখানে ছাত্র, knowledge base হলো বই।

Level 2 — technical ভাবে কী হচ্ছে

Document গুলো chunk করে embed করা হয়, vector DB-তে রাখা হয়। query এলে সেটাও embed হয়, nearest chunk গুলো retrieve হয়। সেগুলো prompt-এ context হিসেবে বসিয়ে LLM-কে দেওয়া হয়, LLM শুধু সেই context-এর ভিত্তিতে উত্তর দেয়।

Level 3 — engineer perspective

Engineer-এর কাছে RAG মানে দুটো আলাদা pipeline (ingestion + query), retrieval quality মাপা, prompt-এ source citation যোগ করা, আর "context-এ না থাকলে বলবে 'জানি না'" — এমন নিয়ম বসানো। এটা model train করা নয়; এটা একটা application architecture

🏗️ ৫. সম্পূর্ণ RAG architecture — বক্স বাই বক্স

┌──────────────── INGESTION (offline, একবার) ─────────────────┐ │ Documents → Clean → Chunking → Embedding → Vector DB store │ └──────────────────────────────────────────────────────────────┘ ┌──────────────── QUERY (online, প্রতি প্রশ্নে) ───────────────┐ │ Question → Embedding → Vector DB (retrieval, top-K) │ │ → Context বানাও → Prompt-এ বসাও → LLM → Answer │ └──────────────────────────────────────────────────────────────┘

মূল অন্তর্দৃষ্টি: ভারী কাজ (embed + index) একবার, offline-এ হয়। প্রতি প্রশ্নে শুধু একটা embedding + একটা fast search + একটা LLM call — তাই সিস্টেম দ্রুত ও সস্তা থাকে।

🔁 ৬. দুই pipeline: Ingestion বনাম Query

Ingestion Pipeline (offline)
কখন: নতুন document এলে
কাজ: clean → chunk → embed → store
গতি: ধীর হলেও চলে (batch)
ফলাফল: একটা তৈরি vector DB
Query Pipeline (online)
কখন: প্রতিটা user প্রশ্নে
কাজ: embed query → retrieve → prompt → LLM
গতি: দ্রুত হতে হবে (latency)
ফলাফল: একটা উত্তর
এই দুই pipeline আলাদা রাখা junior-দের বড় শেখা — অনেকে সব এক জায়গায় মিশিয়ে ফেলে, ফলে প্রতি প্রশ্নে document আবার embed করে (ভুল ও ধীর)।

✂️ ৭. Chunking কেন ও কীভাবে

একটা পুরো ৫০ পৃষ্ঠার document এক embedding-এ ধরলে অর্থ "গড়" হয়ে যায় — নির্দিষ্ট উত্তর হারিয়ে যায়। তাই আমরা document-কে ছোট chunk-এ ভাগ করি (যেমন ~৩০০-৫০০ token), প্রতিটা chunk আলাদা embed করি। তখন retrieval নির্দিষ্ট, প্রাসঙ্গিক অংশ আনতে পারে।

def chunk_text(text: str, size: int = 500, overlap: int = 50): """text-কে overlapping chunk-এ ভাগ করে। overlap রাখি যাতে chunk-এর সীমানায় অর্থ কাটা না পড়ে।""" words = text.split() chunks = [] step = size - overlap for i in range(0, len(words), step): chunk = " ".join(words[i:i + size]) if chunk: chunks.append(chunk) return chunks
Overlap কেন? একটা বাক্য/ধারণা chunk-এর সীমানায় দুই ভাগ হয়ে গেলে অর্থ নষ্ট হয়। অল্প overlap রাখলে সীমানার তথ্যও অন্তত এক chunk-এ পুরো থাকে।

💻 ৮. Code: একটা minimal RAG (pgvector + LLM)

কেন এই stack? — pgvector (আগের episode) retrieval-এর জন্য, আর একটা LLM API generation-এর জন্য। LangChain ছাড়াই এটা লিখছি যাতে ভেতরে কী হচ্ছে পরিষ্কার বোঝা যায় (LangChain optional, magic নয়)।

from openai import OpenAI from sentence_transformers import SentenceTransformer import psycopg2 client = OpenAI() embed = SentenceTransformer( "sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2" ) conn = psycopg2.connect("dbname=jobs user=postgres") def retrieve(question: str, k: int = 4): q = embed.encode(question, normalize_embeddings=True).tolist() cur = conn.cursor() cur.execute( "SELECT content FROM jd_chunks " "ORDER BY embedding <=> %s::vector LIMIT %s", (q, k), ) return [row[0] for row in cur.fetchall()] def answer(question: str) -> str: chunks = retrieve(question) context = "\n\n---\n\n".join(chunks) system = ( "তুমি একজন সহায়ক assistant। শুধুমাত্র নিচের CONTEXT " "ব্যবহার করে বাংলায় উত্তর দাও। CONTEXT-এ উত্তর না থাকলে " "স্পষ্ট বলবে: 'এই তথ্য আমার কাছে নেই।' কিছু বানিয়ে বলবে না।" ) user = f"CONTEXT:\n{context}\n\nপ্রশ্ন: {question}" resp = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": system}, {"role": "user", "content": user}, ], temperature=0, ) return resp.choices[0].message.content print(answer("Python জানা কোন কোন job আছে?"))
লক্ষ্য করুন system prompt-এর নিয়ম: "CONTEXT-এ না থাকলে বলবে জানি না"। এই একটা লাইন hallucination অনেক কমায় — এটাই RAG-কে raw LLM-এর চেয়ে নির্ভরযোগ্য করে।

🚫 ৯. RAG ≠ Fine-tuning (গুরুত্বপূর্ণ পার্থক্য)

RAG
model unchanged; knowledge আসে retrieval থেকে
নতুন তথ্য = শুধু DB update
source citation সম্ভব
দ্রুত, সস্তা, dynamic data-তে আদর্শ
Fine-tuning
model-এর weight বদলায়
নতুন তথ্য = আবার train
citation কঠিন
style/format শেখাতে ভালো, fact-এ নয়
নিয়ম: নতুন fact/knowledge লাগলে → RAG। নির্দিষ্ট style/behavior/format শেখাতে হলে → fine-tuning। প্রায়ই দুটো একসাথেও ব্যবহার হয়। (fine-tuning-এর episode আসছে।)

👻 ১০. Hallucination — RAG কী কমায়, কী কমায় না

Hallucination মানে LLM আত্মবিশ্বাসের সাথে ভুল/বানানো তথ্য দেওয়া। RAG সঠিক context দিয়ে এটা কমায় — কিন্তু সম্পূর্ণ দূর করে না। কেন?

🇧🇩 ১১. বাস্তব উদাহরণ: বাংলাদেশের context

💼 ১২. Boss Question

Boss: "এই RAG জিনিসটা আমার business-এ কী কাজে দেবে?"

উত্তর: আপনার কোম্পানির document (policy, manual, report) থেকে একটা internal knowledge assistant — কর্মীরা মিনিটে উত্তর পাবে, ঘণ্টার পর ঘণ্টা খুঁজতে হবে না। support cost কমবে, নতুন কর্মীর onboarding দ্রুত হবে। আর নতুন document এলে শুধু DB update — model retrain লাগবে না। মানে কম খরচে, নিজের data-র উপর, নির্ভরযোগ্য উত্তর।

🔎 ১৩. Job Requirement Decoder

JD: "Experience building RAG systems / LLM applications with retrieval"

১. কী বোঝায়? vector search + LLM জুড়ে document-ভিত্তিক প্রশ্নোত্তর system বানাতে পারা।
২. কেন চায়? কোম্পানি নিজের private data-র উপর AI চায়, model train না করেই — RAG সেই সবচেয়ে জনপ্রিয় পথ।
৩. কোন সমস্যা সমাধান করে? context window সীমা, খরচ, hallucination, dynamic data।
৪. junior-এর কী জানা লাগে? ingestion vs query pipeline, chunking, retrieval, prompt-এ context বসানো, "জানি না" নিয়ম।
৫. এখনই কী master লাগে না? advanced reranking, distributed retrieval, RAG evaluation framework (পরে আসছে)।
৬. GitHub-এ কীভাবে দেখাবে? একটা end-to-end RAG repo — ingestion script, pgvector, query API, README-তে architecture diagram।
৭. interview প্রশ্ন? "RAG-এর ধাপগুলো?", "RAG vs fine-tuning?", "chunking কেন?", "hallucination RAG দূর করে?"

⚠️ ১৪. সাধারণ ভুল

ভুল ১: প্রতি প্রশ্নে document আবার embed করা। → ingestion একবার, query-তে শুধু retrieve।

ভুল ২: পুরো document এক chunk হিসেবে রাখা। → ছোট, overlapping chunk করুন।

ভুল ৩: context না দিয়ে/অপ্রাসঙ্গিক context দিয়ে LLM-কে প্রশ্ন করা → hallucination।

ভুল ৪: "জানি না" নিয়ম না রাখা → context-এ না থাকলেও model বানিয়ে বলে।

ভুল ৫: RAG-কে fine-tuning ভাবা → RAG-এ কোনো training নেই।

🎤 ১৫. Interview Prep

প্র: RAG-এর পূর্ণরূপ ও ধাপ?
উ: Retrieval-Augmented Generation; chunk→embed→store, তারপর query embed→retrieve→context→LLM→answer।

প্র: RAG আর fine-tuning কখন?
উ: নতুন fact → RAG; নির্দিষ্ট style/format → fine-tuning।

প্র: chunking কেন দরকার?
উ: বড় document-এর embedding অর্থ "গড়" করে ফেলে; ছোট chunk নির্দিষ্ট retrieval দেয়।

প্র: RAG কি hallucination দূর করে?
উ: কমায়, দূর করে না — retrieval খারাপ হলে বা model context উপেক্ষা করলে ভুল আসতে পারে।

✍️ ১৬. হাতে-কলমে

৩টা ছোট বাংলা text document নিন। chunk_text() দিয়ে chunk করুন, pgvector-এ store করুন। একটা প্রশ্ন করুন যার উত্তর document-এ আছে — সঠিক উত্তর আসছে কিনা দেখুন। এবার এমন প্রশ্ন করুন যার উত্তর নেই — "জানি না" নিয়ম কাজ করছে কিনা যাচাই করুন।

🚀 ১৭. Project Connection

এটাই আমাদের flagship-এর V6 (LLM/RAG core) এবং standalone P8 (RAG system)। এখন "Bangladesh Tech Career Assistant"-কে JD corpus নিয়ে প্রশ্ন করা যাবে, আর সে retrieve করে context-ভিত্তিক উত্তর দেবে। পরের episode-এ আমরা এই RAG-এর মান বাড়াব — chunking strategy, metadata filter আর reranking দিয়ে।

📌 ১৮. সারসংক্ষেপ ও পরবর্তী পর্ব

✓ কেন সব document সরাসরি LLM-কে দেওয়া যায় না (context, খরচ, relevance)
✓ RAG = Retrieval + Augmented + Generation
✓ দুই pipeline: ingestion (offline) ও query (online)
✓ chunking + overlap কেন জরুরি
✓ pgvector + LLM দিয়ে একটা minimal RAG
✓ RAG ≠ fine-tuning; RAG hallucination কমায় কিন্তু দূর করে না
পরবর্তী Episode: "RAG-এর মান বাড়ানো" — একটা কাজ করা RAG আর একটা ভালো RAG-এর পার্থক্য কী? chunking strategy, metadata filtering, reranking আর hallucination কমানোর কৌশল।
© 2025 Sheikh Thanbir Alam. All Rights Reserved. thanbirtamim.github.io
এই লেখা মূল লেখকের সম্পত্তি — লিখিত অনুমতি ছাড়া কপি করে অন্য কোনো ওয়েবসাইট, ব্লগ, বই বা প্ল্যাটফর্মে প্রকাশ/বিতরণ করা কঠোরভাবে নিষিদ্ধ। Content may not be copied or republished without permission.