মূল বিষয়ে যান
WebGuideBD AI Academy

প্রজেক্ট: Rule-based Support Assistant

60 মিনিট · আবশ্যক

  • টেক্সট স্বাভাবিকীকরণ, intent স্কোরিং ও fallback সহ একটি সম্পূর্ণ পাইপলাইন বানাতে পারবেন
  • প্রতিটি সিদ্ধান্তের মধ্যবর্তী ধাপ (trace) দেখাতে পারবেন
  • অন্তত তিন ধরনের ব্যর্থতা খুঁজে কারণ ব্যাখ্যা করতে পারবেন

সমস্যাটা কী?

দোকানে রাত ১২টার পরেও মেসেজ আসে। মালিক চান একটি স্বয়ংক্রিয় প্রথম-সাড়া: সাধারণ প্রশ্নে (ডেলিভারি চার্জ, ফেরত নীতি, অর্ডার স্ট্যাটাস) তাৎক্ষণিক উত্তর, আর অনিশ্চিত হলে “সকালে একজন প্রতিনিধি উত্তর দেবেন”। শর্ত: ভুল তথ্য দেওয়ার চেয়ে “জানি না” বলা ভালো।

শেখার লক্ষ্য

  • টেক্সট স্বাভাবিকীকরণ, intent স্কোরিং ও fallback সহ একটি সম্পূর্ণ পাইপলাইন বানাতে পারবেন
  • প্রতিটি সিদ্ধান্তের মধ্যবর্তী ধাপ (trace) দেখাতে পারবেন
  • অন্তত তিন ধরনের ব্যর্থতা খুঁজে কারণ ব্যাখ্যা করতে পারবেন

আনুমানিক সময়: 60 মিনিট

পূর্বশর্ত

ব্যাখ্যা

পাইপলাইন (ইনপুট → রূপান্তর → আউটপুট):

  1. Normalize: ছোট হাতের অক্ষর, বিরামচিহ্ন সরানো, অতিরিক্ত স্পেস বাদ। “ORD-17?” → “ord-17”।
  2. Extract: অর্ডার নম্বর খোঁজা (প্যাটার্ন: ord- তারপর অঙ্ক)।
  3. Score intents: প্রতিটি intent-এর কীওয়ার্ড তালিকা; মেসেজে যতগুলো মেলে ততো স্কোর।
  4. Decide: সর্বোচ্চ স্কোর নির্দিষ্ট সীমার (threshold) কম হলে বা দুটি intent সমান হলে → fallback (মানুষের কাছে)।
  5. Act: status intent ও অর্ডার নম্বর থাকলে fixture থেকে স্ট্যাটাস আনা (async)।
  6. Trace: প্রতিটি ধাপের মান ফেরত দেওয়া যাতে কেন এই উত্তর তা দেখা যায়।

নতুন সিনট্যাক্স: /ord-(d+)/ হলো একটি regular expression—টেক্সটে প্যাটার্ন খোঁজার ছোট ভাষা। d+ মানে “এক বা একাধিক অঙ্ক”। text.match(regex) মিললে একটি array দেয়, না মিললে null। এখানে শুধু এই একটি প্যাটার্ন ব্যবহার করব।

ডেলিভারি চার্জ ও ফেরত নীতির সংখ্যাগুলো এই প্রজেক্টের জন্য উদাহরণ। আসল দোকানে এগুলো দোকানের নিজস্ব নীতি থেকে আসবে, কোডে হার্ডকোড করা অনুমান থেকে নয়।

উপমা — এবং কোথায় ভাঙে

উপমা: অ্যাসিস্ট্যান্ট হলো রাতের পাহারাদার যার হাতে একটি নির্দেশিকা-কার্ড: “কেউ ডেলিভারি চার্জ জিজ্ঞেস করলে এটা বলো; বুঝতে না পারলে লিখে রাখো, সকালে ম্যানেজার দেখবেন।”

কোথায় ভাঙে: পাহারাদার নতুন ধরনের প্রশ্ন শুনে বুদ্ধি খাটাতে পারেন; আমাদের অ্যাসিস্ট্যান্ট কার্ডের বাইরে এক পা-ও যায় না—“পাঠানোর খরচ” আর “ডেলিভারি চার্জ” একই জিনিস তা সে জানে না যদি না কীওয়ার্ড যোগ করা হয়।

