math-tutor/app.py

130 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
小学数学苏格拉底导师 — Flask Web 应用
"""
import json
import uuid
from flask import Flask, request, jsonify, render_template, session
from tutor.wiki import StudentWiki
from tutor.socratic import build_socratic_prompt, detect_topic, build_visual_hint
from tutor.hermes_bridge import call_hermes
from tutor.visualize import (
pie_chart, number_line, grid_array, bar_chart, shapes_library
)
app = Flask(__name__)
app.secret_key = "math-tutor-socratic-2026"
def get_or_create_wiki() -> StudentWiki:
"""为当前会话获取/创建学生 Wiki"""
if "student_id" not in session:
session["student_id"] = f"student_{uuid.uuid4().hex[:8]}"
return StudentWiki(session["student_id"])
@app.route("/")
def index():
wiki = get_or_create_wiki()
return render_template("index.html",
student_id=wiki.student_id,
shapes=shapes_library())
@app.route("/ask", methods=["POST"])
def ask():
"""核心接口:学生提问 → Hermes 苏格拉底引导 + 可视化"""
data = request.json or {}
question = data.get("question", "").strip()
if not question:
return jsonify({"error": "请输入你的问题哦~"}), 400
wiki = get_or_create_wiki()
student_profile = wiki.get_knowledge_summary()
topic = detect_topic(question)
# 构建苏格拉底 prompt
prompt = build_socratic_prompt(question, student_profile, topic)
socratic_reply = call_hermes(prompt)
visual_hint = build_visual_hint(topic, question)
# 生成可视化 SVG
svg = _generate_svg_for_topic(topic, question)
# 摄入 Wiki
wiki.ingest_session(question, "", [], "")
return jsonify({
"reply": socratic_reply,
"visual_hint": visual_hint,
"topic": topic,
"svg": svg,
"student_profile": student_profile
})
@app.route("/draw", methods=["POST"])
def draw():
"""学生请求可视化 → 生成 SVG"""
data = request.json or {}
viz_type = data.get("type", "pie")
params = data.get("params", {})
svg = ""
if viz_type == "pie":
fractions = params.get("fractions", [1, 1])
title = params.get("title", "")
svg = pie_chart(fractions, title)
elif viz_type == "numberline":
svg = number_line(
params.get("start", 0),
params.get("end", 10),
params.get("highlights", [])
)
elif viz_type == "grid":
svg = grid_array(
params.get("rows", 3),
params.get("cols", 4),
params.get("highlight_cells", [])
)
elif viz_type == "bar":
svg = bar_chart(
params.get("values", [3, 7, 2, 5]),
params.get("labels", []),
params.get("title", "")
)
return jsonify({"svg": svg})
@app.route("/profile", methods=["GET"])
def profile():
wiki = get_or_create_wiki()
return jsonify(wiki.get_schema())
@app.route("/milestone", methods=["POST"])
def milestone():
wiki = get_or_create_wiki()
data = request.json or {}
wiki.record_milestone(data.get("topic", ""), data.get("description", ""))
return jsonify({"status": "ok"})
def _generate_svg_for_topic(topic: str, question: str) -> str:
"""根据主题和问题自动生成 SVG"""
if topic == "fraction":
return pie_chart([1, 1, 1, 1], "试试分披萨?")
elif topic == "geometry":
return grid_array(3, 4, title_tag=False) if "面积" in question else ""
elif topic == "arithmetic":
if "" in question or "×" in question:
return grid_array(3, 4, list(range(12)))
return number_line(0, 10)
return ""
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8765, debug=True)