حساب التكلفة وإدارة الرموز (Tokens): سكربت لمعرفة حجم استهلاك الـ API قبل الإرسال

دقائق القراءة: 5

لماذا يجب حساب Tokens قبل إرسال الطلب؟

في التطبيقات المعتمدة على LLMs لا تُقاس التكلفة عادة بعدد الطلبات فقط، بل بحجم الرموز النصية الداخلة والخارجة. لذلك فإن بناء طبقة مسبقة لحساب Tokenization قبل استدعاء الواجهة البرمجية ليس تحسيناً ثانوياً، بل مكوّن هندسي أساسي في أي نظام إنتاجي. هذه الطبقة تتيح لك منع تجاوز حد السياق، تقدير الفاتورة المتوقعة، تطبيق سياسات قصّ ذكية للنصوص، واختيار النموذج المناسب ديناميكياً بناءً على الميزانية.

عملياً، أي رسالة تُرسل إلى النموذج تمر عبر سلسلة من العناصر: system، وuser، وربما سجل محادثة سابق، ثم تعليمات مهيكلة أو أمثلة few-shot. كل هذا يُحوَّل إلى رموز، ثم يُضاف إليه هامش للإخراج المتوقع. من هنا تظهر الحاجة إلى سكربت استباقي يحسب الاستهلاك قبل الإرسال، وليس بعده فقط.

المعمارية الصحيحة لطبقة تقدير التكلفة

أفضل تصميم هندسي لاحتساب الاستهلاك يقوم على فصل المسؤوليات إلى أربع طبقات واضحة:

  • طبقة تجميع المدخلات Input Assembly لتوحيد الرسائل والنصوص المسترجعة.
  • طبقة ترميز وعدّ Tokenizer Layer لحساب عدد الرموز بدقة قدر الإمكان.
  • طبقة سياسات Policy Layer لتحديد الحدود القصوى، القصّ، أو تبديل النموذج.
  • طبقة التسعير Pricing Layer لحساب تكلفة الإدخال والإخراج المتوقعة.

هذا التصميم مهم خصوصاً في الأنظمة التي تستخدم Python مع أطر مثل LangChain أو مع تدفّق طلبات OpenAI API مباشرة، لأن النص النهائي غالباً لا يكون مجرد prompt واحد، بل ناتج دمج عدة مصادر بيانات.

تدفق البيانات من الطلب إلى الفاتورة المتوقعة

التدفق النموذجي يبدأ باستقبال إدخال المستخدم، ثم حقن التعليمات النظامية، ثم ضمّ السياق المسترجع إذا كان التطبيق يستخدم RAG أو Embeddings. بعد ذلك يُقدَّر عدد رموز الإدخال، ثم يضاف حد مخصّص لمخرجات النموذج، ثم تُحسب التكلفة الإجمالية، وأخيراً يُتخذ قرار: إرسال الطلب أو اختصاره أو إعادة تشكيله.

إذا تجاوز الطلب 80% من حد السياق للنموذج، قم تلقائياً بتقليل سجل المحادثة، واختصر المستندات المسترجعة، ثم أعد حساب tokens قبل تنفيذ الاستدعاء النهائي.

بناء سكربت عملي لحساب عدد الرموز والتكلفة

الفكرة الأساسية هي استخدام مكتبة الترميز المناسبة للنموذج، ثم قياس مجموع الرسائل، ثم ضرب الناتج في تسعيرة الإدخال، مع تقدير منفصل للإخراج. ويمكن لاحقاً ربط السكربت مع قوالب Prompt Engineering أو مع أنظمة فرض الإخراج المهيكل بصيغة JSON.

import tiktoken

PRICING = {
    "gpt-4o-mini": {
        "input_per_1m": 0.15,
        "output_per_1m": 0.60,
        "context_window": 128000
    }
}

def count_text_tokens(text: str, model: str = "gpt-4o-mini") -> int:
    encoding = tiktoken.encoding_for_model(model)
    return len(encoding.encode(text))

def count_messages_tokens(messages: list, model: str = "gpt-4o-mini") -> int:
    encoding = tiktoken.encoding_for_model(model)
    total = 0

    for msg in messages:
        content = msg.get("content", "")
        role = msg.get("role", "")
        total += len(encoding.encode(role))
        total += len(encoding.encode(content))
        total += 4  # overhead approximation per message

    total += 2  # reply priming approximation
    return total

def estimate_cost(input_tokens: int, output_tokens: int, model: str = "gpt-4o-mini") -> dict:
    pricing = PRICING[model]
    input_cost = (input_tokens / 1_000_000) * pricing["input_per_1m"]
    output_cost = (output_tokens / 1_000_000) * pricing["output_per_1m"]

    return {
        "input_tokens": input_tokens,
        "output_tokens": output_tokens,
        "input_cost_usd": round(input_cost, 6),
        "output_cost_usd": round(output_cost, 6),
        "total_cost_usd": round(input_cost + output_cost, 6)
    }

messages = [
    {"role": "system", "content": "You are a precise technical assistant."},
    {"role": "user", "content": "Explain token cost estimation before sending API requests."}
]

