You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
6.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 结合 lab-task-description、仓库扫描与「实训步骤 / 评测对齐」表生成学习建议,
* 与 evaluation.learning 合并(任务侧证据优先),供「学习建议(结合本实训)」区块使用。
*/
function buildLearningSectionHint(report) {
const src = report?.source || '';
const base =
'以下建议依据 config/lab-task-description.md、远程仓库脚本/数据/图表扫描与「实训步骤 / 评测对齐」表,并与对话日志信号交叉核验后生成。';
if (src === 'heuristic_fast') {
return (
base +
' 当前为快速模式(?fast=1以可机器核验的结论为主若需更长文深度点评可去掉 URL 中的 fast 并配置 CURSOR_API_KEY。'
);
}
if (src === 'heuristic_only') {
return base + ' 当前未调用整页大模型;建议配置 CURSOR_API_KEY 以生成更细致的学业与工具使用分析。';
}
if (src === 'ai_full') {
return base + ' 已与整页大模型给出的建议合并去重;前列条目优先对应本实训交付物与扫描证据。';
}
if (src === 'heuristic_fallback') {
return base + ' 整页大模型生成失败,以下为启发式与仓库扫描合并结果。';
}
return base;
}
/**
* @param {object} ctx loadStudentRepoContext 返回值
* @param {object} report 报告 JSON需含 summary.rubric_steps、evaluation.meta 等)
* @returns {string[]}
*/
function buildTaskAlignedLearningList(ctx, report) {
const scan = ctx?.scan || {};
const flags = scan.flags || {};
const steps = ctx?.rubric_steps || report?.summary?.rubric_steps || [];
const sig = report?.evaluation?.meta?.evaluation_signals || {};
const hookN = report?.summary?.hook_event_count ?? ctx?.heuristic?.summary?.hook_event_count ?? 0;
const convN = report?.summary?.conversation_count ?? ctx?.heuristic?.summary?.conversation_count ?? 0;
const winN =
(typeof sig.transcript_window_count === 'number' ? sig.transcript_window_count : null) ??
convN;
const labPct = report?.summary?.lab_progress_percent ?? ctx?.labPct ?? 0;
const tips = [];
const seen = new Set();
const add = (s) => {
const t = String(s).trim();
if (!t || seen.has(t)) return;
seen.add(t);
tips.push(t);
};
add(
'【实训目标】读取 scores.csv用 pandas 完成个人/全班统计并在终端输出;用 matplotlib 绘制全班各科平均分柱状图并保存 score_chart.png运行报错时附上 Traceback 与工作目录再请 AI 协助。'
);
for (const s of steps) {
if (s.done) continue;
const id = s.id;
if (id === 'prep') {
add(
'【数据交付】未稳定检出 scores.csv请按头歌路径提交成绩表代码中路径与评测工作区一致如与任务说明中的 /data/workspace/myshixun/ 对齐)。'
);
} else if (id === 's1') {
add('【仓库】检出文件很少:请确认已 push 代码与数据,或检查报告 URL 是否指向正确学员仓库。');
} else if (id === 's2') {
add(
'【统计脚本】未完成或未检出 read_csv/均值:在 score_analysis.py 中使用 pandas.read_csv 与 mean 等统计,运行后核对终端输出是否与预期一致。'
);
} else if (id === 's3') {
add(
'【运行调试】未体现运行—报错—修复闭环:请在终端运行脚本,出错时把完整 Traceback、当前目录与相关文件名写进对话再问 AI。'
);
} else if (id === 's4') {
add(
'【可视化】未检出 score_chart.png 或作图未完成:用 matplotlib 柱状图展示全班各科平均分plt.savefig("score_chart.png") 后提交图片文件。'
);
} else if (id === 's5') {
add('【理解代码】请选择核心统计与作图片段请 AI 解释数据流与参数含义,避免只生成代码不复盘。');
} else if (id === 's6') {
add('【提交脚本】请保存并提交 score_analysis.py 到远程仓库,确保自动评测能检出主程序。');
} else {
const note = (s.eval_note || '').trim();
add(note ? `${s.label}${note.slice(0, 200)}${note.length > 200 ? '…' : ''}` : `${s.label}】对照任务补全该环节。`);
}
}
if (scan.score_analysis_py && !flags.has_read_csv) {
add('【代码扫描】脚本片段未出现 read_csv请显式使用 pandas.read_csv 指向你的 scores.csv 相对路径。');
}
if (scan.score_analysis_py && !flags.has_mean) {
add('【代码扫描】未识别均值/汇总:请按任务输出各科或全班平均,并用 print 自查结果。');
}
if (scan.score_analysis_py && !flags.has_matplotlib) {
add('【代码扫描】未识别 matplotlib请增加柱状图与 savefig生成评测所需的 score_chart.png。');
}
if (!scan.score_chart_png?.exists && flags.has_matplotlib) {
add('【产出文件】脚本含作图但未检出 png请在本地运行脚本生成 score_chart.png 并加入仓库提交。');
}
if (
typeof sig.transcript_window_count === 'number' &&
typeof sig.conversation_bucket_count === 'number' &&
sig.conversation_bucket_count > sig.transcript_window_count
) {
add(
'【上下文】多条日志会话已按同一 Cursor transcript_path 合并;学习建议与作业评价以合并后的聊天窗口口径统计交互,与页面「对话与提问」一致。'
);
}
if (sig.heuristic_untrustworthy) {
add(
'【对话方式】日志多为长文单次粘贴,不利于体现个人推敲过程;请拆成小步提问,每轮附带路径、期望输出或报错。'
);
} else if (winN >= 1 && hookN > 0 && hookN < 4) {
add('【对话方式】交互轮次偏少:可分阶段验证「读表 → 统计 → 作图」,每轮让模型看到上一轮代码或输出。');
} else if (winN >= 1 && hookN >= 16) {
add('【对话方式】与模型交互较频:注意每轮目标单一、写清约束,避免同轮混杂无关需求导致跑偏。');
}
if (labPct >= 85) {
add('【巩固】交付物较齐:可为图表加标题与坐标轴标签,并对照头歌评测脚本再做一次本地自检。');
}
return tips;
}
/**
* 将任务侧学习建议与已有 evaluation.learning 合并(去重,任务生成的条目前置)。
*/
function mergeLearningWithTaskEvidence(ctx, report) {
if (!ctx || !report?.evaluation) return;
const taskTips = buildTaskAlignedLearningList(ctx, report);
const existing = Array.isArray(report.evaluation.learning) ? report.evaluation.learning : [];
const seen = new Set();
const out = [];
for (const x of [...taskTips, ...existing]) {
const t = String(x).trim();
if (!t || seen.has(t)) continue;
seen.add(t);
out.push(t);
}
report.evaluation.learning = out.slice(0, 16);
report.ui = report.ui || {};
report.ui.learning_section_hint = buildLearningSectionHint(report);
}
module.exports = {
buildTaskAlignedLearningList,
mergeLearningWithTaskEvidence,
buildLearningSectionHint,
};