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:
- From
question/sourceread out the known conditions - Use Python for actual calculation (quick reference table below)
- Compare whether
answer/solutionmatches - 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)
geometryis precise geometry (calm),illustrationis 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 Wordprompt: 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: defaultlow(NT$0.3), only upgrade on key pagesmediumwidth_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
viewtool to view/home/claude/geometry_output/*.svg, and confirm the diagram is correct before continuing. If there is an error, modify the corresponding problem'sgeometry.spec.configand 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
viewtool 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:
illustrationgenerated (Step 5.7)geometrygenerated (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.ttcwith a system Chinese bold font (for examplePingFang.ttcorNoto Sans CJK TC) - Windows without cairo:
geometry_rendererafter 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
- Fractions: always use {frac(numerator,denominator)}; do not substitute /
- Exponents: use {x^2}; do not paste the ² character directly
- Square roots: use {sqrt(x)}; do not paste the √ character directly
- Simple equations: for things like 2x + 3 = 7, no {} is needed; keep it as ordinary text
- 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, use200×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"}}} |