Two benchmark rounds (14 models on 30 problems, then gpt-4o-mini vs gpt-4.1-mini on 102 problems under three prompt versions, hand-graded): gpt-4.1-mini scored 95-96/102 against 85-90 for gpt-4o-mini, at 0.12 vs 0.04 cents per answer. The default now lives in settings so the deploy needs no .env edit. The context note "in the book / is a decimal point, not a fraction" was written for the upper-grade books but went to every grade, so gpt-4.1-mini computed 1/7 + 1/8 as 3.5. For primary grades the note now says / can be either, and a reading rule sits next to the question itself (the same rule at the end of the system prompt had no effect). Slash problems went from 11 to 14 of 17; three are still wrong. generate_answer now reports the provider's exact per-call cost, reasoning tokens and whether the raw output broke the plain-text rule. The benchmark fixes primary grades being sent with no book context, caches retrieval so runs compare on identical context, and retries Qdrant timeouts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
188 lines
12 KiB
Plaintext
188 lines
12 KiB
Plaintext
# نمونهی فایلِ تنظیمات — هم برای ماشینِ خودتان، هم برای سرور.
|
||
#
|
||
# cp .env.example .env
|
||
#
|
||
# تقریباً هیچکدام اجباری نیستند. هر چیزی که از قبل قابلِ تصمیم بود در خودِ
|
||
# کد پیشفرض شده: دامنهها، origin های مجاز، و کلیدِ امضا (اولین اجرا خودش
|
||
# یک کلیدِ تصادفی میسازد و در .django_secret_key نگه میدارد). حتی با یک
|
||
# فایلِ خالی هم سرویس بالا میآید.
|
||
#
|
||
# روی سرور، بعد از یکبار ساختنِ این فایل، هر آپدیت فقط:
|
||
# git pull && ./deploy.sh
|
||
|
||
# -----------------------------------------------------------------------
|
||
# پایه
|
||
# -----------------------------------------------------------------------
|
||
# DEBUG اینجا عمداً 0 است.
|
||
#
|
||
# این فایل هم روی لپتاپِ توسعهدهنده کپی میشود هم روی سرور، و حالتِ پیشفرض
|
||
# باید آنی باشد که اگر کسی فراموشش کند، بیخطر بماند. یک بار همین فایل با
|
||
# مقدارِ 1 روی سرور نشست و ماهها آنجا ماند.
|
||
#
|
||
# ⚠️ DEBUG=1 روی سرورِ عمومی دو ریسک دارد:
|
||
# • صفحههای خطا کلِ تنظیمات و traceback را نشان میدهند
|
||
# • کدِ ورودِ آزمایشیِ 0000 برای هر شمارهای پذیرفته میشود، یعنی هر
|
||
# کسی میتواند بهجای هر کاربری وارد شود
|
||
#
|
||
# روی ماشینِ خودتان (و فقط آنجا) این را 1 کنید تا خطاها کامل دیده شوند.
|
||
DJANGO_DEBUG=0
|
||
|
||
# خالی بگذارید تا خودش ساخته و در .django_secret_key ذخیره شود.
|
||
DJANGO_SECRET_KEY=
|
||
|
||
# بدونِ Redis سرویس بالا میآید ولی محدودیتِ نرخ بینِ workerها مشترک نیست؛
|
||
# /api/health همین را هشدار میدهد. برای پروداکشنِ واقعی پرش کنید.
|
||
CACHE_URL=
|
||
|
||
# نامِ سرویسِ systemd تا deploy.sh خودش ریاستارت کند (فقط روی سرور).
|
||
# TADRISINO_SERVICE=
|
||
|
||
# -----------------------------------------------------------------------
|
||
# آدرسِ سرویس — این چهار تا پیشفرضِ درست دارند
|
||
# -----------------------------------------------------------------------
|
||
# تا پیش از این هر چهارتا باید دستی روی سرور اضافه میشدند، و چون در همین
|
||
# فایل نبودند هر بار از نو کشف میشدند: نبودِ اولی یعنی «Disallowed Host»،
|
||
# دومی یعنی ردِ ورود به پنلِ ادمین، سومی یعنی بلاک شدنِ فرانت در مرورگر، و
|
||
# چهارمی یعنی شکستنِ بیسروصدای مسیرِ پرداخت.
|
||
#
|
||
# حالا هر چهارتا مقدارِ پیشفرضِ دامنهی خودمان را دارند و لازم نیست ست شوند.
|
||
# فقط اگر دامنه عوض شد، اینها را باز کنید:
|
||
#
|
||
# DJANGO_ALLOWED_HOSTS=api.math.tadrisino2.ir,math.tadrisino2.ir,localhost,127.0.0.1
|
||
# CSRF_TRUSTED_ORIGINS=https://api.math.tadrisino2.ir
|
||
# CORS_ALLOWED_ORIGINS=https://math.tadrisino2.ir
|
||
# PUBLIC_BASE_URL=https://api.math.tadrisino2.ir
|
||
|
||
# اختیاری — بدونش OTP واقعی ارسال نمیشه (فقط خطای صریح ۵۰۳ برمیگرده،
|
||
# طبق رفتارِ موجودِ accounts/services.py)
|
||
KAVENEGAR_API_KEY=
|
||
|
||
# شمارههای تستِ تیم — «شماره:کد» با کامای جداکننده.
|
||
#
|
||
# بدونِ این، روی سروری که DEBUG=0 است و کاوهنگار ندارد، هیچ توسعهدهندهای
|
||
# نمیتواند وارد API شود مگر اینکه کسی دستی از پنلِ ادمین یک OTPCode بسازد.
|
||
# با این، هر عضوِ تیم شماره و کدِ خودش را دارد و به کسی وابسته نیست.
|
||
#
|
||
# برای این شمارهها پیامکی ارسال نمیشود و کدشان هیچوقت در پاسخِ API
|
||
# برنمیگردد. هر شمارهی دیگری مسیرِ کاملاً عادیِ OTP را میرود.
|
||
#
|
||
# کد را تصادفی بگذارید (نه 1234)، شماره را دقیقاً همانطور که کلاینت
|
||
# میفرستد بنویسید، و پیش از باز کردنِ سرویس روی کاربرانِ واقعی خالیاش کنید —
|
||
# /api/health تا وقتی پر باشد هشدار میدهد.
|
||
#
|
||
# TEST_LOGIN_PHONES=09120000001:834512,09120000002:110947
|
||
TEST_LOGIN_PHONES=
|
||
|
||
# رمزِ ادمینِ Grafana (کاربرِ admin) — فقط برای دسترسیِ محلی
|
||
GRAFANA_ADMIN_PASSWORD=admin
|
||
|
||
# -----------------------------------------------------------------------
|
||
# RAG / AI tutor (اپ rag) — موقعِ تست پر کنید
|
||
# -----------------------------------------------------------------------
|
||
# بدونِ OPENAI_API_KEY و QDRANT_API_KEY، مسیرِ RAG غیرفعاله و
|
||
# POST /api/ai/ask همون پاسخِ stubِ قبلی رو میده (سازگاریِ عقبرو).
|
||
# فعلاً از GapGPT (پروکسیِ OpenAI برای ایران) استفاده میکنیم.
|
||
OPENAI_API_KEY=
|
||
OPENAI_BASE_URL=https://api.gapgpt.app/v1
|
||
# اگه بعداً خواستی مستقیم OpenAI واقعی رو تست کنی: کلیدِ واقعی رو این پایین
|
||
# پارک کن، بعد فقط دو خطِ بالا رو باهاش عوض کن. هیچ منطقِ سوییچی در کد نیست
|
||
# تا ساده و بیاشتباه بمونه.
|
||
# OPENAI_BASE_URL=https://api.openai.com/v1
|
||
OPENAI_API_KEY_OPENAI_DIRECT=
|
||
OPENAI_CHAT_MODEL=gpt-4.1-mini
|
||
OPENAI_VISION_MODEL=gpt-4.1-mini
|
||
# این رو تغییر نده؛ باید با مدلی که تیمِ دیگه باهاش collectionها رو ساخته یکی باشه.
|
||
OPENAI_EMBEDDING_MODEL=text-embedding-3-small
|
||
|
||
QDRANT_URL=https://qdrant.tadrisino2.ir:443
|
||
QDRANT_API_KEY=
|
||
|
||
# نامِ collection = f"{subject} {grade}".lower() . collectionهای موجود روی
|
||
# سرورِ تیمِ دیگه (بررسیشده): «علوم تجربی سوم» ، «مطالعات اجتماعی چهارم» ،
|
||
# «مطالعات اجتماعی ششم» ، «ریاضی سوم» ، «ریاضی ششم» ، «ریاضی هفتم».
|
||
# یعنی جزءِ گرید فقط «سوم/چهارم/ششم/هفتم» است (بدونِ «دبستان»).
|
||
#
|
||
# کلیدِ فیلترِ درس دستی تنظیم نمیشه — rag/services/known_collections.py خودش
|
||
# تشخیص میده: ریاضی → `chapter` (و شمارهی فصل رو به رشتهی دقیق تبدیل میکنه)،
|
||
# بقیه → `lesson_number`.
|
||
RAG_COLLECTION_NAME_TEMPLATE={subject} {grade}
|
||
RAG_RETRIEVAL_TOP_K=4
|
||
# RAG_FORCE_MOCK=1 یعنی حتی با وجودِ کلیدها هم مسیرِ RAG خاموش بمونه.
|
||
RAG_FORCE_MOCK=
|
||
|
||
# -----------------------------------------------------------------------
|
||
# زرینپال (اپ payment) — درگاهِ اصلیِ پرداختِ ما
|
||
# -----------------------------------------------------------------------
|
||
# بدونِ ZARINPAL_MERCHANT_ID، مسیرِ پرداخت fail-closed میمونه (خطای صریحِ
|
||
# ۵۰۳ بهجایِ پرداختِ ساختگیِ موفق).
|
||
# PUBLIC_BASE_URL بالاتر توضیح داده شده؛ پیشفرض دارد.
|
||
ZARINPAL_MERCHANT_ID=
|
||
# 1 = محیطِ sandbox زرینپال (برای تست بدونِ پرداختِ واقعی)
|
||
ZARINPAL_SANDBOX=1
|
||
# اختیاری — بعدِ تاییدِ پرداخت، کاربر به این URL (مثلاً یک deep link اپ) با
|
||
# ?orderId=...&status=... ریدایرکت میشه. خالی = پاسخِ JSON ساده.
|
||
ZARINPAL_REDIRECT_URL=
|
||
|
||
# -----------------------------------------------------------------------
|
||
# تنظیماتِ مقیاسپذیری (همه دیفالتِ امن دارن)
|
||
# -----------------------------------------------------------------------
|
||
# persistent DB connection (ثانیه). با SQLite بیاثر؛ بعد از Postgres مهم.
|
||
DB_CONN_MAX_AGE=60
|
||
# کشِ کوتاهِ ردیفِ کاربر در auth تا هر request یک SELECT نزنه. 0 = خاموش
|
||
# (پیشفرض). توصیهی پروداکشن با Redis: 30. با هر save/delete روی User باطل میشه.
|
||
AUTH_USER_CACHE_TTL=0
|
||
# سقفِ ارزانِ خواندن روی endpointهای پرترافیکِ GET (home/leaderboard/summary/...).
|
||
READ_THROTTLE_RATE=120/min
|
||
|
||
# ⚠️ تعدادِ پروکسیهایی که جلوی اپ نشستهاند (nginx = ۱). محدودیتِ نرخِ
|
||
# کاربرِ مهمان بر اساسِ IP است و IP از X-Forwarded-For خوانده میشود؛ اگر این
|
||
# عدد از تعدادِ واقعیِ پروکسیها بیشتر باشد، کلاینت میتواند هدر را جعل کند و
|
||
# محدودیت را دور بزند. کمتر باشد، همه پشتِ IP پروکسی یکی شمرده میشوند.
|
||
NUM_PROXIES=1
|
||
|
||
# -----------------------------------------------------------------------
|
||
# بقیه — همه پیشفرضِ کارا دارند و معمولاً لازم نیست دست بخورند
|
||
# -----------------------------------------------------------------------
|
||
# پیامک: بدونِ SENDER هم کاوهنگار با خطِ پیشفرضِ حساب میفرستد، ولی اگر خطِ
|
||
# اختصاصی دارید اینجا بگذارید. قالبِ متنِ پیامک هم قابلِ تغییر است ({code} لازم).
|
||
KAVENEGAR_SENDER=
|
||
# OTP_SMS_TEMPLATE=کد ورود شما به تدریسینو: {code}
|
||
|
||
# کلیدِ endpointهای داخلی (ingest در اپ rag، ساختِ سفارشِ سرور-به-سرور). با
|
||
# DEBUG=1 مقدارِ توسعه دارد؛ با DEBUG=0 خالی است و آن مسیرها بسته میمانند
|
||
# تا وقتی مقدارِ واقعی بگذارید.
|
||
INTERNAL_SERVICE_KEY=
|
||
|
||
# با صفر قلب جلوی جواب دادن گرفته شود؟ (۱ = بله). فقط وقتی فرانت برای ۴۰۳ِ
|
||
# no_hearts صفحهی «قلبها تمام شد» دارد.
|
||
# HEARTS_ENFORCED=0
|
||
|
||
# پرداختِ درونبرنامهایِ کافهبازار. بدونِ این چهار تا، مسیرِ بازار
|
||
# fail-closed میماند (payment/bazaar.py). REFRESH_TOKEN یکبار و دستی از
|
||
# پنلِ devapi گرفته میشود.
|
||
BAZAAR_CLIENT_ID=
|
||
BAZAAR_CLIENT_SECRET=
|
||
BAZAAR_REFRESH_TOKEN=
|
||
# BAZAAR_PACKAGE_NAME=ir.tadrisino.app
|
||
# BAZAAR_API_BASE=https://pardakht.cafebazaar.ir/devapi/v2
|
||
|
||
# آدرسهای زرینپال از روی ZARINPAL_SANDBOX بالا انتخاب میشوند؛ فقط اگر
|
||
# زرینپال دامنهاش را عوض کرد این سه را دستی بدهید.
|
||
# ZARINPAL_API_BASE=
|
||
# ZARINPAL_STARTPAY_BASE=
|
||
# ZARINPAL_CALLBACK_URL=
|
||
|
||
# نوشتنِ لاگ در فایل علاوه بر stdout (داخلِ Docker ست میشود). خالی = فقط stdout.
|
||
LOG_DIR=
|
||
# سقفِ گیرندهی اکشنِ «ارسالِ پیامک به انتخابشدهها» در پنلِ ادمین. این اکشن
|
||
# داخلِ همان request اجرا میشود، پس سقف جلوی تایماوت را میگیرد.
|
||
ADMIN_SMS_MAX_RECIPIENTS=200
|
||
# سقفِ نرخِ /api/health برای پایشِ بیرونی.
|
||
HEALTH_THROTTLE_RATE=60/min
|
||
# تایماوت و پارامترهای RAG. EMBEDDING_DIM باید با مدلِ embedding بالا بخواند.
|
||
OPENAI_TIMEOUT_SECONDS=60
|
||
QDRANT_TIMEOUT_SECONDS=30
|
||
OPENAI_EMBEDDING_DIM=1536
|
||
# کشِ پاسخِ دستیار (ثانیه) — سوالِ تکراری دوباره به مدل نمیرود.
|
||
RAG_ANSWER_CACHE_TTL=1800
|