Files
MathApp/rag/services/known_collections.py
Zareian b115ccd37a stop losing the pages that matter most to a stray backslash
مدلِ vision در متنِ ریاضی LaTeX می‌نویسد (\frac، \sqrt، \vec) و بک‌اسلشِ تنها
در JSON نامعتبر است، پس json.loads کلِ صفحه را دور می‌ریخت — دقیقاً صفحه‌های
پرفرمول، یعنی باارزش‌ترین‌ها. روی حسابان یازدهم/دوازدهم چهار صفحه هرچه تلاش
شد «خطادار» ماندند تا همین‌جا پیدا شد.

⚠️ نسخه‌ی اولِ تعمیر خودش تله داشت: \f، \t، \b، \r و \n از نظرِ JSON معتبرند،
پس «سالم» رد می‌شدند و \frac به formfeed+«rac» و \theta به tab+«heta» تبدیل
می‌شد — خرابیِ بی‌صدا به‌جای خطای بلند. قاعده‌ی تفکیک: دستورِ LaTeX همیشه با
حرفِ ASCII ادامه می‌یابد، ولی escapeِ واقعی با رقم، فارسی یا پایانِ رشته.

کنارش: پایه‌های یازدهم و دوازدهم به persona و تشخیصِ پایه اضافه شدند («دوازدهم»
پیش از «دهم» بررسی می‌شود چون زیررشته‌اش را دارد)، سقفِ max_tokens از ۳۰۰۰ به
۶۰۰۰ رفت چون قاعده‌ی self-contained خروجی را بلندتر کرده، و حسابان یازدهم و
دوازدهم با نگاشتِ فصل‌به‌صفحه ثبت شدند.

«حسابان» به MATH_SUBJECTS اضافه شد: بدونِ آن retrieval این کتاب‌ها را «درسِ
متنی» می‌دید و به‌جای chapter روی lesson_number فیلتر می‌کرد — بی‌هیچ خطایی
همیشه صفر نتیجه.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 12:35:46 +03:30

