تصور کنید یک عامل هوش مصنوعی در یک حلقه تکرار گیر کند و کل بودجه ماهانه شما را در عرض چند دقیقه بسوزاند، آن هم در حالی که سیستم هشدار شرکت ارائهدهنده هنوز حتی یک ایمیل هم نفرستاده است. در ۹ اوت ۲۰۲۶، توسعهدهندهای ابزار Burnix را معرفی کرد؛ یک سامانه کنترل هزینه محلی که برای متوقف کردن این فجایع مالی پیش از وقوع طراحی شده است.
بسیاری از محدودیتهای هزینه در سرویسهای ابری، صرفاً هشدار هستند و ترمز واقعی نیستند. این سیستمها به خط لولههای صورتحسابی متکیاند که تأخیری از چند دقیقه تا چند ساعت دارند. این نقص ساختاری زمانی که بدترین حالت، یک سرور فراموششده با هزینه ۴ دلار در ساعت بود، قابل تحمل بود؛ اما برای عاملهای هوش مصنوعی (AI Agents) — که مثل کارمندانی خودکار هستند که میتوانند هزاران دستور را در ثانیه اجرا کنند — این وضعیت خطرناک است. این نقص منجر به کابوسهای مستندی شده است: یک توسعهدهنده سقف ۲۵۰ دلاری تعیین کرده بود اما با صورتحسابی ۱۰,۱۳۸ دلاری بیدار شد. در موردی دیگر، یک مشتری AWS که سیستم تشخیص ناهنجاری (Anomaly Detection) را فعال کرده بود، برای تنها یک بار اجرای استنتاج در سرویس Bedrock، مبلغ ۳۰,۱۴۱ دلار هزینه پرداخت کرد، بدون آنکه حتی یک هشدار صادر شود. برخی تیمهای مالی (FinOps) حتی گزارش دادهاند که کل بودجه سالانه توکنهای خود را تنها در چهار ماه اول سال مصرف کردهاند. این چالشها نشان میدهد که مدلهای فعلی پرداخت بر اساس توکن برای سیستمهای حساس ناکارآمد هستند، موضوعی که در بررسی مدلهای جایگزین برای اتوماسیون صنعتی به تفصیل به آن پرداختیم.
همانطور که در تحلیل قبلی ما دربارهی ابزارهایی مثل LLMLingua و کاهش توکنهای پرامپت برای مدیریت هزینهها اشاره کردیم، Burnix روی ضلع دیگر معادله یعنی «اجرای سختگیرانه» تمرکز کرده است. این ابزار به عنوان یک پوشش (Wrapper) عمل میکند و بدون نیاز به تغییر در کد برنامه، درخواستها را رهگیری میکند. کاربران میتوانند با دستور سادهای مثل bash burnix --cap 5.00 -- npm run agent یک سقف سخت برای هزینهها تعیین کنند.
مکانیزم رهگیری
Burnix از متغیر محیطی NODE_OPTIONS استفاده میکند تا یک قلاب (Hook) را به فرآیندهای فرزند تزریق کند. هنگام ایجاد یک فرآیند فرزند، این ابزار ماژولی را تزریق میکند که پیش از هر کد کاربر، از طریق --require ${hookPath} بارگذاری میشود. این قلاب متد globalThis.fetch را تغییر میدهد (Patch میکند)، که به ابزار اجازه میدهد هر درخواستی که به ارائهدهنده مدل زبانی بزرگ (LLM) — شبیه کتابخانهداری که میلیاردها صفحه را خوانده و حالا با همان لحن جواب میدهد — ارسال میشود را پیش از آنکه SDK ارجاعی به آن پیدا کند، رصد کند.
- تأییدیه: توسعهدهنده ۳۰ دقیقه زمان صرف کرد تا تأیید کند که SDK شرکت Anthropic واقعاً از
globalThis.fetchاستفاده میکند تا روزها وقت خود را روی یک فرض غلط تلف نکند. - مدیریت استریم: برای پاسخهای جریانی (Streaming)، مقدار
res.bodyیکReadableStreamاست که تنها یک بار میتواند مصرف شود. برای حل این مشکل، ابزار از عملیاتtee()استفاده میکند تا یک شاخه را در قالب یک Response بازسازی شده برای فراخواننده بفرستد و شاخه دیگر را برای تحلیل رویدادهای Server-Sent Events (SSE) جهت استخراج رویداد نهایی مصرف توکن بخواند.
شکست در بارهای موازی
تستهای اولیه نشان داد ابزار برای درخواستهای متوالی عالی عمل میکند؛ مثلاً با سقف ۰.۰۵ دلار، درخواست سوم مسدود میشد و برنامه با یک وضعیت غیرصفر (non-zero status) خارج میشد. اما در بارهای موازی، ابزار شکست خورد. در تستی که از Promise.all برای اجرای ۲۰ درخواست همزمان استفاده شد، سقف ۰.۰۵ دلار کاملاً نادیده گرفته شد و هزینه به ۰.۲۱ دلار رسید.
علت این اتفاق یک «شرایط رقابتی» (Race Condition) بود: ۲۰ درخواست همزمان، هزینه فعلی را ۰ دلار خواندند، پیش از آنکه هر کدام پاسخ بگیرند و وضعیت را بهروز کنند. تا زمانی که اولین پاسخ در میلیثانیه ۸۰۰ بازگشت و وضعیت را بهروز کرد، هر ۲۰ درخواست قبلاً تایید و ارسال شده بودند. سقف هزینه ۷۵۰ میلیثانیه دیرتر فعال شد؛ دقیقاً در همان وضعیتی که عاملهای AI برای فراخوانی موازی ابزارها استفاده میکنند. این نیاز به کنترل دقیق و محلی بر روی اجرای عاملها، با روند انتقال محیطهای اجرای عاملها از ابر به دسکتاپ برای کاهش تأخیر و افزایش کنترل همسو است.
راهکار: رزرو و تطبیق
برای حل این مشکل، توسعهدهنده گردش کار «اول رزرو، بعد تطبیق» (Reserve, then Reconcile) را پیاده کرد تا هزینه پیش از ارسال درخواست کسر شود:
- رزرو: ابزار یک تخمین بدبینانه از بدترین حالت هزینه محاسبه کرده و بلافاصله آن را با یک شناسه رزرو (Reservation ID) به وضعیت اضافه میکند. این تخمین به صورت
(inputTokens * inPrice + outputTokens * outPrice) / 1e6محاسبه میشود، که در آنinputTokensبر اساس طول رشته تقسیم بر ۴ وoutputTokensبر اساس محدودیتmax_tokensتعیین میشود. - بررسی: اگر مجموع هزینه مصرفشده و رزرو شده (
spent + reserved) بزرگتر یا مساوی سقف باشد، ابزار رزرو را آزاد کرده و درخواست را رد میکند. - تطبیق: پس از دریافت پاسخ، مبلغ رزرو شده حذف و هزینه واقعی جایگزین میشود. این کار باید در یک بلوک
finallyانجام شود تا رزروهای رها شده باعث تورم دائمی و کاذب بودجه نشست نشوند.
اهمیت وضعیت همگام (Synchronous)
نکته حیاتی این است که مرحله رزرو باید همگام باشد. توسعهدهنده دریافت که استفاده از await بین خواندن و نوشتن وضعیت، دوباره راه را برای شرایط رقابتی باز میکند، زیرا حلقه رویداد Node.js میتواند در این پنجره زمانی، پرومیسهای دیگر را به صورت متناوب اجرا کند.
برای تضمین یک جابهجایی اتمیک (Atomic)، ابزار از fs.readFileSync برای خواندن وضعیت استفاده میکند، آن را تغییر میدهد و سپس از fs.writeFileSync برای نوشتن در یک فایل موقت و در نهایت fs.renameSync استفاده میکند. چون Node تکرشتهای است، این بلوک خواندن-تغییر-نوشتن همگام نمیتواند متوقف شود. در تست بعدی، این راهکار عالی عمل کرد: از ۲۰ درخواست، ۶ مورد موفق و ۱۴ مورد مسدود شدند و هزینه نهایی ۰.۰۴۵۴ دلار شد که دقیقاً زیر سقف ۰.۰۵ دلار بود.
حل شکاف تجربه کاربری
این رویکرد بدبینانه مشکلی جدید ایجاد کرد: ابزار درخواستها را مسدود میکرد در حالی که هزینه قطعی (Settled) هنوز تنها ۸۷٪ سقف بود. از نظر کاربر، ابزار داشت دروغ میگفت. راهکار این بود که رزروهای «در جریان» (In-flight) در نوار پیشرفت نمایش داده شوند.
نمایش جدید، هم هزینه قطعی و هم هزینههای در جریان را نشان میدهد (مثلاً: total $0.0017 / $0.0020 ██████▒▒ 87% (+$0.0005 in flight)). بلوکهای توپر هزینه قطعی و بلوکهای سایهدار رزروهای در جریان هستند. این تغییر معماری ثابت کرد وقتی وضعیت داخلی باعث یک تصمیم برای کاربر میشود، نمایش دادن تنها نتیجه نهایی، رفتار درست سیستم را شبیه به یک باگ جلوه میدهد.
محدودیتها و چشمانداز
Burnix از طریق npm install -g @burnix/cli در دسترس است و با لایه رایگان Groq کار میکند، اما محدودیتهای خاصی دارد:
- پشتیبانی زبانی: قلاب
NODE_OPTIONSفقط برای فرزندان Node کار میکند و نمیتواند به فرآیندهای پایتون یا Go دسترسی داشته باشد. گام بعدی، ساخت یک پروکسی محلی مستقل از زبان است. - رقابت فرآیندها: در حالی که نوشتن همگام رقابتهای داخلی را حل میکند، اما دو فرآیند مجزای Node که یک نشست را به اشتراک میگذارند، هنوز میتوانند تداخل داشته باشند، هرچند این پنجره به میکروثانیه کاهش یافته است.
- بیشبرآورد: چون رزروها بر اساس
max_tokensهستند، اگر پاسخهای واقعی معمولاً بسیار کوتاهتر از محدودیت باشند، ابزار ممکن است زودتر از موعد سقف را فعال کند.
برای توسعهدهندگانی که از کلیدهای مشترک تیمی استفاده میکنند، هنوز یک شکاف بزرگ وجود دارد. داشبوردهای فعلی هزینه کل را نشان میدهند اما هیچ راهی برای تخصیص هزینههای خاص به کاربران یا عاملهای فردی ارائه نمیدهند و تیمها مجبورند صرفاً برای دیدن مجموع کلی بدون جزئیات دقیق، «داشبورد را چک کنند».
گام بعدی شما
- اگر از عاملهای AI در محیط توسعه استفاده میکنید، Burnix را نصب کنید تا از شوک صورتحسابهای پایان ماه جلوگیری کنید.
- در تنظیمات مدلهای خود،
max_tokensرا به اندازه نیاز محدود کنید تا رزروهای بدبینانه Burnix باعث مسدود شدن زودهنگام درخواستها نشود. - برای مدیریت هزینههای تیمی، به دنبال ابزارهای Proxy-based بگردید که قابلیت Attribution یا تخصیص هزینه به هر کاربر را دارند.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو