کش کردن پرامپت هزینه استنتاج هوش مصنوعی را تا ۹۰ درصد کاهش میدهد، اما اکثر تیمها درست از آن استفاده نمیکنند

اکثر تیمهایی که برای دسترسی API مدلهای زبانی بزرگ (LLM) هزینه میکنند، بین ۵۰ تا ۹۰ درصد صرفهجویی بالقوه را از دست میدهند، در حالی که مکانیزم دستیابی به آن بیش از یک سال است در محیط تولید وجود دارد. کش کردن پرامپت (Prompt Caching) به ارائهدهنده API اجازه میدهد محاسباتی را که قبلاً برای بخشهای ثابت یک درخواست انجام داده — دستورالعملهای سیستم، تعاریف ابزار (tool)، اسناد مرجع طولانی — دوباره استفاده کند، بهجای پردازش مجدد آنها در هر فراخوانی.
مشکل این نیست که کش کردن پرامپت کار نمیکند. مشکل این است که اکثر تیمهای مهندسی پرامپتهای خود را طوری میسازند که پیش از آنکه کش فرصت کمک کردن پیدا کند، آن را خنثی میکنند، سپس نتیجه میگیرند که این قابلیت «تأثیری نداشته».
کش واقعاً چگونه کار میکند
وقتی یک مدل ترنسفورمر یک پرامپت را پردازش میکند، برای هر توکن ورودی جفتهای کلید-مقدار (KV) محاسبه میکند — بازنماییهای داخلی که مدل برای پیشبینی توکن بعدی استفاده میکند. این محاسبه بخش پرهزینه استنتاج (inference) است و با طول ورودی مقیاس میگیرد. کش کردن پرامپت این جفتهای KV را روی سرورهای ارائهدهنده، پس از اولین درخواست، بر اساس دقیقاً همان پیشوند (prefix) توکنهایی که آنها را تولید کرده ذخیره میکند.
در درخواست بعدی، اگر پرامپت جدید همان پیشوند را داشته باشد، ارائهدهنده از محاسبه مجدد جفتهای KV برای بخش مشترک صرفنظر میکند و فقط توکنهای جدید افزودهشده در انتها را پردازش میکند. برای مثال، پیادهسازی Anthropic برای خواندن از کش تقریباً ۱۰ درصد نرخ پایه توکن ورودی و برای نوشتن اولیه کش حدود ۲۵ درصد بیشتر از نرخ پایه هزینه میگیرد — معاملهای که برای هر پیشوندی که بیش از دو بار استفاده شود، بهسرعت سودآور میشود. کش خودکار پرامپت OpenAI تخفیف ۵۰ درصدی روی توکنهای ورودی کششده اعمال میکند بدون هزینه جداگانه برای نوشتن، و کش کردن کانتکست Google در مدلهای Gemini با یک شیء کش صریح که خودتان میسازید و ارجاع میدهید، مشابه عمل میکند.
صرفهجویی واقعی از کجا میآید
صرفهجوییها فرضی نیستند. یک عامل پشتیبانی مشتری که در هر فراخوانی شامل یک پرامپت سیستم ۳۰۰۰ توکنی، یک بخش ۵۰۰۰ توکنی از پایگاه دانش محصول، و یک مجموعه ۲۰۰۰ توکنی از تعاریف ابزار است، پیش از آنکه سؤال واقعی کاربر — شاید ۵۰ توکن — وارد ماجرا شود، بابت ۱۰۰۰۰ توکن ورودی هزینه میپردازد. در یک برنامه با حجم بالا که روزانه دهها هزار درخواست پردازش میکند، این سربار از هزینه نهایی خود پرسش بسیار بیشتر است.
با پیکربندی صحیح کش، آن پیشوند ۱۰۰۰۰ توکنی یکبار محاسبه میشود و سپس در هر فراخوانی بعدی در بازه TTL کش (معمولاً ۵ دقیقه در سطح استاندارد Anthropic، قابل تمدید تا یک ساعت در برخی ارائهدهندگان) از کش خوانده میشود. اثر عملی برای یک پایپلاین RAG با پنجره کانتکست بازیابیشده بزرگ، یا یک ایجنت با اسکیمای فراخوانی ابزار گسترده، کاهش ۶۰ تا ۸۰ درصدی هزینه مؤثر ورودی است — بدون تغییر کیفیت مدل، رفتار خروجی، یا تأخیری که کاربر متوجه آن شود. تأخیر (latency) در واقع کمی بهبود مییابد، چون توکنهای کششده کاملاً از پاس روبهجلو صرفنظر میکنند.
اشتباهی که نرخ برخورد کش را از بین میبرد
کش کردن پرامپت فقط روی تطبیق دقیق پیشوند کار میکند. اگر حتی یک توکن پیش از مرز کششده تغییر کند، کل ورودی کش خطا میخورد (miss) و ارائهدهنده از ابتدا محاسبه میکند. اینجاست که اکثر پیادهسازیها بیسروصدا خودشان را خراب میکنند.
رایجترین الگوی شکست: قرار دادن محتوای پویا — یک برچسب زمانی، شناسه نشست، یا فهرست تصادفیمرتبشده اسناد بازیابیشده — نزدیک ابتدای پرامپت، پیش از دستورالعملهای ثابت سیستم و تعاریف ابزار. در این حالت هر درخواست پیشوند منحصربهفرد خود را دارد و کش هرگز فرصت انباشت برخورد پیدا نمیکند. راهحل مکانیکی است اما نظم میخواهد: هر پرامپت را طوری بسازید که محتوای ثابت اول بیاید (دستورالعملهای سیستم، نمونههای few-shot، اسکیمای ابزارها، اسناد مرجع پایدار) و محتوای متغیر آخر (پیام واقعی کاربر، تاریخ جاری در صورت نیاز، وضعیت مختص نشست).
اشتباه رایج دوم، شخصیسازی بیشازحد پرامپت است. تیمهایی که ترجیحات مختص کاربر یا جزئیات حساب را مستقیماً در پرامپت سیستم تزریق میکنند — بهجای انتقال آنها بهعنوان بلوکی جداگانه و بهروشنی مشخص پس از پیشوند قابلکش — نرخ برخورد کش خود را برای بهبود کیفیتی حاشیهای که غالباً قابلاندازهگیری هم نیست، از بین میبرند. اگر داده شخصیسازی بهازای هر کاربر یا هر نشست تغییر میکند، جایش پس از مرز کش است، نه بافتهشده در دستورالعملهای مشترک.
کانتکست بازیابیشده سختترین حالت است
برنامههای RAG با یک تنش معماری واقعی روبهرو هستند: کل هدف بازیابی، نمایش اسناد متفاوت برای پرسشهای متفاوت است، که بنا به تعریف تطبیق پیشوند را روی خودِ محتوای بازیابیشده میشکند. راهحل با بیشترین اثر اینجا کش کردن قطعات بازیابیشده نیست — بلکه کش کردن همه چیز اطراف آنهاست. پرامپت سیستم، دستورالعملهای بازیابی، و مشخصات فرمت خروجی را در یک پیشوند پایدار نگه دارید و اسناد بازیابیشده را بهعنوان پسوند متغیر در نظر بگیرید. تیمهایی که کتابخانههای ابزار بزرگی اجرا میکنند میتوانند با کش کردن جداگانه بلوک تعاریف ابزار از محتوای بازیابیشده، سود اضافی کسب کنند، چون اسکیمای ابزارها بهندرت در یک نشست تغییر میکند حتی وقتی پرسش تغییر میکند.
برخی تیمها این را با یک استراتژی کش دولایه پیش میبرند: یک کش بلندمدت برای محتوای واقعاً ثابت (مستندات محصول، استانداردهای کدنویسی، راهنمای لحن برند) که فقط هنگام تغییر منبع بهروزرسانی میشود، و یک کش کوتاهمدت برای کانتکست سطح نشست که در طول یک مکالمه چندمرحلهای دوباره استفاده میشود و پس از پایان نشست دور ریخته میشود.
اندازهگیری درست
داشبوردهای ارائهدهندگان معمولاً تعداد توکنهای خواندن و نوشتن کش را جدا از توکنهای ورودی استاندارد گزارش میکنند، که محاسبه نرخ برخورد واقعی را ساده میکند: تعداد خواندنهای کش تقسیم بر کل توکنهای واجد شرایط پیشوند. تیمها باید این عدد را بهصراحت پیگیری کنند نه اینکه صرفهجویی را از صورتحساب کل استنتاج کنند، چون نرخ برخورد پایین میتواند پشت یک فاکتور نسبتاً معقول پنهان بماند اگر حجم درخواست هم در نوسان باشد.
نکات کاربردی
- ابتدا ساختار پرامپت خود را ممیزی کنید: محتوای ثابت (دستورالعملهای سیستم، اسکیمای ابزار، اسناد مرجع) باید بدون هیچ استثنایی پیش از هر محتوای متغیر مختص هر درخواست بیاید.
- در داخل پیشوند قابلکش شخصیسازی نکنید — دادههای مختص کاربر را بهعنوان بلوکی بهروشنی جدا پس از دستورالعملهای مشترک تزریق کنید.
- برای پایپلاینهای RAG، داربست پایدار اطراف محتوای بازیابیشده را کش کنید، نه خودِ محتوای بازیابیشده را.
- نرخ برخورد کش را بهعنوان یک معیار صریح پیگیری کنید، نه چیزی که از کل هزینه استنتاج میکنید.
- بازه TTL کش ارائهدهندهتان و الگوی ترافیک را بررسی کنید — شکافهای ترافیکی طولانیتر از پنجره TTL، صرفنظر از ساختار پرامپت، نوشتن مجدد کش را تحمیل میکنند.