Context Budget للـ AI: قلّل تاريخ الشات قبل ما يكسرك
هتعرف تبني بوابة بسيطة تقلل تاريخ محادثة الـ AI قبل الإرسال، فتقلل التكلفة والبطء بدون ما تفقد أهم القرارات القديمة.
مستوى القارئ: متوسط
المشكلة باختصار
لو عندك chatbot لخدمة العملاء أو مساعد داخلي للمطورين، أول 10 رسائل بتعدي عادي. بعد 80 رسالة، الطلب الواحد ممكن يبقى 100K token input قبل ما النموذج يكتب كلمة واحدة. الطريقة الشائعة الغلط هي إنك تبعت كل history كما هي. الطريقة دي بتفشل لما المستخدم يفتح نفس المحادثة أسبوع كامل، أو لما الفريق يحط logs وملفات طويلة في نفس thread.
الافتراض هنا إن عندك تطبيق شات يرسل messages array إلى نموذج LLM، وعندك حد آمن مثل 40K input tokens لكل طلب. الرقم مش مقدس. المهم إن الحد يبقى صريح ومقاس.
مثال بسيط قبل التعريف العلمي
ركز في المثال ده: عميل فتح تذكرة دعم يوم السبت. يوم الأحد أضاف لقطة خطأ. يوم الاثنين غير الخطة. يوم الثلاثاء سأل: “ينفع تلخصلي آخر قرار؟”. لو بعت كل الرسائل القديمة، النموذج هيقرأ لقطات وتفاصيل انتهت. الأفضل إنك تحتفظ بثلاث حاجات: system prompt، آخر 8 رسائل، وملخص قصير للقرارات القديمة.
التعريف الدقيق: Context Budget هو حد تشغيلي لحجم السياق المرسل للنموذج، مع سياسة واضحة لتقسيم التاريخ بين محتوى ثابت، رسائل حديثة، وملخصات مضغوطة. الهدف مش حذف عشوائي. الهدف إنك تبعت أقل سياق كافي لاتخاذ القرار الصحيح.
الحل العملي: بوابة قبل استدعاء النموذج
أفضل طريقة هنا هي إضافة خطوة قبل API call. هذه الخطوة تعدّ tokens تقريبية، تحتفظ بالرسائل الأحدث، وتضيف summary للقرارات القديمة عند الحاجة. لو عايزها تدعم أدوات tool calls، خليك حذر: لا تكسر ترتيب tool message بعد assistant message.
const MAX_INPUT_TOKENS = 40000;
const KEEP_LAST_MESSAGES = 8;
function estimateTokens(text) {
return Math.ceil(String(text).length / 4);
}
function msgTokens(msg) {
const content = Array.isArray(msg.content)
? msg.content.map(p => p.text || '').join('\n')
: msg.content || '';
return estimateTokens(`${msg.role}: ${content}`) + 6;
}
function buildContext(messages, oldSummary) {
const system = messages.find(m => m.role === 'system');
const recent = messages.filter(m => m.role !== 'system').slice(-KEEP_LAST_MESSAGES);
const summaryMsg = oldSummary ? {
role: 'system',
content: `ملخص القرارات القديمة: ${oldSummary}`
} : null;
let selected = [system, summaryMsg, ...recent].filter(Boolean);
let total = selected.reduce((sum, m) => sum + msgTokens(m), 0);
while (total > MAX_INPUT_TOKENS && recent.length > 2) {
recent.shift();
selected = [system, summaryMsg, ...recent].filter(Boolean);
total = selected.reduce((sum, m) => sum + msgTokens(m), 0);
}
return { messages: selected, estimatedTokens: total };
}