let a book be re-read without duplicating everything in it

ingestِ دوباره جایگزین نمی‌کرد: qdrant_point_id یک uuid4 تازه به ازای هر ردیف
است، پس خواندنِ دوباره‌ی یک صفحه نسخه‌ی تکراری کنارِ نسخه‌ی قدیمی می‌گذاشت و
جستجو نمی‌توانست تشخیص بدهد کدام کدام است.

--refresh {figures,unjudgeable,both,all} و --refresh-pages اضافه شدند.

سه تصمیمِ طراحی که عمدی‌اند:

۱. پاک‌سازی صفحه‌به‌صفحه است و درست پیش از خواندنِ همان صفحه، نه یک‌جا از اول.
   وسطِ تست یک crash همین را ثابت کرد: فقط یک صفحه خالی ماند (۲۷۹→۲۷۶) و بقیه‌ی
   کتاب دست‌نخورده. پاک‌سازیِ یک‌جا همان crash را به ۸۱ صفحه‌ی خالی تبدیل می‌کرد.

۲. اول Qdrant، بعد دیتابیس. شناسه‌ی نقطه فقط در ردیفِ DB است، پس ترتیبِ برعکس
   یعنی شکستِ Qdrant نقاط را برای همیشه بی‌صاحب می‌گذارد — نه قابلِ یافتن، نه
   قابلِ پاک کردن.

۳. order از Max گرفته می‌شود نه count: بازخوانی ردیف‌ها را از وسط پاک می‌کند و
   count عددی می‌داد که با orderهای موجود تصادم داشت.

تخمینِ هزینه حالا از اندازه‌گیریِ واقعیِ پنلِ صورتحساب می‌آید نه از جدولِ قیمت،
و وعده‌ی «با Batch API نصف» حذف شد چون GapGPT اصلاً /v1/batches ندارد.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-20 12:36:16 +03:30
parent b115ccd37a
commit fa347915c4
3 changed files with 277 additions and 23 deletions

View File

@@ -16,11 +16,15 @@
| پایه | وضعیت |
|---|---|
| ششم، هفتم | از قبل روی Qdrant موجود است (ساخته‌ی تیم دیگر) |
| نهم، دهم | PDF موجود است؛ ingest با `rag_ingest_book` |
| هشتم | PDF موجود است، هنوز در `textbooks.py` ثبت نشده |
| سوم، ششم، هفتم | روی Qdrant هست (ساخته‌ی تیم دیگر) — PDF نداریم جز هفتم |
| هشتم، نهم، دهم | ingest شده؛ بازخوانیِ صفحه‌های شکل‌دار مانده |
| یازدهم، دوازدهم | در نوبتِ ingest (۱۴۰۵/۰۶/۲۸) |
| اول، دوم، چهارم، پنجم | PDF موجود است، خارج از اولویتِ فعلی |
⚠️ کتابِ ریاضیِ یازدهم و دوازدهم چند رشته دارد (تجربی، ریاضی‌فیزیک، انسانی).
نامِ فایل و `Textbook.grade` باید رشته را مشخص کند، وگرنه دو کتابِ متفاوت
سرِ یک collection دعوا می‌کنند.
## نام‌گذاری
نامِ فایل باید دقیقاً با `filename` در `rag/services/textbooks.py` یکی باشد،
@@ -42,6 +46,35 @@ python manage.py rag_ingest_book --grade نهم
هر اجرا صفحه‌هایی را که قبلاً chunk ساخته‌اند رد می‌کند، پس قطع کردنش با
Ctrl-C بی‌خطر است و اجرای دوباره هزینه‌ی رفته را دوباره نمی‌پردازد.
## بازخوانیِ صفحه‌های معیوب
کتاب‌هایی که پیش از افزوده شدنِ قاعده‌ی «self-contained» به پرامپت ingest
شده‌اند، مسئله‌هایی دارند که به «شکلِ زیر» یا «جدولِ روبه‌رو» ارجاع می‌دهند
بی‌آنکه محتوای آن شکل در متن آمده باشد. مدل آن شکل را **دیده بود**؛ فقط از او
خواسته نشده بود بنویسدش. پس با خواندنِ دوباره‌ی همان صفحه‌ها جبران می‌شود.
```bash
# اول ببینید چند صفحه و چه هزینه‌ای — رایگان، هیچ فراخوانیِ مدلی ندارد
python manage.py rag_ingest_book --grade نهم --refresh both --plan
# فقط صفحه‌های ارجاع‌دهنده به شکل/جدول
python manage.py rag_ingest_book --grade نهم --refresh figures
# صفحه‌های مشخص (وقتی خودتان فهرست دارید)
python manage.py rag_ingest_book --grade نهم --refresh-pages 18,44,60-65
```
سه نکته که باید بدانید:
- **بازخوانی پاک می‌کند.** واحدِ کار «صفحه» است نه chunk، چون vision یک صفحه را
یک‌جا می‌خواند. پس chunkهای سالمِ همان صفحه هم دور ریخته و از نو ساخته
می‌شوند و `solution_verdict`شان پاک می‌شود. فرمان پیش از خرج کردن نشان
می‌دهد چند تای آن‌ها رأیِ «agrees» داشته‌اند و تأیید می‌گیرد (`--yes` رد می‌کند).
- **بعدش `rag_verify_solutions` را دوباره اجرا کنید،** وگرنه chunkهای تازه
بی‌رأی می‌مانند و تولیدِ سؤال — که فقط `agrees` می‌خواند — نادیده‌شان می‌گیرد.
- **پاک‌سازی صفحه‌به‌صفحه است،** درست پیش از خواندنِ همان صفحه. پس قطعِ وسطِ کار
(Ctrl-C، تمام شدنِ اعتبار) فقط یک صفحه را خالی می‌گذارد، نه کلِ کتاب را.
## افزودنِ کتابِ جدید
در `rag/services/textbooks.py` یک `Textbook` بسازید با: