درسهایی از توسعه Claude Code
اهمیت کش کردن پرامپت
در مهندسی اغلب گفته میشود که «کش بر تمام کارهای من حاکم است» (Cache rules everything around me)، و همین قاعده در مورد عاملها (Agents) نیز صدق میکند.
آنتروپیک در بلاگ Claude بهترین شیوهها برای بهینهسازی کش کردن پرامپت (Prompt Caching) در Claude Code را به اشتراک میگذاشته است، از جمله اینکه چگونه ساختار پرامپت خود را به مؤثرترین شکل طراحی کنید، از ابزارها بهره ببرید و لایههای فشردهسازی (Compaction) را پیادهسازی کنید.
کش کردن پرامپت
محصولات عاملمحور با زمینه طولانیمدت مانند Claude Code، به واسطه قابلیت کش کردن پرامپت امکانپذیر شدهاند؛ قابلیتی که به ما اجازه میدهد از محاسباتِ رفتوبرگشتهای قبلی مجدداً استفاده کنیم و تأخیر و هزینهها را به میزان قابلتوجهی کاهش دهیم.
آنتروپیک تمام زیرساخت خود در Claude Code را حول محور کش کردن پرامپت بنا کرده است. بالابودن نرخ اصابت به کش پرامپت (Prompt cache hit rate) هزینهها را کاهش میدهد؛ به همین دلیل، هشدارهایی را برای نرخ اصابت به کش پرامپت تنظیم کرده و در صورت پایینبودن این نرخ، وضعیتهای بحرانی (SEVs) اعلام میکند. در ادامه، درسهایی (اغلب غیربدیهی) که از بهینهسازی کش کردن پرامپت در مقیاس وسیع را مرور میکنیم.
ساختاردهی پرامپت برای کش کردن
پرامپت سیستمی (System prompt) در Claude Code بهگونهای سازماندهی شده است که بخشهای ثابت در کش باقی میمانند و تنها خود مکالمه است که نوبتبهنوبت گسترش مییابد.
کش کردن پرامپت از طریق تطابق پیشوند (Prefix matching) عمل میکند؛ به این معنا که API همه چیز را از ابتدای درخواست تا هر نقطه شکست cache_control کش میکند. این یعنی ترتیبی که موارد را قرار میدهید بسیار مهم است؛ شما باید کاری کنید که بیشترین تعداد درخواستهایتان در یک پیشوندِ مشترک سهیم باشند.
بهترین روش برای انجام این کار، قراردادن محتوای ایستا در ابتدا و محتوای پویا در انتها است. برای Claude Code این ساختار به شکل زیر است:
- پرامپت سیستمی ایستا و ابزارها (بهصورت سراسری کش میشوند)
- فایل CLAUDE.md (درون یک پروژه کش میشود)
- زمینه نشست یا Session context (درون یک نشست کش میشود)
- پیامهای مکالمه
با این روش اشتراک اصابتهای کش (Cache hits) در چند نشست به حداکثر میرساند.
بااینحال، این رویکرد میتواند شکننده باشد. آنتروپیک پیشازاین نیز این ترتیببندی را به دلایل مختلفی نقض کرده است؛ از جمله قراردادن یک برچسب زمانی دقیق در پرامپت سیستمی ایستا، بههمریختن تصادفی ترتیب تعریف ابزارها و بهروزرسانی پارامترهای ابزارها (مثلاً اینکه ابزار Agent میتواند چه عاملهایی را فراخوانی کند).

