اگر امروز برای اجرای عاملهای هوش مصنوعی بودجهای تعیین میکنید، احتمالاً متوجه شدهاید که مدلها دستور «توقف در ۴۰ هزار توکن» را نادیده میگیرند. حقیقت این است که درخواستهای زبانی هرگز جایگزین محاسبات ریاضی در کد نمیشوند. یک پرامپت سیستمی که به عامل میگوید «وقتی به ۴۰ هزار توکن نزدیک شدی متوقف شو» صرفاً یک درخواست است، اما محاسبات سختافزاری که در کد تحمیل شده باشند، یک بودجه واقعی ایجاد میکنند.
در ۹ اکتبر ۲۰۲۶، توسعهدهندهای به نام هاریش کوترا (Harish Kotra) ابزاری به نام Allowance را منتشر کرد. این کنترلپنل حفاظهای زبانی شکننده را با محدودیتهای قطعی جایگزین میکند تا بودجه به جای یک پیشنهاد، به یک قانون تبدیل شود.
بسیاری از چارچوبهای عاملمحور در سال ۲۰۲۶، بودجه را به صورت جملاتی در پرامپت سیستمی (System Prompt) — شبیه به یادداشتی که برای یک کارمند میگذارید و امیدوارید رعایت کند — پیاده میکنند. اما این رویکرد درست در لحظهای که مدل یک «روز بد» دارد شکست میخورد و منجر به هزینههای مالی واقعی میشود، حتی اگر محدودیتی تئوریک تعریف شده باشد. این عدم کنترل دقیق بر هزینهها میتواند فاجعهبار باشد؛ برای مثال، تجربه تلخ اوبر نشان داد که چگونه هزینههای پیشبینینشده توکنها میتواند بودجه سالانه یک بخش بزرگ را در عرض چند ماه نابود کند. همانطور که در تحلیلهای قبلی ما دربارهی امنیت مدلهای بازمتن اشاره کردیم، تکیه بر رفتار مدل برای کنترل منابع، ریسک بالایی دارد. Allowance با تبدیل سقف هزینهها (توکن، دلار و زمان) به ویژگیهای کد برنامه به جای یک پیشنهاد به LLM، این مشکل را حل میکند.
این پروژه با استفاده از Inngest AgentKit، TypeScript، Express 5، React 18 و SQLite ساخته شده است. ساختار آن شامل تقریباً ۴۵۰۰ خط کد برنامه، ۴۷۴ خط تأیید سناریو و ۲۷۸ خط تست واحد است. در این سیستم، بودجه از یک پاراگراف متن به یک کنترلپنل تبدیل شده است؛ شما وظیفه و سقف هزینه را تعیین میکنید، پر شدن دفتر کل را گامبهگام تماشا میکنید و وقتی مصرف به ۶۰٪ برسد، سیستم یک درخواست بودجه برای تأیید ارسال میکند. در صورت رد شدن درخواست، سیستم با یک نتیجه ناقص و یک گزارش هزینه دقیق، به طور تمیز متوقف میشود.
ریاضیاتِ تحمیلِ بودجه
طبق مستندات پروژه، Allowance بر اساس یک مکانیسم ساده اما سختگیرانه عمل میکند: پیش از هر فراخوانی مدل، سیستم فضای باقیمانده (Headroom) را در سه منبع مستقل محاسبه میکند. سختترین محدودیت، همواره تعیینکنندهٔ اجرای عملیات است. در کد این سیستم (server/src/agent/budget.ts)، ابتدا ضربالاجل زمانی بررسی میشود چون حتی با وجود پول، زمان میتواند در میانه یک برنامه منقضی شود.
- توکنها: تعداد توکنهای باقیمانده (
cap_tokens - used_tokens). - دلار: مبلغ باقیمانده که به توکن تبدیل شده است (
cap_usd - used_usd). - زمان: ثانیههای باقیمانده تا ضربالاجل (
deadline - now).
سیستم از تابع min() برای یافتن محدودکننده اصلی استفاده میکند. ممکن است یک اجرا ۹۵٪ از توکنهای خود را داشته باشد، اما چون ثانیههای اختصاص یافته تمام شده است، درخواست رد شود.
اگر هزینه تخمینی گام بعدی بیشتر از کمترین مقدار این سه منبع باشد، آن گام اجرا نمیشود. سپس سیستم بررسی میکند آیا طرح ارزانتری (که توسط MIN_CALL_TOKENS تعریف شده) با بودجه فعلی سازگار است یا خیر؛ اگر بله، برنامهریزی مجدد میکند و تغییر را ثبت میکند. در غیر این صورت، وضعیت REFUSED را برمیگرداند و دقیقاً مشخص میکند کدام منبع — tokens، usd یا time — باعث مسدود شدن شده است. این موضوع باعث میشود رد شدنها برای رابط کاربری (UI) مفید باشند و بتوانند شکافهای خاصی را گزارش کنند (مثلاً: «۹۰۹ توکن نیاز بود، اما ۲۶۶ توکن باقی مانده است»)، در حالی که متن گفتگو اعداد دقیق را حفظ میکند.

