Lesson content complete: examples/steps for all 3,778 lessons, 1,775 lessons with fixed figures, 38 quiz fixes
- curriculum.json: example/steps for every real lesson, figures (2,224) hand-written and visually checked - questions.json: 3,703 questions (24 added, 14 keys fixed) - gen_lesson_figures: strict check, thin-figure filter, recheck, --max-usd; API figures limited to plane/fraction/numberline/solid Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,16 +1,28 @@
|
|||||||
"""
|
"""
|
||||||
شکلِ ثابتِ درسها را یک بار میسازد و در `Lesson.figures` مینویسد.
|
شکلِ ثابتِ درسها را یک بار میسازد و در `Lesson.figures` مینویسد.
|
||||||
|
|
||||||
همان مسیرِ ارزانِ دستیار: همان مدلِ چت، همان کاتالوگِ <diagram>، همان
|
همان مسیرِ ارزانِ دستیار: همان مدلِ چت، همان کاتالوگِ <diagram>، همان اعتبارسنجِ
|
||||||
اعتبارسنجِ `rag.services.diagram.validate`. فرقش این است که نتیجه ذخیره میشود و
|
`rag.services.diagram.validate`. فرقش این است که نتیجه ذخیره میشود و بعد از تأییدِ چشمی با
|
||||||
بعد از تأییدِ چشمی با `seed_content --export` به curriculum.json میرود.
|
`seed_content --export` به curriculum.json میرود.
|
||||||
فقط درسهایی که شکل لازم دارند شکل میگیرند؛ مدل میتواند `[]` بدهد.
|
|
||||||
آزموده نشده: درستیِ هندسی/ریاضیِ شکل؛ باید با چشم دید.
|
|
||||||
|
|
||||||
python manage.py gen_lesson_figures --grade 12 --limit 4 [--redo] [--dry-run]
|
سه چیز که هزینه و خطا را پایین میآورد:
|
||||||
|
1. **پیشصافیِ رایگان:** اگر متنِ درس به هیچ بخشِ کاتالوگ نمیخورد (حسابِ عددیِ ساده)، مدل صدا
|
||||||
|
زده نمیشود و درس «بینیازِ شکل» ثبت میشود.
|
||||||
|
2. **فقط بخشهای مربوطِ کاتالوگ** (نه کلِ آن، ~۳٬۵۰۰ توکن) کنارِ درس میرود.
|
||||||
|
3. **گامِ وارسیِ مستقل:** هر شکلی که مدل ساخت، مدلِ دوم با متنِ درس مقایسه میکند؛ شکلی که
|
||||||
|
همان چیزِ متن را نشان نمیدهد (عدد/برچسبِ ناهمخوان، قدمِ اشتباه) ذخیره نمیشود.
|
||||||
|
|
||||||
|
سقفِ هزینه با فرمولِ رسمیِ gpt-4.1-mini محاسبه میشود (۰٫۴۰ و ۱٫۶۰ دلار در هر میلیون توکن). صورتحسابِ
|
||||||
|
واقعیِ GapGPT در سنجشهای قبلی چند برابر کمتر بود، پس سقف محافظهکارانه است. آزموده نشده: درستیِ
|
||||||
|
هندسی/ریاضیِ شکل؛ وارسیِ مدلِ دوم جایگزینِ دیدنِ چشمی نیست.
|
||||||
|
|
||||||
|
python manage.py gen_lesson_figures --grade 12 --limit 20 --max-usd 2 --ledger fig_ledger.json
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
import threading
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand, CommandError
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
|
||||||
@@ -18,85 +30,259 @@ from content.models import Lesson
|
|||||||
from rag.services import clients, diagram as diagram_mod
|
from rag.services import clients, diagram as diagram_mod
|
||||||
from rag.services.generation import chat_model_for, completion_kwargs
|
from rag.services.generation import chat_model_for, completion_kwargs
|
||||||
|
|
||||||
|
USD_PER_M_IN = 0.40
|
||||||
|
USD_PER_M_OUT = 1.60
|
||||||
|
MAX_FIGURES = 3
|
||||||
|
|
||||||
PROMPT = (
|
PROMPT = (
|
||||||
'تو برای یک درسِ ریاضی (اپِ آموزشی) شکلهای آموزشی میسازی. متنِ درس: «توضیح»، «مثال» و '
|
'تو برای یک درسِ ریاضی (اپِ آموزشی) شکلهای آموزشی میسازی. متنِ درس: «توضیح»، «مثال» و «قدمها» '
|
||||||
'«قدمها» (شمارهی قدمها از ۱، همان شمارهای که کاربر میبیند). فقط جاهایی شکل بگذار که دیدنش مفهوم را روشن میکند (نمودار، '
|
'(شمارهی قدمها از ۱، همان شمارهای که کاربر میبیند).\n'
|
||||||
'انتقال، تقارن، محور، جدول، هندسه)؛ هر شکل باید دقیقاً همان چیزی را نشان دهد که آن بخش '
|
'قاعدهها:\n'
|
||||||
'میگوید، با همان اعداد. بخشِ بینیاز شکل نمیگیرد؛ اگر هیچجا لازم نبود []. حداکثر ۵ شکل.\n'
|
f'- فقط جایی شکل بگذار که دیدنش مفهوم یا مسئله را روشن میکند؛ حداکثر {MAX_FIGURES} شکل. '
|
||||||
'خروجی فقط یک آرایهی JSON: [{"where":"intro"|"example"|"step","index":0,"diagram":{...}}] '
|
'شکلِ تزیینی یا تکراری نساز. اگر هیچجا لازم نبود []. ترجیح با «example» و «step» است؛ '
|
||||||
'(index فقط برای step و همان شمارهی ۱بهبعدِ قدم). بدونِ توضیح و بدونِ ```.\n'
|
'«intro» فقط وقتی متنِ توضیح خودش عدد یا نمادِ مشخص میدهد.\n'
|
||||||
'مثالهای کاتالوگ فقط قالباند: نقطه، تابع یا آیتمی را که این درس لازم ندارد کپی نکن؛ فقط '
|
'- هر شکل باید دقیقاً همان را نشان دهد که آن بخش میگوید، با همان اعداد و نامها؛ چیزی که در '
|
||||||
'چیزهایی بگذار که همین شکل نشان میدهد (برای نمودارِ تابع معمولاً فقط items با type=function). نمودارِ خطشکسته یا تکهای از نقطهها را '
|
'متن نیست اختراع نکن. برای بخشی که در متن نیست (مثال/قدم) شکل نساز.\n'
|
||||||
'با type=segment بین نقطههای متوالی بکش، نه type=line (آن خطِ بیپایان است). نقطههایی که '
|
'- نمودارِ خطشکسته یا تکهای را با type=segment بین نقطههای متوالی بکش، نه type=line. '
|
||||||
'در items نمیآیند تعریف نکن. شکلِ هر قدم باید همان چیزی را نشان دهد که متنِ همان قدم میگوید '
|
'برای نمودارِ تابع بازهی x را کوچک بگیر (مقدارِ y از حدود ۹ بیشتر نشود) و فقط items لازم را بگذار؛ '
|
||||||
'(نه قدمِ قبل یا بعد). بازهی x نمودارِ تابع را کوچک بگیر (مثلاً دو واحد دور از رأس/نقطههای مهم) تا '
|
'marks فقط برای یک منحنی. نقطههایی که در items نمیآیند تعریف نکن.\n'
|
||||||
'مقدارِ y از حدود ۹ بیشتر نشود و منحنی له نشود؛ marks فقط برای یک منحنی بگذار تا برچسبها روی هم نیفتند.\n\n'
|
'- شکلِ فقطنقطه (چند نقطهی بیارتباط) نساز؛ نقطه را روی نمودار یا شکلِ مربوطش بگذار. ضابطهی تابع را '
|
||||||
|
'عیناً از متن بردار (ضریب و انتقال را حذف نکن). برای مثلثِ متساویالاضلاع/متساویالساقین و مانندش '
|
||||||
|
'طولها را با نوعش سازگار بده. نوعِ pattern/groups را فقط وقتی بگذار که خودِ متن شکل یا شمارشِ مشخصی میدهد.\n'
|
||||||
|
'- شکلِ هر قدم همان را نشان دهد که متنِ همان قدم میگوید (نه قدمِ قبل یا بعد).\n'
|
||||||
|
'- مثالهای کاتالوگ فقط قالباند؛ نقطه، تابع یا آیتمی را که این درس لازم ندارد کپی نکن.\n'
|
||||||
|
'خروجی فقط یک آرایهی JSON: [{"where":"intro"|"example"|"step","index":1,"diagram":{...}}] '
|
||||||
|
'(index فقط برای step و همان شمارهی ۱بهبعدِ قدم). بدونِ توضیح و بدونِ ```.\n\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CHECK = (
|
||||||
|
'بازبینِ سختگیرِ ریاضی هستی. یک درس و شکلهایی که برایش ساختهاند (هر شکل یک JSON) داده میشود. '
|
||||||
|
'برای هر شکل ابتدا در یک جمله بنویس «shows»: دقیقاً چه چیزی میکشد (ضابطهی هر تابع، مختصات نقطهها، '
|
||||||
|
'طول ضلعها و زاویهها، بازهها، عددهای جدول، موقعیت و برچسبها). بعد با متنِ همان بخشِ درس مقایسه کن. '
|
||||||
|
'ok=false بده اگر: ضابطه یا عددی با متن نمیخواند (مثلاً ضریب یا انتقال گم شده)؛ شکل از نظر ریاضی/هندسی '
|
||||||
|
'ناسازگار است (مثلاً مثلثِ متساویالاضلاع با ضلعهای نابرابر)؛ موقعیتِ نقطهها با برچسبشان نمیخواند؛ '
|
||||||
|
'شکل مفهومِ اصلیِ آن بخش را نشان نمیدهد یا فقط نقطههای پراکنده/برچسبِ بیمعنی است؛ چیزی اختراع شده؛ '
|
||||||
|
'شکلِ قدم به همان قدم مربوط نیست. هر شکِ جدی → ok=false. سلیقهای ایراد نگیر. '
|
||||||
|
'خروجی فقط JSON: {"verdicts":[{"i":0,"shows":"...","ok":true|false,"why":"یک جمله"}]} به ترتیبِ شکلها.'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _lesson_text(lesson: Lesson) -> str:
|
||||||
|
steps = '\n'.join(f'قدم {i}: {s}' for i, s in enumerate(lesson.steps or [], 1))
|
||||||
|
return f'عنوان: {lesson.title}\nتوضیح: {lesson.intro_text}\nمثال: {lesson.example}\n{steps}'
|
||||||
|
|
||||||
|
|
||||||
|
def _spec_for(text: str) -> str:
|
||||||
|
"""فقط بخشهایی از کاتالوگ که کلمههایشان در متن هست؛ بیبخش → '' (مدل صدا زده نمیشود)."""
|
||||||
|
parts = [section for pattern, section in diagram_mod.SECTIONS if pattern.search(text)]
|
||||||
|
return '\n'.join([diagram_mod._HEAD, *parts]) if parts else ''
|
||||||
|
|
||||||
|
|
||||||
|
_SUBSTANTIVE = {'function', 'segment', 'line', 'ray', 'polygon', 'triangle', 'quad', 'regularPolygon', 'circle',
|
||||||
|
'angle', 'exteriorAngle', 'arc', 'vector'}
|
||||||
|
|
||||||
|
|
||||||
|
def _is_thin(diagram: dict) -> bool:
|
||||||
|
"""
|
||||||
|
شکلِ صفحهای که هیچ منحنی، پارهخط یا شکلی ندارد و فقط نقطهی پراکنده است چیزی نشان نمیدهد
|
||||||
|
(مثلاً چند نقطهی A و B و C بیارتباط). نوعِ تازهای که این فهرست نمیشناسد «محتوادار» حساب میشود.
|
||||||
|
"""
|
||||||
|
if diagram.get('kind') != 'plane':
|
||||||
|
return False
|
||||||
|
items = diagram.get('items') or []
|
||||||
|
if any(i.get('type') in _SUBSTANTIVE or i.get('type') not in ('point', 'label', None) for i in items):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _json_list(raw: str):
|
||||||
|
raw = re.sub(r'^```(?:json)?|```$', '', (raw or '').strip()).strip()
|
||||||
|
start = raw.index('[')
|
||||||
|
items, _ = json.JSONDecoder().raw_decode(raw[start:])
|
||||||
|
if not isinstance(items, list):
|
||||||
|
raise ValueError('not a list')
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = 'ساختِ شکلِ ثابتِ درسها (یک بار، ذخیره در Lesson.figures).'
|
help = 'ساختِ شکلِ ثابتِ درسها (یک بار، ذخیره در Lesson.figures).'
|
||||||
|
|
||||||
def add_arguments(self, p):
|
def add_arguments(self, p):
|
||||||
p.add_argument('--grade', type=int, required=True)
|
p.add_argument('--grade', type=int, help='بدونِ آن همهی پایهها')
|
||||||
p.add_argument('--limit', type=int, default=4)
|
p.add_argument('--limit', type=int, default=4)
|
||||||
p.add_argument('--lesson', type=int, help='فقط همین شناسه')
|
p.add_argument('--lesson', type=int, help='فقط همین شناسه')
|
||||||
p.add_argument('--redo', action='store_true', help='درسهایی که شکل دارند را هم بساز')
|
p.add_argument('--redo', action='store_true', help='درسهایی که شکل دارند هم')
|
||||||
p.add_argument('--dry-run', action='store_true')
|
p.add_argument('--dry-run', action='store_true')
|
||||||
|
p.add_argument('--workers', type=int, default=4)
|
||||||
|
p.add_argument('--max-usd', type=float, default=2.0, help='سقفِ هزینه (فرمولِ رسمی)؛ رسید، میایستد')
|
||||||
|
p.add_argument('--ledger', help='JSON از درسهای بررسیشده ({id: نتیجه}) تا دوباره پول نگیرند')
|
||||||
|
p.add_argument('--quiet', action='store_true')
|
||||||
|
p.add_argument('--recheck', action='store_true',
|
||||||
|
help='شکلهای ذخیرهشده را دوباره با وارسیِ سختگیر بسنج و ناموفقها را پاک کن (بیساخت)')
|
||||||
|
p.add_argument('--no-check', action='store_true', help='گامِ وارسیِ مستقل را رد کن (فقط برای آزمایش)')
|
||||||
|
|
||||||
def handle(self, *a, **o):
|
def handle(self, *a, **o):
|
||||||
qs = Lesson.objects.filter(node__unit__grade__number=o['grade']).order_by(
|
if o['recheck']:
|
||||||
'node__unit__order', 'node__order', 'id')
|
return self._recheck(o)
|
||||||
|
qs = Lesson.objects.exclude(example='').order_by(
|
||||||
|
'node__unit__grade__number', 'node__unit__order', 'node__order', 'id')
|
||||||
|
if o['grade']:
|
||||||
|
qs = qs.filter(node__unit__grade__number=o['grade'])
|
||||||
if o['lesson']:
|
if o['lesson']:
|
||||||
qs = qs.filter(pk=o['lesson'])
|
qs = qs.filter(pk=o['lesson'])
|
||||||
if not o['redo']:
|
if not o['redo']:
|
||||||
qs = qs.filter(figures=[])
|
qs = qs.filter(figures=[])
|
||||||
|
ledger_path = Path(o['ledger']) if o['ledger'] else None
|
||||||
|
ledger = json.loads(ledger_path.read_text()) if ledger_path and ledger_path.exists() else {}
|
||||||
|
if not o['redo']:
|
||||||
|
qs = qs.exclude(pk__in=[int(k) for k in ledger])
|
||||||
lessons = list(qs[:o['limit']])
|
lessons = list(qs[:o['limit']])
|
||||||
if not lessons:
|
if not lessons:
|
||||||
raise CommandError('درسی پیدا نشد.')
|
raise CommandError('درسی پیدا نشد.')
|
||||||
|
|
||||||
model = chat_model_for('')
|
model = chat_model_for('')
|
||||||
client = clients.get_openai_client()
|
client = clients.get_openai_client()
|
||||||
spent = prompt_t = out_t = 0
|
lock = threading.Lock()
|
||||||
for lesson in lessons:
|
usage = {'in': 0, 'out': 0}
|
||||||
body = (f'عنوان: {lesson.title}\nتوضیح: {lesson.intro_text}\nمثال: {lesson.example}\n'
|
stats = {'no_trigger': 0, 'none': 0, 'saved': 0, 'figures': 0, 'rejected': 0, 'invalid': 0, 'stopped': 0}
|
||||||
+ '\n'.join(f'قدم {i}: {s}' for i, s in enumerate(lesson.steps or [], 1)))
|
|
||||||
|
def spent():
|
||||||
|
return usage['in'] / 1e6 * USD_PER_M_IN + usage['out'] / 1e6 * USD_PER_M_OUT
|
||||||
|
|
||||||
|
def ask(system, user, **extra):
|
||||||
r = client.chat.completions.create(
|
r = client.chat.completions.create(
|
||||||
model=model, **completion_kwargs(model),
|
model=model, **completion_kwargs(model), **extra,
|
||||||
messages=[{'role': 'system', 'content': PROMPT + diagram_mod.DIAGRAM_SPEC},
|
messages=[{'role': 'system', 'content': system}, {'role': 'user', 'content': user}])
|
||||||
{'role': 'user', 'content': body}])
|
|
||||||
u = getattr(r, 'usage', None)
|
u = getattr(r, 'usage', None)
|
||||||
spent += (getattr(u, 'total_tokens', 0) or 0)
|
with lock:
|
||||||
prompt_t += (getattr(u, 'prompt_tokens', 0) or 0)
|
usage['in'] += getattr(u, 'prompt_tokens', 0) or 0
|
||||||
out_t += (getattr(u, 'completion_tokens', 0) or 0)
|
usage['out'] += getattr(u, 'completion_tokens', 0) or 0
|
||||||
raw = re.sub(r'^```(?:json)?|```$', '', (r.choices[0].message.content or '').strip()).strip()
|
return r.choices[0].message.content or ''
|
||||||
|
|
||||||
|
def record(lesson, outcome):
|
||||||
|
with lock:
|
||||||
|
if ledger_path and not o['dry_run']:
|
||||||
|
ledger[str(lesson.id)] = outcome
|
||||||
|
ledger_path.write_text(json.dumps(ledger, ensure_ascii=False))
|
||||||
|
|
||||||
|
def work(lesson):
|
||||||
|
text = _lesson_text(lesson)
|
||||||
|
spec = _spec_for(text)
|
||||||
|
if not spec:
|
||||||
|
with lock:
|
||||||
|
stats['no_trigger'] += 1
|
||||||
|
record(lesson, 'no_trigger')
|
||||||
|
return
|
||||||
|
if spent() >= o['max_usd']:
|
||||||
|
with lock:
|
||||||
|
stats['stopped'] += 1
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
start = raw.index('[')
|
items = _json_list(ask(PROMPT + spec, text))
|
||||||
items, _ = json.JSONDecoder().raw_decode(raw[start:])
|
except (ValueError, KeyError) as e:
|
||||||
if not isinstance(items, list):
|
with lock:
|
||||||
raise ValueError('not a list')
|
stats['invalid'] += 1
|
||||||
except ValueError as e:
|
|
||||||
self.stderr.write(f'درس {lesson.id}: خروجیِ نامعتبر ({e}); ذخیره نشد.')
|
self.stderr.write(f'درس {lesson.id}: خروجیِ نامعتبر ({e}); ذخیره نشد.')
|
||||||
continue
|
return
|
||||||
figures, dropped = [], 0
|
figures, dropped = [], 0
|
||||||
for it in items:
|
for it in items[:MAX_FIGURES + 2]:
|
||||||
where = it.get('where') if isinstance(it, dict) else None
|
where = it.get('where') if isinstance(it, dict) else None
|
||||||
spec = diagram_mod.validate(it.get('diagram')) if where else None
|
s = diagram_mod.validate(it.get('diagram')) if where else None
|
||||||
idx = it.get('index')
|
idx = it.get('index')
|
||||||
if where == 'step' and isinstance(idx, int):
|
if where == 'step' and isinstance(idx, int):
|
||||||
idx -= 1 # مدل ۱بهبعد مینویسد؛ ذخیره از صفر
|
idx -= 1 # مدل ۱بهبعد مینویسد؛ ذخیره از صفر
|
||||||
if (spec is None or where not in ('intro', 'example', 'step')
|
if (s is None or where not in ('intro', 'example', 'step')
|
||||||
or (where == 'step' and not (isinstance(idx, int) and 0 <= idx < len(lesson.steps or [])))
|
or (where == 'step' and not (isinstance(idx, int) and 0 <= idx < len(lesson.steps or [])))
|
||||||
or (where == 'example' and not lesson.example)):
|
or (where == 'example' and not lesson.example)):
|
||||||
dropped += 1
|
dropped += 1
|
||||||
continue
|
continue
|
||||||
for item in spec.get('items', []):
|
if _is_thin(s):
|
||||||
|
dropped += 1
|
||||||
|
continue
|
||||||
|
for item in s.get('items', []):
|
||||||
if item.get('type') == 'segment':
|
if item.get('type') == 'segment':
|
||||||
item.pop('ticks', None) # علامتِ «ضلع مساوی» روی نمودارِ تابع فقط شلوغی است
|
item.pop('ticks', None) # علامتِ «ضلع مساوی» روی نمودارِ تابع فقط شلوغی است
|
||||||
figures.append({'where': where, **({'index': idx} if where == 'step' else {}), 'diagram': spec})
|
figures.append({'where': where, **({'index': idx} if where == 'step' else {}), 'diagram': s})
|
||||||
self.stdout.write(f'درس {lesson.id} «{lesson.title}»: {len(figures)} شکل، {dropped} ردشده')
|
figures = figures[:MAX_FIGURES]
|
||||||
for f in figures:
|
if not figures:
|
||||||
self.stdout.write(' ' + json.dumps(f, ensure_ascii=False))
|
with lock:
|
||||||
if figures and not o['dry_run']:
|
stats['none'] += 1
|
||||||
lesson.figures = figures
|
record(lesson, 'none' if not items else 'invalid_all')
|
||||||
|
return
|
||||||
|
kept, notes = figures, []
|
||||||
|
if not o['no_check']:
|
||||||
|
try:
|
||||||
|
verdicts = json.loads(ask(CHECK, text + '\n\nشکلها:\n' + '\n'.join(
|
||||||
|
f'{i}: ' + json.dumps(f, ensure_ascii=False) for i, f in enumerate(figures)),
|
||||||
|
response_format={'type': 'json_object'})).get('verdicts', [])
|
||||||
|
ok = {v.get('i'): v for v in verdicts if isinstance(v, dict)}
|
||||||
|
kept = [f for i, f in enumerate(figures) if ok.get(i, {}).get('ok') is True]
|
||||||
|
notes = [f"{i}: {v.get('why', '')}" for i, v in ok.items() if v.get('ok') is not True]
|
||||||
|
except (ValueError, KeyError, AttributeError) as e:
|
||||||
|
kept, notes = [], [f'وارسی ناموفق: {e}']
|
||||||
|
with lock:
|
||||||
|
stats['rejected'] += len(figures) - len(kept)
|
||||||
|
stats['figures'] += len(kept)
|
||||||
|
if kept:
|
||||||
|
stats['saved'] += 1
|
||||||
|
self.stdout.write(f'درس {lesson.id} «{lesson.title}»: {len(kept)} شکل از {len(figures)} '
|
||||||
|
f'({dropped} ردشده در اعتبارسنج)' + (f' | رد: {"; ".join(notes)}' if notes else ''))
|
||||||
|
if not o['quiet']:
|
||||||
|
for f in kept:
|
||||||
|
self.stdout.write(' ' + json.dumps(f, ensure_ascii=False))
|
||||||
|
if kept and not o['dry_run']:
|
||||||
|
lesson.figures = kept
|
||||||
lesson.save(update_fields=['figures'])
|
lesson.save(update_fields=['figures'])
|
||||||
self.stdout.write(f'توکنِ مصرفی: {spent} (ورودی {prompt_t}، خروجی {out_t})')
|
record(lesson, f'figures:{len(kept)}' if kept else 'rejected')
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=max(1, o['workers'])) as ex:
|
||||||
|
list(ex.map(work, lessons))
|
||||||
|
self.stdout.write(f'{stats}\nتوکن: ورودی {usage["in"]}، خروجی {usage["out"]}؛ هزینهی فرمولی ≈ ${spent():.3f}')
|
||||||
|
|
||||||
|
def _recheck(self, o):
|
||||||
|
qs = Lesson.objects.exclude(figures=[]).order_by('node__unit__grade__number', 'id')
|
||||||
|
if o['grade']:
|
||||||
|
qs = qs.filter(node__unit__grade__number=o['grade'])
|
||||||
|
lessons = list(qs[:o['limit']])
|
||||||
|
model = chat_model_for('')
|
||||||
|
client = clients.get_openai_client()
|
||||||
|
lock = threading.Lock()
|
||||||
|
usage = {'in': 0, 'out': 0}
|
||||||
|
stats = {'lessons': 0, 'kept': 0, 'dropped': 0, 'errors': 0}
|
||||||
|
|
||||||
|
def work(lesson):
|
||||||
|
figures = [f for f in lesson.figures if not _is_thin(f['diagram'])]
|
||||||
|
thin = len(lesson.figures) - len(figures)
|
||||||
|
text = _lesson_text(lesson)
|
||||||
|
try:
|
||||||
|
r = client.chat.completions.create(
|
||||||
|
model=model, **completion_kwargs(model), response_format={'type': 'json_object'},
|
||||||
|
messages=[{'role': 'system', 'content': CHECK}, {'role': 'user', 'content': text + '\n\nشکلها:\n' + '\n'.join(
|
||||||
|
f'{i}: ' + json.dumps(f, ensure_ascii=False) for i, f in enumerate(figures))}])
|
||||||
|
u = getattr(r, 'usage', None)
|
||||||
|
ok = {v.get('i'): v for v in json.loads(r.choices[0].message.content or '{}').get('verdicts', [])
|
||||||
|
if isinstance(v, dict)} if figures else {}
|
||||||
|
except (ValueError, KeyError, AttributeError) as e:
|
||||||
|
with lock:
|
||||||
|
stats['errors'] += 1
|
||||||
|
self.stderr.write(f'درس {lesson.id}: وارسی ناموفق ({e}); دست نخورد.')
|
||||||
|
return
|
||||||
|
kept = [f for i, f in enumerate(figures) if ok.get(i, {}).get('ok') is True]
|
||||||
|
with lock:
|
||||||
|
usage['in'] += getattr(u, 'prompt_tokens', 0) or 0
|
||||||
|
usage['out'] += getattr(u, 'completion_tokens', 0) or 0
|
||||||
|
stats['lessons'] += 1
|
||||||
|
stats['kept'] += len(kept)
|
||||||
|
stats['dropped'] += len(lesson.figures) - len(kept)
|
||||||
|
why = '; '.join(f"{i}: {v.get('why', '')}" for i, v in ok.items() if v.get('ok') is not True)
|
||||||
|
if len(kept) != len(lesson.figures) and not o['quiet']:
|
||||||
|
self.stdout.write(f'درس {lesson.id} «{lesson.title}»: {len(kept)}/{len(lesson.figures)}'
|
||||||
|
f' (بیمحتوا {thin}) | {why}')
|
||||||
|
if len(kept) != len(lesson.figures) and not o['dry_run']:
|
||||||
|
lesson.figures = kept
|
||||||
|
lesson.save(update_fields=['figures'])
|
||||||
|
|
||||||
|
with ThreadPoolExecutor(max_workers=max(1, o['workers'])) as ex:
|
||||||
|
list(ex.map(work, lessons))
|
||||||
|
cost = usage['in'] / 1e6 * USD_PER_M_IN + usage['out'] / 1e6 * USD_PER_M_OUT
|
||||||
|
self.stdout.write(f'{stats}\nتوکن: ورودی {usage["in"]}، خروجی {usage["out"]}؛ هزینهی فرمولی ≈ ${cost:.3f}')
|
||||||
|
|||||||
101692
content/seed/curriculum.json
101692
content/seed/curriculum.json
File diff suppressed because it is too large
Load Diff
@@ -7232,15 +7232,15 @@
|
|||||||
"معادله",
|
"معادله",
|
||||||
"حل معادله با استفاده از رسم شکل"
|
"حل معادله با استفاده از رسم شکل"
|
||||||
],
|
],
|
||||||
"prompt": "حاصل جمع پنج عدد متوالی طبیعی ۶۵ شده است. عدد وسط چه عددی است؟",
|
"prompt": "جواب معادلهٔ ۵x + ۱۰ = ۶۵ کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۱۴",
|
"۱۱",
|
||||||
|
"۹",
|
||||||
"۱۳",
|
"۱۳",
|
||||||
"۱۲",
|
"۱۲"
|
||||||
"۲۶"
|
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -7249,15 +7249,15 @@
|
|||||||
"معادله",
|
"معادله",
|
||||||
"حل معادله با استفاده از روشهای نمادین"
|
"حل معادله با استفاده از روشهای نمادین"
|
||||||
],
|
],
|
||||||
"prompt": "حاصل جمع پنج عدد متوالی طبیعی ۶۵ شده است. عدد وسط چه عددی است؟",
|
"prompt": "سه برابر عددی را با ۵ جمع کردهایم و ۲۰ شده است. آن عدد کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۱۳",
|
"۴",
|
||||||
"۱۴",
|
"۵",
|
||||||
"۲۶",
|
"۶",
|
||||||
"۱۲"
|
"۱۵"
|
||||||
],
|
],
|
||||||
"correct": 0
|
"correct": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -9187,7 +9187,7 @@
|
|||||||
"توان و جذر — گروه 1",
|
"توان و جذر — گروه 1",
|
||||||
"ترتیب انجام عملیات"
|
"ترتیب انجام عملیات"
|
||||||
],
|
],
|
||||||
"prompt": "۲ × (۳۲ + ۲) = ؟",
|
"prompt": "۲ × (۳² + ۲) = ؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۱۸",
|
"۱۸",
|
||||||
@@ -17194,13 +17194,13 @@
|
|||||||
"ضرب عددها — گروه 5",
|
"ضرب عددها — گروه 5",
|
||||||
"محاسبه ضرب اعداد"
|
"محاسبه ضرب اعداد"
|
||||||
],
|
],
|
||||||
"prompt": "۲۰ × ۲۰ = ؟",
|
"prompt": "۳۰ × ۴۰ = ؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۴۰",
|
"۱۲۰",
|
||||||
"۴۰۰",
|
"۱۲۰۰",
|
||||||
"۲۰۰",
|
"۷۰",
|
||||||
"۸۰"
|
"۱۲٬۰۰۰"
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 1
|
||||||
},
|
},
|
||||||
@@ -17670,15 +17670,15 @@
|
|||||||
"ضرب عددها — گروه 5",
|
"ضرب عددها — گروه 5",
|
||||||
"محاسبه ضرب چند رقمی"
|
"محاسبه ضرب چند رقمی"
|
||||||
],
|
],
|
||||||
"prompt": "۸۰ × ۷۰ = ؟",
|
"prompt": "۶۰ × ۹۰ = ؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۵۶۰",
|
"۵۴۰",
|
||||||
"۵۶۰۰",
|
"۱۵۰",
|
||||||
"۶۳۰۰",
|
"۵۴۰۰",
|
||||||
"۴۹۰۰"
|
"۵۴٬۰۰۰"
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -20033,15 +20033,15 @@
|
|||||||
"معادله",
|
"معادله",
|
||||||
"حل معادله با استفاده از حدس و آزمایش"
|
"حل معادله با استفاده از حدس و آزمایش"
|
||||||
],
|
],
|
||||||
"prompt": "حاصل جمع پنج عدد متوالی طبیعی برابر ۶۵ است. عدد وسط کدام است؟",
|
"prompt": "عددی که سه برابرش ۲۷ شود کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۱۱",
|
"۶",
|
||||||
"۱۳",
|
|
||||||
"۱۲",
|
"۱۲",
|
||||||
"۱۴"
|
"۹",
|
||||||
|
"۸"
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -20050,13 +20050,13 @@
|
|||||||
"معادله",
|
"معادله",
|
||||||
"حل معادله با استفاده از رسم شکل"
|
"حل معادله با استفاده از رسم شکل"
|
||||||
],
|
],
|
||||||
"prompt": "حاصل جمع پنج عدد متوالی طبیعی برابر ۶۵ است. عدد وسط کدام است؟",
|
"prompt": "جواب معادلهٔ ۲x + ۳ = ۱۵ کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۱۱",
|
"۹",
|
||||||
"۱۳",
|
"۶",
|
||||||
"۱۲",
|
"۸",
|
||||||
"۱۴"
|
"۷"
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 1
|
||||||
},
|
},
|
||||||
@@ -20067,15 +20067,15 @@
|
|||||||
"معادله",
|
"معادله",
|
||||||
"حل معادله با استفاده از روشهای نمادین"
|
"حل معادله با استفاده از روشهای نمادین"
|
||||||
],
|
],
|
||||||
"prompt": "حاصل جمع پنج عدد متوالی طبیعی برابر ۶۵ است. عدد وسط کدام است؟",
|
"prompt": "اگر x − ۴ = ۹ باشد ، x کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۱۱",
|
"۵",
|
||||||
|
"۳۶",
|
||||||
"۱۳",
|
"۱۳",
|
||||||
"۱۲",
|
"−۵"
|
||||||
"۱۴"
|
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -31576,15 +31576,15 @@
|
|||||||
"تناوب و تانژانت",
|
"تناوب و تانژانت",
|
||||||
"تشخیص درست یا نادرست بودن جملات مربوط به تابع تناوبی"
|
"تشخیص درست یا نادرست بودن جملات مربوط به تابع تناوبی"
|
||||||
],
|
],
|
||||||
"prompt": "کدام یک از جملات زیر دربارهی توابع تناوبی درست است؟",
|
"prompt": "آیا sin(x + ۲π) = sin x است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"تابع تناوبی در هر بازهی تعریفشده صعودی است",
|
"بله ، چون ۲π دورهٔ تناوب sin است",
|
||||||
"تابع تناوبی در تمام دامنهاش صعودی است",
|
"خیر ، همیشه نادرست است",
|
||||||
"تابع تناوبی میتواند در بازهای نزولی باشد",
|
"فقط برای x = ۰ درست است",
|
||||||
"هر تابع تناوبی حتماً زوج است"
|
"فقط برای x = π درست است"
|
||||||
],
|
],
|
||||||
"correct": 2
|
"correct": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -39464,15 +39464,15 @@
|
|||||||
"محاسبه حد توابع کسری",
|
"محاسبه حد توابع کسری",
|
||||||
"محاسبه حد توابع مثلثاتی"
|
"محاسبه حد توابع مثلثاتی"
|
||||||
],
|
],
|
||||||
"prompt": "مقدار حدِ زیر را بیابید:\nlim (x→π/۲) (۱ - sin x) / cos x",
|
"prompt": "مقدار lim(x→۰) tan x / x کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۰",
|
"۰",
|
||||||
"۱",
|
"۱",
|
||||||
"-۱",
|
"∞",
|
||||||
"وجود ندارد"
|
"π"
|
||||||
],
|
],
|
||||||
"correct": 0
|
"correct": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -39600,15 +39600,15 @@
|
|||||||
"پیوستگی",
|
"پیوستگی",
|
||||||
"رسم نمودار توابع با شرایط پیوستگی و نقاط تعریف نشده"
|
"رسم نمودار توابع با شرایط پیوستگی و نقاط تعریف نشده"
|
||||||
],
|
],
|
||||||
"prompt": "تابعی در x=۳ تعریف نشده، اما حدِ آن در x=۳ وجود دارد. این تابع در x=۳ چگونه است؟",
|
"prompt": "مقدار lim(x→۱) (x² − ۱)/(x − ۱) کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"پیوسته نیست، چون تعریف نشده است.",
|
"۰",
|
||||||
"پیوسته است، چون حد وجود دارد.",
|
"۱",
|
||||||
"پیوسته است، چون همهجا تعریف شده.",
|
"۲",
|
||||||
"نمیتوان گفت؛ نمودار لازم است."
|
"وجود ندارد"
|
||||||
],
|
],
|
||||||
"correct": 0
|
"correct": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -39787,15 +39787,15 @@
|
|||||||
"پیوستگی",
|
"پیوستگی",
|
||||||
"ارائه بازه پیوستگی تابع شامل رادیکال"
|
"ارائه بازه پیوستگی تابع شامل رادیکال"
|
||||||
],
|
],
|
||||||
"prompt": "تابعِ f(x) = ۲ - √(۷-x) را در نظر بگیرید. کدامیک بازهای بسته است که تابع بر آن پیوسته باشد؟",
|
"prompt": "تابع f(x) = √(x − ۳) − ۴ روی کدام بازه پیوسته است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"[-۲,۸]",
|
"[−۳ ، ∞)",
|
||||||
"[۷,۱۰]",
|
"(−∞ ، ۳]",
|
||||||
"[۸,۱۲]",
|
"[۳ ، ∞)",
|
||||||
"[-۲,۷]"
|
"(−∞ ، ∞)"
|
||||||
],
|
],
|
||||||
"correct": 3
|
"correct": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -40569,15 +40569,15 @@
|
|||||||
"جهت تقعر نمودار یک تابع و نقطهی عطف آن",
|
"جهت تقعر نمودار یک تابع و نقطهی عطف آن",
|
||||||
"تشخیص درست یا نادرست بودن گزارههای مربوط به نقاط عطف"
|
"تشخیص درست یا نادرست بودن گزارههای مربوط به نقاط عطف"
|
||||||
],
|
],
|
||||||
"prompt": "کدام گزاره دربارهی نقطهی عطف نادرست است؟",
|
"prompt": "نقطهٔ عطف f(x) = x³ − ۳x + ۲ کدام است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"تابعِ صعودیِ اکید عطف ندارد",
|
"(۱ ، ۰)",
|
||||||
"عطف به تغییرِ تقعر مربوط است",
|
"(۰ ، ۲)",
|
||||||
"تابع میتواند چند عطف داشته باشد",
|
"(−۱ ، ۴)",
|
||||||
"در عطف علامتِ f'' تغییر میکند"
|
"(۰ ، ۰)"
|
||||||
],
|
],
|
||||||
"correct": 0
|
"correct": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": [
|
"path": [
|
||||||
@@ -54152,13 +54152,13 @@
|
|||||||
"ضرب عددهای مخلوط",
|
"ضرب عددهای مخلوط",
|
||||||
"مقایسه کسرها و تعیین نادرستی"
|
"مقایسه کسرها و تعیین نادرستی"
|
||||||
],
|
],
|
||||||
"prompt": "کدام مقایسه درست است؟",
|
"prompt": "آیا ۳/۵ از ۱/۲ بزرگتر است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"۵ و (۱)/(۳) < ۶",
|
"بله ، چون ۶/۱۰ > ۵/۱۰",
|
||||||
"۵ و (۱)/(۳) > ۶",
|
"خیر ، چون ۳ < ۵",
|
||||||
"۵ و (۱)/(۳) = ۶",
|
"برابرند",
|
||||||
"۵ و (۱)/(۳) < ۵"
|
"نمیتوان مقایسه کرد"
|
||||||
],
|
],
|
||||||
"correct": 0
|
"correct": 0
|
||||||
},
|
},
|
||||||
@@ -61972,13 +61972,13 @@
|
|||||||
"چهارضلعیها",
|
"چهارضلعیها",
|
||||||
"تشخیص و تکمیل ویژگیهای چهارضلعیها"
|
"تشخیص و تکمیل ویژگیهای چهارضلعیها"
|
||||||
],
|
],
|
||||||
"prompt": "مستطیل علاوه بر ویژگیهای متوازیالاضلاع چه ویژگی دیگری دارد؟",
|
"prompt": "کدام ویژگی در لوزی همیشه برقرار است؟",
|
||||||
"type": "multiple_choice",
|
"type": "multiple_choice",
|
||||||
"choices": [
|
"choices": [
|
||||||
"همهی ضلعهایش برابر است",
|
"چهار زاویهٔ قائمه",
|
||||||
"همهی زاویههایش قائمه است",
|
"چهار ضلع برابر",
|
||||||
"فقط یک جفت ضلع موازی دارد",
|
"قطرهای برابر",
|
||||||
"سه ضلعش برابر است"
|
"فقط یک جفت ضلع موازی"
|
||||||
],
|
],
|
||||||
"correct": 1
|
"correct": 1
|
||||||
},
|
},
|
||||||
@@ -62542,6 +62542,414 @@
|
|||||||
"به طور حتم اتفاق نمیافتد"
|
"به طور حتم اتفاق نمیافتد"
|
||||||
],
|
],
|
||||||
"correct": 2
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۱: عدد و الگوهای عددی",
|
||||||
|
"عدد و الگوهای عددی — گروه 6",
|
||||||
|
"رابطه بین تعداد مربعها و شماره شکلها"
|
||||||
|
],
|
||||||
|
"prompt": "شکل n از یک الگو ۲n مربع دارد. شکل پنجم چند مربع دارد؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۷",
|
||||||
|
"۱۰",
|
||||||
|
"۱۲",
|
||||||
|
"۲۵"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۴: تقارن و مختصات",
|
||||||
|
"تقارن و مختصات — گروه 1",
|
||||||
|
"بررسی تقارن چرخشی"
|
||||||
|
],
|
||||||
|
"prompt": "مربع را حول مرکزش حداقل چند درجه بچرخانیم تا روی خودش بیفتد؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۴۵",
|
||||||
|
"۹۰",
|
||||||
|
"۱۸۰",
|
||||||
|
"۳۶۰"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۴: تقارن و مختصات",
|
||||||
|
"تقارن و مختصات — گروه 2",
|
||||||
|
"تقارن محوری"
|
||||||
|
],
|
||||||
|
"prompt": "قرینهٔ نقطهٔ (۲ ، ۳) نسبت به محور y کدام است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"(۲ ، −۳)",
|
||||||
|
"(−۲ ، ۳)",
|
||||||
|
"(−۲ ، −۳)",
|
||||||
|
"(۳ ، ۲)"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۴: تقارن و مختصات",
|
||||||
|
"تقارن و مختصات — گروه 2",
|
||||||
|
"تقارن چرخشی"
|
||||||
|
],
|
||||||
|
"prompt": "مثلث متساویالاضلاع را حداقل چند درجه بچرخانیم تا روی خودش بیفتد؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۶۰",
|
||||||
|
"۹۰",
|
||||||
|
"۱۲۰",
|
||||||
|
"۱۸۰"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۴: تقارن و مختصات",
|
||||||
|
"تقارن و مختصات — گروه 6",
|
||||||
|
"یافتن نقطه تقارن"
|
||||||
|
],
|
||||||
|
"prompt": "نقطهای که وسط پارهخط به سرهای (۲ ، ۲) و (۶ ، ۴) است کدام است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"(۴ ، ۲)",
|
||||||
|
"(۴ ، ۳)",
|
||||||
|
"(۳ ، ۴)",
|
||||||
|
"(۸ ، ۶)"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۵: اندازه گیری",
|
||||||
|
"اندازه گیری — گروه 3",
|
||||||
|
"زاویههای متقابل به رأس"
|
||||||
|
],
|
||||||
|
"prompt": "دو خط متقاطع زاویهای ۵۰ درجه میسازند. زاویهٔ متقابل به رأس آن چند درجه است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۵۰",
|
||||||
|
"۱۳۰",
|
||||||
|
"۴۰",
|
||||||
|
"۳۱۰"
|
||||||
|
],
|
||||||
|
"correct": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه ششم",
|
||||||
|
"فصل ۵: اندازه گیری",
|
||||||
|
"اندازه گیری — گروه 6",
|
||||||
|
"محاسبه مساحت اشکال ترکیبی"
|
||||||
|
],
|
||||||
|
"prompt": "شکلی از یک مستطیل ۶×۴ و یک مربع ۳×۳ (جدا از هم) ساخته شده است. مساحت کل چقدر است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۳۰",
|
||||||
|
"۲۷",
|
||||||
|
"۳۳",
|
||||||
|
"۶۰"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه هفتم",
|
||||||
|
"فصل ۳: جبر و معادله",
|
||||||
|
"جبر و معادله — گروه 6",
|
||||||
|
"نمودارهای ورودی و خروجی"
|
||||||
|
],
|
||||||
|
"prompt": "رابطهٔ ماشین y = ۲x + ۱ است. اگر ورودی ۵ باشد خروجی چند است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۱۰",
|
||||||
|
"۱۱",
|
||||||
|
"۱۲",
|
||||||
|
"۷"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه هفتم",
|
||||||
|
"فصل ۴: هندسه و استدلال",
|
||||||
|
"هندسه و استدلال — گروه 4",
|
||||||
|
"محاسبه زاویههای مثلث"
|
||||||
|
],
|
||||||
|
"prompt": "زاویههای یک مثلث ۳x ، ۴x و ۵x هستند. بزرگترین زاویه چند درجه است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۶۰",
|
||||||
|
"۴۵",
|
||||||
|
"۹۰",
|
||||||
|
"۷۵"
|
||||||
|
],
|
||||||
|
"correct": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه هفتم",
|
||||||
|
"فصل ۶: سطح و حجم",
|
||||||
|
"سطح و حجم — گروه 4",
|
||||||
|
"گستردهسازی استوانه"
|
||||||
|
],
|
||||||
|
"prompt": "گستردهٔ یک استوانه از چه شکلهایی ساخته شده است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"یک دایره و یک مثلث",
|
||||||
|
"دو دایره و یک مستطیل",
|
||||||
|
"شش مربع",
|
||||||
|
"دو مربع و دو مستطیل"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه هفتم",
|
||||||
|
"فصل ۶: سطح و حجم",
|
||||||
|
"سطح و حجم — گروه 4",
|
||||||
|
"مقاطع اشکال هندسی"
|
||||||
|
],
|
||||||
|
"prompt": "اگر مکعب را با صفحهای افقی ببریم ، مقطع چه شکلی است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"دایره",
|
||||||
|
"مثلث",
|
||||||
|
"مربع",
|
||||||
|
"ذوزنقه"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه هفتم",
|
||||||
|
"فصل ۸: بردار و مختصات",
|
||||||
|
"بردار و مختصات — گروه 5",
|
||||||
|
"پاسخ به سوالات مختصات"
|
||||||
|
],
|
||||||
|
"prompt": "نقطهٔ A(۲ ، ۳) چقدر از محور y فاصله دارد؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۳",
|
||||||
|
"۲",
|
||||||
|
"۵",
|
||||||
|
"۱"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه هفتم",
|
||||||
|
"فصل ۹: آمار و احتمال",
|
||||||
|
"آمار و احتمال — گروه 4",
|
||||||
|
"مقایسه دادهها"
|
||||||
|
],
|
||||||
|
"prompt": "میانگین نمرهٔ کلاس A برابر ۱۵ و کلاس B برابر ۱۷ است. میانگین کدام کلاس بیشتر است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"کلاس A",
|
||||||
|
"کلاس B",
|
||||||
|
"برابرند",
|
||||||
|
"نمیتوان گفت"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۱: الگوها",
|
||||||
|
"الگوها — گروه 3",
|
||||||
|
"جمع ذهنی دستهها"
|
||||||
|
],
|
||||||
|
"prompt": "در باغی ۱۲ سیب و ۸ پرتقال هست. تعداد کل میوهها چند است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۴",
|
||||||
|
"۹۶",
|
||||||
|
"۲۰",
|
||||||
|
"۱۸"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۱: الگوها",
|
||||||
|
"الگوها — گروه 3",
|
||||||
|
"جمع رنگها در اشکال"
|
||||||
|
],
|
||||||
|
"prompt": "شکل اول ۳ مربع قرمز و ۲ مربع آبی و شکل دوم ۴ مربع قرمز و ۵ مربع آبی دارد. روی هم چند مربع قرمز داریم؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۹",
|
||||||
|
"۷",
|
||||||
|
"۱۴",
|
||||||
|
"۱۲"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۱: الگوها",
|
||||||
|
"الگوها — گروه 5",
|
||||||
|
"ماشین حساب ساده"
|
||||||
|
],
|
||||||
|
"prompt": "ماشینی هر عدد را ۵ تا افزایش میدهد. اگر ورودی ۷ باشد خروجی چند است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۲",
|
||||||
|
"۳۵",
|
||||||
|
"۱۲",
|
||||||
|
"۷"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۱: الگوها",
|
||||||
|
"الگوها — گروه 5",
|
||||||
|
"شناسایی الگوهای عددی"
|
||||||
|
],
|
||||||
|
"prompt": "در الگوی ۲ ، ۴ ، ۶ ، ۸ ، … عدد بعدی کدام است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۹",
|
||||||
|
"۱۰",
|
||||||
|
"۱۲",
|
||||||
|
"۱۶"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۳: عددهای کسری",
|
||||||
|
"عددهای کسری — گروه 1",
|
||||||
|
"اندازهگیری طول با کسر"
|
||||||
|
],
|
||||||
|
"prompt": "نواری ۸ سانتیمتری است و بخش رنگی آن ۲ سانتیمتر است. بخش رنگی چه کسری از نوار است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۱/۲",
|
||||||
|
"۳/۴",
|
||||||
|
"۱/۴",
|
||||||
|
"۱/۳"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۳: عددهای کسری",
|
||||||
|
"عددهای کسری — گروه 2",
|
||||||
|
"تشخیص کسر از شکل"
|
||||||
|
],
|
||||||
|
"prompt": "در یک دایره ۸ بخش مساوی هست و ۳ بخش رنگی شده است. کسر بخش رنگی چیست؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۵/۸",
|
||||||
|
"۳/۸",
|
||||||
|
"۳/۵",
|
||||||
|
"۸/۳"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۵: محیط و مساحت",
|
||||||
|
"محیط و مساحت — گروه 4",
|
||||||
|
"محاسبه مساحت با شمارش مربعها"
|
||||||
|
],
|
||||||
|
"prompt": "مستطیلی با طول ۴ مربع و عرض ۳ مربع مساحتش چند مربع است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۷",
|
||||||
|
"۱۴",
|
||||||
|
"۱۲",
|
||||||
|
"۱۰"
|
||||||
|
],
|
||||||
|
"correct": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۷: آمار و احتمال",
|
||||||
|
"آمار و احتمال — گروه 3",
|
||||||
|
"تکمیل جدول و نمودار دایرهای"
|
||||||
|
],
|
||||||
|
"prompt": "۴ نفر رنگ آبی و ۴ نفر رنگ قرمز را دوست دارند. در نمودار دایرهای هر رنگ چه بخشی از دایره است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"یکچهارم",
|
||||||
|
"نصف",
|
||||||
|
"یکسوم",
|
||||||
|
"سهچهارم"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۷: آمار و احتمال",
|
||||||
|
"آمار و احتمال — گروه 4",
|
||||||
|
"رسم نمودار ستونی"
|
||||||
|
],
|
||||||
|
"prompt": "جدول: شنبه ۳ کتاب ، یکشنبه ۵ کتاب. در نمودار ستونی کدام روز ستون بلندتر دارد؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"شنبه",
|
||||||
|
"یکشنبه",
|
||||||
|
"هر دو برابر",
|
||||||
|
"هیچکدام"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۷: آمار و احتمال",
|
||||||
|
"آمار و احتمال — گروه 6",
|
||||||
|
"نمایش دادهها با نمودار تصویری"
|
||||||
|
],
|
||||||
|
"prompt": "در نمودار تصویری هر 🙂 یعنی ۲ کتاب. برای ۴ کتاب چند 🙂 میگذاریم؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۴",
|
||||||
|
"۲",
|
||||||
|
"۱",
|
||||||
|
"۸"
|
||||||
|
],
|
||||||
|
"correct": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"پایه سوم",
|
||||||
|
"فصل ۸: ضرب عددها",
|
||||||
|
"ضرب عددها — گروه 2",
|
||||||
|
"ضرب با استفاده از شکلهای نمایشی"
|
||||||
|
],
|
||||||
|
"prompt": "در یک باغ ۳ ردیف درخت هست و در هر ردیف ۴ درخت. روی هم چند درخت است؟",
|
||||||
|
"type": "multiple_choice",
|
||||||
|
"choices": [
|
||||||
|
"۷",
|
||||||
|
"۱۶",
|
||||||
|
"۳۴",
|
||||||
|
"۱۲"
|
||||||
|
],
|
||||||
|
"correct": 3
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user