استفاده از پیام برای بهروزرسانی
ممکن است مواقعی پیش بیاید که اطلاعات قرار دادهشده در پرامپت شما منقضی شوند، مثلاً با گذشت زمان یا تغییر یک فایل توسط کاربر. شاید وسوسه شوید که پرامپت را بهروز کنید، اما این کار منجر به ازدسترفتن کش (Cache miss) میشود و در نهایت میتواند هزینه زیادی برای کاربر در بر داشته باشد.
در عوض، بررسی کنید که آیا میتوانید این اطلاعات را در نوبت بعدیِ عامل از طریق پیامها ارسال کنید. در Claude Code، در پیام بعدی کاربر یا در نتیجه ابزار (Tool result)، یک تگ <system-reminder> حاوی اطلاعات بهروزرسانیشده برای مدل اضافه شده است که به حفظ کش کمک میکند.
در میانه نشست مدلها را تغییر ندهید
کشهای پرامپت منحصر به هر مدل هستند و این موضوع میتواند محاسبات کش کردن پرامپت را کاملاً غیربدیهی کند. برای مثال، اگر در یک مکالمه با Opus از ۱۰۰ هزار توکن عبور کردهاید و میخواهید سؤالی بپرسید که پاسخ به آن نسبتاً آسان است، تغییر مدل به Haiku در واقع هزینه بیشتری نسبت به پاسخگویی خود Opus خواهد داشت، زیرا میبایست کش پرامپت برای Haiku از نو ساخته شود.
اگر مجبور به تغییر مدل هستید، بهترین راه استفاده از عاملهای فرعی (Subagents) است؛ در ادامه مثال بالا، شما میتوانید یک عامل فرعی مستقر کنید که به Opus پرامپت دهد تا یک پیام «انتقال وظیفه» (Hand-off) را برای مدل دیگری آماده کند تا آن مدل کار موردنیاز را انجام دهد. آنتروپیک این کار را اغلب در عاملهای Explore در Claude Code که از Haiku استفاده میکنند، انجام میدهد.
هرگز ابزارها را در میانه نشست اضافه یا حذف نکنید
تغییر مجموعه ابزارها در وسط یک مکالمه یکی از رایجترین راههایی است که باعث شکستهشدن کش پرامپت میشود. به نظر منطقی میرسد که بخواهید تنها ابزارهایی را به مدل بدهید که فکر میکنید در حال حاضر به آنها نیاز دارد. اما ازآنجاییکه ابزارها بخشی از پیشوندِ کششده هستند؛ اضافه یا حذفکردن یک ابزار، کش کل مکالمه را باطل میکند.
استفاده از حالت برنامهریزی (Plan Mode) برای طراحی بر اساس کش
«حالت برنامهریزی» (Plan Mode) یک مثال عالی برای طراحی ویژگیها با درنظرگرفتن محدودیتهای کش است. رویکرد بدیهی این است: وقتی کاربر وارد حالت برنامهریزی میشود، مجموعه ابزارها را تغییر یابد تا تنها شامل ابزارهای خواندنی (Read-only) باشد؛ اما این کار کش را میشکند.
در عوض، آنتروپیک تمام ابزارها را همواره در درخواست نگه میدارد و از EnterPlanMode و ExitPlanMode بهعنوان ابزارهای مستقل استفاده میکند. هنگامی که کاربر حالت برنامهریزی را روشن میکند، عامل یک پیام سیستمی دریافت میکند که توضیح میدهد در حالت برنامهریزی قرار دارد و دستورالعملها چیست.
این روش یک مزیت اضافی نیز دارد؛ ازآنجاییکه EnterPlanMode ابزاری است که مدل خودش میتواند آن را فراخوانی کند، در صورت مواجهه با یک مشکل سخت میتواند به طور مستقل و بدون شکستن کش وارد حالت برنامهریزی شود.
استفاده از جستوجوی ابزار برای تعویق بارگذاری بهجای حذف ابزارها
همین اصل در مورد قابلیت «جستوجوی ابزار» (tool search tool) نیز صدق میکند. Claude Code میتواند دهها ابزار MCP بارگذاریشده داشته باشد و گنجاندن همه آنها در هر درخواست بسیار پرهزینه خواهد بود، اما حذف آنها در وسط مکالمه نیز کش را میشکند.
راهحل defer_loading است. بهجای حذف ابزارها، نسخههای سبک و خالی (Stubs – فقط شامل نام ابزار به همراه defer_loading: true) ارسال میشود که مدل میتواند در صورت نیاز آنها را از طریق جستوجوی ابزار «کشف» کند. طرحوارههای (Schemas) کامل ابزارها تنها زمانی بارگذاری میشوند که مدل آنها را انتخاب کند. این کار پیشوندِ کششده را پایدار نگه میدارد؛ زیرا همان نسخههای سبک همیشه به همان ترتیب وجود دارند. شما همچنین میتوانید از طریق API آنتروپیک از ابزار جستوجوی ابزار برای سادهسازی این روند استفاده کنید.
فشردهسازی بدون شکستن کش
هنگامی که پنجره زمینه پر میشود، Claude Code یک فراخوانیِ کششده را منشعب میکند تا مکالمه را خلاصهسازی کند، و سپس با جایگزینکردن خلاصه بهجای پیامهای اصلی، کار را از سر میگیرد. فشردهسازی (Compaction) زمانی اتفاق میافتد که شما فضای پنجره کانتکست را به پایان میرسانید؛ سپس مدل مکالمه را تا آن لحظه خلاصه میکند و نشست جدیدی را با آن خلاصه ادامه میدهد.
تعامل فشردهسازی با کش کردن پرامپت بهگونهای است که اشتباهکردن در آن بسیار آسان است. برای فشردهسازی یک مکالمه، باید کل مکالمه را به مدل بفرستید تا بتواند خلاصهای بنویسد. سادهترین راه برای این کار یک فراخوانی API جداگانه با پرامپت سیستمیِ مختص به خود (چیزی شبیه به «این را خلاصه کن») و بدون پیوست هیچ ابزاری است؛ اما دقیقاً تله هزینه همینجاست. کش کردن پرامپت تنها زمانی اعمال میشود که پیشوند یک درخواست بایتبهبایت با آنچه از ابتدا کش شده مطابقت داشته باشد. مکالمه اصلی شما تحت یک پرامپت سیستمی و یک مجموعه ابزار مشخص کش شده است؛ فراخوانیِ خلاصهسازی از یک پرامپت سیستمی متفاوت و بدون ابزار استفاده میکند، بنابراین پیشوندها در همان توکن اول از هم منحرف میشوند و هیچکدام از محتوای کششده اعمال نمیشوند. در نهایت مجبور میشوید هزینه ورودیِ کشنشده و کامل را برای کل مکالمهای که ارسال میکنید بپردازید و هرچه مکالمه طولانیتر باشد (یعنی هرچه بیشتر به فشردهسازی نیاز داشته باشید)، آن تکفراخوانی گرانتر میشود.

راهحل: انشعاب ایمن برای کش
وقتی فشردهسازی اجرا میشود، از دقیقاً همان پرامپت سیستمی، کانتکست کاربر، کانتکست سیستم و تعاریف ابزاری استفاده میشود که در مکالمه والد (Parent conversation) وجود داشت. پیامهای مکالمه والد در ابتدا قرار داده شده و سپس پرامپت فشردهسازی بهعنوان یک پیام کاربری جدید در انتها اضافه میشود.
از دیدگاه API، این درخواست تقریباً با آخرین درخواست والد یکسان به نظر میرسد (همان پیشوند، همان ابزارها، همان تاریخچه)؛ بنابراین پیشوند کششده مجدداً استفاده میشود. تنها توکنهای جدید، خود پرامپت فشردهسازی هستند. بااینحال، این بدان معناست که باید یک «بافر فشردهسازی» (Compaction buffer) ذخیره شود تا فضای کافی در پنجره کانتکست برای گنجاندن پیام فشردهسازی و توکنهای خروجی خلاصه وجود داشته باشد.
فشردهسازی پیچیده است، اما نیازی نیست این درسها را خودتان بیاموزید؛ Claude Code فشردهسازی را مستقیماً درون API ساخته است تا بتوانید این الگوها را در برنامههای خود اعمال کنید.
درسهای آموختهشده
در ادامه چند الگو آورده شده است که برای بهینهسازی کش کردن پرامپت هنگام ساخت یک عامل مفید است:
- کش کردن پرامپت یک تطابق پیشوند است. هر تغییری در هر کجای پیشوند، تمام محتوای بعد از آن را باطل میکند. کل سیستم خود را حول این محدودیت طراحی کنید. اگر ترتیب را درست انجام دهید، بیشترِ کش کردن بهصورت رایگان کار خواهد کرد.
- بهجای تغییرات در پرامپت سیستمی از پیامها استفاده کنید. ممکن است وسوسه شوید پرامپت سیستمی را برای کارهایی مانند ورود به حالت برنامهریزی، تغییر تاریخ و غیره ویرایش کنید، اما در واقع بهتر است این موارد را در طول مکالمه درون پیامها درج کنید.
- ابزارها یا مدلها را در وسط مکالمه تغییر ندهید. برای مدلسازیِ تغییرات وضعیت (مانند حالت برنامهریزی) از ابزارها استفاده کنید تا اینکه مجموعه ابزارها را تغییر دهید. بهجای حذف ابزارها، بارگذاری آنها را به تعویق بیندازید.
- نرخ اصابت به کش خود را همانند زمانِ دردسترسبودن سیستم (Uptime) پایش کنید. شکستهای کش نوعی هشدار ایجاد میکنند با آنها مانند یک حادثه برخورد میشود. چند درصد کاهش در نرخ اصابت به کش میتواند تأثیر چشمگیری بر هزینه و تأخیر داشته باشد.
- عملیات انشعاب (Fork) باید در پیشوندِ مکالمه والد شریک باشند. اگر نیاز دارید یک محاسبه جانبی (مانند فشردهسازی، خلاصهسازی، اجرای یک مهارت) انجام دهید، از پارامترهای یکسان و ایمن برای کش استفاده کنید تا اصابت کش در پیشوند والد را به دست آورید.