تبدیل پول به توکن
برای اینکه بودجه هرگز سخاوتمندانهتر از ارزانترین فراخوانی ممکن نباشد، Allowance قیمت توکنها را محافظهکارانه محاسبه میکند. این سیستم گرانترین قیمت بین ورودی یا خروجی را ملاک قرار میدهد: Math.max(prices.per1kInput, prices.per1kOutput) / 1000.
در این پروژه هیچ هزینهای به صورت سختافزاری (Hardcoded) تعریف نشده است. مبالغ دلاری از یک جدول قیمت استخراج میشوند که کاربر میتواند در پنل تنظیمات آن را ویرایش کند. چون کاربر میتواند این قیمتها را تغییر دهد، هر عدد مشتق شده به عنوان یک «تخمین» برچسب میخورد. به قول کوترا: «بودجهای که نتوان قیمت آن را تغییر داد، یک صورتحساب است، و این یک سیستم صورتحساب نیست».
حل مشکل وضعیت با SQLite
یکی از اصلیترین چالشهای فنی در تحمیل بودجه، اجتناب از استفاده از متغیرهای ساده جاوااسکریپت است. کوترا اشاره میکند که استفاده از یک متغیر JS برای نگه داشتن remainingBudget از سه جهت اشتباه است:
- تکرارها (Retries) باعث شمارش دوبارگان میشوند: گامی که پس از کسر هزینه شکست میخورد و دوباره اجرا میشود، بسته به اینکه خطا کجا رخ دهد، ممکن است همان پول را دوبار خرج کند یا هیچ هزینهای ثبت نکند.
- ریاستارتها وضعیت را از بین میبرند: اگر پروسه متوقف شود، متغیر میمیرد و اجرای بعدی طوری شروع میشود که انگار روز اول است.
- فقدان قابلیت حسابرسی (Audit): عددی که گام ۷ را محدود کرده بود، تا زمانی که کاربر هزینه گام ۷ را بررسی کند، دیگر وجود ندارد.
برای حل این مشکل، بودجه در SQLite ذخیره میشود. سیستم به جای افزایش یک شمارنده، مجموع هزینهها را از روی دفتر کلی که هر گام را ثبت میکند، مجدداً محاسبه میکند. این کار با استفاده از یک تراکنش انجام میشود که یک INSERT در دفتر کل و یک UPDATE در جدول اجراها (با استفاده از SUM ردیفهای دفتر کل) را انجام میدهد. برای تضمین Idempotency، دستور INSERT از عبارت ON CONFLICT(run_id, step_key) DO UPDATE استفاده میکند.
این ساختار تضمین میکند که سیستم:
- Idempotent باشد: محدودیت
UNIQUE(run_id, step_key)به این معنی است که یک گام تکرار شده، ردیف خودش را بازنویسی میکند به جای اینکه هزینه دومی ایجاد کند. - خود-سازگار (Self-consistent) باشد: چون
used_tokensمجموع ردیفهاست، شمارنده و دفتر کل نمیتوانند با هم اختلاف داشته باشند؛ این یک ناپایداری ساختاری است و هیچ انحرافی برای تطبیق وجود ندارد. - قابل حسابرسی باشد: سیستم میتواند تأیید کند که
rows == executed_steps == COUNT(DISTINCT step_key). اسکریپت تأییدیه این سه مقدار را پس از هر پنج سناریو بررسی میکند.
ادغام با Inngest AgentKit
این ابزار از Inngest AgentKit برای مدیریت پایداری (Durability) استفاده میکند. به طور خاص، از step.ai.infer برای Memoize کردن فراخوانیهای ارائهدهنده استفاده میکند. درخواست HTTP توسط Runtime صادر و پاسخ ذخیره میشود؛ در صورت بازپخش (Replay)، پاسخ ذخیره شده برگردانده میشود به جای اینکه دوباره هزینه پرداخت شود. این دقیقاً همان ویژگی است که یک دفتر کل هزینه نیاز دارد بدون اینکه نیاز به پیادهسازی دستی باشد.
در این معماری دو دروازه (Gate) مجزا وجود دارد:
۱. دروازه ارکستراسیون (Orchestration Gate): این دروازه درباره جریان کنترل (اجرا، برنامهریزی مجدد، پرسش یا رد) تصمیم میگیرد و داخل یک گام Memoized اجرا میشود.
۲. دروازه پیش-فراخوانی (Pre-call Gate): این دروازه به عنوان یک شبکه ایمنی با استفاده از هوک onStart عمل میکند. این بخش conservativeCallNeed (پرامپت توکنایزر + max_completion_tokens) را محاسبه میکند.
با تثبیت max_completion_tokens در هر درخواست، ارائهدهنده فیزیکی نمیتواند بیش از آنچه دروازه شارژ کرده است، پاسخ برگرداند. این کار سقف بودجه را به ویژگی کد تبدیل میکند، نه یک شرطبندی روی رفتار مدل.
خطر توکنهای «نامرئی»
در حین توسعه، کوترا متوجه نقص شدیدی در تکیه صرف به گزارشهای مصرف ارائهدهندگان شد. با استفاده از DeepSeek-v4.1-Flash (یک مدل استدلالی)، سیستم مواردی را یافت که مدل یک رشته خالی (message.content: "") برمیگرداند اما ۷۰۰ توکن استدلال و ۷۰۰ توکن تکمیل را صورتحساب میکرد. مدل برای یک پاسخ خالی، هزینه کامل را دریافت کرده بود.
برای مقابله با این موضوع، Allowance هر دو عدد تخمینی و گزارششده را در ستونهای جداگانه برای هر فراخوانی ذخیره میکند. تابع resolveUsage موارد زیر را ردیابی میکند:
- تخمینی: بر اساس
cl100k(با افزودن ۳ توکن قاببندی برای هر پیام). - گزارششده: مقادیر واقعی
usage.prompt_tokensوusage.completion_tokensاز ارائهدهنده.
در یک تست تطبیق، توکنایزر برای چهار فراخوانی ۱۳۸۷ توکن تخمین زد، اما ارائهدهنده ۲۲۵۷ توکن را شارژ کرد — یک تخمین کمتر از حد واقعی به میزان ۳۹٪. این اتفاق به این دلیل افتاد که قاببندی cl100k توکنهای پنهان استدلال را نمیشمارد. با جدا نگه داشتن این ستونها، سیستم «حاشیه خطا» را که کاربر هنگام حذف دادههای مصرف توسط ارائهدهنده میپذیرد، آشکار میکند. جدول تطبیق صراحتاً اینها را جداگانه جمع میکند تا اختلاف بین شارژهای دفتر کل و مجموعهای گزارششده ارائهدهنده را نشان دهد.
تأییدات انسانی در حلقه
وقتی بودجه تمام شود، عامل میتواند درخواستی برای بودجه بیشتر ارسال کند. این درخواستها بر اساس estCostUsd از طریق تأییدات لایهبندی شده مدیریت میشوند:
- تأیید خودکار: درخواستهایی که در حد یا کمتر از
APPROVE_UNDER_USDهستند بدون نیاز به انسان پذیرفته میشوند، هرچند همچنان به عنوان ردیفهای تأیید ثبت شده و توسط اجرای پایدار اعمال میشوند. - تأیید انسانی: درخواستهای بزرگتر، اجرا را با استفاده از
step.waitForEventمتوقف میکنند. اجرا وارد وضعیتawaiting-approvalمیشود و اینباکس، دلیل درخواست، مقدار مورد نیاز و هزینه تخمینی را نشان میدهد.
این توقف در برابر ریاستارتهای سرور مقاوم است. نکته حیاتی این است که مدل هیچ مسیری به تابعی که پول اعطا میکند ندارد؛ خروجی مدل برای یافتن یک «طرح» (Plan) تجزیه میشود، نه برای «مجوز» (Authorization). سقفها فقط داخل یک گام و پس از اتخاذ تصمیم، با استفاده از applyGrant برای نوشتن مقادیر جدید cap_tokens، cap_usd یا deadline_at تغییر میکنند. این رویکرد سختگیرانه برای جلوگیری از آسیبپذیریهای مالی است؛ چرا که برخی سیستمهای پرداخت عاملهای هوش مصنوعی به دلیل نقص در مدیریت تراکنشهای موازی، ریسک تخطی شدید از بودجه را به همراه داشتند.
تأییدیه و موارد خاص
کوترا برای اطمینان از اینکه سیستم واقعاً کار میکند، یک مجموعه تأیید (npm run verify) پیاده کرد که پنج سناریو را از طریق HTTP روی یک ارائهدهنده زنده اجرا میکند. این تستها چهار باگ بحرانی را شناسایی کردند که تستهای واحد (Unit Tests) آنها را نادیده گرفته بودند:
۱. مارپیچ مرگ توان عملیاتی (Observed-Throughput Death Spiral):
در ابتدا، تبدیل زمان به توکن از توان عملیاتی مشاهده شده در اجرا استفاده میکرد. اگر اولین فراخوانی کند بود (مثلاً ۲۳۵ توکن در ۴۰ ثانیه)، سیستم نرخ پایینی (۵.۹ توکن بر ثانیه) محاسبه میکرد که فضای باقیمانده را کوچک میکرد و تمام گامهای بعدی را رد میکرد، حتی اگر ۹۷٪ توکنها و ۹۹٪ پول باقی مانده بود. راه حل این بود که نرخ از خودِ پاکت بودجه (capTokens / capSeconds) استخراج شود. اکنون سقف توکن دقیقاً همان چیزی است که در سقف زمان جای میگیرد و زمان به طور متناسب با جلو رفتن ساعت، محدودکننده میشود.
۲. ناپایداری وضعیت در بازپخش (Replay State Inconsistency):
Inngest هنگام Resume شدن، بدنه توابع را از ابتدا دوباره اجرا میکند. در ابتدا، دروازه onStart مستقیماً از SQLite میخواند. اگر یک اجرا از یک توقف با بودجهای تقریباً خالی Resume میشد، هوک ممکن بود فراخوانیای را که در ابتدا موفق شده بود، مسدود کند. این باعث میشد بازپخش مسیری متفاوت از اجرای اصلی طی کند و وضعیت REFUSED را روی یک رد تمیز ثبت کند. راه حل، خواندن بودجه داخل یک step.run بود که Memoize شده است تا تضمین شود بازپخش همان مسیر را طی میکند. این ثابت میکند که پایداری فقط درباره هزینه نیست، بلکه درباره جریان کنترل بازتولیدپذیر است.
۳. مذاکره و ضربالاجل:
دو باگ منطقی یافت شد: JSONهای غیرقابل تجزیه از مدل به اشتباه به عنوان درخواست بودجه بیشتر تفسیر میشدند و تخطی از ضربالاجل به جای رد کردن، باعث شروع مذاکرات میشد. اکنون، طرحهای غیرقابل تجزیه منجر به نتایج ناقص میشوند و اتمام زمان همیشه منجر به REFUSED{at:"time"} میشود. علاوه بر این، پنجره تأیید اکنون توسط ساعت خودِ بودجه محدود شده است: Math.min(APPROVAL_TIMEOUT_MS, Math.max(1000, remainingMs)). اگر ضربالاجل در حین انتظار بگذرد، درخواست به عنوان Timeout ثبت میشود.
۴. تبخیر توکنهای استدلال (Reasoning Token Evaporation):
همانطور که در مورد DeepSeek ذکر شد، سیستم مواردی را گرفت که بودجه در «تفکر نامرئی» تبخیر میشد. با افزودن reasoning_effort: "none" به بدنه درخواست، سیستم میتواند توکنهای استدلال را به صفر کاهش دهد و ۲۵۵ توکن خروجی واقعی دریافت کند. حسابداری دقیق باعث شد این هزینه نامرئی قابل مشاهده شود و ثابت کرد که دفتر کل تنها راه متوجه شدن این است که یکسوم بودجه در استدلالهای پنهان ناپدید شده است.
نتایج مجموعه تأیید
مجموعه npm run verify پانزده ادعای مجزا را در پنج سناریو تأیید میکند:
- بودجه سخاوتمندانه: تأیید میکند که اجرا کامل میشود و حکم تطبیق با مصرف گزارششده ارائهدهنده همخوانی دارد.
- بودجه محدود: سیستم را مجبور به برنامهریزی مجدد یا وضعیت
REFUSEDمیکند؛ یک تکمیل ساده در این سناریو شکست میخورد. - تلاش برای دور زدن سقف: طرحی را که بسیار بزرگتر از سقف است رد میکند. تخمین خوشبینانه مدل (مثلاً ۰.۹ برابر سقف) را با قیمتگذاری واقعی دروازه (مثلاً ۴.۴ برابر سقف) مقایسه کرده و روی مقدار بزرگتر تأیید میکند.
- مسیر تأیید: تضمین میکند که یک رد (Denial) باعث توقف تمیز اجرا با یک گزارش هزینه میشود.
- لایه تأیید خودکار: تأیید میکند درخواستهای زیر
APPROVE_UNDER_USDبدون دخالت انسان پذیرفته میشوند.
مسیرهای آینده
کوترا قصد دارد Allowance را با یک مجموعه وظایف خصمانه (Adversarial Task Suite) گسترش دهد تا مطمئن شود مدل نمیتواند سیستم را با پرامپت دادن به سیستم برای افزایش بودجه، فریب دهد تا سقف خودش را بالا ببرد. ویژگیهای برنامهریزی شده دیگر شامل دترمینیسم در کرش و بازگشت (دفتر کلهای با بایتهای یکسان)، یک شبکه بودجهبندی برای چندین عامل که یک پاکت بودجه مشترک دارند (با گیجهای مصرف برای هر عامل)، و پیشبینی بر اساس میانگین هزینه هر گام است تا تاییدکنندگان انسانی زمینه بهتری نسبت به حدس مدل داشته باشند. در نهایت، او قصد دارد دفتر کل را به جای گزارشهای ارائهدهنده، با صورتحسابهای واقعی (Invoices) تطبیق دهد.
گام بعدی شما
- اگر از عاملهای هوش مصنوعی در محیط تولید استفاده میکنید، منطق کنترل بودجه را از پرامپت به لایه دیتابیس منتقل کنید.
- برای مدلهای استدلالی (Reasoning Models)، حتماً ستونهای جداگانه برای توکنهای تخمینی و گزارششده ایجاد کنید تا از «هزینههای نامرئی» آگاه شوید.
- از مکانیزمهای Memoization برای جلوگیری از پرداخت هزینه دوبارگان در هنگام Retry استفاده کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو