مرحباً بكم أيها المطورون! في هذا الدرس الاحترافي، سنتعلم كيفية بناء سكربت بايثون قوي لتقدير عدد الرموز (tokens) في رسائل الـ API قبل إرسالها. هذا سيساعدنا على إدارة التكلفة بفعالية وتجنب المفاجآت غير المرغوبة في فواتير استهلاك نماذج اللغة الكبيرة (LLMs).
الخطوة الأولى: فهم مكتبة tiktoken وتحميل الترميز
قبل أن نرسل أي طلب إلى نماذج اللغة، من الضروري معرفة "حجمه" بالرموز. الرموز هي الوحدات الأساسية التي تتعامل بها هذه النماذج، وكل رمز له تكلفة. مكتبة tiktoken من OpenAI هي الأداة المثالية لهذه المهمة. إنها تسمح لنا بحساب الرموز بنفس الطريقة التي تفعل بها نماذج OpenAI، مما يضمن دقة التقدير.
ملاحظة تقنية: تختلف طريقة حساب الرموز باختلاف النموذج. لذا، من المهم تحديد الترميز الصحيح (encoding) الذي يتوافق مع النموذج الذي تخطط لاستخدامه (مثل
gpt-4أوgpt-3.5-turbo).
لنبدأ بتثبيت المكتبة وتحميل الترميز الأساسي:
# تثبيت مكتبة tiktoken إذا لم تكن مثبتة
# pip install tiktoken
import tiktoken
# تحميل الترميز الخاص بالنموذج الذي سنستخدمه
# cl100k_base هو الترميز المستخدم لمعظم نماذج OpenAI الحديثة مثل gpt-4 و gpt-3.5-turbo
encoding = tiktoken.get_encoding("cl100k_base")
print("تم تحميل الترميز بنجاح.")
الخطوة الثانية: حساب الرموز للنصوص البسيطة ورسائل المحادثة
الآن بعد أن أصبح لدينا الترميز، يمكننا استخدامه لعد الرموز. الأمر بسيط لسلسلة نصية واحدة، لكنه يصبح أكثر تعقيداً قليلاً عند التعامل مع تنسيق رسائل المحادثة (chat messages) التي تحتوي على أدوار مختلفة (system, user, assistant).
سنقوم بإنشاء دالة قوية يمكنها التعامل مع قائمة من الرسائل، مع الأخذ في الاعتبار النموذج المستخدم، لأن بعض النماذج تضيف رموزاً إضافية في بداية ونهاية كل رسالة أو المحادثة بأكملها.
import tiktoken
def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
"""
إرجاع عدد الرموز المستخدمة في قائمة الرسائل.
تأخذ الدالة قائمة من الرسائل (على شكل قاموس) واسم النموذج.
"""
try:
# تحميل الترميز الخاص بالنموذج المحدد
encoding = tiktoken.encoding_for_model(model)
except KeyError:
# إذا لم يتم العثور على الترميز، نستخدم الترميز الافتراضي ونحذر المستخدم
print("تحذير: النموذج غير موجود. استخدام الترميز cl100k_base الافتراضي.")
encoding = tiktoken.get_encoding("cl100k_base")
# تحديد عدد الرموز الإضافية التي يضيفها النموذج لكل رسالة أو للمحادثة بأكملها
# هذه القيم مستمدة من توثيق OpenAI وقد تتغير
if model in {
"gpt-3.5-turbo-0613",
"gpt-3.5-turbo-16k-0613",
"gpt-4-0613",
"gpt-4-32k-0613",
}:
tokens_per_message = 3 # رموز إضافية لكل رسالة (مثل دور، محتوى، اسم)
tokens_per_name = 1 # رمز إضافي إذا كان هناك اسم للمرسل
elif model == "gpt-3.5-turbo":
print("تحذير: gpt-3.5-turbo قديم. استخدم gpt-3.5-turbo-0613 بدلاً من ذلك.")
tokens_per_message = 4 # قد يختلف هذا للنماذج القديمة
tokens_per_name = -1 # بعض النماذج القديمة قد تطرح رمزاً
elif model == "gpt-4":
print("تحذير: gpt-4 قديم. استخدم gpt-4-0613 بدلاً من ذلك.")
tokens_per_message = 3
tokens_per_name = 1
else:
# للنماذج غير المدعومة بشكل صريح، نستخدم القيم الافتراضية ونحذر
tokens_per_message = 3
tokens_per_name = 1
print(f"تحذير: num_tokens_from_messages لم يتم اختبارها للنموذج {model}. الافتراضيات هي: tokens_per_message={tokens_per_message}, tokens_per_name={tokens_per_name}.")
num_tokens = 0
for message in messages:
num_tokens += tokens_per_message # إضافة الرموز الأساسية لكل رسالة
for key, value in message.items():
num_tokens += len(encoding.encode(value)) # حساب رموز محتوى الرسالة (الدور، المحتوى)
if key == "name":
num_tokens += tokens_per_name # إضافة رمز إذا كان هناك اسم
num_tokens += 3 # كل محادثة تبدأ بـ "assistant" وتنهي بـ "user" و "system"
return num_tokens
# مثال على رسائل المحادثة
example_messages = [
{"role": "system", "content": "أنت مساعد ذكي يساعد المستخدمين."},
{"role": "user", "content": "ما هي عاصمة فرنسا؟"},
{"role": "assistant", "content": "عاصمة فرنسا هي باريس."},
{"role": "user", "content": "وكم عدد سكانها؟"}
]
# حساب الرموز للرسائل باستخدام نموذج gpt-3.5-turbo-0613
tokens_count = num_tokens_from_messages(example_messages, model="gpt-3.5-turbo-0613")
print(f"عدد الرموز للرسائل: {tokens_count}")
الخطوة الثالثة: تقدير التكلفة بناءً على عدد الرموز
بمجرد أن نحصل على عدد الرموز، يمكننا تقدير التكلفة الأولية للطلب. تعتمد التكلفة على سعر الرموز لكل ألف (tokens per 1K) والذي يختلف باختلاف النموذج. سنفترض أسعاراً تقديرية لأغراض هذا الدرس.
ملاحظة تقنية: أسعار الـ API تتغير باستمرار. تأكد دائماً من مراجعة صفحة تسعير OpenAI الرسمية للحصول على أحدث الأسعار.
لنحسب التكلفة المتوقعة لرسائلنا:
# تحديد أسعار تقديرية للنماذج (لكل 1000 رمز)
# هذه الأسعار لأغراض توضيحية وقد لا تكون هي الأسعار الحالية
# أسعار الإدخال (prompt tokens)
PRICING = {
"gpt-3.5-turbo-0613": {"input": 0.0015, "output": 0.002},
"gpt-4-0613": {"input": 0.03, "output": 0.06},
}
def estimate_cost(tokens, model_name, type="input"):
"""
تقدير التكلفة بناءً على عدد الرموز واسم النموذج ونوع الاستخدام (إدخال/إخراج).
"""
if model_name not in PRICING:
print(f"تحذير: لا توجد معلومات تسعير للنموذج {model_name}. لا يمكن تقدير التكلفة.")
return 0.0
price_per_1k_tokens = PRICING[model_name][type]
cost = (tokens / 1000) * price_per_1k_tokens
return cost
# استخدام الدالة لحساب التكلفة للرسائل السابقة
model_to_use = "gpt-3.5-turbo-0613"
estimated_input_cost = estimate_cost(tokens_count, model_to_use, type="input")
print(f"النموذج المستخدم: {model_to_use}")
print(f"عدد الرموز في رسائل الإدخال: {tokens_count}")
print(f"التكلفة التقديرية لرسائل الإدخال: ${estimated_input_cost:.6f}")
# إذا كنت تتوقع رداً، يمكنك تقدير تكلفة الرد أيضاً
# لنفترض أن الرد سيكون 50 رمزاً
expected_output_tokens = 50
estimated_output_cost = estimate_cost(expected_output_tokens, model_to_use, type="output")
print(f"عدد الرموز المتوقعة في الرد: {expected_output_tokens}")
print(f"التكلفة التقديرية للرد: ${estimated_output_cost:.6f}")
print(f"التكلفة الإجمالية التقديرية (إدخال + إخراج): ${estimated_input_cost + estimated_output_cost:.6f}")
الكود النهائي الكامل
إليك السكربت كاملاً، جاهزاً للنسخ واللصق والتجربة:
import tiktoken
def num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613"):
"""
إرجاع عدد الرموز المستخدمة في قائمة الرسائل.
تأخذ الدالة قائمة من الرسائل (على شكل قاموس) واسم النموذج.
"""
try:
# تحميل الترميز الخاص بالنموذج المحدد
encoding = tiktoken.encoding_for_model(model)
except KeyError:
# إذا لم يتم العثور على الترميز، نستخدم الترميز الافتراضي ونحذر المستخدم
print("تحذير: النموذج غير موجود. استخدام الترميز cl100k_base الافتراضي.")
encoding = tiktoken.get_encoding("cl100k_base")
# تحديد عدد الرموز الإضافية التي يضيفها النموذج لكل رسالة أو للمحادثة بأكملها
# هذه القيم مستمدة من توثيق OpenAI وقد تتغير
if model in {
"gpt-3.5-turbo-0613",
"gpt-3.5-turbo-16k-0613",
"gpt-4-0613",
"gpt-4-32k-0613",
}:
tokens_per_message = 3 # رموز إضافية لكل رسالة (مثل دور، محتوى، اسم)
tokens_per_name = 1 # رمز إضافي إذا كان هناك اسم للمرسل
elif model == "gpt-3.5-turbo":
print("تحذير: gpt-3.5-turbo قديم. استخدم gpt-3.5-turbo-0613 بدلاً من ذلك.")
tokens_per_message = 4 # قد يختلف هذا للنماذج القديمة
tokens_per_name = -1 # بعض النماذج القديمة قد تطرح رمزاً
elif model == "gpt-4":
print("تحذير: gpt-4 قديم. استخدم gpt-4-0613 بدلاً من ذلك.")
tokens_per_message = 3
tokens_per_name = 1
else:
# للنماذج غير المدعومة بشكل صريح، نستخدم القيم الافتراضية ونحذر
tokens_per_message = 3
tokens_per_name = 1
print(f"تحذير: num_tokens_from_messages لم يتم اختبارها للنموذج {model}. الافتراضيات هي: tokens_per_message={tokens_per_message}, tokens_per_name={tokens_per_name}.")
num_tokens = 0
for message in messages:
num_tokens += tokens_per_message # إضافة الرموز الأساسية لكل رسالة
for key, value in message.items():
num_tokens += len(encoding.encode(value)) # حساب رموز محتوى الرسالة (الدور، المحتوى)
if key == "name":
num_tokens += tokens_per_name # إضافة رمز إذا كان هناك اسم
num_tokens += 3 # كل محادثة تبدأ بـ "assistant" وتنهي بـ "user" و "system"
return num_tokens
# تحديد أسعار تقديرية للنماذج (لكل 1000 رمز)
# هذه الأسعار لأغراض توضيحية وقد لا تكون هي الأسعار الحالية
PRICING = {
"gpt-3.5-turbo-0613": {"input": 0.0015, "output": 0.002},
"gpt-4-0613": {"input": 0.03, "output": 0.06},
}
def estimate_cost(tokens, model_name, type="input"):
"""
تقدير التكلفة بناءً على عدد الرموز واسم النموذج ونوع الاستخدام (إدخال/إخراج).
"""
if model_name not in PRICING:
print(f"تحذير: لا توجد معلومات تسعير للنموذج {model_name}. لا يمكن تقدير التكلفة.")
return 0.0
price_per_1k_tokens = PRICING[model_name][type]
cost = (tokens / 1000) * price_per_1k_tokens
return cost
# أمثلة على الاستخدام:
example_messages = [
{"role": "system", "content": "أنت مساعد ذكي يساعد المستخدمين."},
{"role": "user", "content": "ما هي عاصمة فرنسا؟"},
{"role": "assistant", "content": "عاصمة فرنسا هي باريس."},
{"role": "user", "content": "وكم عدد سكانها؟"}
]
# تحديد النموذج المراد استخدامه
model_to_use = "gpt-3.5-turbo-0613"
# 1. حساب الرموز لرسائل الإدخال
tokens_count = num_tokens_from_messages(example_messages, model=model_to_use)
print(f"عدد الرموز في رسائل الإدخال للنموذج {model_to_use}: {tokens_count}")
# 2. تقدير تكلفة الإدخال
estimated_input_cost = estimate_cost(tokens_count, model_to_use, type="input")
print(f"التكلفة التقديرية لرسائل الإدخال: ${estimated_input_cost:.6f}")
# 3. تقدير تكلفة الإخراج (بافتراض عدد معين من الرموز للرد)
expected_output_tokens = 50 # افتراض أن الرد سيكون بحجم 50 رمزاً
estimated_output_cost = estimate_cost(expected_output_tokens, model_to_use, type="output")
print(f"عدد الرموز المتوقعة في الرد: {expected_output_tokens}")
print(f"التكلفة التقديرية للرد: ${estimated_output_cost:.6f}")
# 4. التكلفة الإجمالية التقديرية
total_estimated_cost = estimated_input_cost + estimated_output_cost
print(f"التكلفة الإجمالية التقديرية (إدخال + إخراج): ${total_estimated_cost:.6f}")
# مثال آخر لنموذج مختلف
# model_to_use_gpt4 = "gpt-4-0613"
# tokens_count_gpt4 = num_tokens_from_messages(example_messages, model=model_to_use_gpt4)
# print(f"\nعدد الرموز في رسائل الإدخال للنموذج {model_to_use_gpt4}: {tokens_count_gpt4}")
# estimated_input_cost_gpt4 = estimate_cost(tokens_count_gpt4, model=model_to_use_gpt4, type="input")
# print(f"التكلفة التقديرية لرسائل الإدخال: ${estimated_input_cost_gpt4:.6f}")
النتيجة المتوقعة
عند تشغيل السكربت أعلاه، ستحصل على مخرجات مشابهة لما يلي في نافذة الطرفية (Terminal)، توضح عدد الرموز لكل من رسائل الإدخال، والتكلفة التقديرية للإدخال، بالإضافة إلى تقدير لتكلفة الإخراج والتكلفة الإجمالية:
عدد الرموز في رسائل الإدخال للنموذج gpt-3.5-turbo-0613: 56
التكلفة التقديرية لرسائل الإدخال: $0.000084
عدد الرموز المتوقعة في الرد: 50
التكلفة التقديرية للرد: $0.000100
التكلفة الإجمالية التقديرية (إدخال + إخراج): $0.000184
هذه الأرقام تساعدك على اتخاذ قرارات مستنيرة بشأن حجم طلباتك واختيار النموذج المناسب لميزانيتك.