Files
MathApp/docs/ai-cost-and-guardrails.md
Zareian 64c4c09326 Paywall: first unit of each grade free; 1/3/6-month subscriptions; AI sold separately
- content: unit_is_free/has_unit_access; questions, node quiz, answer and lesson/node
  complete return 402 subscription_required outside the first unit without an active
  subscription. /api/home units carry requiresSubscription.
- subscription.0013: monthly 498k, quarterly 798k, six-month 1,298k toman, unlimited
  hearts, 3 AI questions/day; packs stay on sale for more questions.
- AI_DAILY_QUOTA_FREE default 3 -> 2.
- /api/subscription/status adds planTitle, planDurationDays, aiDailyQuota.
- tests: content/tests_paywall.py; existing unlock-order tests get a subscription.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:19:02 +03:30

10 KiB
Raw Blame History

هزینه‌ی دستیار و محدود کردنش به ریاضی

نسخه‌ی کامل برای مدیرعامل (جدول و نمودار، 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 ذخیره می‌شوند. تا پیش از آن برای جواب‌های اپ صفر ثبت می‌شد. هزینه‌ی واقعیِ هر کاربر در ماه از این پرس‌وجو درمی‌آید:

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.0009، subscription.0010):

بسته قیمت (تومان) هدیه سود اگر کامل خرج شود حاشیه‌ی سود
۲۰ سوال ۴۵٬۰۰۰ ۷ روز قلبِ نامحدود ۲۴٬۰۰۰ ۶۳٪
۶۰ سوال ۱۱۹٬۰۰۰ ۳۰ روز ۵۹٬۰۰۰ ۵۸٪
۱۵۰ سوال ۲۵۹٬۰۰۰ ۶۰ روز ۱۱۵٬۰۰۰ ۵۲٪

با ۷۰۰ تومان برای هر سوال (دستِ بالا) و پس از ۱۵٪یِ کافه‌بازار؛ اگر همه دبیرستانی باشند ۴۸ تا ۶۰٪. چرا بسته: همه هر روز نمی‌آیند، ۱۷۹ هزار تومان در ماه برای خیلی‌ها زیاد بود، و مشترکِ پرکار زیان می‌ساخت. با بسته هر سوال جدا سود دارد. مسیرِ قیمت‌ها پیش از این:

  • سالانه ۲۹۹ هزار تومان، با ۱۵ سوال در روز برای همه.
  • ماهانه/سالانه ۹۹ و ۹۹۰ هزار تومان، با ۵ سوال در روز.
  • ماهانه/سالانه ۱۷۹ و ۱٬۷۹۰ هزار تومان، با ۱۰ سوال در روز.

پلن‌های اشتراکی پاک نشدند (on_sale=False) تا مشترکِ فعلی سقفش را نگه دارد.

  • ترتیبِ خرج: اول سقفِ روزانه (بی‌اشتراک 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، با مدلِ ساختگی و بی‌هزینه.