فعال کردن کشِ پرامپت (prompt caching) هیچ سودی برای من نداشت؛ در واقع، صورتحساب OpenAI-API من حدود یکچهارم افزایش یافت. مقصر، تنها یک خط بود که با هر درخواست تغییر میکرد: یک برچسب زمانی (timestamp) که در پرامپت سیستم گنجانده شده بود.
ارائهدهندگان LLM به توسعهدهندگان اجازه میدهند قطعات پرامپت را کش کنند تا هزینههای پردازش توکن را کاهش دهند. خواندن از کش (یک "hit") تنها یکدهم نرخ معمول هزینه دارد، در حالی که نوشتن در کش (یک "miss") تقریباً ۱.۲۵ برابر قیمت عادی هزینه دارد. اگر عملیات نوشتن انجام شود اما قطعهی کششده هرگز خوانده نشود، آن ۲۵ درصد هزینه اضافی هدر میرود. این دقیقاً همان اتفاقی بود که وقتی برچسب زمانی مانع از مطابقت پرامپت با هیچ ورودی کش موجود میشد، رخ داد.
چرا کش کردن میتواند نتیجه معکوس داشته باشد
کش کردن پرامپت با مطابقت دادن دقیقِ توالی بایتهای بخش کششده کار میکند. ارائهدهنده ورودی را هش (hash) میکند؛ اگر هش با یک ورودی ذخیرهشده مطابقت داشته باشد، سیستم از محاسبات قبلی استفاده کرده و نرخ ارزانِ خواندن را اعمال میکند. هرگونه تغییر — حتی یک کاراکتر واحد — باعث شکست در مطابقت شده و سیستم را مجبور به انجام یک محاسبه جدید میکند که با نرخ بالاترِ نوشتن محاسبه میشود.
در مورد من، پرامپت سیستم با این عبارت شروع میشد:
Current session started: 2026-07-14T09:41:07Z
از آنجایی که برچسب زمانی برای هر فراخوانی API بهروزرسانی میشد، چند بایت اول درخواست هرگز یکسان نبودند. ارائهدهنده با هر فراخوانی به عنوان یک ورودی کش جدید برخورد میکرد، هزینه اضافی نوشتن را دریافت میکرد و هرگز خواندنی ثبت نمیشد. نتیجه، افزایش مداوم در cache_creation_input_tokens در حالی بود که cache_read_input_tokens روی صفر باقی میماند؛ نشانهای واضح از اینکه کش هرگز مورد استفاده (hit) قرار نمیگرفت.
چگونه متوجه خرابی کش شویم
لاگهای استفاده که توسط API ارائه میشوند، دو شمارنده کلیدی دارند:
- cache_creation_input_tokens – توکنهایی که باعث ایجاد یک عملیات نوشتن (write) شدند.
- cache_read_input_tokens – توکنهایی که از عملیات خواندن (read) بهرهمند شدند.
وقتی اولی بالا میرود و دومی ثابت میماند، یعنی از کش دوباره استفاده نمیشود. یک بررسی سریع برای اطمینان این است که دقیقاً همان درخواست را دو بار تکرار کنید؛ اگر کش به درستی کار کند، فراخوانی دوم باید جهشی در توکنهای خواندن نشان دهد.
رفع مشکل
راه حل ساده است: مطمئن شوید که ناحیه کششده در طول فراخوانیها ایستا (static) است. این دو قانون را دنبال کنید:
۱. محتوای تغییرناپذیر را در ابتدا قرار دهید. پرامپتهای سیستم، تعاریف ابزار (tool definitions) یا هر دستورالعملی که هرگز تغییر نمیکند، باید بایتهای ابتدایی درخواست را اشغال کنند. ۲. محتوای تغییرپذیر را در انتها اضافه کنید. برچسبهای زمانی، متنهای تولید شده توسط کاربر، شناسههای درخواست (request IDs) یا هر دادهای که در هر فراخوانی متفاوت است، باید بعد از بخش کششده بیایند.
اگر حتی یک کاراکتر جابهجا شود، هش تغییر کرده و عدم مطابقت با کش (cache miss) ادامه مییابد. بازآرایی پرامپت به گونهای که برچسب زمانی در انتها قرار گیرد، نرخ برخورد با کش (cache hit rate) را بازیابی کرده و صورتحساب را به سطح کمهزینه مورد انتظار بازمیگرداند.
چه زمانی کش کردن واقعاً کمک میکند
کش کردن پرامپت در سناریوهایی که از یک مجموعه دستورالعمل یکسان بارها استفاده میشود، میدرخشد:
- حلقههای عامل (Agent loops) که در آن یک هوش مصنوعی مکرراً مجموعهای ثابت از ابزارها را فراخوانی میکند.
- جلسات چت (Chat sessions) که به یک سند طولانی و ایستا ارجاع میدهند، در حالی که فقط آخرین پرسش کاربر تغییر میکند.
- استخراج انبوه داده (Bulk data extraction) که در آن یک پرامپت تجزیه (parsing) یکسان برای رکوردهای زیادی اعمال میشود.
برای فراخوانیهای تکمرحلهای (single-shot) که هر بار شامل یک بافت (context) تازه هستند — مانند یک سوال گذرا با یک مقدمه منحصربهفرد — کش کردن هیچ سودی ندارد و حتی اگر درخواست بهطور ناخواسته باعث ایجاد یک عملیات نوشتن شود، ممکن است هزینه را افزایش دهد.
تلههای پنهان
حتی اگر خودِ پرامپت ایستا باشد، درخواست میتواند در مراحل بعدی تغییر کند:
- پراکسیها یا تجمیعکنندهها (aggregators) که ترتیب را تغییر میدهند یا فضای خالی (whitespace) تزریق میکنند، میتوانند مطابقت بایتبهبایت را از بین ببرند.
- سرویسهای درگاه (Gateway services) که هدرهای احراز هویت را به ابتدای درخواست اضافه میکنند یا قالببندی JSON را تغییر میدهند، ممکن است بهطور ناخواسته قطعه کششده را تغییر دهند.
تست کردن از طریق درگاه با ارسال دو بار یک درخواست یکسان و بررسی شمارندههای خواندن، به تأیید این موضوع کمک میکند که مسیر کشسازی دستنخورده باقی مانده است.
تصویر گستردهتر هزینهها
جریمه ۲۵ درصدی برای نوشتن، جریمهای برای استفاده از کش نیست؛ بلکه نشاندهنده محاسبات اضافی مورد نیاز برای ذخیره قطعه جهت استفاده مجدد در آینده است. وقتی یک برخورد با کش (cache hit) رخ میدهد، هزینه بهطور چشمگیری کاهش مییابد — اغلب به کسری از نرخ معمول. نکته کلیدی این است که اجازه دهید سیستم واقعاً به کش دسترسی پیدا کند (hit کند). در غیر این صورت، شما هزینه اضافی را بدون هیچ صرفهجویی پرداخت میکنید.
استدلال مخالف: کش کردن از بین نرفته است
برخی از توسعهدهندگان استدلال میکنند که پیچیدگی مدیریت بخشهای ایستا در مقابل بخشهای پویای پرامپت، از میزان صرفهجویی حاصل از آن بیشتر است. این دیدگاه این واقعیت را نادیده میگیرد که بسیاری از خط لولههای عملیاتی (production pipelines) در حال حاضر پیکربندی (ایستا) را از دادههای کاربر (پویا) جدا میکنند. با ساختاردهی مناسب به پرامپتها، میتوان از همان مکانیزم کشینگ که توسعهدهندگان اولیه API را نجات داد، بدون تلاش اضافی بهره برد. این موازنه، تنها نیازمند کمی نظم در طراحی پرامپت است، نه یک نقص بنیادی در فناوری.
آنچه در ادامه باید بررسی کنید
- دو شمارنده کش (cache counters) را بهصورت هفتگی در داشبورد میزان استفاده خود نظارت کنید.
- ساختار پرامپت را بازرسی کنید تا مطمئن شوید هر عنصر متغیر پس از بلوک کششده قرار میگیرد.
- تستهای A/B را با و بدون کشینگ روی یک حجم کاری معرف اجرا کنید تا میزان صرفهجویی واقعی را تعیین کنید.
- درگاه (gateway) را با مقایسه محمولههای خام درخواست (raw request payloads) قبل و بعد از هر پروکسی، اعتبارسنجی کنید.
نکته کلیدی
کشینگ پرامپت میتواند هزینههای LLM API را به شدت کاهش دهد، اما تنها در صورتی که بخش کششده در فراخوانیهای مختلف کاملاً یکسان باشد. وجود یک برچسب زمانی ناخواسته یا هر توکن پویای دیگر در ابتدای پرامپت، باعث میشود که هر بار یک عملیات نوشتن پرهزینه انجام شود و صورتحساب شما افزایش یابد. با قرار دادن دستورالعملهای ایستا در ابتدا و انتقال دادههای متغیر به انتهای پرامپت، اجازه میدهید کش وظیفه خود را انجام دهد و هزینههای خود را تحت کنترل نگه میدارید.
