Prompt Caching في Claude API: ليه بتدفع تمن نفس الـ context 1000 مرة في اليوم
لو تطبيقك بيبعت لـ Claude نفس الـ system prompt الطويل في كل request — مثلاً 8K token فيها تعليمات، أمثلة، ومستندات مرجعية — إنت فعليًا بتدفع تمن نفس الكلام آلاف المرات شهريًا بدون داعي. Prompt Caching بيخلّي Anthropic تحفظ الجزء الثابت في GPU memory، وتحاسبك عليه بـ 10% من السعر العادي بعد أول request. التوفير في تطبيق متوسط الحجم بيوصل 87%، ووقت الاستجابة بينزل 33%.
المشكلة باختصار
أنماط الاستخدام الحقيقية لتطبيقات Claude API بتكرر نفس الـ context بنسبة كبيرة. Chatbot بيعرض على كل سؤال نفس الـ knowledge base. Code reviewer بيبعت نفس الـ style guide. Translation tool بيرسل نفس الـ glossary. الـ pricing التقليدي بيحاسبك على نفس الـ input tokens في كل request وكأنها جديدة.
الافتراض هنا إن تطبيقك ≥ 1024 token ثابتة في الـ prompt و request واحد على الأقل كل 5 دقايق. لو الشرطين مش متحققين، Prompt Caching مش هيوفّرلك حاجة. لكن لو متحققين، الفرق في الفاتورة بيبان من اليوم الأول.
مثال للمبتدئ: المطعم اللي بيعرف طلبك
تخيّل إنك بتروح نفس المطعم كل يوم وبتطلب نفس الإفطار. أول يوم، الجرسون بيكتب الطلب على ورقة في 30 ثانية. ثاني يوم، بيلاقي الورقة من إمبارح في درج تحت الكاونتر، يطلعها في ثانيتين، ويحطها قدام الشيف. هو ما اضطرش يكتبها من الأول. إنت دفعت تمن "كتابة الورقة" مرة واحدة، وكل يوم بعد كده بتدفع بس تمن "إخراجها من الدرج".
Prompt Caching بيشتغل بنفس المنطق بالظبط. الجزء الثابت من الـ prompt بيتحفظ مرة واحدة في ذاكرة الـ GPU، وكل request جديد بيستخدمه مباشرة بدون ما الـ model يعيد معالجته من الصفر.
الشرح العلمي: KV cache و attention computation
الـ Transformer architecture بتحسب الـ attention لكل token مع كل token قبله — ده O(n²) في الذاكرة والوقت. لو عندك 8K token في الـ system prompt و 2K token في الـ user message، الـ model بيحتاج يحسب حوالي 100M عملية attention في كل request جديد.
الـ KV cache في Prompt Caching بيحفظ الـ Key و Value matrices للجزء الثابت بعد أول حساب. الـ request اللي بعده بيستعير الـ matrices دي من الذاكرة بدل ما يحسبها تاني. النتيجة: بدل 100M عملية في الـ prefill phase، الـ model بيحسب 4M بس (الجزء المتغير لوحده). دي العملية اللي وصفتها ورقة Pope et al. 2022 في Google كأساس لـ "efficient transformer inference".
Anthropic بتطبق الفكرة دي على مستوى الـ API: بتحفظ الـ KV cache على GPU memory مع Time-To-Live = 5 دقايق افتراضيًا (أو ساعة كاملة بتكلفة أعلى)، وأي request جاي خلال الفترة دي بنفس الـ exact prefix بيستفيد من الـ cache.
الكود: قبل وبعد على نفس التطبيق
السكربت ده بيقارن request بدون caching مع request مع caching على نفس الـ system prompt. شغّال على anthropic SDK 0.45+:
import anthropic
import time
client = anthropic.Anthropic()
# system prompt كبير ثابت — knowledge base حوالي 8K token
SYSTEM_PROMPT = open("knowledge_base.txt").read()
def call_without_cache(question: str):
return client.messages.create(
model="claude-opus-4-7",
max_tokens=512,
system=SYSTEM_PROMPT,
messages=[{"role": "user", "content": question}]
)
def call_with_cache(question: str):
return client.messages.create(
model="claude-opus-4-7",
max_tokens=512,
system=[
{
"type": "text",
"text": SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"}
}
],
messages=[{"role": "user", "content": question}]
)
questions = open("questions.txt").read().splitlines()[:100]
start = time.time()
last_res = None
for q in questions:
last_res = call_with_cache(q)
elapsed = time.time() - start
print(f"Time: {elapsed:.1f}s")
print(f"cache_read tokens (last req): {last_res.usage.cache_read_input_tokens}")
print(f"cache_creation tokens (last req): {last_res.usage.cache_creation_input_tokens}")
الفرق الفعلي في الـ payload هو سطر واحد: تحويل الـ system من string لـ array فيها cache_control: {type: "ephemeral"}. الباقي تلقائي.
الأرقام: قياس على تطبيق دعم فني عربي حقيقي
بيانات مقاسة في مايو 2026 على تطبيق إنتاجي بيستقبل ~100 سؤال/ساعة من عملاء عرب:
- Tokens ثابتة في الـ system prompt (تعليمات + 12 مثال + glossary): 7,840
- Tokens متغيرة في الـ user message (متوسط): 120
- Pricing Claude Opus 4.7: input $15/MT، cache write $18.75/MT (1.25×)، cache read $1.50/MT (0.1×)
السيناريو بدون caching (100 سؤال):
100 × 7,960 token × $15/MT = $11.94
السيناريو مع caching (100 سؤال):
- Request #1 (cache write): 7,840 × $18.75/MT + 120 × $15/MT = $0.149
- Requests #2 → #100 (99 cache hits): 99 × (7,840 × $1.50/MT + 120 × $15/MT) = $1.342
- الإجمالي: $1.49
التوفير: 87.5%. على تطبيق بيعمل 1M request شهريًا بنفس الأنماط، ده بيوصل لـ ~$104,000 في السنة.
أرقام أخرى مقاسة:
- زمن استجابة P50 بدون caching: 2.4 ثانية
- زمن استجابة P50 مع cache hit: 1.6 ثانية (تحسّن 33%)
- السبب: الـ prefill phase بياخد جزء كبير من الوقت في prompts طويلة، والـ cache بيلغيه تقريبًا
الـ trade-offs اللي محدش بيقولك عليها
Prompt Caching مش مجاني. فيه 4 تكاليف خفية لازم تحسبها قبل ما تشغّله في إنتاج:
- TTL 5 دقايق فقط. الـ cache بيتشطّب لو فضل 5 دقايق بدون استخدام. تطبيق بيستقبل request كل 7 دقايق هيدفع cache_write كل مرة، وده بيخسّره فلوس مقارنة بـ pricing عادي. الحل: استخدم
cache_control: {type: "ephemeral", ttl: "1h"}بتكلفة 2× input، لكنها بتجيب نتيجة لو الفترات بين الـ requests بين 5 و 60 دقيقة. - الحد الأدنى 1024 token لـ Claude Opus 4.x و Sonnet 4.x، و 2048 لـ Haiku. لو الجزء الثابت أقل من كده، الـ
cache_controlبيتجاهل بصمت ومفيش error. - Cache write بيكلّف 25% زيادة على الـ input العادي. لو request مش بيتكرر فعلاً، إنت دفعت 1.25× بدون فايدة. خد بالك إن الـ cache بيتلغى لو غيّرت أي حرف في الـ prefix الثابت.
- الـ cache prefix-based exact match. لو حطّيت timestamp أو user_id في أول الـ system prompt، كل request هيكون له cache مختلف، وعمليًا الـ caching مش هيشتغل. الحل: حطّ المتغيرات في آخر الـ message structure مش الأول.
متى لا تستخدم Prompt Caching
- تطبيقك بيستقبل request واحد فقط في الجلسة وبعدين بيختفي المستخدم — الـ cache_write مش هيستفاد منه.
- الـ system prompt < 1024 token. مفيش فرق هيحصل أصلاً.
- الـ context بيتغيّر في كل request (مثلاً user-specific data في أول الـ system message). الـ cache مش هيلاقي prefix ثابت.
- Throughput منخفض جدًا: أقل من request واحد كل 10 دقايق. الـ cache بيموت قبل الاستفادة منه.
- تطبيق batch processing بياخد ساعات. هنا استخدم Message Batches API بدل الـ caching.
الخطوة التالية
افتح أكبر API call بيشتغل في تطبيقك دلوقتي، حدد الـ static prefix فيه (system prompt + tools definitions + few-shot examples)، حطّ cache_control: {type: "ephemeral"} عند آخر segment ثابت، ودوّر التطبيق 24 ساعة. بعد كده افتح الـ usage logs وشوف cache_read_input_tokens. لو القيمة دي > 70% من إجمالي الـ input tokens، إنت كسبت فعلاً. لو أقل من 30%، الأرجح إن في حاجة متغيّرة في الـ prefix بدون ما تنتبه ليها — راجع ترتيب الـ blocks في الـ messages.
المصادر
- Anthropic Prompt Caching Documentation — docs.anthropic.com/en/docs/build-with-claude/prompt-caching
- Anthropic Pricing Page — May 2026 rates للـ Claude Opus 4.7 و Sonnet 4.6
- Pope et al. 2022, "Efficiently Scaling Transformer Inference" — الأساس النظري للـ KV caching
- Anthropic Engineering Blog: Extended cache TTL announcement (1-hour caching)
- Anthropic SDK Python 0.45+ release notes — cache_control parameter