ধাপে ধাপে উদাহরণ

মেসেজ: ORD-17 এর ডেলিভারি স্ট্যাটাস কী?

একটি মেসেজের সম্পূর্ণ ট্রেস
ধাপমান
normalize"ord-17 এর ডেলিভারি স্ট্যাটাস কী"
extractorderId = "ORD-17"
scorestatus: ২ (স্ট্যাটাস, ord-), shipping: ১ (ডেলিভারি), returns: ০
decideসর্বোচ্চ status = ২ ≥ threshold ১, দ্বিতীয় ১ → টাই নয় → status
actfixture → "shipped"
reply“অর্ডার ORD-17 এর অবস্থা: পাঠানো হয়েছে”

লক্ষ্য করুন “ডেলিভারি” শব্দ shipping-এর দিকেও টেনেছিল। ট্রেস না দেখলে আমরা জানতাম না সিদ্ধান্ত কত কাছাকাছি ছিল।

অনুশীলন / অ্যাক্টিভিটি

এই রানারে console.log আউটপুট দেখায়। ব্রাউজারের DOM, নেটওয়ার্ক (fetch), টাইমার বা ফাইল এখানে নেই—ইচ্ছাকৃতভাবে। Ctrl/⌘ + Enter চাপলেও Run হয়।

প্রজেক্ট মানদণ্ড: (১) প্রদত্ত ৮টি পরীক্ষা মেসেজের প্রতিটিতে ট্রেসসহ উত্তর, (২) অন্তত তিনটি ব্যর্থতার কারণ নিজের ভাষায় লেখা, (৩) একটি উন্নতি (নতুন কীওয়ার্ড বা নিয়ম) যোগ করে আগের সঠিক উত্তরগুলো ভাঙেনি তা দেখানো।

প্রজেক্ট: নিয়মভিত্তিক সাপোর্ট অ্যাসিস্ট্যান্ট (AI মডেল নয়)

// ---- কনফিগারেশন (উদাহরণ নীতি) ----
const INTENTS = {
  shipping: { keywords: ["ডেলিভারি", "চার্জ", "শিপিং", "কত দিন"], reply: "ঢাকায় ডেলিভারি ৮০ টাকা, ঢাকার বাইরে ১৫০ টাকা।" },
  returns:  { keywords: ["ফেরত", "রিটার্ন", "বদল"], reply: "পণ্য হাতে পাওয়ার ৩ দিনের মধ্যে ফেরতের অনুরোধ করা যায়।" },
  status:   { keywords: ["স্ট্যাটাস", "কোথায়", "ord-", "পৌঁছাবে"], reply: null },
};
const THRESHOLD = 1;
const FALLBACK = "দুঃখিত, নিশ্চিত হতে পারছি না। একজন প্রতিনিধি সকালে উত্তর দেবেন।";

// ---- লোকাল fixture (আসল অর্ডার সিস্টেম নয়) ----
const ORDERS = { "ORD-17": "shipped", "ORD-21": "processing" };
const STATUS_BN = { shipped: "পাঠানো হয়েছে", processing: "প্রস্তুত হচ্ছে" };
const fakeOrderApi = (id) => Promise.resolve(ORDERS[id]);

// ---- পাইপলাইন ----
function normalize(text) {
  return text.toLowerCase().replace(/[?!,।]/g, " ").replace(/s+/g, " ").trim();
}

function extractOrderId(text) {
  const m = text.match(/ord-(d+)/);
  return m ? "ORD-" + m[1] : null;
}

function scoreIntents(text) {
  const scores = {};
  for (const name of Object.keys(INTENTS)) {
    scores[name] = INTENTS[name].keywords.filter((k) => text.includes(k)).length;
  }
  return scores;
}

function decide(scores) {
  const ranked = Object.entries(scores).sort((a, b) => b[1] - a[1]);
  const [best, second] = ranked;
  if (best[1] < THRESHOLD) return { intent: null, why: "কোনো কীওয়ার্ড মেলেনি" };
  if (best[1] === second[1]) return { intent: null, why: "টাই: " + best[0] + " = " + second[0] };
  return { intent: best[0], why: best[0] + " স্কোর " + best[1] };
}

