# ساختن و ویرایشِ محتوا هدفِ این مسیر یک جمله است: **محتوا باید بدونِ توسعه‌دهنده قابلِ ویرایش باشد.** اجرای یک management command روی SSH آخرین راهِ چاره است، نه روالِ کار. ## محتوا کجا زندگی می‌کند `content/seed/` کلِ برنامه‌ی درسی و بانکِ سوال را به‌صورتِ JSONِ کامیت‌شده نگه می‌دارد: | فایل | محتوا | |---|---| | `curriculum.json` | ۸ پایه، ۵۸ فصل، ۲۶۳ نود، ۲٬۶۱۷ درس | | `questions.json` | ۲٬۴۶۳ سوالِ چندگزینه‌ای | `deploy.sh` در هر انتشار `seed_content` را اجرا می‌کند، پس یک سرورِ تازه خودش را با یک فرمان پر می‌کند — **بدونِ Qdrant، بدونِ اعتبارِ API، بدونِ نشستِ SSH.** > **کد و محتوا دو مسیرِ جدا دارند.** `db.sqlite3` در gitignore است. سوالی که > فقط در دیتابیسِ محلیِ شماست به سرور نمی‌رسد مگر از راهِ > `seed_content --export --only questions` و کامیتِ `content/seed/questions.json`. > پوش کردنِ کد به‌تنهایی محتوا را نمی‌برد. دو رفتارِ `seed_content` که دانستنشان لازم است: - **idempotent است و هرگز چیزی پاک نمی‌کند.** هر چه در فایل هست ساخته یا به‌روز می‌شود؛ هر چه نیست دست‌نخورده می‌ماند. پس یک فایلِ ناقص محتوای سرور را از بین نمی‌برد. - **سوال‌ها با مسیرِ عنوان به درس وصل می‌شوند** (پایه › فصل › نود › درس)، نه با کلیدِ اصلی. شناسه‌ها بینِ هر دو دیتابیس فرق دارند و اتصال با id، سوال‌ها را بی‌صدا به درسِ اشتباه می‌چسباند. `seed_content --export` فایل‌ها را از دیتابیسِ محلی بازنویسی می‌کند. اگر دیتابیس سوالی نداشته باشد از بازنویسیِ یک `questions.json` غیرخالی خودداری می‌کند (این قبلاً فایل را بی‌صدا پاک می‌کرد)؛ وقتی فقط برنامه‌ی درسی را می‌خواهید `--export --only curriculum` بزنید. ## از پنلِ ادمین (روالِ عادی) | کار | کجا | |---|---| | افزودن یا ویرایشِ **یک** سوال | `/admin/content/question/` → «افزودن» (انتخابِ درس با جستجو) | | وارد کردنِ **فله‌ای** سوال | همان صفحه → «وارد کردنِ سوال‌ها از فایل» | | گرفتنِ خروجیِ سوال‌ها | چند سوال را انتخاب کنید → اکشنِ «دریافتِ فایل» | | وارد کردنِ **برنامه‌ی درسی** | `/admin/content/grade/` → «وارد کردنِ برنامه‌ی درسی از فایل» | | گرفتنِ خروجیِ برنامه‌ی درسی | پایه‌ها را انتخاب کنید → اکشنِ «دریافتِ فایلِ برنامه‌ی درسی» | | افزودنِ یک **پایه‌ی تازه** | `/admin/content/grade/add/` — عنوان به شکلِ «پایه چهارم»؛ شماره و id خودکار ۴ می‌شوند | خروجیِ هر کدام **دقیقاً** همان قالبی‌ست که ورودی‌اش می‌پذیرد، پس چرخه‌ی «بگیر → ویرایش کن → برگردان» بسته است و برای دیدنِ یک نمونه لازم نیست کسی مستندات بخواند. دو تضمین: - **سوال‌ها:** فایلِ نامعتبر هیچ چیزی نمی‌نویسد — یا همه وارد می‌شوند یا هیچ‌کدام. - **برنامه‌ی درسی:** چیزی پاک نمی‌شود، به همان دلیلِ بالا. ## ساختِ انبوهِ سوال از یک کتابِ تازه وقتی کتابی تازه روی Qdrant آمده، مسیرِ رایگان این است: ```bash # اسکلتِ برنامه‌ی درسی از فهرستِ خودِ کتاب (بدونِ هزینه) python manage.py seed_curriculum_from_textbook --subject ریاضی --grade نهم --dry-run python manage.py seed_curriculum_from_textbook --subject ریاضی --grade نهم # جفت‌های (درس، مسئله‌ی تأییدشده) را برای سوال‌نویسی بیرون بده (بدونِ هزینه) python manage.py generate_questions_from_chunks --subject ریاضی --grade نهم \ --export /tmp/batches ``` `--dry-run` روی هر دو کار می‌کند و پیش از هر نوشتنی نشان می‌دهد چه ساخته خواهد شد. خروجی چند فایلِ JSON است به‌همراهِ یک `*-RULES.txt` که **عینِ همان پرامپتی** را دارد که به مدلِ پولی می‌رفت — تا کیفیتِ دو مسیر از هم جدا نیفتد. سوال‌ها را با آن قواعد بنویسید و از «وارد کردنِ فله‌ای سوال» در جدولِ بالا واردشان کنید. همان فرمان بدونِ `--export` کار را به مدلِ پولی می‌سپارد. **این کار را نکنید مگر دلیلِ خاصی داشته باشید**: اندازه‌گیری‌شده، گران‌تر است و کیفیتِ بهتری نمی‌دهد. جزئیات و هزینه‌های واقعی در [`extraction-playbook.md`](extraction-playbook.md). ## کیفیتِ محتوا کتاب‌ها با یک مدلِ **ویژن از تصویرِ صفحه** خوانده می‌شوند، پس خطای استخراج از همان‌جا وارد می‌شود: رقمِ بدخوانده‌شده، جداکننده‌ی اعشارِ افتاده، راه‌حلی که به مسئله‌ی بغلی چسبیده. این‌ها در بازیابی دیده نمی‌شوند و فقط وقتی لو می‌روند که چیزی به متن اعتماد کند. `rag_verify_solutions` هر مسئله‌ی استخراج‌شده را مستقلاً دوباره حل می‌کند و نتیجه را مقایسه می‌کند. `LessonChunk.solution_verdict` سه‌حالته است — `agrees` / `disagrees` / `unjudgeable`. **ساختِ سوال فقط از chunkهای `agrees` می‌خواند.** دو درسی که گران تمام شدند و حالا در پرامپت‌ها کدگذاری شده‌اند: - **رأیِ دوحالته غلط است.** با دو حالت، هر «نمی‌توانم قضاوت کنم» تبدیل به «غلط» می‌شود و نرخِ خطا را حدودِ سه برابرِ واقعیت گزارش می‌کند. پیش از اعتماد به هر اجرای انبوه، چند نمونه را با چشم بخوانید. - `۳/۱۰۴` در این کتاب‌ها عددِ اعشاریِ ۳٫۱۰۴ است، نه کسرِ ۳ تقسیم بر ۱۰۴. خودِ `solution` هرگز بازنویسی نمی‌شود — متنِ استخراج‌شده به‌عنوانِ سند می‌ماند و پاسخِ مستقل جداگانه در `checked_answer` ذخیره می‌شود. `solution_verdict='agrees'` تضمینِ کامل نیست، فقط فیلترِ اول: چند بار خطای حسابِ خودِ کتاب در همین مرحله گرفته شده است. ## داده‌ی ساختگی برای توسعه `seed_test_data` یک دیتاستِ کوچک و idempotent می‌سازد که همه‌ی اپ‌ها را پوشش می‌دهد. **هرگز روی سروری که کاربرِ واقعی دارد اجرا نشود** — ردیف‌هایی با پیشوندِ `[SEED]`/`[DEMO]` می‌سازد که در API دیده می‌شوند. محتوای واقعی از مسیرِ بالا می‌رود، نه از این فرمان. ```bash python manage.py seed_test_data # فقط با DEBUG=True اجرا می‌شود python manage.py seed_test_data --force # staging، با DEBUG=False python manage.py seed_test_data --reset # پاک کردنِ داده‌ی seedِ قبلی و ساختِ دوباره ```