اگر صورتحساب API شما پس از افزودن دادههای زمینهای بهطور ناگهانی جهش کرده است، احتمالاً با یک «مالیات پایداری» روبرو هستید. بسیاری از توسعهدهندگان برای کاهش هزینهها بهطور غریزی مدل خود را به نسخههای ضعیفتر ارتقا میدهند، اما مقصر واقعی بهندرت قیمت مدل است و تقریباً همیشه یک شکست در حافظه موقت (Cache Miss) است. وقتی توکنهای ورودی به دلیل بازیابی دادهها یا پرامپتهای سیستمی طولانیتر، در هر درخواست بهطور کامل و با قیمت کامل پردازش میشوند، شما هزینهای را میپردازید که بدون تغییر در انتخاب مدل، قابل حذف است. اصلاح جایگاه نقاط شکست (Breakpoint) و پایداری پیشوند رایگان است، اما پایین آوردن سطح مدل، کیفیت خروجی شما را میکرباید.
این چالش زمانی رخ میدهد که توسعهدهندگان از پرامپتهای ساده به سمت معماریهای پیچیده تولید بازیابیافزا (RAG) — شبیه دانشآموزی که قبل از جواب دادن، اول کتاب درسی را باز میکند و از آن نقل میآورد — و عاملهای ابزار-محور (Tool-heavy agents) حرکت میکنند. در این مسیر، استفاده از تکنیکهای فشردهسازی پرامپت میتواند راهکاری بهینهتر از تعویض مدل برای مدیریت هزینههای عاملها باشد. همانطور که در تحلیل قبلی ما دربارهی کاهش ۹۶ درصدی هزینههای ارزیابی با استفاده از داورهای ترکیبی اشاره کردیم، تمرکز اکنون از مسیریابی معماری به فیزیک دقیق نحوه ذخیرهسازی توکنها در محیط عملیاتی تغییر کرده است. برای اکثر توسعهدهندگان، تفاوت بین یک قابلیت سودآور و یک چاه هزینه، تنها چند بایت متن ناپایدار در ابتدای پرامپت است.
کالبدشکافی یک شکست در حافظه موقت
برای شناسایی این نشت مالی، باید تحلیل کل صورتحساب را متوقف کنید و ثبت توکنها را بهصورت هر-درخواست (per-request) بررسی کنید. در API شرکت Anthropic، هر پاسخ شامل یک شیء مصرف (usage object) است که توکنهای ورودی را به سه دسته متمایز تقسیم میکند:
- input_tokens: باقیمانده توکنهای پردازشنشده از پرامپت. این همان فیلدی است که اغلب باعث سردرگمی میشود؛ زیرا این مقدار، اندازه کل پرامپت نیست.
- cache_creation_input_tokens: توکنهایی که برای نخستین بار در حافظه نوشته میشوند (که به آن «پاداش نوشتن» یا write premium میگویند).
- cache_read_input_tokens: توکنهایی که با موفقیت از حافظه بازیابی شدهاند.
اندازه کل پرامپت مجموع این سه فیلد است: input_tokens + cache_creation_input_tokens + cache_read_input_tokens. من شاهد بودم تیمی به این نتیجه رسید که پرامپت آنها کوچک است چون مقدار input_tokens برابر با 4K بود، در حالی که دو فیلد دیگر ده برابر این مقدار را تشکیل میدادند.
شرکت OpenAI نیز دادههای مشابهی را از طریق usage.prompt_tokens_details.cached_tokens ارائه میدهد. در هر دو اکوسیستم، درس ریاضی یکسان است: یک درخواست را دو بار پشت سر هم اجرا کنید و مقایسه کنید. در یک حالت سالم، درخواست دوم باید پیشوند مشترک را بخواند و فقط تغییرات (Delta) را بنویسد. اگر درخواست دوم نشاندهنده یک «نوشتن در حافظه» تقریباً برابر با کل پرامپت و مقدار «خواندن» صفر باشد، پیشوند شما ناپایدار است.
چه چیزهایی بهطور خاموش حافظه را میشکنند؟
حافظه موقت بر اساس تطابق دقیق بایتبهبایت پیشوند عمل میکند. ترتیب رندرینگ معمولاً به صورت «ابزارها $\rightarrow$ سیستمی $\rightarrow$ پیامها» است. هر تغییر در یک بایت در ابتدای این زنجیره، تمام موارد بعد از آن را باطل میکند. این موضوع باعث میشود حالت شکست بسیار موذیانه باشد: درخواست همچنان با موفقیت اجرا میشود، خروجی درست است و تنها صورتحساب شما متوجه خطا میشود.
رایجترین متخلف، «پرامپت سیستمی ناپایدار» است. گنجاندن برچسب زمانی (Timestamp) یا متادیتای خاص کاربر در ابتدای پرامپت سیستمی تضمین میکند که هیچ دو درخواستی هرگز پیشوند مشترک نداشته باشند. برای مثال، پرامپتی که با Current time: {datetime.now().isoformat()} User: {user.email} (plan: {user.plan}) شروع میشود و سپس ۵۰۰ خط دستورالعمل دارد، در چهار خط اول شامل سه عامل باطلکننده است. هیچچیز بعد از این سربرگ هرگز کش نمیشود.
سایر «قاتلان حافظه» که باید در کد خود با دستور grep جستوجو کنید شامل موارد زیر است:
- JSONهای نامرتب: استفاده از
json.dumps()بدون تنظیمsort_keys=Trueکه منجر به ترتیب تصادفی کلیدها میشود. - مجموعههای بدون ترتیب: پیمایش روی یک
setدر پایتون برای ساخت پرامپت. - ابزارهای کاربر-محور: اسمبل کردن لیست ابزارها بهصورت مجزا برای هر کاربر. چون ابزارها در موقعیت صفر رندر میشوند، مجموعهی ابزار اختصاصی هر کاربر، بازاستفاده متقاطع (cross-user reuse) را غیرممکن میکند.
- پرچمهای ویژگی (Feature Flags): بخشهای شرطی سیستمی که در آن هر ترکیبی از پرچمها به یک پیشوند متمایز تبدیل میشود.
- تعویض مدل: تغییر مدل باعث ابطال حافظهها میشود، زیرا حافظهها در سطح هر مدل (model-scoped) تعریف شدهاند.
استراتژی جایگذاری نقاط شکست
قرار دادن نقطه شکست (Breakpoint) در انتهای پرامپت معمولاً یک اشتباه است. اگر بلوک نهایی شامل سؤال منحصربهفرد کاربر یا ردیفهای تازه بازیابیشده از RAG باشد، نقطه شکست بعد از بایتهایی قرار میگیرد که هرگز تکرار نمیشوند. این منجر به پرداخت «پاداش نوشتن» در هر درخواست و مقدار خواندن صفر میشود.
برای بهینهسازی، باید پیشوند را منجمد کرده و مقادیر متغیر را به بعد از آخرین نقطه شکست منتقل کنید. برچسبهای زمانی و طرحهای کاربر را به پیامهای بعدی ببرید. این کار تضمین میکند که تغییر در نوبت پنجم یک گفتگو، دستورات ایستا در نوبت اول را باطل نکند.
این الگوهای رایج پرامپت و حالتهای شکست آنها را در نظر بگیرید:
- مقدمه ثابت بزرگ و سؤال متغیر: اگر نقطه شکست در انتهای پرامپت باشد، شما در هر درخواست هزینه نوشتن را میپردازید و خواندنها نزدیک به صفر میمانند. نقطه شکست باید در انتهای مقدمه مشترک باشد.
- گفتگوهای چند-نوبتی در حال رشد: قرار دادن نقطه شکست در آخرین بلوک از جدیدترین نوبت، باعث میشود کل تاریخچه در هر نوبت مجدداً پردازش شود.
- پیشوندهای منحصربهفرد در هر درخواست: اگر پیشوند از بایت اول متفاوت باشد، حافظه عملاً بیفایده است و شما صرفاً یک هزینه اضافی بدون هیچ خواندنی میپردازید.
- نرخهای تغییر ترکیبی: برای بخشهایی با پایداری متفاوت، از یک نقطه شکست به ازای هر مرز پایداری استفاده کنید (مثلاً زمینهای که روزانه تغییر میکند نباید ابزارهای هرگز-تغییرناپذیر را باطل کند).
تا اواخر سال ۲۰۲۶، دو محدودیت جدید ظاهر شده است. نخست، حداقل طول پیشوند قابل ذخیره وجود دارد که بسته به مدل متفاوت است و در نسلهای مختلف یکنواخت نیست؛ مدلهای جدیدتر ممکن است پیشوندهای کوتاهتری را نسبت به مدلهای قدیمیتر کش کنند. زیر این حد، حافظه بهطور خاموش بدون هیچ عملیاتی (no-op) میماند. دوم، درخواستهای موازی روی یک پیشوند یکسان همگی شکست میخورند، زیرا یک ورودی تنها زمانی قابل خواندن است که اولین پاسخ شروع به استریم شدن کند. برای بارهای کاری با توزیع بالا (high-fan-out)، باید یک درخواست بفرستید، منتظر اولین توکن بمانید و سپس بقیه را ارسال کنید.
اقتصاد دستهبندی و مسیریابی
برای کارهایی که کاربر منتظر پاسخ آنی نیست، نقاط انتهایی دستهبندی (Batch endpoints) ارائهدهندگان بزرگ، بیشترین بازدهی را دارند. تا اواخر ۲۰۲۶، این نقاط انتهایی ناهمگام با تقریباً نصف نرخ استاندارد هر توکن اجرا میشوند. این روش برای خلاصهسازیهای شبانه، پر کردن دادههای قدیمی (backfills)، اجراهای ارزیابی، طبقهبندی انبوه یا تولید بردار معنایی (embedding generation) ایدهآل است.
در برنامهریزی برای دستهبندی، سه نکته را به یاد داشته باشید:
۱. ترتیب: نتایج با ترتیب تصادفی میرسند؛ آنها را با custom_id خودتان شناسایی کنید و هرگز به موقعیت (position) تکیه نکنید.
۲. تأخیر: تکمیل دستهها در یک بازه زمانی (window) رخ میدهد، نه یک هدف زمانی دقیق. جابها را طوری بسازید که تأخیر در یک دسته، تنها باعث افت کیفیت داشبورد شود نه اینکه کاربر را مسدود کند.
۳. ترکیب: دستهبندی با همه چیز ترکیب نمیشود؛ جفت کردن آن با ترفندهای حافظه یا کدهای وابسته به استریم، اغلب نیاز به بازطراحی درخواست دارد.
مسیریابی به سمت مدلهای کوچکتر برای کاهش هزینهها باید آخرین گام باشد. حافظهها محدود به مدل هستند، به این معنی که استراتژی «آبشاری» (cascade) که ترافیک را بین دو مدل تقسیم میکند، بازاستفاده از حافظه بین آنها را از بین میبرد. یک صرفهجویی ۴۰ درصدی تئوریک روی کاغذ، میتواند توسط پیشوندهای سرد (Cold Prefixes) کاملاً از بین برود.
علاوه بر این، معیار واقعی «هزینه به ازای هر تسک تکمیلشده» است، نه هزینه هر درخواست. مدل ارزانتری که نیاز به تلاشهای مجدد بیشتر، زنجیره طولانیتر یا اصلاح انسانی دارد، در نهایت گرانتر از یک مدل توانمند است که با تنظیمات «تلاش» (effort) یا استنتاج پایینتر اجرا میشود. در مدلهای نسل فعلی، یک اجرای با تلاش کاهشیافته اغلب با حداکثر تلاش نسل قبل برابری میکند در حالی که یک فضای نام حافظه (cache namespace) را حفظ میکند. این مورد را برای هر مسیر تنظیم کنید: طبقهبندی و استخراج معمولاً کیفیت خود را در سطح پایین حفظ میکنند، اما کدنویسی و حلقههای عاملهای بلندمدت اینگونه نیستند.
تحلیل: تغییر به سمت مهندسی توکن
این تغییر نشان میدهد که بهینهسازی هزینه AI از «خرید مدل» به «مهندسی توکن» در حال حرکت است. صنعت در حال درک این است که هوش مدل یک مقدار ثابت است، اما کارایی مکانیسم تحویل آن یک متغیر است. برای کیف پول توسعهدهنده، این بدان معناست که توانایی مدیریت وضعیت (state) و پایداری پیشوند اکنون به اندازه توانایی نوشتن یک پرامپت خوب اهمیت دارد.
با تلقی کردن نرخ خواندن صفر در یک پیشوند تکراری به عنوان یک «باگ» به جای یک واقعیت قیمتگذاری، تیمها میتوانند مدلهای با کیفیت بالا را حفظ کنند در حالی که پروفایل هزینهای مدلهای بسیار کوچکتر را به دست میآورند. اثر مرتبه دوم این است که مدلهای «استدلالی» (reasoning) که معمولاً گرانتر هستند، در صورتی که پرامپتهای سیستمی عظیم آنها بهدرستی کش شوند، برای محیط عملیاتی توجیهپذیر میشوند. اگر میخواهید بهطور کلی از حسابداری نقاط شکست اجتناب کنید، حافظه خودکار API شرکت Anthropic پیشفرض مناسبی برای چتهای معمولی چند-نوبتی است، هرچند پرامپتهایی با چندین مرز پایداری همچنان به نشانگرهای صریح نیاز دارند.
اگر مطمئن نیستید از کجا شروع کنید، یک لایه مشاهدهپذیری مانند Langfuse میتواند تجزیه و تحلیل توکن و هزینه را به ازای هر Trace فراهم کند بدون اینکه به یک موتور تجمیع سفارشی نیاز داشته باشید، البته به قیمت اضافه شدن یک سرویس دیگر یا پرداخت هزینه لایه میزبانی.
برای شروع بهینهسازی از امروز، مقدار cache_read_input_tokens را برای پرتکرارترین مسیرهای خود ثبت کنید. اگر این عدد صفر است، دو بدنه درخواست متوالی را (با حذف مارکرهای حافظه) با هم مقایسه (diff) کنید تا بایت دقیقی که باعث ابطال میشود را بیابید. نرخ خواندن صفر در یک پیشوند تکراری را به عنوان یک باگ با یک تیکت پیگیری کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید. این چالشهای نرمافزاری در واقع بازتابی از گلوگاههای حافظه در سطح سختافزار هستند که باعث اتلاف شدید توان پردازشی GPUها میشوند.




گفتگو