input_tokens = count_messages_tokens(messages)
estimated_output_tokens = 700
report = estimate_cost(input_tokens, estimated_output_tokens)

print(report)

لاحظ أن قيمة overhead تختلف باختلاف واجهة الرسائل وبنية النموذج، لذا هذا السكربت مناسب كطبقة تقدير تشغيلية، لكن يجب معايرته دورياً عبر مقارنة النتائج الفعلية بما تعيده الواجهة البرمجية بعد التنفيذ.

كيف نمنع تجاوز حد السياق تلقائياً؟

إدارة التكلفة لا تنفصل عن إدارة نافذة السياق Context Window. إذا كان مجموع الإدخال مع الإخراج المتوقع قريباً من الحد الأعلى، يصبح التطبيق هشاً: قد يفشل الطلب أو يتم بتر أجزاء مهمة من السياق. لهذا نحتاج إلى سياسة استباقية تتضمن:

  • حجز نسبة ثابتة لمخرجات النموذج مثل 15% أو 20% من النافذة.
  • تقليص سجل المحادثة الأقدم أولاً.
  • اختصار المستندات المسترجعة في أنظمة RAG.
  • تبديل النموذج تلقائياً إذا كانت المهمة لا تستدعي نموذجاً أعلى كلفة.
def fits_context(input_tokens: int, reserved_output: int, model: str = "gpt-4o-mini") -> bool:
    max_context = PRICING[model]["context_window"]
    return (input_tokens + reserved_output) <= max_context

def trim_messages(messages: list, max_input_tokens: int, model: str = "gpt-4o-mini") -> list:
    trimmed = messages[:]
    while count_messages_tokens(trimmed, model=model) > max_input_tokens and len(trimmed) > 1:
        trimmed.pop(1)  # remove oldest user/assistant turn after system
    return trimmed

هذه المقاربة مفيدة جداً عندما تستخدم Streaming Responses، لأن البث لا يعني أن التكلفة أقل؛ بل يعني فقط أن الإخراج يُعرض تدريجياً بينما الاستهلاك يُحسب بالكامل.

دمج السكربت داخل تطبيقات LangChain أو الأنظمة المخصصة

في المشاريع الجادة لا يُستدعى سكربت العدّ يدوياً، بل يوضع كـ middleware قبل طبقة الإرسال. هذا يسمح لك ببناء سجلات تشغيل telemetry دقيقة، ومقارنة الاستهلاك حسب المستخدم، المهمة، أو نوع النموذج.

def preflight_check(messages: list, model: str = "gpt-4o-mini", reserved_output: int = 800) -> dict:
    input_tokens = count_messages_tokens(messages, model=model)
    is_valid = fits_context(input_tokens, reserved_output, model=model)
    cost_report = estimate_cost(input_tokens, reserved_output, model=model)

    return {
        "model": model,
        "fits_context": is_valid,
        "report": cost_report
    }

بعد ذلك يمكنك ربط النتيجة بمنطق الأعمال. فإذا كانت التكلفة مرتفعة لمستخدم في خطة مجانية، يُطبَّق اختصار على المدخلات. وإذا كانت المهمة تحليلية طويلة، يمكن اختيار نموذج أرخص مع تعليمات أوضح. هنا يظهر الأثر المباشر لفهم المعاملات مثل Temperature أيضاً، لأن الإخراج المفتوح جداً قد يزيد طول الإجابة ويؤثر على التكلفة.

أفضل ممارسات تشغيلية لتقليل الاستهلاك دون الإضرار بالجودة

  • اجعل تعليمات system موجزة ومستقرة بدلاً من إعادة فقرات طويلة كل مرة.
  • لا ترسل كامل المستندات إذا كان يكفي إرسال المقاطع ذات الصلة.
  • استخدم تلخيصاً مرحلياً للمحادثات الطويلة بدلاً من تخزين كل التبادلات حرفياً.
  • راقب الفرق بين التقدير المسبق والاستهلاك الفعلي وحدث معاملاتك بشكل دوري.
  • ابنِ لوحة قياس داخلية تُظهر عدد tokens لكل ميزة في المنتج.

خلاصة هندسية

حساب تكلفة واستهلاك API قبل الإرسال هو طبقة حوكمة تقنية، وليس مجرد أداة محاسبية. عندما تبني سكربتاً يقدّر الرموز، يتحقق من حد السياق، ويحسب السعر المتوقع قبل التنفيذ، فأنت تحوّل التطبيق من تجربة عشوائية إلى نظام يمكن التنبؤ به وتشغيله على نطاق واسع. هذه الفكرة بالذات ضرورية في كل مشروع يعتمد على LLM، سواء كان دردشة، agent، أو خط معالجة مستندات واسع.

القاعدة الذهبية: لا ترسل أي طلب إلى النموذج قبل المرور على preflight token audit يحدد الحجم، يقدّر الكلفة، ويتحقق من أن المهمة ضمن حدود السياق والميزانية.

28 comments

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *