SuperAPI AI Directory

jh-math-context-questions

Writing & research

View repository

Updated 10-15 17:50

About this skill

Junior High School Mathematics Real-Life Context Non-Multiple-Choice Question Design Skill

Skill Overview

This skill, according to the junior high school mathematics unit specified by the user, searches online for related real-life contexts and current news events, transforms real materials into non-multiple-choice questions, and exports a correctly formatted Word document.

  • Five major questions, each major question contains two sub-questions
  • Sub-question (1): Read the material; answer by understanding the conditions (Bloom Level 2: Understanding)
  • Sub-question (2): Follows sub-question (1), combined with the mathematics unit, reaching the application or analysis level (Bloom Levels 3-4)
  • Each major question includes a complete solution
  • Geometry figure support: If the question involves a geometric context, automatically render a PNG and insert it into Word (built-in, no external skill required)
  • Export Word document, mathematical expressions presented in correct OMML format

Mathematical Markup Syntax (Question Writing Rules)

In the question text, enclose mathematical expressions in curly braces {}. The system will automatically convert them to the correct Word format:

Markup syntax Description
{x^2} x squared (superscript)
{x^{n+1}} Superscript containing an expression
{x_1} x subscript 1
{sqrt(2)} Square root of 2
{sqrt3} Cube root of 8
{frac(3,4)} Fraction 3/4
{(a+b)/2} Fraction containing an expression
{ x-3
{<=} Less than or equal to symbol
{>=} Greater than or equal to symbol
{!=} Not equal to symbol

Process Flow

Step 1: Confirm requirements

Confirm with the user (ask if not provided): mathematics unit, grade level (7th/8th/9th grade), version (optional)


Step 2: Search online for real-life contexts

Use the web_search tool to perform at least 3–5 searches, finding 5 different topic materials:

  • Shopping/consumption, environment/climate, transportation/sports, health/medical, technology/finance

Step 3: Design questions

Structure of each major question: material paragraph + sub-question (1) (understanding level) + sub-question (2) (application/analysis level). Mathematical expressions must use {} markup. The two sub-questions must be coherent, with sub-question (2) using the answer from sub-question (1).


Step 4: Write solutions

Each major question includes: (1) key to solving + calculation process, (2) solution process + answer + Bloom level annotation.


Step 4.5: Mathematical self-verification (required, before image generation and export)

⚠️ Before producing mathematics questions, Python must be run for actual calculation. AI often has the problem that "the solution description is correct, but the final numerical value is calculated incorrectly." If verification fails, correct it, then continue.

For each (1)/(2) sub-question:

  1. From question / source read out the known conditions
  2. Use Python for actual calculation (quick reference table below)
  3. Compare whether answer / solution matches
  4. If there is a discrepancy → correct the JSON (prioritize changing solution/answer) and rerun

Quick Reference for Verifying Common Question Types:

Question type Verification method
Proportional expression (a:b = c:d) a*d == b*c
Allocate a total amount proportionally Total × each share / total shares
Time/Speed/Distance t = d/v, sum of multiple segments
Pythagorean theorem c² = a² + b²
Law of cosines c² = a² + b² − 2ab·cos(C)
Inscribed angle theorem Inscribed angle = central angle / 2
Arc length/sector area 2πr × (θ/360) / πr² × (θ/360)
Triangle congruence SAS/SSS/ASA Corresponding sides/angles equal
Budget/construction cost Quantity × unit price vs budget

Execution template:

# /home/claude/verify_math.py
import json, math
from pathlib import Path

exam = json.loads(Path('/home/claude/exam_data.json').read_text(encoding='utf-8'))
issues = []

for q in exam['questions']:
    n = q['number']
    # 對本份題目逐題實算(見速查表)
    # 範例:餘弦定理
    # ac_sq = 12**2 + 8**2 - 2*12*8*math.cos(math.radians(60))
    # if abs(ac_sq - 112) > 0.1: issues.append(f"q{n} AC² 算錯")
    pass

if issues:
    print("[ABORT] 驗算失敗:", issues)
    raise SystemExit(1)
print(f"[OK] 驗算通過 {len(exam['questions'])} 題")

Implementation discipline: if verification fails → Step 5 is not allowed.


Step 5: Organize into JSON data

Organize the problem into the following format and save it as /home/claude/exam_data.json:

{
  "title": "國中數學非選擇題",
  "unit": "單元名稱",
  "grade": "年級",
  "questions": [
    {
      "number": 1,
      "total_points": 10,
      "source": "素材文字,可含 {math} 標記",
      "geometry": null,
      "sub1": {
        "points": 4,
        "question": "第(1)題題目,可含 {math} 標記",
        "answer": "答案說明",
        "solution": "解題過程",
        "geometry": null
      },
      "sub2": {
        "points": 6,
        "question": "第(2)題題目,可含 {math} 標記",
        "answer": "答案說明",
        "solution": "解題過程",
        "bloom_level": "第3級(應用)",
        "bloom_reason": "判定理由",
        "geometry": null
      }
    }
  ]
}

▶ illustration field description (scenario atmosphere image, generated by the draw skill)

illustration Allows the problem to embed a "daily-life scenario atmosphere image," supplementing the sense of immersion that precise geometric diagrams lack.Can coexist with geometry coexist。

When to add illustration:

  • Scenario problems inherently should have a sense of scenario (bubble tea shop, coastal track, technology classroom, lotus pond)
  • geometry is precise geometry (calm),illustration is scenario atmosphere (warm)
  • Especially suitable for covers and action-page-style major problems

illustration field format(placed at the problem level or sub1/sub2):

"illustration": {
  "id": "q1_illus",
  "prompt": "一個溫馨的手搖飲店櫃檯,一杯檸檬紅茶,旁邊有一包糖與一瓶水,扁平向量插畫,柔和色調,無任何文字",
  "size": "1024x1024",
  "quality": "low",
  "width_cm": 5.5
}

Field description:

  • id: unique identifier, used when inserting into Word
  • prompt: the complete prompt sent to the draw skill (append "no text at all" at the end to prevent the AI from writing random text)
  • size:1024x1024(square)/1024x1536(portrait)/1536x1024(landscape)
  • quality: default low(NT$0.3), only upgrade on key pages medium
  • width_cm: width when inserting into Word (cm), default 5.5

Image generation call(automatically executed in Step 5.7):

python ~/.claude/skills/draw/draw.py \
  "<prompt>" --size <size> --quality <quality> \
  --name <id> --outdir /home/claude/illustrations/

▶ geometry field description

geometry Can be placed at the major problem level (source side, one diagram shared by the whole problem) or at the subproblem level (sub1/sub2, each with its own diagram). When no diagram is needed, set it to null。

When geometry needs to be filled in:

  • The problem text contains "as shown in the figure," "figure on the right," "figure below"
  • The scenario involves geometric shapes (land area, building structure, route distance, etc.)
  • A diagram is needed to assist reading comprehension of the problem

geometry field format:

"geometry": {
  "caption": "圖一",
  "spec": {
    "type": "triangle",
    "config": {
      "subtype": "right",
      "vertex_labels": ["A", "B", "C"],
      "right_angle_at": "C",
      "side_labels": {"AB": "5", "BC": "3", "CA": "4"}
    },
    "canvas": {"width": 260, "height": 210}
  }
}

For the type and config parameters of all geometry.spec, see the "Geometry Parameter Quick Reference (inline)" section at the end of this SKILL.md


Step 5.5: Geometry rendering (if the JSON has a geometry field)

Determine whether execution is needed: scan exam_data.json, if the geometry field of any problem or subproblem is not null, then execute this step.

# ── 0. 確認幾何腳本位置 ────────────────────────────────────
GEOM_DIR=""
for d in /mnt/skills/user/jh-math-geometry/scripts \
          /tmp/jh-math-geometry/scripts; do
  [ -f "$d/geometry_renderer.py" ] && GEOM_DIR="$d" && break
done
echo "幾何腳本目錄:$GEOM_DIR"

# ── 1. 安裝依賴 ─────────────────────────────────────────────
pip install cairosvg python-docx --break-system-packages -q

# ── 2. 從 exam_data.json 提取所有 geometry spec ─────────────
python3 - <<'PYEOF'
import json

exam = json.load(open('/home/claude/exam_data.json', encoding='utf-8'))
figures = []

def collect(geo, prefix):
    if geo and geo.get('spec'):
        spec = dict(geo['spec'])
        spec['id'] = prefix
        spec.setdefault('canvas', {'width': 260, 'height': 210})
        figures.append({
            'id': spec['id'],
            'type': spec['type'],
            'config': spec.get('config', {}),
            'canvas': spec['canvas'],
            '_caption': geo.get('caption', ''),
            '_key': prefix
        })

for q in exam.get('questions', []):
    n = q['number']
    collect(q.get('geometry'), f"q{n}_main")
    collect(q.get('sub1', {}).get('geometry'), f"q{n}_sub1")
    collect(q.get('sub2', {}).get('geometry'), f"q{n}_sub2")

if figures:
    spec_data = {
        'figures': [{'id': f['id'], 'type': f['type'], 'config': f['config'], 'canvas': f['canvas']} for f in figures],
        'options': {'format': 'png', 'dpi': 150}
    }
    json.dump(spec_data, open('/home/claude/geometry_spec.json', 'w', encoding='utf-8'), ensure_ascii=False, indent=2)
    mapping = {f['id']: {'caption': f['_caption'], 'key': f['_key']} for f in figures}
    json.dump(mapping, open('/home/claude/geometry_mapping.json', 'w', encoding='utf-8'), ensure_ascii=False, indent=2)
    print(f"✅ 需要渲染 {len(figures)} 張幾何圖形")
else:
    print("ℹ️ 本份題目無幾何圖形,跳過渲染")
PYEOF

# ── 3. 渲染 ────────────────────────────────────────────────
if [ -f /home/claude/geometry_spec.json ]; then
  mkdir -p /home/claude/geometry_output
  python3 "$GEOM_DIR/geometry_renderer.py" \
      /home/claude/geometry_spec.json \
      /home/claude/geometry_output/
  echo "✅ 幾何圖形渲染完成:"
  ls /home/claude/geometry_output/*.png 2>/dev/null
fi

Visual confirmation: use the view tool to view /home/claude/geometry_output/*.svg, and confirm the diagram is correct before continuing. If there is an error, modify the corresponding problem's geometry.spec.config and rerun.


Step 5.7: Generate scenario illustrations (if the JSON has an illustration field)

Determine whether execution is needed: scan exam_data.json, if the illustration field of any problem or subproblem is not nullthen execute this step.

mkdir -p /home/claude/illustrations
DRAW="python ~/.claude/skills/draw/draw.py"   # draw 生圖技能路徑;請改成你環境的對應路徑

python3 - <<'PYEOF'
import json, subprocess, sys
from pathlib import Path

exam = json.load(open('/home/claude/exam_data.json', encoding='utf-8'))
jobs = []
def collect(block):
    if block and block.get('illustration'):
        jobs.append(block['illustration'])

for q in exam.get('questions', []):
    collect(q)
    collect(q.get('sub1'))
    collect(q.get('sub2'))

print(f"→ 共 {len(jobs)} 張情境圖要生")
for job in jobs:
    cmd = [
        sys.executable,
        "~/.claude/skills/draw/draw.py",
        job['prompt'],
        '--size', job.get('size', '1024x1024'),
        '--quality', job.get('quality', 'low'),
        '--name', job['id'],
        '--outdir', '/home/claude/illustrations/',
    ]
    result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8', errors='replace')
    print(f"  [draw] {job['id']}  rc={result.returncode}")
PYEOF

ls /home/claude/illustrations/*.png 2>/dev/null

Visual confirmation: after generation, use the view tool to check whether the scenario image matches the theme (classroom scenario, in-store scenario, etc.). If it does not meet the standard, regenerate that image.


Step 6: Export the Word document (required)

# Step 6-1:找腳本目錄
for d in /mnt/skills/user/jh-math-context-questions/scripts \
          /tmp/jh-math-context-questions/scripts; do
  [ -f "$d/generate_exam_docx.py" ] && SKILL_SCRIPTS="$d" && break
done
echo "腳本目錄:$SKILL_SCRIPTS"

# Step 6-2:安裝相依套件
pip install python-docx lxml --break-system-packages -q

# Step 6-3:產生文件
cd "$SKILL_SCRIPTS"
python3 generate_exam_docx.py /home/claude/exam_data.json /home/claude/exam_output.docx
echo "✅ Word 初稿產出完成"

Step 6.5: Insert geometric figures into Word (if there are geometric figures)

if [ ! -f /home/claude/geometry_mapping.json ]; then
  echo "ℹ️ 無幾何圖形,跳過插入步驟"
else

GEOM_DIR=""
for d in /mnt/skills/user/jh-math-geometry/scripts \
          /tmp/jh-math-geometry/scripts; do
  [ -f "$d/insert_to_docx.py" ] && GEOM_DIR="$d" && break
done

python3 - <<PYEOF
import json, sys, re
from pathlib import Path
from docx import Document
from docx.shared import Cm, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH
sys.path.insert(0, "$GEOM_DIR")

mapping = json.load(open('/home/claude/geometry_mapping.json', encoding='utf-8'))
doc = Document('/home/claude/exam_output.docx')

# 建立「題號關鍵字 → 段落索引」對照
para_index = {}
for i, para in enumerate(doc.paragraphs):
    text = para.text.strip()
    # 大題標記:「第1題」「第一題」等
    m = re.search(r'第\s*([1-5一二三四五])\s*題', text)
    if m:
        raw = m.group(1)
        n = {'一':1,'二':2,'三':3,'四':4,'五':5}.get(raw, int(raw) if raw.isdigit() else None)
        if n:
            para_index.setdefault(f"q{n}_main", i)
    # 子題標記
    for sub_label, sub_key_suffix in [('(1)','sub1'),('(1)','sub1'),('(2)','sub2'),('(2)','sub2')]:
        if sub_label in text:
            for back in range(1, 10):
                if i - back < 0:
                    break
                prev = doc.paragraphs[i - back].text.strip()
                m2 = re.search(r'第\s*([1-5一二三四五])\s*題', prev)
                if m2:
                    raw2 = m2.group(1)
                    n2 = {'一':1,'二':2,'三':3,'四':4,'五':5}.get(raw2, int(raw2) if raw2.isdigit() else None)
                    if n2:
                        para_index.setdefault(f"q{n2}_{sub_key_suffix}", i)
                    break

# 從後往前插入
insertions = []
for fig_id, info in mapping.items():
    png = Path(f"/home/claude/geometry_output/{fig_id}.png")
    if not png.exists():
        print(f"⚠️ 找不到圖形:{png}")
        continue
    key = info['key']
    caption = info.get('caption', '')
    idx = para_index.get(key, len(doc.paragraphs) - 1)
    insertions.append((idx, png, caption))

insertions.sort(key=lambda x: -x[0])

for para_idx, png, caption in insertions:
    img_para = doc.add_paragraph()
    img_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    img_para.add_run().add_picture(str(png), width=Cm(6.0))
    if caption:
        cap_para = doc.add_paragraph(caption)
        cap_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
        for r in cap_para.runs:
            r.font.size = Pt(9)
            r.font.italic = True
    target_el = doc.paragraphs[para_idx]._element
    if caption:
        target_el.addnext(cap_para._element)
    target_el.addnext(img_para._element)
    print(f"✅ 插入 {png.name} → 段落 {para_idx}")

doc.save('/home/claude/exam_output.docx')
print("✅ 幾何圖形插入完成")
PYEOF

fi

Step 6.6: Insert scenario illustrations into Word (if there is an illustration)

if [ ! -d /home/claude/illustrations ] || [ -z "$(ls -A /home/claude/illustrations/*.png 2>/dev/null)" ]; then
  echo "ℹ️ 無情境插圖,跳過"
else

python3 - <<'PYEOF'
import json, re, glob
from pathlib import Path
from docx import Document
from docx.shared import Cm, Pt
from docx.enum.text import WD_ALIGN_PARAGRAPH

exam = json.load(open('/home/claude/exam_data.json', encoding='utf-8'))
doc = Document('/home/claude/exam_output.docx')

# 建立「題號 → 段落索引」對照
para_idx = {}
for i, p in enumerate(doc.paragraphs):
    m = re.search(r'第\s*([1-5一二三四五])\s*題', p.text.strip())
    if m:
        raw = m.group(1)
        n = {'一':1,'二':2,'三':3,'四':4,'五':5}.get(raw, int(raw) if raw.isdigit() else None)
        if n:
            para_idx.setdefault(f"q{n}_main", i)

# 蒐集每題 illustration
inserts = []
for q in exam['questions']:
    il = q.get('illustration')
    if not il:
        continue
    key = f"q{q['number']}_main"
    if key not in para_idx:
        continue
    cands = sorted(glob.glob(f"/home/claude/illustrations/{il['id']}_*.png"))
    if not cands:
        continue
    inserts.append((para_idx[key], cands[-1], float(il.get('width_cm', 5.5))))

# 從後往前插(避免索引變動)
inserts.sort(key=lambda x: -x[0])
for idx, png_path, width_cm in inserts:
    img_para = doc.add_paragraph()
    img_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    img_para.add_run().add_picture(png_path, width=Cm(width_cm))
    doc.paragraphs[idx]._element.addnext(img_para._element)
    print(f"✅ 插入情境圖:{Path(png_path).name} → 段落 {idx}")

doc.save('/home/claude/exam_output.docx')
print("✅ 情境圖插入完成")
PYEOF

fi

Step 6.7: Copy the final output

UNIT=$(python3 -c "import json; d=json.load(open('/home/claude/exam_data.json')); print(d['unit'])")
cp /home/claude/exam_output.docx "/mnt/user-data/outputs/非選擇題_${UNIT}.docx"
echo "輸出:/mnt/user-data/outputs/非選擇題_${UNIT}.docx"

Finally, use the present_files tool to provide the Word file for download.


Step 7 (optional): Produce a "Word-style portrait PNG" (run only if the user requests it)

If the user says "make another Word-style image version」、「output the same thing as a portrait image」、「make it something shareable on LINE," execute this step.

Features: portrait A4 PNG (1240×1754, 150dpi), text 100% accurate (Pillow rendering), scenario image + precise geometric figure side by side, can be posted directly to LINE/IG/FB.

Prerequisites:

  • illustration generated (Step 5.7)
  • geometry generated (Step 5.5)
  • Self-verification passed (Step 4.5)

Run script:

# /home/claude/compose_word_style.py
import json, re, glob, sys
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont

W, H = 1240, 1754
MARGIN = 70
BLACK = (30, 30, 30); BLUE = (30, 58, 95); GOLD = (201, 152, 0); GRAY = (150, 150, 150)

# Windows:C:/Windows/Fonts/msjh*.ttc;macOS:/System/Library/Fonts/...
FONT_DIR = "C:/Windows/Fonts"
F_TITLE = ImageFont.truetype(f"{FONT_DIR}/msjhbd.ttc", 36)
F_SUB = ImageFont.truetype(f"{FONT_DIR}/msjh.ttc", 20)
F_INFO = ImageFont.truetype(f"{FONT_DIR}/msjh.ttc", 16)
F_QNUM = ImageFont.truetype(f"{FONT_DIR}/msjhbd.ttc", 22)
F_PTS = ImageFont.truetype(f"{FONT_DIR}/msjhbd.ttc", 18)
F_SRC = ImageFont.truetype(f"{FONT_DIR}/msjh.ttc", 18)
F_CAP = ImageFont.truetype(f"{FONT_DIR}/msjh.ttc", 12)

def clean_math(t):
    t = re.sub(r"\{frac\(([^,]+),([^)]+)\)\}", r"\1/\2", t)
    t = t.replace("{x^2}", "x²")
    return re.sub(r"\{([^{}]+)\}", r"\1", t)

def wrap(draw, text, font, max_w):
    out = []
    for para in text.split("\n"):
        line = ""
        for ch in para:
            if draw.textlength(line + ch, font=font) > max_w and line:
                out.append(line); line = ch
            else:
                line += ch
        out.append(line)
    return out

def block(draw, text, x, y, font, max_w, gap=6, color=BLACK):
    for ln in wrap(draw, text, font, max_w):
        draw.text((x, y), ln, font=font, fill=color)
        y += font.size + gap
    return y

def badge(draw, x, y, text, font, bg, fg):
    tw = draw.textlength(text, font=font); th = font.size
    w, h = int(tw + 24), int(th + 12)
    draw.rounded_rectangle((x, y, x+w, y+h), radius=8, fill=bg)
    draw.text((x+12, y+4), text, font=font, fill=fg)
    return y + h

exam = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
out = sys.argv[2] if len(sys.argv) > 2 else "/home/claude/exam_word_style.png"

img = Image.new("RGB", (W, H), "white")
d = ImageDraw.Draw(img)

# 頂部
y = MARGIN
title = exam.get("title", "國中數學非選擇題")
d.text(((W - d.textlength(title, font=F_TITLE))//2, y), title, font=F_TITLE, fill=BLACK)
y += F_TITLE.size + 8
sub = f"{exam.get('grade','')} {exam.get('unit','')} 滿分 20 分"
d.text(((W - d.textlength(sub, font=F_SUB))//2, y), sub, font=F_SUB, fill=GRAY)
y += F_SUB.size + 12
info = "班級 _______ 座號 _______ 姓名 ________________"
d.text(((W - d.textlength(info, font=F_INFO))//2, y), info, font=F_INFO, fill=BLACK)
y += F_INFO.size + 16
d.line([(MARGIN, y), (W-MARGIN, y)], fill=BLACK, width=2); y += 20

base = Path(sys.argv[1]).parent
for q in exam["questions"]:
    n = q["number"]
    y2 = badge(d, MARGIN, y, f"第 {n} 題", F_QNUM, BLUE, (255,255,255))
    # 分數
    pts = f"{q.get('total_points',10)} 分"
    pw = d.textlength(pts, font=F_PTS)
    d.rounded_rectangle((W-MARGIN-pw-24, y, W-MARGIN, y2), radius=8, fill=GOLD)
    d.text((W-MARGIN-pw-12, y+6), pts, font=F_PTS, fill=(255,255,255))
    y = y2 + 14

    # 右側圖區
    img_x = W - MARGIN - 320
    img_y = y

    # 情境圖
    cands = sorted(glob.glob(str(base / "illustrations" / f"q{n}_illus_*.png"))) + \
            sorted(glob.glob(f"/home/claude/illustrations/q{n}_illus_*.png"))
    if cands:
        il = Image.open(cands[-1]).convert("RGB")
        il.thumbnail((320, 320), Image.LANCZOS)
        img.paste(il, (img_x, img_y))
        img_y += il.height + 20

    # 幾何圖
    geom_cands = [base / "geometry_output" / f"q{n}_main.png",
                  Path(f"/home/claude/geometry_output/q{n}_main.png")]
    for gp in geom_cands:
        if gp.exists():
            g = Image.open(gp).convert("RGB")
            g.thumbnail((320, 320), Image.LANCZOS)
            img.paste(g, (img_x, img_y))
            img_y += g.height + 20
            break

    # 左側文字
    text_w = img_x - MARGIN - 20
    y = block(d, clean_math(q.get("source","")), MARGIN, y, F_SRC, text_w)
    y += 12
    for key, lab in [("sub1","(1)"), ("sub2","(2)")]:
        s = q.get(key, {})
        txt = f"{lab} ({s.get('points',0)}分) {clean_math(s.get('question',''))}"
        y = block(d, txt, MARGIN, y, F_SRC, text_w)
        y += 58  # 作答空白
    y = max(y, img_y) + 14
    d.line([(MARGIN, y), (W-MARGIN, y)], fill=GRAY, width=1)
    y += 20

img.save(out, "PNG")
print(f"[OK] {out}")

Run:

python3 /home/claude/compose_word_style.py /home/claude/exam_data.json /home/claude/exam_word_style.png
UNIT=$(python3 -c "import json; print(json.load(open('/home/claude/exam_data.json'))['unit'])")
cp /home/claude/exam_word_style.png "/mnt/user-data/outputs/非選擇題_${UNIT}_word風.png"

Cross-platform notes:

  • macOS/Linux: replace C:/Windows/Fonts/msjhbd.ttc with a system Chinese bold font (for example PingFang.ttc or Noto Sans CJK TC)
  • Windows without cairo:geometry_renderer after generating SVG, you can use Edge headless to convert to PNG:
    "C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" \
      --headless --disable-gpu --window-size=520,420 \
      --screenshot="<abs_out.png>" "file:///<abs_in.svg>"
    

Math format notes

  1. Fractions: always use {frac(numerator,denominator)}; do not substitute /
  2. Exponents: use {x^2}; do not paste the ² character directly
  3. Square roots: use {sqrt(x)}; do not paste the √ character directly
  4. Simple equations: for things like 2x + 3 = 7, no {} is needed; keep it as ordinary text
  5. Inequality symbols: always use {<=}, {>=}, {!=}

Bloom level quick reference

Level Definition Corresponding sub-question
Level 2: Understanding Understand the conditions, organize information, convert Sub-question (1)
Level 3: Applying Substitute into formulas, solve, calculate results Sub-question (2) (applying)
Level 4: Analyzing Compare, infer, judge reasonableness Sub-question (2) (analyzing)

Common math unit notation examples

Unit Notation example
Linear equation in one variable Let the unknown be {x}, write the equation {2*x+3=15}
Linear inequality in one variable {x} {>=} 100
Ratio and proportion {frac(a,b)} = {frac(c,d)}
Polynomials (Grade 8) {x^2} + 2x - 3 = 0
Radicals (Grade 9) {sqrt(2)}、{sqrt(3)}
Quadratic equations {x^2} + bx + c = 0

Geometric figure parameter quick reference (inline)

This section fully includes all supported figure types and parameters; when writing geometry.spec , consult it directly; no need to read external files.

General structure

{
  "id": "fig1",
  "type": "<圖形類型>",
  "config": { ... },
  "canvas": { "width": 260, "height": 210 }
}

Suggested canvas for figures used in non-multiple-choice scenario questions:260×210(single figure); for two figures side by side, use 200×160


1. triangle (三角形)

Parameters Description Optional values
subtype Type general(default),right、isosceles、equilateral
vertex_labels Vertex labels String array, default ["A","B","C"]
right_angle_at Right-angle symbol vertex Vertex label string
angle_arcs Number of angle arcs at each vertex {"A":1, "B":2}
side_labels Text label for each side {"AB":"5", "BC":"3"}
equal_marks Number of equal-side tick marks {"AB":1, "CD":2}
altitude_from Vertex for drawing altitude Vertex label string
median_from Vertex for drawing median Vertex label string
dashed_sides Sides drawn as dashed lines ["AB"]
show_dots Vertex dots true(default)

Common examples:

Right triangle (right angle at C, labeled side lengths):

{"type":"triangle","config":{"subtype":"right","vertex_labels":["A","B","C"],"right_angle_at":"C","side_labels":{"AB":"5","BC":"3","CA":"4"}}}

Isosceles triangle (marked equal sides + base angles):

{"type":"triangle","config":{"subtype":"isosceles","equal_marks":{"AB":1,"AC":1},"angle_arcs":{"B":1,"C":1}}}

2. quadrilateral (四邊形)

Parameters Description
subtype parallelogram / rectangle / rhombus / square / trapezoid / right_trapezoid / general
vertex_labels Vertex label array, default ["A","B","C","D"]
side_labels Text label for each side
equal_marks Equal-side tick marks
right_angles Vertex array marked with right angles
diagonals Whether to draw diagonals,true/false
diagonal_labels Diagonal labels {"AC":"m","BD":"n"}

Common examples:

Parallelogram (with diagonals):

{"type":"quadrilateral","config":{"subtype":"parallelogram","vertex_labels":["A","B","C","D"],"diagonals":true,"equal_marks":{"AB":1,"CD":1,"BC":2,"AD":2}}}

Isosceles trapezoid:

{"type":"quadrilateral","config":{"subtype":"trapezoid","vertex_labels":["A","B","C","D"],"equal_marks":{"AB":1,"CD":1}}}

Rectangle (labeled side lengths):

{"type":"quadrilateral","config":{"subtype":"rectangle","vertex_labels":["A","B","C","D"],"side_labels":{"AB":"8","BC":"5"}}}

3. circle (圓)

Parameters Description
center_label Center label, default "O"
show_center Whether to show the center point
points Points on the circumference:{"A": 60, "B": 160}(angle, 0=right, 90=up, counterclockwise)
radius_lines Array of points for drawing radius lines
radius_label Radius labels
chords Chord:[["A","B"],["C","D"]]
diameter Diameter endpoints ["A","C"]
tangent_at Tangent point
central_angle Two endpoints of the filled sector (central angle)
inscribed_angle Inscribed angle:{"vertex":"C","arc":["A","B"]}

Common examples:

Central angle and inscribed angle:

{"type":"circle","config":{"center_label":"O","points":{"A":30,"B":150,"C":270},"radius_lines":["A","B"],"central_angle":["A","B"],"inscribed_angle":{"vertex":"C","arc":["A","B"]}}}

Sector (filled central angle):

{"type":"circle","config":{"center_label":"O","points":{"A":20,"B":110},"radius_lines":["A","B"],"central_angle":["A","B"],"radius_label":"r"}}

4. coordinate_plane (坐標平面)

Parameters Description
x_range x-axis range, e.g. [-5, 5]
y_range y-axis range
show_grid Whether to show grid lines
tick_interval Tick interval
points Point array:[{"x":1,"y":2,"label":"A","color":"#cc0000"}]
lines Line:[{"slope":2,"intercept":-1,"label":"y=2x-1"}]
parabolas Parabola:[{"a":1,"b":0,"c":-2,"label":"y=x²-2"}]
segments Line segment:[{"x1":0,"y1":0,"x2":3,"y2":4}]

Common examples:

Linear function y=2x-1:

{"type":"coordinate_plane","config":{"x_range":[-3,5],"y_range":[-4,8],"lines":[{"slope":2,"intercept":-1,"label":"y=2x-1"}],"points":[{"x":0,"y":-1,"label":"(0,-1)"}]}}

Parabola y=x²-2x-3:

{"type":"coordinate_plane","config":{"x_range":[-2,5],"y_range":[-5,6],"parabolas":[{"a":1,"b":-2,"c":-3,"label":"y=x²-2x-3"}]}}

5. solid_3d (立體圖形)

subtype Description Vertex order
rectangular_prism Quadrilateral prism (rectangular cuboid) ABCD (top) EFGH (bottom)
cylinder Cylinder labels: radius, height
cone Cone labels: apex, base, radius, slant, height
triangular_prism Triangular prism ABC (back) DEF (front)
square_pyramid Quadrilateral pyramid ABCD (base) P (apex)
triangular_pyramid Triangular pyramid ABCD

Shared parameters:show_hidden(dashed edges),vertex_labels、labels(label radius/height/slant height)

Quadrilateral prism (labeled vertices + dashed lines):

{"type":"solid_3d","config":{"subtype":"rectangular_prism","vertex_labels":["A","B","C","D","E","F","G","H"],"show_hidden":true}}

6. parallel_lines (平行線截角)

Parameters Description
n_parallel Number of parallel lines (usually 2)
line_labels Parallel line label ["l","m"]
transversal_angle Transversal angle (degrees)
transversal_labels Transversal label ["t"]
angle_marks Angle markers:[{"line":0,"position":"upper_left","label":"A"}]; position is upper_left/upper_right/lower_left/lower_right
{"type":"parallel_lines","config":{"n_parallel":2,"line_labels":["l","m"],"transversal_angle":55,"angle_marks":[{"line":0,"position":"upper_left","label":"A"},{"line":1,"position":"upper_left","label":"E"}]}}

7. triangle_center (center of a triangle)

center_type Description Default label
centroid Centroid (three medians) G
circumcenter Circumcenter (circumcircle) O
incenter Incenter (incircle) I
{"type":"triangle_center","config":{"center_type":"centroid","center_label":"G","triangle":{"subtype":"general","vertex_labels":["A","B","C"]}}}

8. similar_triangles (similar triangles)

The two triangles are each configured separately and displayed side by side; identical angle arcs indicate equal angles:

{"type":"similar_triangles","config":{"triangle1":{"vertex_labels":["A","B","C"],"angle_arcs":{"A":1,"B":2},"side_labels":{"AB":"3","BC":"4","CA":"5"}},"triangle2":{"vertex_labels":["D","E","F"],"angle_arcs":{"D":1,"E":2},"side_labels":{"DE":"6","EF":"8","FD":"10"}}}}

Quick copy section (most commonly used for scenario problems)

Scenario problem type Spec to copy directly
Right triangle (Pythagorean theorem) {"type":"triangle","config":{"subtype":"right","vertex_labels":["A","B","C"],"right_angle_at":"C","side_labels":{"AB":"c","BC":"a","CA":"b"}}}
Rectangular land/building {"type":"quadrilateral","config":{"subtype":"rectangle","vertex_labels":["A","B","C","D"],"side_labels":{"AB":"長","BC":"寬"}}}
Trapezoidal cross-section {"type":"quadrilateral","config":{"subtype":"trapezoid","vertex_labels":["A","B","C","D"],"side_labels":{"AD":"上底","BC":"下底"}}}
Circular site/sector {"type":"circle","config":{"center_label":"O","points":{"A":20,"B":110},"radius_lines":["A","B"],"central_angle":["A","B"],"radius_label":"r"}}
Route on the coordinate plane {"type":"coordinate_plane","config":{"x_range":[-1,6],"y_range":[-1,5],"segments":[{"x1":0,"y1":0,"x2":4,"y2":3}],"points":[{"x":0,"y":0,"label":"A"},{"x":4,"y":3,"label":"B"}]}}
Rectangular prism packaging box {"type":"solid_3d","config":{"subtype":"rectangular_prism","vertex_labels":["A","B","C","D","E","F","G","H"],"show_hidden":true}}
Cylindrical container {"type":"solid_3d","config":{"subtype":"cylinder","labels":{"radius":"r","height":"h"}}}