118 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
کاتالوگِ دقیقِ collectionهای موجود روی Qdrant تیمِ دیگر
(qdrant.tadrisino2.ir) — استخراج‌شده و بررسی‌شده در ۱۴۰۵/۰۶/۰۶ (2026-08-28).
هدف: کاربر لازم نباشه رشته‌های دقیقِ گرید/فصل رو حفظ کنه. با داشتنِ subject و
یک شماره‌ی ساده (شماره‌ی درس یا شماره‌ی فصل)، retrieval خودش:
- کلیدِ فیلترِ درست رو انتخاب می‌کنه (ریاضی → chapter ، بقیه → lesson_number)
- برای ریاضی، شماره‌ی فصل رو به رشته‌ی دقیقِ فصل تبدیل می‌کنه.
اگه تیمِ دیگه collection جدید اضافه/تغییر داد، فقط همین فایل به‌روز بشه.
"""
from .textbooks import TEXTBOOKS, chapter_titles
# --- درس‌های متنی: payload کلیدِ `lesson_number` (رشته) + `summary` دارد ---
# نامِ collection = f"{subject} {grade}".lower()
TEXT_SUBJECT_LESSON_FILTER_KEY = 'lesson_number'
# --- ریاضی: payload کلیدِ `chapter` (رشته) + `generalSolution` دارد، `summary` ندارد ---
MATH_LESSON_FILTER_KEY = 'chapter'
# subjectهایی که ساختارِ «ریاضی» دارند (فیلتر با chapter، متن از generalSolution)
# «حسابان» کتابِ رشته‌ی ریاضی‌فیزیک در پایه‌های یازدهم/دوازدهم است و دقیقاً
# همان شکلِ payload را دارد. اگر اینجا نباشد، retrieval آن را درسِ متنی حساب
# می‌کند و به‌جای `chapter` روی `lesson_number` فیلتر می‌زند — بدونِ هیچ خطایی
# همیشه صفر نتیجه.
MATH_SUBJECTS = {'ریاضی', 'حسابان'}
# فصول ریاضی به تفکیکِ (subject, grade) — کلیدِ داخلی هم شماره‌ی فصل و هم
# رشته‌ی کامل رو می‌پذیره.
#
# پایه‌های سوم/ششم/هفتم را تیمِ دیگر آپلود کرده بود و این جدول از روی دادهٔ
# زنده‌ی Qdrant درآمده. پایه‌های نهم/دهم را خودمان ingest می‌کنیم، پس عنوانِ
# فصلشان از rag/services/textbooks.py می‌آید تا رشته‌ای که ingest می‌نویسد و
# رشته‌ای که retrieval رویش فیلتر می‌کند نتوانند از هم جدا بیفتند.
MATH_CHAPTERS = {
('ریاضی', 'سوم'): {
1: 'فصل ۱: الگوها',
2: 'فصل ۲: عددهای چهار رقمی',
3: 'فصل ۳: عددهای کسری',
4: 'فصل ۴: ضرب و تقسیم',
5: 'فصل ۵: محیط و مساحت',
6: 'فصل ۶: جمع و تفریق',
7: 'فصل ۷: آمار و احتمال',
8: 'فصل ۸: ضرب عددها',
},
('ریاضی', 'ششم'): {
1: 'فصل ۱: عدد و الگوهای عددی',
2: 'فصل ۲: کسر',
3: 'فصل ۳: اعداد اعشاری',
4: 'فصل ۴: تقارن و مختصات',
5: 'فصل ۵: اندازه گیری',
6: 'فصل ۶: تناسب و درصد',
7: 'فصل ۷: تقریب',
},
('ریاضی', 'هفتم'): {
1: 'فصل ۱: راهبردهای حل مسئله',
2: 'فصل ۲: عددهای صحیح',
3: 'فصل ۳: جبر و معادله',
4: 'فصل ۴: هندسه و استدلال',
5: 'فصل ۵: شمارنده‌ها و اعداد اول',
6: 'فصل ۶: سطح و حجم',
7: 'فصل ۷: توان و جذر',
8: 'فصل ۸: بردار و مختصات',
9: 'فصل ۹: آمار و احتمال',
},
}
MATH_CHAPTERS.update({
(b.subject, b.grade): chapter_titles(b) for b in TEXTBOOKS.values()
})
# درس‌های علوم/اجتماعی — فقط برای مرجع/اعتبارسنجی (lesson_number → عنوان).
TEXT_LESSONS = {
('علوم تجربی', 'سوم'): {
1: 'زنگ علوم', 2: 'خوراکی‌ها', 3: 'اندازه‌گیری مواد', 4: 'مواد اطراف ما',
5: 'آب ماده‌ی باارزش', 6: 'زندگی ما و آب', 7: 'نور و مشاهده‌ی اجسام',
8: 'جستجو کنیم و بسازیم', 9: 'نیرو همه‌جا ۱', 10: 'نیرو همه‌جا ۲',
11: 'بکارید و ببینید', 12: 'هرکدام جای خود ۱', 13: 'هرکدام جای خود ۲',
14: 'از گذشته تا آینده(نگهداری مواد غذایی)',
},
('مطالعات اجتماعی', 'ششم'): {1: 'lesson_1', 2: 'lesson_2', 3: 'lesson_3', 9: 'lesson_9'},
('مطالعات اجتماعی', 'چهارم'): {1: 'lesson_1', 2: 'lesson_2', 3: 'lesson_3'},
}
# فهرستِ کاملِ collectionهای موجود (نامِ نهایی، lowercase).
KNOWN_COLLECTIONS = {
'ریاضی سوم', 'ریاضی ششم', 'ریاضی هفتم',
'علوم تجربی سوم', 'مطالعات اجتماعی ششم', 'مطالعات اجتماعی چهارم',
} | {b.key for b in TEXTBOOKS.values()}
def is_math_subject(subject: str) -> bool:
return (subject or '').strip() in MATH_SUBJECTS
def filter_key_for_subject(subject: str) -> str:
return MATH_LESSON_FILTER_KEY if is_math_subject(subject) else TEXT_SUBJECT_LESSON_FILTER_KEY
def resolve_math_chapter(subject: str, grade: str, lesson_or_chapter) -> str | None:
"""
برای ریاضی: ورودی می‌تونه شماره‌ی فصل (2 / '2' / '۲') یا خودِ رشته‌ی
کامل باشه. خروجی رشته‌ی دقیقِ `chapter` در Qdrant یا None.
"""
if lesson_or_chapter is None:
return None
table = MATH_CHAPTERS.get((( subject or '').strip(), (grade or '').strip()))
text = str(lesson_or_chapter).strip()
if text.startswith('فصل'):
return text
# نرمال‌سازی ارقامِ فارسی → لاتین
trans = str.maketrans('۰۱۲۳۴۵۶۷۸۹', '0123456789')
digits = text.translate(trans)
if table and digits.isdigit() and int(digits) in table:
return table[int(digits)]
return text or None