Files
MathApp/docs/extraction-playbook.md
Zareian acce78bb03 write down the extraction method so another team can reuse it
docs/extraction-playbook.md: چهار تصمیمی که تفاوتِ چند برابری در هزینه و کیفیت
ساختند، با عددهای اندازه‌گیری‌شده‌ی واقعی نه تخمین.

سرفصل‌ها: انتخابِ مدل از روی اندازه‌گیری (gpt-4.1-mini هم دقیق‌تر از gpt-4o بود
هم ۳٫۵ برابر ارزان‌تر، و gemini برخلافِ جدولِ قیمت سه برابر گران‌تر)، قاعده‌ی
self-contained، تعمیرِ بک‌اسلشِ LaTeX، محافظِ شکل، و ساختِ سوال با subagent
به‌جای API.

وجهِ مشترکِ هر چهار مورد: هیچ‌کدام خطا تولید نمی‌کردند — نه در لاگ، نه در تست.
فقط دادهٔ کمتر یا بدتر می‌ساختند. برای همین توصیه‌ی محوریِ فایل این است که قبل
از اعتماد به هر ران انبوه، نمونه‌ای ۴۰-۵۰تایی با چشم خوانده شود.

تست‌های rag هم اینجا می‌آیند: بازخوانی، تعمیرِ JSON، و محافظِ شکل.

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

7.2 KiB
Raw Blame History

روشِ استخراجِ ارزان و دقیق از کتابِ درسی

این فایل نتیجه‌ی یک روزِ اندازه‌گیریِ واقعی روی پنج کتابِ ریاضی است (۱۴۰۵/۰۶/۲۹). هر عددی که اینجا می‌بینید از پنلِ صورتحساب یا از دادهٔ واقعی درآمده، نه از جدولِ قیمتِ رسمی و نه از تخمین.

برای تیم‌های دیگر: اگر کتابِ درسی را با مدلِ vision استخراج می‌کنید، این چهار تصمیم تفاوتِ چند برابری در هزینه و کیفیت می‌سازند.


۱. مدل را اندازه بگیرید، از جدولِ قیمت نخوانید

قیمتِ GapGPT با قیمتِ رسمیِ OpenAI یکی نیست. اگر از جدولِ رسمی تخمین بزنید، گران‌ترین گزینه را ارزان می‌بینید.

اندازه‌گیریِ واقعی روی سه صفحه‌ی یکسان، با خواندنِ /v1/dashboard/billing/usage قبل و بعد:

مدل سنت/صفحه (واقعی) عددهای درست¹ مسئله‌ی جعلی
gpt-4.1-mini ۰٫۲۲ ۹۸٪ خیر
gpt-4o ۰٫۷۷ ۹۷٪ بله
gemini-3.6-flash ۲٫۱۳ ۱۰۰٪ خیر
gpt-4o-mini — ۰٪ مختصات را از خود ساخت

¹ سنجه‌ی رایگان: هر عددی که مدل در متنِ مسئله می‌نویسد باید در لایه‌ی متنیِ همان صفحه‌ی PDF وجود داشته باشد. عددی که آنجا نیست یا از شکل خوانده شده (خوب) یا جعل شده (بد)، و نرخِ نسبیِ مدل‌ها قابلِ مقایسه است.

نتیجه: gpt-4.1-mini هم دقیق‌تر از gpt-4o بود هم ۳٫۵ برابر ارزان‌تر. gemini دقیق‌ترین بود ولی سه برابرِ gpt-4o گران. gpt-4o-mini صریحاً مختصات را از خود درمی‌آورد («با فرض مختصات A(1,2)…») — شکستِ بی‌صدا، بدترین نوع.

Batch API روی GapGPT وجود ندارد (/v1/batches → ۴۰۴)، پس تخفیفِ ۵۰٪ در دسترس نیست. هر ابزاری که آن را وعده بدهد دارد گمراه می‌کند.

DPI اهرمِ هزینه نیست. OpenAI تصویر را به ضلعِ کوتاهِ ۷۶۸ نرمال می‌کند، پس رندر با ۲۰۰ یا ۱۱۰ DPI توکنِ یکسانی می‌دهد. ۲۰۰ را نگه دارید.

۲. قاعده‌ی self-contained را در پرامپت بگذارید