async function respond(message) {
  const text = normalize(message);
  const orderId = extractOrderId(text);
  const scores = scoreIntents(text);
  const decision = decide(scores);
  let reply = FALLBACK;
  if (decision.intent === "status") {
    if (!orderId) {
      reply = "অর্ডার নম্বর (যেমন ORD-17) দিলে স্ট্যাটাস দেখাতে পারব।";
    } else {
      const status = await fakeOrderApi(orderId);
      reply = status ? "অর্ডার " + orderId + " এর অবস্থা: " + STATUS_BN[status] : "অর্ডার " + orderId + " খুঁজে পাইনি।";
    }
  } else if (decision.intent) {
    reply = INTENTS[decision.intent].reply;
  }
  return { message, text, orderId, scores, decision: decision.why, reply };
}

// ---- পরীক্ষা ----
const tests = [
  "ORD-17 এর ডেলিভারি স্ট্যাটাস কী?",
  "ঢাকার বাইরে ডেলিভারি চার্জ কত?",
  "পণ্য ফেরত দিতে চাই",
  "ORD-99 কোথায়?",
  "আমার পার্সেল কবে আসবে",
  "ফেরত চাই না, শুধু ডেলিভারি কবে বলুন",
  "delivery charge koto?",
  "ধন্যবাদ!",
];

async function main() {
  for (const t of tests) {
    const r = await respond(t);
    console.log("▶ " + r.message);
    console.log("   scores=" + JSON.stringify(r.scores) + " | " + r.decision + " | order=" + r.orderId);
    console.log("   ↳ " + r.reply);
  }
}
main();

JavaScript চালু থাকলে এখানে Run বাটনসহ এডিটর আসবে।

লাইন ধরে বুঝি
  • normalize: দুটি regex replace—প্রথমটি বিরামচিহ্নকে স্পেস বানায়, দ্বিতীয়টি (/s+/g) একাধিক স্পেসকে এক স্পেস। g মানে “সব জায়গায়”।
  • extractOrderId: m[1] হলো বন্ধনীর ভেতরের অংশ (অঙ্কগুলো)। না মিললে null—পরে if দিয়ে যাচাই।
  • scoreIntents: প্রতিটি intent-এর জন্য কতটি কীওয়ার্ড টেক্সটে আছে (filter(...).length)।
  • Object.entries(scores).sort(...): [নাম, স্কোর] জোড়ার array, বড় থেকে ছোট সাজানো। const [best, second] = ranked প্রথম দুটি বের করে।
  • decide: threshold ও টাই—দুই অনিশ্চয়তায় fallback। এটি “ভুল তথ্যের চেয়ে জানি না ভালো” নীতির কোড রূপ।
  • respond একটি পুরো ট্রেস object ফেরত দেয়—শুধু উত্তর নয়। পরে মূল্যায়ন (অধ্যায় ১৩) এই ট্রেস দিয়েই হবে।
এখানে এটা কেন ব্যবহার করেছি?

প্রতিটি ধাপ আলাদা function হওয়ায় আলাদাভাবে পরীক্ষা ও বদলানো যায়। Fallback-কে প্রথম শ্রেণির ফল হিসেবে রাখা হয়েছে কারণ সাপোর্ট সিস্টেমে আত্মবিশ্বাসী ভুল উত্তর গ্রাহকের আস্থা নষ্ট করে। ট্রেস দেখানো হয়েছে যাতে কোনো সিদ্ধান্ত “কালো বাক্স” না থাকে।

কোন বিষয়গুলো মনে রাখব?
  • পাইপলাইন: normalize → extract → score → decide → act
  • অনিশ্চিত হলে fallback—threshold ও টাই
  • ট্রেস ছাড়া ব্যর্থতা বোঝা যায় না
  • নিয়মভিত্তিক সিস্টেম অর্থ নয়, শব্দ মেলায়
এখনই কোনগুলো মুখস্থ করতে হবে না?
  • regular expression-এর বাকি সিনট্যাক্স
  • Object.entries/sort-এর সব বিকল্প
  • বাস্তব চ্যাট প্ল্যাটফর্মে সংযোগ
নিজে পরিবর্তন করে দেখুন

তিনটি পরিবর্তন একটি একটি করে করুন এবং প্রতিবার সব ৮টি পরীক্ষা দেখুন: (১) status-এ "কবে আসবে" যোগ, (২) shipping-এ "delivery", "charge" যোগ, (৩) "না" শব্দ থাকলে returns স্কোর থেকে ১ বাদ। কোন পরিবর্তন অন্য কোনো উত্তর ভেঙে দিল? এটিই regression—অধ্যায় ১৩-এ এর পরীক্ষা স্বয়ংক্রিয় করব।

