新增作文区与 AI 解读开关,修复 CSV 导出。
系统设置可关闭成绩复盘 AI;学生详情增加作文区(OCR/手动题目、方案与范文、历史与 MD 下载);导出改用 UTF-8 文件名响应。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -306,4 +306,81 @@ async def generate_review_insight(
|
||||
careless_hint=careless_hint,
|
||||
subject_hints=subject_hints,
|
||||
)
|
||||
return await generate_text(prompt, cfg, temperature=0.2)
|
||||
return await generate_text(prompt, cfg, temperature=0.2)
|
||||
|
||||
|
||||
CURRICULUM_CHINESE_JUNIOR = """初中作文:记叙文、写人记事、简单议论文为主,通常 600-800 字。
|
||||
语言平实,素材来自课内与日常生活,禁止成人化腔调与超纲典故堆砌。"""
|
||||
|
||||
CURRICULUM_CHINESE_SENIOR = """高中作文:记叙、议论、材料作文为主,通常 800-1000 字。
|
||||
可适度展开论证,仍须符合课内要求,禁止大学论文式写法与超纲理论。"""
|
||||
|
||||
COMPOSITION_PROMPT = """你是一位{stage}语文老师,正在辅导{grade_text}学生完成作文。
|
||||
|
||||
【学段年级 — 严禁超纲】
|
||||
{curriculum}
|
||||
|
||||
作文题目:
|
||||
{topic}
|
||||
|
||||
请严格按以下 Markdown 结构输出(不要增加其他一级标题):
|
||||
|
||||
## 写作方案
|
||||
(审题、立意、结构提纲、段落安排、可用素材方向,分条列出,贴合{grade_text}水平)
|
||||
|
||||
## 范文
|
||||
(完整作文一篇,字数与语言风格必须符合{grade_text}课内要求,禁止超纲)
|
||||
|
||||
注意:范文必须是可直接参考的学生习作水准,不要写成评论或教案。
|
||||
"""
|
||||
|
||||
|
||||
def _chinese_curriculum(level, grade: str | None) -> str:
|
||||
is_senior = level == SchoolLevel.senior_high or level == "senior_high"
|
||||
return CURRICULUM_CHINESE_SENIOR if is_senior else CURRICULUM_CHINESE_JUNIOR
|
||||
|
||||
|
||||
def _grade_text(grade: str | None) -> str:
|
||||
if grade and grade.strip():
|
||||
return grade.strip()
|
||||
return "该学段学生"
|
||||
|
||||
|
||||
def split_composition_sections(text: str) -> tuple[str, str]:
|
||||
import re
|
||||
|
||||
text = text.strip()
|
||||
if "## 范文" not in text:
|
||||
return text.replace("## 写作方案", "").strip(), ""
|
||||
parts = re.split(r"\n##\s*范文\s*\n", text, maxsplit=1)
|
||||
plan = parts[0].replace("## 写作方案", "").strip()
|
||||
essay = parts[1].strip() if len(parts) > 1 else ""
|
||||
return plan, essay
|
||||
|
||||
|
||||
async def generate_composition(
|
||||
cfg: AIConfig,
|
||||
topic: str,
|
||||
school_level=None,
|
||||
grade: str | None = None,
|
||||
) -> tuple[str, str]:
|
||||
stage = school_level_label(school_level)
|
||||
grade_text = _grade_text(grade)
|
||||
curriculum = _chinese_curriculum(school_level, grade)
|
||||
prompt = COMPOSITION_PROMPT.format(
|
||||
stage=stage,
|
||||
grade_text=grade_text,
|
||||
curriculum=curriculum,
|
||||
topic=topic.strip(),
|
||||
)
|
||||
full = await generate_text(prompt, cfg, temperature=0.35)
|
||||
return split_composition_sections(full)
|
||||
|
||||
|
||||
def composition_markdown(topic: str, writing_plan: str | None, sample_essay: str | None) -> str:
|
||||
parts = [f"# 作文题目\n\n{topic.strip()}", ""]
|
||||
if writing_plan:
|
||||
parts.extend(["## 写作方案", "", writing_plan.strip(), ""])
|
||||
if sample_essay:
|
||||
parts.extend(["## 范文", "", sample_essay.strip(), ""])
|
||||
return "\n".join(parts).strip() + "\n"
|
||||
@@ -91,3 +91,13 @@ def run_migrations() -> None:
|
||||
if "review_statuses_json" not in ss_columns:
|
||||
with engine.begin() as conn:
|
||||
conn.execute(text("ALTER TABLE subject_scores ADD COLUMN review_statuses_json TEXT"))
|
||||
|
||||
if "system_settings" in tables:
|
||||
ss_columns = {col["name"] for col in inspector.get_columns("system_settings")}
|
||||
if "ai_review_enabled" not in ss_columns:
|
||||
with engine.begin() as conn:
|
||||
conn.execute(
|
||||
text(
|
||||
"ALTER TABLE system_settings ADD COLUMN ai_review_enabled BOOLEAN NOT NULL DEFAULT TRUE"
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user