প্রকাশিত: ২৩ জুন, ২০২৬
প্রতিটি LanguageModel সেশনের একটি নির্দিষ্ট কনটেক্সট উইন্ডো থাকে। কথোপকথন যত দীর্ঘ হতে থাকে, মডেলটি তার কনটেক্সটে সম্পূর্ণ মেসেজ হিস্ট্রি জমা করে: ব্যবহারকারীর প্রতিটি প্রম্পট এবং অ্যাসিস্ট্যান্টের প্রতিটি উত্তর। যখন উইন্ডোটি ভরে যায়, তখন ব্রাউজারের স্বয়ংক্রিয় ওভারফ্লো হ্যান্ডলিং ব্যবস্থা সক্রিয় হয়। এটি নতুন প্রম্পটের জন্য জায়গা খালি করতে সবচেয়ে পুরোনো মেসেজ জোড়াগুলোকে, একবারে একটি করে প্রম্পট ও উত্তরের জোড়া, সরিয়ে দেয়। যদি আগত প্রম্পটটি এতটাই বড় হয় যে পুরো কথোপকথনের হিস্ট্রি সরিয়ে ফেললেও তাতে জায়গা হয় না, তাহলে QuotaExceededError দেখিয়ে কলটি সরাসরি ব্যর্থ হয়ে যায়।
সেশন কম্প্যাক্টিং একটি সক্রিয় বিকল্প: Summarizer API ব্যবহার করে কথোপকথনের ইতিহাস সংক্ষিপ্ত করুন, তারপর সেই সারাংশগুলোকে initialPrompts হিসেবে ব্যবহার করে একটি নতুন সেশন পুনরায় শুরু করুন। রানটাইম ওভারফ্লো হ্যান্ডলিংয়ের সময় ব্রাউজার কখনোই initialPrompts সরিয়ে দেয় না, তাই কম্প্যাক্ট করা সারাংশটি মডেলের কনটেক্সটে স্থায়ীভাবে নোঙর করা থাকে, যতক্ষণ পর্যন্ত create() কল করার সময় সারাংশগুলো কনটেক্সট উইন্ডোর মধ্যে আঁটে। নতুন সেশনটি মূল টোকেন খরচের একটি ভগ্নাংশে একই কথোপকথনের ধারা বহন করে।
সেশন কম্প্যাক্টিং দীর্ঘস্থায়ী LanguageModel কথোপকথনগুলোকে ধারাবাহিকতা না হারিয়ে কনটেক্সট উইন্ডোর মধ্যে থাকার একটি উপায় দেয়। এর মূল ধাপগুলো হলো:
-
contextWindowএর সাপেক্ষেcontextUsageনিরীক্ষণ করুন এবং তা ব্যবহারকারীর কাছে তুলে ধরুন। - প্রাথমিক সতর্কতা হিসেবে
contextoverflowইভেন্টটির জন্য কান পেতে থাকুন। - Language Detector API ব্যবহার করে প্রতিটি বার্তার ভাষা শনাক্ত করুন, তারপর একটি ভাষা-সচেতন Summarizer API ইনস্ট্যান্সের মাধ্যমে সেটির সারসংক্ষেপ তৈরি করুন।
- পুরানো সেশনটি মুছে ফেলুন এবং
initialPromptsদিয়ে একটি নতুন সেশন শুরু করুন। - ভুল থেকে পুনরুদ্ধারের জন্য
fullHistoryএকটি কপি রাখুন।
প্রসঙ্গ ব্যবহার ট্র্যাক করুন
একটি সেশনের কনটেক্সট কতটা পূর্ণ তা নিরীক্ষণ করার জন্য প্রম্পট এপিআই দুটি অ্যাট্রিবিউট প্রদান করে:
-
session.contextUsage: বর্তমানে ব্যবহৃত টোকেনের সংখ্যা। -
session.contextWindow: সেশনের মোট টোকেন ধারণক্ষমতা।
এটি একটি <progress> এলিমেন্টে প্রতিফলিত করুন, যাতে ব্যবহারকারীরা এক নজরে জানতে পারেন যে সেশনটি তার সীমার কতটা কাছাকাছি। value এবং max সরাসরি টোকেন সংখ্যায় সেট করুন; ব্রাউজার বারটিকে স্বয়ংক্রিয়ভাবে স্কেল করবে:
<progress id="token-bar" value="0" max="1"></progress>
<label for="token-bar" id="token-label">Context: — / — tokens</label>
function updateTokenDisplay(session) {
const usage = session.contextUsage;
const total = session.contextWindow;
tokenBar.value = usage;
tokenBar.max = total;
tokenLabel.textContent =
`${Math.round(usage)} / ${Math.round(total)} tokens ` +
`(${Math.round((usage / total) * 100)}%)`;
}
প্রতিটি প্রম্পট প্রতিক্রিয়ার পরে updateTokenDisplay() কল করুন, যাতে বারটি হালনাগাদ থাকে।
প্রসঙ্গের অতিরিক্ত প্রবাহের জন্য শুনুন
যখন একটি নতুন প্রম্পট অবশিষ্ট কনটেক্সটকে ছাড়িয়ে যায়, তখন ব্রাউজারের স্বয়ংক্রিয় পুনরুদ্ধার প্রক্রিয়া শুরু হয়: এটি সবচেয়ে পুরোনো প্রম্পট এবং রেসপন্স জোড়াগুলোকে এক এক করে সরিয়ে দেয় যতক্ষণ না পর্যাপ্ত জায়গা খালি হয়। এই অপসারণ প্রক্রিয়াটি শুরু হওয়ার মুহূর্তে ` contextoverflow ইভেন্টটি ফায়ার হয়। সেশন তৈরি করার ঠিক পরেই একটি হ্যান্ডলার রেজিস্টার করুন:
session.addEventListener('contextoverflow', () => {
showWarning('⚠ Context window nearly full. Consider compacting the session.');
});
এই উচ্ছেদ আচরণের দুটি গুরুত্বপূর্ণ বৈশিষ্ট্য রয়েছে:
- রানটাইমে
initialPromptsসরিয়ে দেওয়া হয় না। ব্রাউজার নতুন কোনো প্রম্পটের জন্য জায়গা করে দিতে এগুলো মুছে ফেলে না। তবে,LanguageModel.create()-এ পাঠানোinitialPromptsসম্মিলিত আকার যদি কনটেক্সট উইন্ডোতে আঁটার জন্য খুব বড় হয়ে যায়, তাহলেcreate()ফাংশনটি একটিQuotaExceededErrorদেখিয়ে বাতিল হয়ে যায়। তাই, কথোপকথন চালিয়ে যাওয়ার জন্য কম্প্যাকশনটি যথেষ্ট ছোট হয় কিনা, তা নিশ্চিত করুন। - উচ্ছেদের একটি সীমা আছে। যদি আগত প্রম্পটটি এতটাই বড় হয় যে পুরো পূর্ববর্তী কথোপকথনটি মুছে ফেলার পরেও তাতে জায়গা হয় না, তাহলে
prompt()বাpromptStreaming()কলটি `QuotaExceededErrorসহ ব্যর্থ হয় এবং কিছুই মুছে ফেলা হয় না।
প্রম্পট এপিআই ডকুমেন্টেশনে কনটেক্সট ওভারফ্লো হ্যান্ডলিং সম্পর্কে আরও পড়ুন।
ব্রাউজার নীরবে কথোপকথনের ইতিহাস মুছে ফেলা শুরু করার আগে ব্যবহারকারীকে সতর্ক করতে, সেন্ড বাটনটি নিষ্ক্রিয় করতে, অথবা স্বয়ংক্রিয়ভাবে কম্প্যাকশন চালু করতে contextoverflow ইভেন্টটি ব্যবহার করুন।
সেশনটি সংক্ষিপ্ত করুন
সংহতকরণের তিনটি ধাপ রয়েছে:
- Summarizer API ব্যবহার করে কথোপকথনের ইতিহাসের প্রতিটি বার্তার সারসংক্ষেপ তৈরি করুন।
- পুরানো সেশনটি ধ্বংস করুন।
- সারাংশগুলিকে
initialPromptsহিসাবে ব্যবহার করে একটি নতুন সেশন তৈরি করুন।
ইতিহাসটি সংক্ষেপে বর্ণনা করুন।
স্বতন্ত্র চ্যাট বার্তা সংকুচিত করার জন্য সামারাইজার এপিআই একটি আদর্শ সমাধান। প্রতিটি বার্তার জন্য, প্রথমে ল্যাঙ্গুয়েজ ডিটেক্টর এপিআই ব্যবহার করে এর ভাষা শনাক্ত করুন, যাতে সামারাইজারটি সঠিকভাবে কনফিগার করা যায়:
async function detectLanguage(text, threshold = 0.7) {
const detector = await LanguageDetector.create();
const results = await detector.detect(text);
if (results.length > 0 && results[0].confidence >= threshold) {
return results[0].detectedLanguage;
}
return null; // confidence too low — caller falls back to navigator.language
}
0.7 কনফিডেন্স থ্রেশহোল্ডটি অনিশ্চিত ডিটেকশনের উপর কাজ করা এড়িয়ে চলে। যখন কনফিডেন্স থ্রেশহোল্ডের নিচে থাকে, তখন navigator.language এ ফিরে যান।
এরপর, শনাক্তকৃত ভাষার জন্য কনফিগার করা একটি সামারাইজার তৈরি করুন। ছোট ও কম-ল্যাটেন্সিযুক্ত মডেল ভ্যারিয়েন্টটি নির্বাচন করতে preference: 'speed' বেছে নিন, এবং যদি দ্রুততর মডেলটি শনাক্তকৃত ভাষাটি সমর্থন না করে, তবে preference: 'auto'
const summarizers = {}; // cache, keyed by `${format}:${lang}`
async function getSummarizer(format, lang) {
const key = `${format}:${lang}`;
if (summarizers[key]) return summarizers[key];
const baseOptions = {
type: 'tldr',
format, // 'markdown' or 'plain-text'
length: 'short',
expectedInputLanguages: [lang],
expectedContextLanguages: [lang],
outputLanguage: lang,
};
let options = { ...baseOptions, preference: 'speed' };
let avail = await Summarizer.availability(options);
if (avail === 'unavailable') {
options = { ...baseOptions, preference: 'auto' };
avail = await Summarizer.availability(options);
}
if (avail === 'unavailable') {
throw new Error('Summarizer API unavailable on this device.');
}
summarizers[key] = await Summarizer.create(options);
return summarizers[key];
}
প্রতিটি format ও lang জোড়া অনুযায়ী সামারাইজার ক্যাশ করে রাখলে, পরপর মেসেজগুলোর ভাষা একই হলে অপ্রয়োজনীয় create() কল এড়ানো যায়।
format আর্গুমেন্টটি মেসেজের বিষয়বস্তু থেকেই নেওয়া হয়। সাধারণ গদ্যের জন্য 'markdown' নির্দিষ্ট করলে অনাকাঙ্ক্ষিত ফরম্যাটিং চলে আসতে পারে, এবং Markdown-এর জন্য 'plain-text' নির্দিষ্ট করলে কোড ফেন্স ও এমফাসিস বাদ পড়ে যায়। একটি ছোট রেগুলার এক্সপ্রেশন এই দুটিকে আলাদা করে:
function looksLikeMarkdown(text) {
return /(?:^#{1,6} |^[-*+] |\d+\. |\*\*|__|\[.+?\]\(|^> |^```)/m.test(text);
}
ভাষা এবং ফরম্যাট ঠিক হয়ে গেলে, প্রতিটি মেসেজের সারসংক্ষেপ করুন এবং একটি context স্ট্রিং পাস করুন, যাতে মডেলটি বুঝতে পারে যে এটি একটি চ্যাট টার্ন কম্প্রেস করছে, কোনো স্বতন্ত্র ডকুমেন্ট নয়:
const compacted = [];
for (const msg of history) {
const lang = (await detectLanguage(msg.content)) ?? navigator.language;
const format = looksLikeMarkdown(msg.content) ? 'markdown' : 'plain-text';
const summarizer = await getSummarizer(format, lang);
const summary = await summarizer.summarize(msg.content.trim(), {
context:
`This is a ${msg.role} turn from a chat conversation. ` +
`Preserve its key meaning as concisely as possible.`,
});
// Only use the summary if it's actually shorter.
compacted.push({
role: msg.role,
content:
summary.trim().length < msg.content.length ? summary.trim() : msg.content,
});
}
পুরানো অধিবেশন ধ্বংস করুন
প্রতিস্থাপন তৈরি করার আগে পুরানো সেশনের রিসোর্সগুলো মুক্ত করুন:
session.destroy();
session = null;
সংকুচিত ইতিহাস সহ একটি নতুন সেশন তৈরি করুন
নতুন সেশনটিকে কথোপকথনের প্রেক্ষাপট দিয়ে শুরু করতে, সংকুচিত বার্তাগুলিকে initialPrompts হিসাবে পাঠান:
// Collect every language the detector was confident about.
const sessionLangs =
confidentLangs.size > 0 ? [...confidentLangs] : [navigator.language];
session = await LanguageModel.create({
expectedInputs: [{ type: 'text', languages: sessionLangs }],
expectedOutputs: [{ type: 'text', languages: sessionLangs }],
initialPrompts: compacted,
});
// Re-register the overflow handler on the new session.
session.addEventListener('contextoverflow', () => {
/* ... */
});
নতুন সেশনটি একটি নিম্নতর contextUsage থেকে শুরু হয়। কথোপকথনটি যেখান থেকে থেমেছিল সেখান থেকেই চলতে থাকে: মডেলটির কাছে সারাংশগুলো তার পূর্ববর্তী প্রসঙ্গ হিসেবে থাকে, তাই এটি আগের বিষয়গুলো সম্পর্কে পরবর্তী প্রশ্নগুলোর উত্তর দিতে পারে।
ত্রুটিগুলি পরিচালনা করুন
পুরানো সেশনটি মুছে ফেলার পর যদি সারসংক্ষেপ তৈরি বা সেশন তৈরি ব্যর্থ হয়, তবে ব্যবহারকারী চ্যাট করার ক্ষমতা হারান। একটি পৃথক fullHistory অ্যারে বজায় রাখুন যা কম্প্যাকশনের মাধ্যমে কখনও ওভাররাইট হয় না এবং এটিকে একটি পুনরুদ্ধার ফলব্যাক হিসাবে ব্যবহার করুন:
const history = []; // current session's view, replaced on each compaction
const fullHistory = []; // every original message, never overwritten
// In the catch block:
if (!session) {
session = await LanguageModel.create({
initialPrompts: fullHistory.map(({ role, content }) => ({ role, content })),
});
session.addEventListener('contextoverflow', () => {
/* ... */
});
}
fullHistory থেকে পুনরুদ্ধার করলে কনটেক্সট আবার ধারণক্ষমতার কাছাকাছি চলে যেতে পারে, কিন্তু ব্যবহারকারী অন্তত একটি কার্যকরী অবস্থায় ফিরে আসেন এবং অবিলম্বে আরেকটি কম্প্যাকশনের চেষ্টা করতে পারেন।
ঐচ্ছিকভাবে কিছু বিষয়বস্তুকে সংকুচিত হওয়া থেকে বিরত রাখুন।
যদি কোনো বার্তার এমন গুরুত্বপূর্ণ অংশ থাকে যা অবশ্যই প্রাসঙ্গিক রাখতে হবে, যেমন কোডের নমুনা, তবে সেগুলোকে আলাদাভাবে প্রক্রিয়া করুন। নিম্নলিখিত উদাহরণটি একটি বার্তাকে পর্যায়ক্রমিক গদ্য এবং কোড-ফেন্স অংশে বিভক্ত করে, তারপর কোডের অংশগুলোকে অক্ষত রেখে শুধুমাত্র গদ্য অংশগুলোর সারসংক্ষেপ করে:
// Splits text into alternating prose and code-fence segments.
// Returns [{ type: 'prose'|'code', content: string }, …]
function splitByCodeFences(text) {
const parts = [];
const re = /^```[^\n]*\n[\s\S]*?^```[ \t]*$/gm;
let lastIndex = 0;
let match;
while ((match = re.exec(text)) !== null) {
if (match.index > lastIndex) {
parts.push({
type: 'prose',
content: text.slice(lastIndex, match.index),
});
}
parts.push({ type: 'code', content: match[0] });
lastIndex = match.index + match[0].length;
}
if (lastIndex < text.length) {
parts.push({ type: 'prose', content: text.slice(lastIndex) });
}
return parts;
}
ডেমোটি চেষ্টা করে দেখুন
সেশন কম্প্যাক্টিং ডেমোটি আপনাকে প্রম্পট এপিআই (Prompt API)-এর সাথে চ্যাট করতে এবং যেকোনো সময় সেশন কম্প্যাক্ট করতে দেয়। টোকেন বারটি রিয়েল-টাইম কনটেক্সট ব্যবহার দেখায় এবং কনটেক্সট পূর্ণ হওয়ার সাথে সাথে এর রঙ পরিবর্তন হয়। প্রতিটি কম্প্যাকশনের পরে, একটি লগ এন্ট্রি আগের ও পরের টোকেন সংখ্যা রেকর্ড করে রাখে, যাতে আপনি সরাসরি এর হ্রাস পর্যবেক্ষণ করতে পারেন।
আপনি পৃষ্ঠার নীচে থাকা সংকোচনযোগ্য 'ডিবাগ: কথোপকথন JSON' বিভাগে সম্পূর্ণ এবং সংকুচিত কথোপকথন JSON পরিদর্শন করতে পারেন।