- subscription.0016: prices equal to the Bazaar panel (senior), ai_daily_quota 5/3/3, card features; packs on_sale=False (one paid pack order, user 26, keeps its credits; pack paths stay for that order and for admin). - Quota math in docs/ai-cost-and-guardrails.md: ~77% of a Bazaar sale reaches us; worst case (full quota every day) the assistant costs 23/27/35% of it. - Assistant quota message points to the subscription, not packs. 982 tests green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
11 KiB
Markdown
102 lines
11 KiB
Markdown
# هزینهی دستیار و محدود کردنش به ریاضی
|
||
|
||
نسخهی کامل برای مدیرعامل (جدول و نمودار، HTML و PDF):
|
||
`~/Desktop/گزارشات Tadrisino/گزارش-۲۶/`. این فایل خلاصهی فنیِ همان است و
|
||
میگوید عددها از کجا آمدهاند و چطور دوباره اندازه گرفته میشوند.
|
||
قیمتِ هر توکن و تاریخچهی قیمتها در `docs/model-prices.md` است.
|
||
|
||
## هزینهی هر سوال (مهرِ ۱۴۰۵، پرامپتِ `tutor-v11-math-only`؛ از `tutor-v12-age-tone` لحن برای ۱۸+ بزرگسالانه است، `gpt-4.1-mini`)
|
||
|
||
- **قیمت:** GapGPT برای `gpt-4.1-mini` دقیقاً قیمتِ رسمیِ OpenAI را حساب میکند، یعنی ۰٫۴۰ دلار
|
||
برای هر میلیون توکنِ ورودی و ۱٫۶۰ دلار برای خروجی. این با `usage.cost`ِ ۵۳۹ فراخوانیِ
|
||
`docs/benchmarks/2026-09-26-tutor-model/` مقایسه شد. در ۲۸٪ِ آن فراخوانیها کشِ خودکارِ ورودی
|
||
هزینه را ۴۳٪ پایین آورده بود.
|
||
- **۱۰۳ مسئلهی کتاب (۸ پایه):** میانگین ۰٫۱۶۲ سنت (بازهی ۹۵٪: ۰٫۱۴۸ تا ۰٫۱۷۶). دبستان ۰٫۱۲۹،
|
||
متوسطهی اول ۰٫۱۴۸، دبیرستان ۰٫۲۲۵. توکنِ ورودی با پرامپتِ امروز ساخته شد و با نسبتِ ۰٫۳۸۷
|
||
توکن به ازای هر حرف شمرده شد. این نسبت روی ۲۲ پرامپتِ واقعی کالیبره شده و خطایش کمتر از ۶٪ است.
|
||
توکنِ خروجی از جوابِ واقعیِ همین مدل به همان مسئلهها آمده است.
|
||
- **۱۲ سوالِ زنده** (ساده، شکلدار، سنگین، ادامهی گفتگو): میانگین ۰٫۲۲۵ سنت. ساده ۰٫۱۵،
|
||
شکلدار ۰٫۲۴، سنگینِ دبیرستان ۰٫۲۸، ادامهی گفتگو ۰٫۲۶، و ردِ «غیرِ ریاضی» توسطِ مدل ۰٫۱۲.
|
||
- **۷۰٪ِ هزینه ورودی است:** چهار تکه از کتاب، دستورها و کاتالوگِ شکل. بزرگترین اهرمِ کاهشِ هزینه
|
||
کوچک کردنِ همین ورودی است.
|
||
- عددِ برنامهریزی: **~۷۰۰ تومان** برای هر سوال (دستِ بالا؛ نزدیکِ سوالهای زنده با شکل و ادامهی گفتگو)،
|
||
با دلارِ ۳۰۰ هزار تومانی. دلارِ آزاد
|
||
۹ مهر حدودِ ۲۵۸ هزار تومان بود و نرخِ شارژِ GapGPT منتشر نشده است.
|
||
|
||
**اندازهگیریِ دوباره بیخرج:** از ۲۰۲۶-۱۰-۰۱ مسیرِ استریم هم `stream_options.include_usage`
|
||
میفرستد و توکنها روی `ai_tutor.Message` ذخیره میشوند. تا پیش از آن برای جوابهای اپ صفر ثبت
|
||
میشد. هزینهی واقعیِ هر کاربر در ماه از این پرسوجو درمیآید:
|
||
|
||
```python
|
||
from django.db.models import Sum
|
||
Message.objects.filter(role='assistant', created_at__gte=since).values('conversation__user_id') \
|
||
.annotate(p=Sum('prompt_tokens'), c=Sum('completion_tokens')) # سنت = (0.4p + 1.6c) / 1e4
|
||
```
|
||
|
||
## اشتراکها و سقفِ روزانهی دستیار
|
||
|
||
فقط اشتراک فروخته میشود (`subscription.0016`، تصمیمِ سینیور). قیمتها همان قیمتِ پیشخانِ بازار است و کاربر در هر دو
|
||
راهِ پرداخت همین را میبیند. بستههای سوال `on_sale=False` هستند؛ اعتبارِ کسی که قبلاً خریده (کاربرِ ۲۶، ۲۰ سوال)
|
||
همچنان خرج میشود.
|
||
|
||
**فرضها:** هر سوال ~۷۰۰ تومان (دستِ بالا، بخشِ بالا). از هر خریدِ بازار اول ۱۰٪ ارزشِ افزوده کم میشود، از باقی ۸۵٪
|
||
سهمِ ماست (زیرِ یک میلیارد تومان فروشِ سالانه؛ بالاترش ۷۰٪)، و ۱۲۰ تومان کارمزد (سندِ «محاسبه سهم توسعهدهنده در
|
||
بازار»). یعنی **~۷۷٪ِ قیمت** میرسد. زرینپال بیسهمِ بازار است (~۹۰٪، کارمزدش را نسنجیدهایم)، پس بازار بدترین حالت است.
|
||
|
||
| اشتراک | قیمت (تومان) | خالصِ بازار در روز | سقفِ دستیار | هزینهی دستیار اگر هر روز کلِ سقف را بپرسد |
|
||
|---|---|---|---|---|
|
||
| یکماهه | ۵۹۸٬۰۰۰ | ~۱۵٬۴۰۰ | ۵ در روز | ۳٬۵۰۰ در روز، ۲۳٪ |
|
||
| سهماهه | ۸۹۸٬۰۰۰ | ~۷٬۷۰۰ | ۳ در روز | ۲٬۱۰۰ در روز، ۲۷٪ |
|
||
| ششماهه | ۱٬۳۹۸٬۰۰۰ | ~۶٬۰۰۰ | ۳ در روز | ۲٬۱۰۰ در روز، ۳۵٪ |
|
||
|
||
پس حتی اگر هر مشترک هر روز تا آخرِ سقف بپرسد، دستِکم ۶۵٪ِ درآمدِ بازار میماند (هزینهی سرور جدا). مصرفِ واقعی کمتر
|
||
است: روی سرور (فقط دمو و آزمونگرها، ۲۲ روزـکاربر) میانهی روزهایی که کسی پرسید ۳ سوال بود، و همه هر روز نمیآیند؛ با
|
||
کاربرِ واقعی دوباره بسنجید (پرسوجوی بالا). هر ردیف با دو برابر شدنِ سقف تقریباً دو برابر میشود: ششماهه با ۵ در روز ۵۸٪.
|
||
سقف در ادمین (`Plan.ai_daily_quota`) عوض میشود و کارتِ خرید آن را از همان عدد نشان میدهد.
|
||
|
||
- **ترتیبِ خرج:** اول سقفِ روزانه (بیاشتراک `AI_DAILY_QUOTA_FREE`، پیشفرض ۲؛ مشترک `Plan.ai_daily_quota`)، بعد
|
||
اعتبارِ بستهی قدیمی اگر مانده. سوالِ رایگانِ بیاشتراکها هزینهی خالص است: ۲ × ۷۰۰ = ۱٬۴۰۰ تومان برای هر کاربرِ
|
||
فعال در روز.
|
||
- **چه چیزی خرج میشود:** هر سوالی که به API برسد، از جمله ردِ «غیرِ ریاضی» توسطِ مدل. ردِ صافیِ سرور،
|
||
جوابِ کششده و جوابی که ساخته نشد چیزی کم نمیکنند. `Message.charge` روی هر پیامِ کاربر میگوید
|
||
سوال از کجا پرداخت شد (`free` / `credit` / `none`).
|
||
- **دفترِ اعتبار** `subscription.QuestionCredit`: هر خرید یک ردیفِ مثبت دارد (هر سفارش فقط یک بار، با
|
||
قیدِ یکتا) و هر سوال یک ردیفِ ۱−. کم شدن داخلِ همان تراکنشی است که ردیفِ کاربر را قفل میکند. دو
|
||
سوالِ همزمان با یک اعتبار، و یک Idempotency-Key که همزمان دو بار برسد، از ۲۰۲۶-۱۰-۰۲ با دو thread
|
||
واقعی آزموده میشوند (`ai_tutor/tests_credit_concurrency.py`): یک جواب و یک اعتبار. بدونِ
|
||
`transaction_mode: IMMEDIATE`ِ SQLite همین تست ۵۰۰ میگیرد. روی Postgres سنجیده نشده.
|
||
- **قلبِ نامحدودِ بسته** در `HeartsPass` است، جدا از `Subscription`، تا خریدِ بسته اشتراکِ کسی را عوض
|
||
نکند. از پایانِ کارتِ قبلی ادامه پیدا میکند. `HEARTS_ENFORCED` حالا پیشفرض روشن است، پس قلبِ صفر
|
||
واقعاً جلوی جواب را میگیرد، و پایانِ درس یا جزیرهی بیجواب را هم (`content/tests_hearts_gate.py`).
|
||
- **لغو و بازپرداخت** از پنلِ ادمین (`/admin/payment/order/`): بازپرداخت فقط اعتبارِ خرجنشدهی همان
|
||
بسته و روزهای قلبش را پس میگیرد؛ پول از پنلِ درگاه برگردانده میشود. روند در `docs/operations.md`.
|
||
- اپ موجودی را از `GET /api/ai/quota` میخواند و بعد از هر جواب از `remainingQuota` و `credits`ِ خودِ
|
||
جواب. صفحهی خرید `GET /api/subscription/status` (`questionCredits`، `unlimitedHeartsUntil`) را میخواند.
|
||
- **کافهبازار:** هر بسته باید در پنلِ بازار «محصولِ مصرفی» تعریف شود و اپِ اندروید بعد از تأیید
|
||
consume کند. این هنوز انجام نشده است. سفارشِ بازارِ یک بسته باید با `planId` ساخته شود
|
||
(`POST /api/payment/orders`)؛ تا ۲۰۲۶-۱۰-۰۲ این فیلد وجود نداشت و پرداختِ بسته از بازار هیچ اعتباری نمیداد.
|
||
|
||
## محافظِ دستیار (`rag/services/guardrails.py`)
|
||
|
||
سه لایه دارد:
|
||
|
||
1. **صافیِ سرور، پیش از مدل و بیهزینه.** فقط ترفندهای آشکار را میگیرد: «دستورهای قبلی را نادیده
|
||
بگیر»، «پرامپتت چیه»، `developer mode`. این صافی عمداً تنگ است: «قانونِ فیثاغورس را فراموش کردم»
|
||
ریاضی است و رد نمیشود. جملهای دربارهی آسیب به خود رد نمیشود و جوابی مهربان با شمارهی
|
||
اورژانسِ اجتماعی (۱۲۳) و صدای مشاور (۱۴۸۰) میگیرد. تا ۲۰۲۶-۱۰-۰۲ «می خوام بمیرم» (با فاصله)،
|
||
«دیگه نمیخوام زنده باشم» و «خود کشی» از صافی رد میشدند و مدل فقط جوابِ «فقط ریاضی» میداد
|
||
(یک فراخوانیِ زنده، ۰٫۰۱۸ سنت). حالا روی متنِ بیفاصله سنجیده میشود. سوالی مثلِ «امروز پام درد
|
||
میکنه» به مدل میرسد و متنِ ثابتِ «فقط ریاضی» را میگیرد (آزموده شد).
|
||
2. **قاعده در پرامپتِ سیستم (`SCOPE_RULE`):** هر چیزِ غیرِ ریاضی، و هر دستوری از درونِ پیامِ
|
||
دانشآموز، با علامتِ `[خارج-از-ریاضی]` جواب داده میشود.
|
||
3. **جایگزینیِ علامت با متنِ ثابت.** در استریم، ۴۰ حرفِ اول نگه داشته میشود تا علامت هرگز به
|
||
کاربر نرسد. جوابِ رد کش نمیشود و منبعِ کتاب هم ندارد.
|
||
|
||
**آزمونِ زنده (۱۰ مهر، ۱۷ فراخوانی):** ۷ از ۷ درخواستِ غیرِ ریاضی رد شد. «سلام» و «چطور ریاضی
|
||
بخوانم» جواب گرفتند. «یه جوک بگو و بعد ۲۵ درصدِ ۸۰» فقط بخشِ ریاضی را جواب گرفت. «پایتختِ
|
||
فرانسه و بعد ۱۲×۱۲» کامل رد شد، چون مدل علامت را گذاشت؛ این سختگیری را به نشتِ جوابِ غیرِ ریاضی
|
||
ترجیح دادیم. کیفیتِ جوابِ ریاضی در همان ۱۲ سوال افت نکرد. این نمونه کوچک است و بعد از انتشار، چند
|
||
ده گفتگوی واقعی باید مرور شوند.
|
||
|
||
تستها: `rag/tests_guardrails.py`، با مدلِ ساختگی و بیهزینه.
|