ingest whole textbooks chapter by chapter, resumably
نقشهی فیزیکیِ کتابها (افستِ صفحه، بازهی هر فصل، درسهای داخلِ فصل از فهرستِ خودِ کتاب) در rag/services/textbooks.py ثبت شد تا عنوانِ فصل از کتاب بیاید نه از حدسِ مدل — رشتهی فصل باید دقیقاً بینِ ingest و retrieval یکی باشد وگرنه نتیجه بیسروصدا خالی برمیگردد. rag_ingest_book کتاب را فصلبهفصل ingest میکند و قابلِ ازسرگیری است: rag.IngestedPage نشانهی «این صفحه پردازش شد» را نگه میدارد حتی وقتی صفحه chunkی نساخته، وگرنه هر بار اجرای دوباره بابتِ صفحههای بیمسئله دوباره پول میگرفت. --plan هزینه را رایگان برآورد میکند. rag_import_collection نقاطِ موجودِ Qdrant را در LessonChunk میریزد — فقط میخواند، رایگان و idempotent، برای پایههایی که تیمِ دیگر ingest کرده بود. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,13 +18,20 @@ from . import clients
|
||||
from .clients import RagNotConfigured
|
||||
|
||||
|
||||
def extract_page_images(pdf_path: str, first_page: int = 1, dpi: int = 200):
|
||||
"""لیستی از تصاویرِ PIL برای صفحاتِ PDF از first_page به بعد."""
|
||||
def extract_page_images(pdf_path: str, first_page: int = 1, dpi: int = 200,
|
||||
last_page: int | None = None):
|
||||
"""
|
||||
لیستی از تصاویرِ PIL برای صفحاتِ PDF در بازهی [first_page, last_page].
|
||||
|
||||
`last_page` برای کتابِ کامل ضروری است: بدونِ آن pdf2image کلِ کتاب را
|
||||
یکجا به تصویر تبدیل میکند (۱۵۰+ صفحه در حافظه) و امکانِ ingest فصلبهفصل
|
||||
هم از بین میرود.
|
||||
"""
|
||||
try:
|
||||
from pdf2image import convert_from_path
|
||||
except ModuleNotFoundError as exc:
|
||||
raise RagNotConfigured('pdf2image نصب نیست یا poppler موجود نیست.') from exc
|
||||
return convert_from_path(pdf_path, dpi=dpi, first_page=first_page)
|
||||
return convert_from_path(pdf_path, dpi=dpi, first_page=first_page, last_page=last_page)
|
||||
|
||||
|
||||
def detect_grade_and_start_page(pdf_path: str) -> tuple[int, str | None]:
|
||||
@@ -37,9 +44,13 @@ def detect_grade_and_start_page(pdf_path: str) -> tuple[int, str | None]:
|
||||
except ModuleNotFoundError as exc:
|
||||
raise RagNotConfigured('pymupdf (fitz) نصب نیست.') from exc
|
||||
|
||||
start_pages = {'پنجم دبستان': 11, 'ششم دبستان': 9}
|
||||
grades = ['هفتم', 'اول دبستان', 'دوم دبستان', 'سوم دبستان',
|
||||
'چهارم دبستان', 'پنجم دبستان', 'ششم دبستان']
|
||||
# صفحهی شروعِ محتوای واقعی؛ هرچه اینجا نباشد پیشفرضِ ۷ میگیرد.
|
||||
start_pages = {'پنجم دبستان': 11, 'ششم دبستان': 9, 'نهم': 9, 'دهم': 7}
|
||||
# ترتیب مهم است: «دهم» زیررشتهی «نهم» نیست ولی «هفتم»/«هشتم» در متنِ
|
||||
# شناسنامهی کتابِ نهم هم میآیند («در ادامهٔ پایههای هفتم و هشتم»)، پس
|
||||
# پایههای بالاتر باید زودتر بررسی شوند.
|
||||
grades = ['دهم', 'نهم', 'هشتم', 'هفتم', 'اول دبستان', 'دوم دبستان',
|
||||
'سوم دبستان', 'چهارم دبستان', 'پنجم دبستان', 'ششم دبستان']
|
||||
|
||||
doc = fitz.open(pdf_path)
|
||||
try:
|
||||
@@ -63,13 +74,38 @@ _VISION_JSON_TEMPLATE = """
|
||||
"""
|
||||
|
||||
|
||||
# پایه → توصیفِ معلمِ متناسب. پرامپت قبلاً «معلمِ ابتدایی، بچههای ۸ تا ۱۲
|
||||
# ساله» را هاردکد کرده بود؛ برای پایههای نهم/دهم این هم لحنِ توضیح را بچگانه
|
||||
# میکرد و هم مدل را به سادهسازیِ بیش از حدِ مفاهیم (اتحاد، مثلثات، تابع)
|
||||
# سوق میداد.
|
||||
_TEACHER_BY_GRADE = {
|
||||
'اول دبستان': 'a specialist elementary math teacher for children aged 6 to 7',
|
||||
'دوم دبستان': 'a specialist elementary math teacher for children aged 7 to 8',
|
||||
'سوم دبستان': 'a specialist elementary math teacher for children aged 8 to 9',
|
||||
'چهارم دبستان': 'a specialist elementary math teacher for children aged 9 to 10',
|
||||
'پنجم دبستان': 'a specialist elementary math teacher for children aged 10 to 11',
|
||||
'ششم دبستان': 'a specialist elementary math teacher for children aged 11 to 12',
|
||||
'هفتم': 'a specialist middle-school math teacher for students aged 12 to 13',
|
||||
'هشتم': 'a specialist middle-school math teacher for students aged 13 to 14',
|
||||
'نهم': 'a specialist middle-school math teacher for students aged 14 to 15',
|
||||
'دهم': 'a specialist high-school math teacher for students aged 15 to 16',
|
||||
}
|
||||
|
||||
|
||||
def teacher_persona(grade: str) -> str:
|
||||
return _TEACHER_BY_GRADE.get(
|
||||
(grade or '').strip(),
|
||||
'a specialist school math teacher',
|
||||
)
|
||||
|
||||
|
||||
def extract_problem_types(image, grade: str, chapter_name: str) -> dict:
|
||||
"""
|
||||
یک عکسِ صفحهی کتاب → ساختارِ JSONِ انواعِ مسئله (فارسی)، با مدلِ vision.
|
||||
خروجی همون شکلیست که uploadِ ریاضیِ تیمِ دیگه هم تولید میکرد.
|
||||
"""
|
||||
prompt = f"""
|
||||
You are a specialist elementary math teacher for children aged 8 to 12.
|
||||
You are {teacher_persona(grade)}.
|
||||
Analyze the uploaded image and extract every unique math problem type for the given chapter.
|
||||
|
||||
For each type produce: type, keywords (3-7, Persian), generalSolution (step-by-step),
|
||||
@@ -83,6 +119,10 @@ Rules:
|
||||
- grade = "{grade}"
|
||||
- chapter = "{chapter_name}"
|
||||
- exampleFromImage must be a real problem from the image (not invented).
|
||||
- Keep the mathematical notation of the textbook; do not simplify the concept
|
||||
to a lower grade level than "{grade}".
|
||||
- If the page has no math problem at all (cover, foreword, table of contents,
|
||||
a page of pure text), return an empty "problemTypes" list instead of inventing one.
|
||||
- Output ONLY the JSON object. No markdown, no backticks.
|
||||
"""
|
||||
|
||||
@@ -110,7 +150,17 @@ Rules:
|
||||
raw = raw[3:].strip()
|
||||
if raw.endswith('```'):
|
||||
raw = raw[:-3].strip()
|
||||
return json.loads(raw)
|
||||
|
||||
parsed = json.loads(raw)
|
||||
# مصرفِ واقعیِ همین فراخوانی را همراهِ نتیجه برمیگردانیم تا ingest بتواند
|
||||
# هزینهی واقعی را گزارش کند. تا پیش از این فقط تخمینِ سرانگشتی داشتیم و
|
||||
# آن تخمین ۲.۳ برابرِ واقعیت بود.
|
||||
usage = getattr(response, 'usage', None)
|
||||
parsed['_usage'] = {
|
||||
'promptTokens': getattr(usage, 'prompt_tokens', 0) or 0,
|
||||
'completionTokens': getattr(usage, 'completion_tokens', 0) or 0,
|
||||
}
|
||||
return parsed
|
||||
|
||||
|
||||
def problem_type_to_text(problem_type: dict) -> str:
|
||||
|
||||
Reference in New Issue
Block a user