مدلِ 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>
118 lines
6.1 KiB
Python
118 lines
6.1 KiB
Python
"""
|
||
کاتالوگِ دقیقِ 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
|