সাধারণ ভুল

  • একটি মেসেজ ঠিক করতে গিয়ে অন্যগুলো পরীক্ষা না করা (regression)
  • fallback না রেখে সবসময় কোনো না কোনো উত্তর দেওয়া
  • fixture-এর অর্ডার ডেটাকে আসল ধরে নেওয়া
  • কীওয়ার্ড তালিকা বাড়াতে বাড়াতে নিয়ম পরস্পরবিরোধী করে ফেলা
সমাধান ও ব্যাখ্যা

প্রত্যাশিত ব্যর্থতাগুলো: “আমার পার্সেল কবে আসবে” — কোনো কীওয়ার্ড নেই (শব্দভাণ্ডারের ফাঁক)। “ফেরত চাই না…” — shipping ও returns টাই, কারণ নিয়ম নেতিবাচকতা বোঝে না। “delivery charge koto?” — বাংলিশ ইনপুট, তালিকায় নেই (distribution shift)। “ধন্যবাদ!” — fallback ঠিক আছে, কিন্তু আদর্শভাবে একটি ভদ্র উত্তর দেওয়া যেত। প্রতিটি সমাধানে নতুন নিয়ম লাগে, আর নিয়ম বাড়লে পারস্পরিক সংঘর্ষ বাড়ে—এটিই শেখা মডেলের দিকে যাওয়ার কারণ। কিন্তু মনে রাখুন, শেখা মডেলেরও fallback ও ট্রেস লাগবে।

জ্ঞান যাচাই

নিচের 4টি প্রশ্নের উত্তর দিয়ে “জমা দিন” চাপুন। স্কোর সার্ভারে যাচাই হয়; প্রতিটি উত্তরের পর কেন সঠিক বা ভুল তা দেখাবে। লগইন থাকলে ফল আপনার অগ্রগতিতে সংরক্ষিত হয়।

সংক্ষেপে মনে রাখুন

  • সম্পূর্ণ পাইপলাইন ট্রেসসহ বানালেন
  • নিয়মভিত্তিক সিস্টেমের তিন ব্যর্থতা: শব্দভাণ্ডারের ফাঁক, নেতিবাচকতা, ভিন্ন লিপি
  • fallback ও regression পরীক্ষা যেকোনো AI সিস্টেমেও লাগবে

পরবর্তী পাঠ

নিয়মের সীমা দেখলেন। শেখা মডেল বুঝতে প্রথমে লাগবে গণিত—অধ্যায় ৩ (লগইন প্রয়োজন, বিনামূল্যে): Scalar, Vector ও Dimension

সূত্র ও যাচাই

যাচাইয়ের তারিখ: 2026-10-19; লেখক: Muhibbullah Ansary। এই পাঠের 1টি JavaScript উদাহরণ বিল্ডের সময় সাইটের একই QuickJS রানারে চালানো হয়েছে এবং প্রত্যাশিত আউটপুট মিলেছে; সংখ্যাগত ফলাফল আলাদা assertion দিয়ে (floating-point tolerance সহ) পরীক্ষিত। এই পাঠের ডেটা/উদাহরণ শেখানোর জন্য তৈরি সিন্থেটিক ডেটা—বাস্তব কোনো দোকান বা গ্রাহকের নয়। স্বাধীন বিশেষজ্ঞ রিভিউ এখনও হয়নি; ভুল পেলে যোগাযোগ পাতায় জানান।

জ্ঞান যাচাই

“ফেরত চাই না, শুধু ডেলিভারি কবে বলুন” মেসেজে অ্যাসিস্ট্যান্ট fallback দিল কেন?
threshold ও টাই নিয়মের মূল উদ্দেশ্য কী?
একটি নতুন কীওয়ার্ড যোগ করার পর সবচেয়ে জরুরি কাজ কোনটি?
fakeOrderApi-কে আসল অর্ডার API দিয়ে বদলালে কী অতিরিক্ত সামলাতে হবে?

আপনার শেখার অগ্রগতি সংরক্ষণ করতে লগইন করুন।

এখানে প্রশ্ন করুন

আপনার শেখার অগ্রগতি সংরক্ষণ করতে লগইন করুন।