自动学习建议解决薄弱知识点

0 阅读3分钟

学习报告不只是“统计”,还会给出:

  • 当前最薄弱的知识点
  • 为什么判定它薄弱
  • 建议先练什么
  • 自动推荐对应练习题

后端 schema 新增

修改 backend/app/schemas.py

class WeakKnowledgeItem(BaseModel):
    name: str
    wrong_count: int
    total_count: int
    wrong_rate: float
    suggestion: str


class StudySuggestionResponse(BaseModel):
    weak_knowledge_points: List[WeakKnowledgeItem]
    overall_suggestion: str

新增建议分析服务

新增 backend/app/suggestion_service.py

import json
from collections import defaultdict
from sqlalchemy.orm import Session
from app.models import QuestionHistory


def build_study_suggestion(db: Session):
    rows = db.query(QuestionHistory).all()

    stat_map = defaultdict(lambda: {"total": 0, "wrong": 0})

    for row in rows:
        try:
            knowledge_points = json.loads(row.knowledge_points or "[]")
        except Exception:
            knowledge_points = []

        for kp in knowledge_points:
            if not kp:
                continue
            stat_map[kp]["total"] += 1
            if row.is_wrong:
                stat_map[kp]["wrong"] += 1

    weak_list = []
    for name, stat in stat_map.items():
        total = stat["total"]
        wrong = stat["wrong"]
        wrong_rate = round((wrong / total * 100), 2) if total > 0 else 0.0

        if wrong > 0:
            if wrong_rate >= 60:
                suggestion = f"建议优先强化“{name}”基础概念,并连续练习 3~5 道同类题。"
            elif wrong_rate >= 30:
                suggestion = f"建议针对“{name}”做专项复习,并配合 2~3 道练习题巩固。"
            else:
                suggestion = f"“{name}”有少量错误,建议复盘错题并适量练习。"

            weak_list.append({
                "name": name,
                "wrong_count": wrong,
                "total_count": total,
                "wrong_rate": wrong_rate,
                "suggestion": suggestion,
            })

    weak_list.sort(key=lambda x: (x["wrong_rate"], x["wrong_count"]), reverse=True)
    weak_list = weak_list[:5]

    if not weak_list:
        overall_suggestion = "当前暂无明显薄弱知识点,建议继续保持练习,并适度拓展更高难度题目。"
    else:
        top_name = weak_list[0]["name"]
        overall_suggestion = f"当前最需要优先突破的知识点是“{top_name}”,建议先复习核心方法,再进行针对性训练。"

    return {
        "weak_knowledge_points": weak_list,
        "overall_suggestion": overall_suggestion,
    }

后端主接口新增

修改 backend/app/main.py

1)补充 import

新增:

from app.suggestion_service import build_study_suggestion
from app.schemas import StudySuggestionResponse

2)新增学习建议接口

把这个接口加到 main.py 里:

@app.get("/api/study-suggestion", response_model=StudySuggestionResponse)
def get_study_suggestion(db: Session = Depends(get_db)):
    try:
        result = build_study_suggestion(db)
        return StudySuggestionResponse(**result)
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

前端 API 新增

修改 frontend/src/api/math.ts

新增类型:

export interface WeakKnowledgeItem {
  name: string
  wrong_count: number
  total_count: number
  wrong_rate: number
  suggestion: string
}

export interface StudySuggestionResponse {
  weak_knowledge_points: WeakKnowledgeItem[]
  overall_suggestion: string
}

新增请求方法:

export function getStudySuggestion() {
  return request.get<StudySuggestionResponse>('/api/study-suggestion')
}

前端页面状态新增

修改 frontend/src/App.vue

1)补充 import

api import 补上:

import {
  solveMathQuestion,
  solveMathImage,
  getHistoryList,
  getWrongQuestionList,
  markWrongQuestion,
  generatePracticeByKnowledge,
  regenerateQuestion,
  getLearningReport,
  getStudySuggestion,
  type SolveResponse,
  type HistoryItem,
  type PracticeQuestionItem,
  type LearningReportResponse,
  type StudySuggestionResponse,
} from './api/math'