مدلِ vision شکل را می‌بیند؛ فقط از او خواسته نشده بنویسدش. بدونِ این قاعده، ۴۲٪ مسئله‌ها به «شکلِ زیر» ارجاع می‌دهند و برای هیچ‌کس قابلِ حل نیستند.

قاعده (در rag/services/pdf.py): هرجا مسئله به شکل/جدول ارجاع می‌دهد، اندازه‌ها، برچسبِ رأس‌ها، زاویه‌ها و مقادیرِ جدول را داخلِ متنِ مسئله بنویس؛ و اگر شکلی واقعاً قابلِ توصیف نیست (رسمِ آزاد، رنگ‌آمیزی) مسئله را رد کن به‌جای نوشتنِ ارجاعِ بی‌فایده.

اثرش با عدد: نرخِ unjudgeable در بازبینی از ۴۰٪ به ۳٪ رسید.

۳. بک‌اسلشِ LaTeX را تعمیر کنید، وگرنه صفحه‌های پرفرمول را می‌بازید

مدل در متنِ ریاضی \frac و \sqrt می‌نویسد و بک‌اسلشِ تنها JSON را می‌شکند. json.loads کلِ صفحه را دور می‌ریزد — یعنی دقیقاً باارزش‌ترین صفحه‌ها.

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

قاعده‌ی تفکیک: دستورِ LaTeX همیشه با حرفِ ASCII ادامه می‌یابد (\frac, \times)، ولی escapeِ واقعی با رقم، فاصله، فارسی یا پایانِ رشته می‌آید (\n۲.). پیاده‌سازی: rag/services/pdf.py::repair_json_escapes.

۴. محافظِ «وابسته به شکل» را با regexِ تخت نسازید

برای صرفه‌جویی، مسئله‌های وابسته به شکل را قبل از فرستادن کنار می‌گذاشتیم. regexِ اولیه شاملِ شکل|جدول|روبه‌رو|مقابل|زیر را بود و فاجعه‌بار گشاد:

متن چرا رد می‌شد واقعیت
«زاویه‌های روبه‌رو در لوزی برابرند؟» کلمه‌ی «روبه‌رو» یعنی زاویه‌ی مقابل
«کسرهای زیر را گویا کنید: ۲/√۳» «زیر را» داده همان‌جاست
«ظرفی به شکل مخروطی با شعاع ۴» کلمه‌ی «شکل» یعنی شبیهِ مخروط

اندازه‌گیری روی نمونه‌ی ۵۰تایی: ۸۴٪ از رد‌شده‌ها واقعاً قابلِ قضاوت بودند — حدود ۷۸۴ مسئله‌ی سالم بی‌صدا از چرخه بیرون مانده بود.

قاعده‌ی درست دو شرط دارد و هر دو باید برقرار باشند: (۱) ارجاعِ صریح به چیزی بیرونِ متن، و (۲) بعد از آن ارجاع هیچ توصیفی نیامده باشد. پیاده‌سازی: rag/management/commands/rag_verify_solutions.py::needs_figure.

۵. ساختِ سوال: subagent به‌جای API

ساختِ سوالِ چهارگزینه‌ای با o4-mini روی GapGPT ۰٫۷۷ سنت برای هر سوال تمام می‌شود (چهار برابرِ تخمینِ خودِ ابزار، چون مدلِ ریزنینگ است).

همان کار با subagentهای Claude Code (مدلِ Sonnet) رایگان است و بازده‌اش روی ۳۴۵ ورودی ۹۲٪ بود. Haiku برای این کار مناسب نیست — جوابِ نهایی را از راه‌حل بیرون نمی‌کشد.

مسیر: جفت‌های (درس، مسئله‌ی تأییدشده) را به JSON بیرون بدهید، subagent فایلِ خروجی بنویسد، و از content.question_io واردش کنید — که اعتبارسنجی دارد و فلسفه‌اش این است که فایلِ بد هیچ‌چیز نمی‌نویسد.


درسِ فراگیر

هر چهار موردِ بالا یک وجهِ مشترک دارند: هیچ‌کدام خطا تولید نمی‌کردند. نه در لاگ، نه در خروجی، نه در تست. فقط دادهٔ کمتر یا بدتر می‌ساختند.

پس: قبل از اعتماد به هر ران انبوه، یک نمونه‌ی ۴۰ تا ۵۰تایی را با چشم بخوانید. هر چهار موردِ این فایل با همین کار پیدا شدند، نه با کدخوانی.