|
|
@@ -6,15 +6,9 @@ const router = express.Router();
|
|
|
const crypto = require("crypto");
|
|
|
// 本地
|
|
|
// const _mysqlLabor = ["183.36.26.8", "pbl"];
|
|
|
-// const _mysqluser = ["183.36.26.8", "cocorobouser"];
|
|
|
-// const _getmysqlLabor2 = ["183.36.26.8", "pbl"];
|
|
|
-// const _getmysqlLabor = ["183.36.26.8", "pbl"];
|
|
|
|
|
|
// 线上
|
|
|
const _mysqlLabor = ["172.16.12.5", "pbl"];
|
|
|
-// const _mysqluser = ["172.16.12.5", "cocorobouser"];
|
|
|
-// const _getmysqlLabor2 = ["172.16.12.7", "pbl"];
|
|
|
-// const _getmysqlLabor = ["172.16.12.7", "pbl"];
|
|
|
|
|
|
const testUrl = "https://test-paper-analyzer.cocorobo.cn";
|
|
|
const GRADE_RETRY_TIMES = 3;
|
|
|
@@ -45,305 +39,10 @@ const callMysqlProc = (procedureName, data) => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
-// const queryAutoScoData = () => {
|
|
|
-// return new Promise((resolve, reject) => {
|
|
|
-// const p = [];
|
|
|
-// p.unshift(_mysqlLabor[0], _mysqlLabor[1], "getcocostudyautosco");
|
|
|
-// mysql.usselect(p, (ret) => {
|
|
|
-// if (ret instanceof Error || (ret && ret.code)) {
|
|
|
-// reject(ret);
|
|
|
-// return;
|
|
|
-// }
|
|
|
-// resolve(ret[0] || []);
|
|
|
-// });
|
|
|
-// });
|
|
|
-// };
|
|
|
-
|
|
|
-// const aiBatchCorrectionBtn = (item, aiBatchCorrection) => {
|
|
|
-// if (!item?.work || !item?.testJson) {
|
|
|
-// console.warn("缺少 work 或 testJson,跳过", item?.id);
|
|
|
-// return;
|
|
|
-// }
|
|
|
-
|
|
|
-// let workParsed;
|
|
|
-// let testjson;
|
|
|
-// try {
|
|
|
-// workParsed = JSON.parse(item.work);
|
|
|
-// testjson = JSON.parse(item.testJson);
|
|
|
-// } catch (err) {
|
|
|
-// console.error("JSON 解析失败", item.id, err.message);
|
|
|
-// return;
|
|
|
-// }
|
|
|
-
|
|
|
-// const _work = workParsed.data || {};
|
|
|
-// const work = {
|
|
|
-// score: {},
|
|
|
-// analysis: {},
|
|
|
-// data: _work,
|
|
|
-// time: workParsed.time,
|
|
|
-// };
|
|
|
-// const _test = [];
|
|
|
-
|
|
|
-// testjson.forEach((w) => {
|
|
|
-// _test.push(w);
|
|
|
-// work.score[w.id] = 0;
|
|
|
-// });
|
|
|
-
|
|
|
-// aiBatchCorrection.push({
|
|
|
-// id: item.id,
|
|
|
-// name: item.name,
|
|
|
-// subject: item.subject,
|
|
|
-// testId: item.testId,
|
|
|
-// userId: item.userId,
|
|
|
-// work,
|
|
|
-// test: _test,
|
|
|
-// });
|
|
|
-// };
|
|
|
-
|
|
|
-// const gradeQuestionWithRetry = async (aiBatchCorrection, params, workId, questionId) => {
|
|
|
-// let lastErr;
|
|
|
-// for (let attempt = 1; attempt <= GRADE_RETRY_TIMES; attempt++) {
|
|
|
-// try {
|
|
|
-// const res = await axios.post(testUrl + "/llm-extract/analyze/grade-question", params, {
|
|
|
-// headers: { "Content-Type": "application/json" },
|
|
|
-// });
|
|
|
-// const _result = res.data?.result || res.data;
|
|
|
-// const _score = _result?.score_awarded ?? 0;
|
|
|
-// const _feedback = _result?.overall_feedback ?? "";
|
|
|
-// saveToAiBatchCorrection(aiBatchCorrection, workId, questionId, _score, _feedback);
|
|
|
-// if (attempt > 1) {
|
|
|
-// console.log(`批改重试成功 workId=${workId} questionId=${questionId} 第${attempt}次`);
|
|
|
-// }
|
|
|
-// return _result;
|
|
|
-// } catch (err) {
|
|
|
-// lastErr = err;
|
|
|
-// console.warn(
|
|
|
-// `批改失败 workId=${workId} questionId=${questionId} 第${attempt}/${GRADE_RETRY_TIMES}次`,
|
|
|
-// err.response?.data || err.message
|
|
|
-// );
|
|
|
-// if (attempt < GRADE_RETRY_TIMES) {
|
|
|
-// await sleep(GRADE_RETRY_DELAY);
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-// saveToAiBatchCorrection(aiBatchCorrection, workId, questionId, 0, "批改失败");
|
|
|
-// throw lastErr;
|
|
|
-// };
|
|
|
-
|
|
|
-// const aiGradingBatchCorrection = async (aiBatchCorrection, val) => {
|
|
|
-// const promises = [];
|
|
|
-// for (let i = 0; i < val.test.length; i++) {
|
|
|
-// const _testIndex = i;
|
|
|
-// const _userAnswer = val.work.data[val.test[i].id];
|
|
|
-// const _sub_questions = [];
|
|
|
-// const _imgSrc = [];
|
|
|
-
|
|
|
-// if (val.test[i].subQuestions) {
|
|
|
-// val.test[i].subQuestions.forEach((sub, index) => {
|
|
|
-// _sub_questions.push({
|
|
|
-// number: `${index + 1}`,
|
|
|
-// stem: sub.title,
|
|
|
-// knowledge_points: sub.knowledgePoint,
|
|
|
-// standard_answer: sub.answer,
|
|
|
-// scoring_criteria: sub.scoringCriteria,
|
|
|
-// });
|
|
|
-// });
|
|
|
-// }
|
|
|
-
|
|
|
-// const params = {
|
|
|
-// question: {
|
|
|
-// number: `${_testIndex}`,
|
|
|
-// type: val.test[i].tool,
|
|
|
-// is_subjective: true,
|
|
|
-// score: val.test[i].score,
|
|
|
-// stem: val.test[i].title,
|
|
|
-// options: {},
|
|
|
-// sub_questions: _sub_questions,
|
|
|
-// knowledge_points: val.test[i].knowledgePoint ? val.test[i].knowledgePoint : [],
|
|
|
-// standard_answer: val.test[i].answer,
|
|
|
-// scoring_criteria: val.test[i].answer,
|
|
|
-// figure_description: "",
|
|
|
-// figure_bbox: {
|
|
|
-// page: 1,
|
|
|
-// x_min: 0,
|
|
|
-// y_min: 0,
|
|
|
-// x_max: 0,
|
|
|
-// y_max: 0,
|
|
|
-// },
|
|
|
-// figure_image_urls: _imgSrc,
|
|
|
-// },
|
|
|
-// student_file_urls: Array.isArray(_userAnswer) ? _userAnswer.map((u) => u.url) : [],
|
|
|
-// student_answer_text: ["fill", "qa"].includes(val.test[i].tool) ? (_userAnswer ? _userAnswer :'暂无数据') : "",
|
|
|
-// model: "gpt-5.4",
|
|
|
-// };
|
|
|
-
|
|
|
-// const questionId = val.test[i].id;
|
|
|
-// promises.push(gradeQuestionWithRetry(aiBatchCorrection, params, val.id, questionId));
|
|
|
-// }
|
|
|
-
|
|
|
-// const results = await Promise.allSettled(promises);
|
|
|
-// const failed = results.filter((r) => r.status === "rejected").length;
|
|
|
-// if (failed > 0) {
|
|
|
-// console.warn(`workId=${val.id} 共 ${failed}/${results.length} 道题批改失败,继续保存已有分数`);
|
|
|
-// }
|
|
|
-// return results;
|
|
|
-// };
|
|
|
-
|
|
|
-// const saveStudent = async (e) => {
|
|
|
-// const _json = {
|
|
|
-// fullScore: 0,
|
|
|
-// errorId: [],
|
|
|
-// correct: [],
|
|
|
-// total: e.test.length,
|
|
|
-// };
|
|
|
-
|
|
|
-// e.test.forEach((i) => {
|
|
|
-// if (e.work.score[i.id] == i.score) {
|
|
|
-// _json.fullScore += 1;
|
|
|
-// _json.correct.push(i.id);
|
|
|
-// } else {
|
|
|
-// _json.errorId.push(i.id);
|
|
|
-// }
|
|
|
-// });
|
|
|
-
|
|
|
-// for (const i of _json.errorId) {
|
|
|
-// let test = e.test.find((t) => t.id == i);
|
|
|
-// if (test) {
|
|
|
-// test = JSON.parse(JSON.stringify(test));
|
|
|
-// test.userAnswer = e.work.data[i];
|
|
|
-// const p = {
|
|
|
-// uid: e.userId,
|
|
|
-// sid: e.subject,
|
|
|
-// tit: test.title,
|
|
|
-// kno: test.knowledge || "",
|
|
|
-// tid: i,
|
|
|
-// json: JSON.stringify(test),
|
|
|
-// clist: _json.correct.join(","),
|
|
|
-// };
|
|
|
-
|
|
|
-// try {
|
|
|
-// const res = await callMysqlProc("addcocostudyminbook", p);
|
|
|
-// console.log("addcocostudyminbook res", res);
|
|
|
-// } catch (err) {
|
|
|
-// console.log("addcocostudyminbook err", err.message || err);
|
|
|
-// }
|
|
|
-// }
|
|
|
-// }
|
|
|
-
|
|
|
-// const params = {
|
|
|
-// id: e.id,
|
|
|
-// status: 2,
|
|
|
-// test: JSON.stringify(e.work),
|
|
|
-// json: JSON.stringify(_json),
|
|
|
-// };
|
|
|
-
|
|
|
-// const res = await callMysqlProc("updateCocostudysco", params);
|
|
|
-// console.log("updateCocostudysco res", res);
|
|
|
-// if (res === 1) {
|
|
|
-// try {
|
|
|
-// const spaceRes = await callMysqlProc("addcocostudySpacetea", {
|
|
|
-// uid: e.userId,
|
|
|
-// rid: e.testId,
|
|
|
-// tit: e.name,
|
|
|
-// });
|
|
|
-// const spaceId = spaceRes[0][0].id;
|
|
|
-// console.log("addcocostudySpacetea res", spaceRes, "spaceId", spaceId);
|
|
|
-// if (spaceId) {
|
|
|
-// return { userId: e.userId, testId: e.testId, spaceId, workId: e.id };
|
|
|
-// }
|
|
|
-// } catch (err) {
|
|
|
-// console.log("addcocostudySpacetea err", err.message || err);
|
|
|
-// }
|
|
|
-// }
|
|
|
-// return null;
|
|
|
-// };
|
|
|
-
|
|
|
-// const runAutoSco = async () => {
|
|
|
-// console.log("[cocostudy] 自动批改开始", new Date().toISOString());
|
|
|
-// const aiBatchCorrection = [];
|
|
|
-// const data = await queryAutoScoData();
|
|
|
-
|
|
|
-// if (!data.length) {
|
|
|
-// console.log("[cocostudy] 没有待批改数据");
|
|
|
-// return { count: 0 };
|
|
|
-// }
|
|
|
-
|
|
|
-// data.forEach((item) => aiBatchCorrectionBtn(item, aiBatchCorrection));
|
|
|
-
|
|
|
-// // 1. 批改:全部并发
|
|
|
-// const gradeResults = await Promise.allSettled(
|
|
|
-// aiBatchCorrection.map((e) => aiGradingBatchCorrection(aiBatchCorrection, e))
|
|
|
-// );
|
|
|
-// gradeResults.forEach((result, index) => {
|
|
|
-// const item = aiBatchCorrection[index];
|
|
|
-// if (result.status === "rejected") {
|
|
|
-// console.error(`学生批改异常 workId=${item.id}`, result.reason?.message || result.reason);
|
|
|
-// }
|
|
|
-// });
|
|
|
-
|
|
|
-// // 2. 保存:全部并发(与错题分析解耦)
|
|
|
-// const saveResults = await Promise.allSettled(
|
|
|
-// aiBatchCorrection.map(async (e) => {
|
|
|
-// const meta = await saveStudent(e);
|
|
|
-// console.log(`保存成功 workId=${e.id}`);
|
|
|
-// return meta;
|
|
|
-// })
|
|
|
-// );
|
|
|
-
|
|
|
-// const analysisJobs = [];
|
|
|
-// saveResults.forEach((result, index) => {
|
|
|
-// const item = aiBatchCorrection[index];
|
|
|
-// if (result.status === "rejected") {
|
|
|
-// console.error(`保存失败 workId=${item.id}`, result.reason?.message || result.reason);
|
|
|
-// return;
|
|
|
-// }
|
|
|
-// if (result.value) {
|
|
|
-// analysisJobs.push(result.value);
|
|
|
-// }
|
|
|
-// });
|
|
|
-
|
|
|
-// // 3. 错题分析:全部并发
|
|
|
-// if (analysisJobs.length) {
|
|
|
-// console.log(`[cocostudy] 开始错题分析 ${analysisJobs.length} 条(全部并发)`);
|
|
|
-// const analysisResults = await Promise.allSettled(
|
|
|
-// analysisJobs.map(async ({ userId, testId, spaceId, workId }) => {
|
|
|
-// await getRidData(userId, testId, spaceId);
|
|
|
-// console.log(`错题分析完成 workId=${workId}`);
|
|
|
-// return workId;
|
|
|
-// })
|
|
|
-// );
|
|
|
-// analysisResults.forEach((result, index) => {
|
|
|
-// const job = analysisJobs[index];
|
|
|
-// if (result.status === "rejected") {
|
|
|
-// console.error(
|
|
|
-// `错题分析失败 workId=${job.workId}`,
|
|
|
-// result.reason?.message || result.reason
|
|
|
-// );
|
|
|
-// }
|
|
|
-// });
|
|
|
-// }
|
|
|
-
|
|
|
-// console.log("[cocostudy] 全部处理完成", new Date().toISOString());
|
|
|
-// return { count: aiBatchCorrection.length };
|
|
|
-// };
|
|
|
|
|
|
let isRunning = false;
|
|
|
|
|
|
-// const runAutoScoSafe = async () => {
|
|
|
-// if (isRunning) {
|
|
|
-// console.warn("[cocostudy] 上一次自动批改/分析仍在执行,跳过本次");
|
|
|
-// return;
|
|
|
-// }
|
|
|
-// isRunning = true;
|
|
|
-// try {
|
|
|
-// return await runAutoSco();
|
|
|
-// } catch (err) {
|
|
|
-// console.error("[cocostudy] 自动批改异常", err.message || err);
|
|
|
-// throw err;
|
|
|
-// } finally {
|
|
|
-// isRunning = false;
|
|
|
-// }
|
|
|
-// };
|
|
|
+
|
|
|
let agent1a = null;
|
|
|
let agent1b = null;
|
|
|
let agent2a = null;
|
|
|
@@ -591,16 +290,16 @@ async function generatedetailedExplanation(quizSummary, errorAnalysis, lectureOu
|
|
|
const errorAnalysisString = JSON.stringify(errorAnalysis).replace(/\\(?![\\"/])/g, "\\\\");
|
|
|
const lectureOutlineString = JSON.stringify(lectureOutline).replace(/\\(?![\\"/])/g, "\\\\");
|
|
|
const oneCExplanationString = String(oneCExplanation ?? "");
|
|
|
- const displayContent = `==\n## 统计性信息如下:\n${quizSummaryString}\n\n==\n\n## 错题深度分析如下:\n${errorAnalysisString}\n\n==\n\n## 讲解大纲如下:\n${lectureOutlineString}\n\n==\n\n## 详细讲解如下:\n${oneCExplanationString}`;
|
|
|
+ const displayContent = `==\n## 该学生本次进行的试卷的统计性信息为:\n${quizSummaryString}\n\n==\n\n## 针对该试卷中,学生错题的深度分析如下:\n${errorAnalysisString}\n\n==\n\n## 推荐你的讲解顺序如下:\n${lectureOutlineString}\n\n==\n\n现在,请按照讲解顺序,开始为学生进行讲解。` ;
|
|
|
|
|
|
const answer = await callAgentChat(agent2a, displayContent, userId, "详细讲解", spaceId);
|
|
|
await insertChat(answer, spaceId, userId);
|
|
|
|
|
|
let json = {
|
|
|
- quizSummary: quizSummaryString,
|
|
|
- errorAnalysis: errorAnalysisString,
|
|
|
- lectureOutline: lectureOutlineString,
|
|
|
- detailedExplanation: oneCExplanationString,
|
|
|
+ quizSummary: quizSummary,
|
|
|
+ errorAnalysis: errorAnalysis,
|
|
|
+ lectureOutline: lectureOutline,
|
|
|
+ detailedExplanation: oneCExplanation,
|
|
|
}
|
|
|
try {
|
|
|
await callMysqlProc("updatecocostudySpacejson", {
|