130 lines
3.7 KiB
Python
130 lines
3.7 KiB
Python
"""
|
||
小学数学苏格拉底导师 — 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)
|