ডেভেলপাররা দেখতে পেয়েছেন যে Claude-এর prompt-caching নিঃশব্দে ব্যর্থ হতে পারে, যেখানে কোনো cached tokens না থাকা সত্ত্বেও প্রিমিয়াম রেটে চার্জ করা হচ্ছে। একটি WhatsApp হ্যান্ডলারের এক সপ্তাহের লগ রান থেকে দেখা গেছে যে কোনো cache read ঘটেনি, তবুও API ক্যাশিং ফিচারের জন্য বিল করেছে—যা খরচ মাসে $১,৮৯০ থেকে কমিয়ে $৪০৬-এ নামিয়ে এনেছিল।
কেন এই সমস্যাটি গুরুত্বপূর্ণ
Prompt caching-এর উদ্দেশ্য হলো একটি প্রম্পটের স্থির অংশ (যাকে “prefix” বলা হয়) পুনরায় ব্যবহার করে খরচ কমানো এবং রেসপন্স দ্রুত করা। এটি যখন সঠিকভাবে কাজ করে, তখন উচ্চ-ট্রাফিক সম্পন্ন অ্যাপগুলো তাদের মাসিক বিল থেকে শত শত ডলার সাশ্রয় করতে পারে। কিন্তু যখন এটি কাজ করে না, তখন ডেভেলপাররা এমন একটি ফিচারের জন্য টাকা দেন যা তারা আসলে কখনও ব্যবহার করেন না, এবং এই নিঃশব্দ ব্যর্থতা (silent failure) সমস্যাটি বোঝার জন্য কোনো এরর (error) বা সতর্কতা প্রদান করে না।
বাগটি কীভাবে প্রকাশ পায়
API একটি cache-control flag এবং একটি prefix গ্রহণ করে, তারপর ক্যাশ থেকে কতগুলো টোকেন পড়া হয়েছে তা রিপোর্ট করে। পর্যবেক্ষণ করা ক্ষেত্রে, প্রতিটি রিকোয়েস্টের cache-read count ছিল শূন্য। কলটি সফল হয়েছিল, কোনো exception বা ত্রুটি দেখা দেয়নি, কিন্তু বিলিংয়ে প্রিমিয়াম ক্যাশ খরচ প্রতিফলিত হয়েছে। আপনি যদি স্পষ্টভাবে read count লগ না করেন, তবে এই ব্যর্থতা ধরা পড়বে না।
ক্যাশ নষ্ট হওয়ার সাধারণ কারণসমূহ
- Prefix খুব ছোট হওয়া – প্রতিটি Claude মডেল ক্যাশযোগ্য prefix-এর জন্য একটি সর্বনিম্ন টোকেন দৈর্ঘ্য নির্ধারণ করে থাকে। Haiku 4.5-এর জন্য কমপক্ষে ৪,০৯৬টি টোকেন প্রয়োজন; Sonnet 4.6-এর জন্য মাত্র ১,০২৪টি। একটি ছোট prefix পাঠালে রিকোয়েস্ট ফরম্যাট ঠিক থাকলেও সার্ভিসটি ক্যাশ নির্দেশটি উপেক্ষা করে।
- একটি পরিবর্তনশীল বাইট সরে যাওয়া – ক্যাশিংয়ের জন্য বাইট-বাইটে হুবহু মিল থাকা প্রয়োজন। সিস্টেম প্রম্পটের শুরুতে কোনো ডাইনামিক এলিমেন্ট যেমন টাইমস্ট্যাম্প,
new Date(), বা ইউজারের ইমেল যোগ করলে বাইট সিকোয়েন্স পরিবর্তিত হয়ে যায়, যার ফলে প্রতিটি রিকোয়েস্টকে একটি নতুন এবং আনক্যাশড (uncached) রাইট হিসেবে গণ্য করা হয়। - টুল লিস্টের ক্রম পরিবর্তন হওয়া – টুলগুলো প্রম্পটের শুরুতে যুক্ত করা হয়। যদি টুল অ্যারেটি (tool array) অবজেক্ট কী (object keys) থেকে তৈরি করা হয়, তবে কলগুলোর মধ্যে ইটারেশন অর্ডার (iteration order) পরিবর্তিত হতে পারে, যা বাইট লেআউট বদলে দেয় এবং ক্যাশ নষ্ট করে ফেলে।
সমাধান যা আপনি আজই প্রয়োগ করতে পারেন
- Prefix-এর দৈর্ঘ্য যাচাই করুন – রিকোয়েস্ট পাঠানোর আগে মডেলের সর্বনিম্ন টোকেন সংখ্যার সাথে prefix-এর টোকেন সংখ্যা মিলিয়ে দেখুন। যদি এটি কম হয়, তবে prefixটি রিজেক্ট করুন অথবা প্যাডিং (pad) করে বাড়িয়ে নিন।
- প্রতিটি কলে ক্যাশ রিড লগ করুন – “cache read tokens” ফিল্ডটি রেকর্ড করুন। টানা শূন্য আসা মানে হলো ক্যাশটি কাজ করছে না।
- প্রম্পটের শুরুর বাইটগুলো স্থির রাখুন – ক্যাশ করা অংশে ডাইনামিক ডেটা রাখবেন না। যদি ইউজার-নির্দিষ্ট তথ্য অন্তর্ভুক্ত করতে হয়, তবে সেটি ক্যাশ করা prefix-এর পরে রাখুন।
- মডেল আইডেন্টিফায়ার সিঙ্ক্রোনাইজ করুন – রাউটিংয়ে ব্যবহৃত মডেল ID যেন আপনার ক্যাশ টেবিলে সংরক্ষিত ID-র সাথে মিলে যায় তা নিশ্চিত করুন; আইডি না মিললে ক্যাশ লুকআপ (cache lookup) সম্ভব হয় না।
খরচের দিকটি
প্রতিদিন হাজার হাজার কল করা একটি অ্যাপের ক্ষেত্রে, আনক্যাশড থেকে ক্যাশড মোডে পরিবর্তন করলে মাসিক খরচ নাটকীয়ভাবে কমে যেতে পারে—রিপোর্টেড ক্ষেত্রে এটি প্রায় $১,৮৯০ থেকে কমিয়ে $৪০৬-এ নামিয়ে আনে। এমনকি মাঝারি ট্রাফিকের ক্ষেত্রেও উল্লেখযোগ্য সাশ্রয় দেখা যায় এবং একটি বড় স্থির প্রম্পট পুনরায় ব্যবহারের ফলে পারফরম্যান্স বৃদ্ধি পায় যা ল্যাটেন্সি (latency) কমিয়ে দেয়।
পাল্টা যুক্তি
তবে, এই ব্যর্থতার নিঃশব্দ প্রকৃতির কারণে আপনি অতিরিক্ত টাকা দিচ্ছেন কি না তা নিশ্চিত হওয়ার একমাত্র উপায় হলো রিড কাউন্ট (read count) পরীক্ষা করা—যা অনেকেই এড়িয়ে যান।
পরবর্তীতে যা খেয়াল রাখতে হবে
- মেট্রিক ড্যাশবোর্ড – রিকোয়েস্ট ভলিউমের পাশাপাশি ক্যাশ-রিড টোকেনের জন্য একটি গেজ (gauge) যুক্ত করুন।
- টুল-অর্ডারিং স্ট্যাবিলিটি – আপনি যদি ডাইনামিকভাবে তৈরি টুল লিস্টের ওপর নির্ভর করেন, তবে প্রম্পটে যুক্ত করার আগে সেগুলোকে ডিটারমিনিস্টিক্যালি (deterministically) সর্ট করার কথা বিবেচনা করুন।
মূল কথা: Claude-এর prompt caching আপনার রিকোয়েস্ট নিঃশব্দে উপেক্ষা করলে কোনো এরর দেখায় না। রিড টোকেন লগ করার মাধ্যমে ক্যাশের কার্যকারিতা যাচাই করুন, সঠিক prefix দৈর্ঘ্য নিশ্চিত করুন এবং প্রম্পটের শুরুর বাইটগুলো অপরিবর্তনীয় (immutable) রাখুন। তবেই আপনি কাঙ্ক্ষিত খরচ এবং গতির সুবিধা পাবেন।