2)activeTab 扩展

改成:

const activeTab = ref<'solve' | 'history' | 'wrong' | 'report' | 'suggestion'>('solve')

3)新增状态

script setup 里新增:

const suggestionLoading = ref(false)
const studySuggestion = ref<StudySuggestionResponse | null>(null)

4)新增方法

const loadStudySuggestion = async () => {
  suggestionLoading.value = true
  try {
    const { data } = await getStudySuggestion()
    studySuggestion.value = data
  } catch (error: any) {
    console.error('加载学习建议失败:', error)
    alert(error?.response?.data?.detail || '加载学习建议失败')
  } finally {
    suggestionLoading.value = false
  }
}

const switchToSuggestion = async () => {
  activeTab.value = 'suggestion'
  await loadStudySuggestion()
}

5)这些操作成功后顺手刷新建议

在下面几个方法成功后追加:

handleSubmit
await loadStudySuggestion()
handleImageChange
await loadStudySuggestion()
toggleWrong
await loadStudySuggestion()

前端 tabs 增加入口

修改 frontend/src/App.vue

在 tabs 按钮区域新增:

<button
  :class="['tab-btn', activeTab === 'suggestion' ? 'active' : '']"
  @click="switchToSuggestion"
>
  学习建议
</button>

前端模板新增“学习建议”页

修改 frontend/src/App.vue

现在已经有:

  • solve
  • history
  • wrong
  • report

所以需要把 report 分支改成 v-else-if="activeTab === 'report'"
然后最后新增一个 v-else 作为 suggestion 页面。


1)先把 report 分支改成:

<template v-else-if="activeTab === 'report'">

2)最后新增 suggestion 分支:

<template v-else>
  <div v-if="suggestionLoading" class="empty">学习建议加载中...</div>

  <div v-else-if="studySuggestion" class="report-panel">
    <div class="result-card">
      <h2>整体学习建议</h2>
      <p>{{ studySuggestion.overall_suggestion }}</p>
    </div>

    <div class="result-card">
      <h2>薄弱知识点分析</h2>

      <div v-if="studySuggestion.weak_knowledge_points.length === 0" class="empty">
        暂无薄弱知识点
      </div>

      <div
        v-for="(item, index) in studySuggestion.weak_knowledge_points"
        :key="index"
        class="weak-item"
      >
        <div class="weak-header">
          <strong>{{ item.name }}</strong>
          <span class="weak-rate">错误率 {{ item.wrong_rate }}%</span>
        </div>

        <div class="weak-meta">
          错误 {{ item.wrong_count }} 次 / 共出现 {{ item.total_count }} 次
        </div>

        <div class="weak-suggestion">
          {{ item.suggestion }}
        </div>

        <button
          class="retry-btn"
          @click="
            practiceKnowledge = item.name;
            activeTab = 'solve';
            handleGeneratePractice();
          "
        >
          生成该知识点练习题
        </button>
      </div>
    </div>
  </div>
</template>

前端样式补充

修改 frontend/src/App.vue

style scoped 里新增:

.weak-item {
  padding: 16px 0;
  border-bottom: 1px solid #eee;
}

.weak-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
}

.weak-rate {
  color: #d03050;
  font-weight: 600;
}

.weak-meta {
  color: #666;
  font-size: 14px;
  margin-bottom: 8px;
}

.weak-suggestion {
  margin-bottom: 12px;
  color: #333;
  line-height: 1.7;
}

初始化时顺手加载建议

修改 onMounted

改成:

onMounted(async () => {
  await loadHistory()
  await loadWrongList()
  await loadReport()
  await loadStudySuggestion()
})

启动查看效果

重启后端

uvicorn app.main:app --reload --port 8000

重启前端

npm run dev

image.png

学习建议来源于错题

image.png

image.png

image.png