状态:设计收敛,待实现 日期:2026-04-23 归属后端:
cococlass-english-speaking-api前端消费:PPT→src/views/Editor/EnglishSpeaking/+enspeak原型的SentenceCard契约 关联设计:doc/EnglishSpeakingIntegration.md
现有评分链路覆盖发音侧四维:accuracy / fluency / completeness / prosody(Azure Speech SDK 的 Pronunciation Assessment)。发音侧回答的是"听起来像不像母语者",不回答"说了什么是否合适"。
enspeak 原型 DetailedReport.tsx:55 的 SentenceCard 已经给出 UI 契约:每个学生句除发音四分外,还展示 feedback.{highlights, corrections, suggestions}。这部分目前为空,本设计补齐。
MVP 目标:用一次 LLM 调用生成这三段结构化评语,不引入任何新 provider,不改动对话主流程。
content_feedback 永远不出现在 /speak 的响应里(无论 SSE 流还是最终帧)。对话进行中前端拿不到任何评分数据,避免分心 + 避免评分阻塞对话节奏。/api/speaking/dialogue/report:学生/教师打开结果页时按 sessionId 拉取,拿到每轮的发音四分 + content_feedback。overallScore / 聚合数据由前端从 per-turn 结果计算(平均分、top-N 亮点),不引入 session 级 LLM 调用。/speak 完成后立即后台触发,绝不延后到会话结束批量跑:
asyncio.create_task(_evaluate_pronunciation) 在本轮 /speak 返回前就挂起,与 AI 回复并发/speak 立即返回,不等待任何评分完成。每轮内部串行 Azure → Content LLM(Content LLM 需要 Azure 的四分作为输入,故不能并行)。/report 或按 status 字段显示"评估中"占位。In scope
content_feedbackPronunciationEvaluation 表加一列 content_feedback JSONGET /api/speaking/dialogue/report 返回值把 content_feedback 并入每个 evaluationOut of scope(后续迭代)
SentenceCard.feedback 对齐){
"highlights": ["..."], // 1–2 条,中文,≤30 字
"corrections": [
{
"original": "I go to park yesterday", // 英文原句
"corrected": "I went to the park yesterday", // 英文改正
"explanation": "过去式应用 went,park 前加 the" // 中文解释
}
],
"suggestions": ["..."] // 1–2 条,中文,≤30 字
}
null(LLM 失败时的降级)pronunciation_evaluation 表新增一列:
content_feedback: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
无独立状态字段:
status == "completed" 且 content_feedback == null → 内容评语调用失败(LLM 超时/解析失败),不阻塞发音结果status == "failed" → 发音失败,内容评语跳过不调用/speak (SSE)
│
├─ 同步:ASR → LLM 对话回复
│
└─ 后台 asyncio.create_task(_evaluate_pronunciation)
│
├─ Azure Pronunciation Assessment → 4 个分数 + word_analysis
│
└─ [新增] ContentEvaluator.evaluate()
· 输入:4 分 + 学生转录 + AI 上一句
· 输出:{highlights, corrections, suggestions}
· 失败:log 并置 content_feedback=null,不影响 status
新增模块一个:app/service/speaking/content_evaluator.py
复用现有 LLMProvider(onehub_llm)。无需新 provider 协议。
gpt-4o-mini 或等价轻量模型(onehub 内部选)temperature = 0response_format = { "type": "json_schema", "json_schema": FEEDBACK_SCHEMA }
{ "type": "json_object" } + prompt 里加 schema 说明content_feedback=nullYou are an English tutor evaluating a student's single spoken turn in an
open dialogue. You receive:
- Azure pronunciation scores (accuracy/fluency/completeness/prosody, 0–100)
- The immediate prior AI turn (context)
- The student's transcript
Produce JSON with three arrays:
- highlights: 1–2 Chinese sentences praising specific strengths. Reference a
pronunciation dimension if that score is ≥ 85. ≤ 30 chars each.
- corrections: grammar / word-choice fixes. Each item:
{ original (EN), corrected (EN), explanation (ZH, ≤ 30 chars) }
- suggestions: 1–2 Chinese actionable improvements. Reference a pronunciation
dimension if that score is < 70. ≤ 30 chars each.
Rules:
- Empty arrays are valid. Do not invent errors to fill quota.
- If the student only said a filler ("yes", "ok", "hmm"), return all empty
arrays plus one encouragement in highlights.
- Never include the scores as raw numbers in output text; describe
qualitatively ("发音准确度很高" not "accuracy 92").
{
"pronunciation": {
"accuracy": 72, "fluency": 85, "completeness": 90, "prosody": 60
},
"ai_said": "What did you do last weekend?",
"student_said": "I go to park with my family and play ball."
}
历史消息只给 AI 上一句(per-turn 评语场景足够;深层连贯性留给后续 session-level 迭代)。
| 场景 | status | content_feedback | 前端表现 |
|---|---|---|---|
| Azure 成功 + LLM 成功 | completed | {...} |
完整卡片 |
| Azure 成功 + LLM 超时 | completed | null |
只显示发音四分 |
| Azure 成功 + JSON 解析失败 | completed | null |
只显示发音四分 |
| Azure 失败 | failed | null(不调用) |
卡片置错误态 |
前端 hasEvaluation = score !== undefined || feedback 的逻辑原样兼容。
gpt-4o-mini ≈ $0.15 / 1M input、$0.60 / 1M output 估算:单轮 < $0.0002GET /api/speaking/dialogue/report 返回的 evaluations[i] 加一个字段:
{
"round": 1,
"status": "completed",
"accuracy_score": 72,
"fluency_score": 85,
"completeness_score": 90,
"prosody_score": 60,
"content_feedback": { // ← 新增,可为 null
"highlights": [...],
"corrections": [...],
"suggestions": [...]
}
}
PPT 前端 src/types/englishSpeaking.ts 的 evaluation 类型同步加 contentFeedback。
tests/service/speaking/test_content_evaluator.py
tests/service/speaking/test_dialogue_service.py(扩充)
pronunciation_evaluation.content_feedback JSON nullableapp/service/speaking/content_evaluator.py,实现 evaluate(transcript, prior_ai_turn, pron_scores) -> dict | Nonedialogue_service._evaluate_pronunciation 的 Azure 调用成功分支后追加 content 评估GET /report 返回值补 content_feedbackEnglishSpeaking/ 下报告渲染补 feedback 展示(enspeak SentenceCard 三段 UI 直接移植)