11wqe1 1 giorno fa
parent
commit
609a5dd8c5
1 ha cambiato i file con 234 aggiunte e 4 eliminazioni
  1. 234 4
      cocostudy.js

+ 234 - 4
cocostudy.js

@@ -3,7 +3,7 @@ const axios = require("axios");
 const schedule = require("node-schedule");
 const mysql = require("./mysql");
 const router = express.Router();
-
+const crypto = require("crypto");
 // 本地
 // const _mysqlLabor = ["183.36.26.8", "pbl"];
 // const _mysqluser = ["183.36.26.8", "cocorobouser"];
@@ -255,11 +255,16 @@ const saveStudent = async (e) => {
         rid: e.testId,
         tit: e.name,
       });
-      console.log("addcocostudySpacetea res", spaceRes);
+      const spaceId = spaceRes[0][0].id;
+      console.log("addcocostudySpacetea res", spaceRes, "spaceId", spaceId);
+      if (spaceId) {
+        return getRidData(e.userId, e.testId, spaceId);
+      }
     } catch (err) {
       console.log("addcocostudySpacetea err", err.message || err);
     }
   }
+  return null;
 };
 
 const runAutoSco = async () => {
@@ -286,8 +291,16 @@ const runAutoSco = async () => {
 
   for (const e of aiBatchCorrection) {
     try {
-      await saveStudent(e);
+      const analysisTask = await saveStudent(e);
       console.log(`保存成功 workId=${e.id}`);
+      if (analysisTask) {
+        try {
+          await analysisTask;
+          console.log(`错题分析完成 workId=${e.id}`);
+        } catch (err) {
+          console.error(`错题分析失败 workId=${e.id}`, err.message || err);
+        }
+      }
     } catch (err) {
       console.error(`保存失败 workId=${e.id}`, err.message || err);
     }
@@ -301,7 +314,7 @@ let isRunning = false;
 
 const runAutoScoSafe = async () => {
   if (isRunning) {
-    console.warn("[cocostudy] 上一次自动批改仍在执行,跳过本次");
+    console.warn("[cocostudy] 上一次自动批改/分析仍在执行,跳过本次");
     return;
   }
   isRunning = true;
@@ -314,6 +327,223 @@ const runAutoScoSafe = async () => {
     isRunning = false;
   }
 };
+let agent1a = null;
+let agent2a = null;
+let agent_data2 = null;
+
+const AGENT_IDS = {
+  detailed: "fe58652f-d32e-4aec-ba8e-fc5a3398ac96",
+  errorAnalysis: "f100cb78-8053-4f5b-9589-cb3a74d1b4da",
+  lectureOutline: "ccb95f55-bf4e-4132-b342-07dfe5bb759e",
+};
+
+const parseAgentJsonMessage = (raw) => {
+  if (Array.isArray(raw)) return raw;
+  if (typeof raw === "string") {
+    const text = raw.trim();
+    const fenced = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
+    const jsonText = (fenced && fenced[1] ? fenced[1] : text).trim();
+    return JSON.parse(jsonText);
+  }
+  if (raw && typeof raw === "object") return [raw];
+  return [];
+};
+
+const loadAgents = async () => {
+  if (agent1a && agent2a && agent_data2) return;
+  const [detailedRes, errorRes, outlineRes] = await Promise.all([
+    axios.get(`https://appapi.cocorobo.cn/api/agents/agent/${AGENT_IDS.detailed}`),
+    axios.get(`https://appapi.cocorobo.cn/api/agents/agent/${AGENT_IDS.errorAnalysis}`),
+    axios.get(`https://appapi.cocorobo.cn/api/agents/agent/${AGENT_IDS.lectureOutline}`),
+  ]);
+  agent_data2 = detailedRes.data;
+  agent1a = errorRes.data;
+  agent2a = outlineRes.data;
+};
+
+const callAgentChat = async (agent, message, userId, stepName = "Agent") => {
+  let lastErr;
+  for (let attempt = 1; attempt <= GRADE_RETRY_TIMES; attempt++) {
+    try {
+      const res = await axios.post("https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat", {
+        id: agent.id,
+        message,
+        userId,
+        model: agent.modelType,
+        file_ids: [],
+        sound_url: "",
+        temperature: 0.1,
+        top_p: 1,
+        max_completion_tokens: 4096000,
+        stream: false,
+        uid: crypto.randomUUID(),
+        session_name: crypto.randomUUID(),
+      });
+      const reply = res.data?.message;
+      if (reply == null || reply === "") {
+        throw new Error("AI 返回内容为空");
+      }
+      if (attempt > 1) {
+        console.log(`[cocostudy] ${stepName} 重试成功 第${attempt}次`);
+      }
+      return reply;
+    } catch (err) {
+      lastErr = err;
+      console.warn(
+        `[cocostudy] ${stepName} 调用失败 第${attempt}/${GRADE_RETRY_TIMES}次`,
+        err.response?.data || err.message
+      );
+      if (attempt < GRADE_RETRY_TIMES) {
+        await sleep(GRADE_RETRY_DELAY);
+      }
+    }
+  }
+  throw lastErr;
+};
+
+const normalizeQuestionData = (questions, workData) => {
+  return questions.map((item) => {
+    const next = { ...item };
+    next.userAnswer = workData.data?.[item.id] ?? "";
+    next.userScore = workData.score?.[item.id] ?? 0;
+
+    if (item.tool === "choice") {
+      if (Array.isArray(next.userAnswer)) {
+        next.userAnswer = next.userAnswer
+          .map((idx) => item.options[idx])
+          .filter(Boolean)
+          .join("、");
+      } else {
+        next.userAnswer = item.options[next.userAnswer] || "";
+      }
+
+      if (Array.isArray(item.answer)) {
+        next.answer = item.answer
+          .map((idx) => item.options[idx])
+          .filter(Boolean)
+          .join("、");
+      } else {
+        next.answer = item.options[item.answer] || "";
+      }
+    }
+
+    return next;
+  });
+};
+
+const buildQuizSummary = (testRow, workRow, questions) => {
+  const totalScore = questions.reduce((pre, cur) => pre + (cur.score || 0), 0);
+  const userScore = Object.values(workRow.score || {}).reduce(
+    (pre, cur) => pre + Number(cur || 0),
+    0
+  );
+  const totalQuestions = questions.length || 1;
+  const wrongQuestions = questions.filter((item) => item.userScore != item.score).length;
+  const correctRate = `${(((totalQuestions - wrongQuestions) / totalQuestions) * 100).toFixed(0)}%`;
+
+  return {
+    试卷名称: testRow.name,
+    学科: testRow.subname,
+    年级: testRow.graname,
+    章节: testRow.chapters,
+    试卷总分: totalScore,
+    试卷得分: userScore,
+    试卷正确率: correctRate,
+    试卷总题目数: totalQuestions,
+    错题数量: wrongQuestions,
+    错题列表: questions,
+  };
+};
+
+// 生成错题分析
+async function getRidData(userId, testId, spaceId) {
+  await loadAgents();
+
+  const res = await callMysqlProc("getCocostudyTestData", {
+    uid: userId,
+    tid: testId,
+  });
+
+  const workRow = res?.[0]?.[0];
+  const testRow = res?.[1]?.[0];
+  if (!workRow?.work || !testRow?.testJson) {
+    throw new Error(`getCocostudyTestData 数据不完整 testId=${testId}`);
+  }
+
+  const workData = JSON.parse(workRow.work);
+  const questions = normalizeQuestionData(JSON.parse(testRow.testJson), workData);
+  const quizSummary = buildQuizSummary(testRow, workData, questions);
+
+  console.log("[cocostudy] 开始错题分析", { userId, testId, spaceId });
+  await generateErrorAnalysis(quizSummary, userId, spaceId);
+}
+
+async function generateErrorAnalysis(quizSummary, userId, spaceId) {
+  const raw = await callAgentChat(
+    agent1a,
+    `题目数据:\n${JSON.stringify(quizSummary)}`,
+    userId,
+    "错因分析"
+  );
+  const errorAnalysis = parseAgentJsonMessage(raw);
+  await generateLectureOutline(quizSummary, errorAnalysis, userId, spaceId);
+}
+
+async function generateLectureOutline(quizSummary, errorAnalysis, userId, spaceId) {
+  const lectureOutline = await callAgentChat(
+    agent2a,
+    `==\n## 统计性信息如下:\n${JSON.stringify(quizSummary)}\n\n==\n\n## 错题深度分析如下:\n${JSON.stringify(errorAnalysis)}`,
+    userId,
+    "讲义大纲"
+  );
+  await generatedetailedExplanation(quizSummary, errorAnalysis, lectureOutline, userId, spaceId);
+}
+
+async function generatedetailedExplanation(quizSummary, errorAnalysis, lectureOutline, userId, spaceId) {
+  const quizSummaryString = JSON.stringify(quizSummary).replace(/\\(?![\\"/])/g, "\\\\");
+  const displayContent =
+    "统计性信息:" +
+    quizSummaryString +
+    "\n错题深度分析:" +
+    JSON.stringify(errorAnalysis) +
+    "\n生成的Agenda顺序:" +
+    JSON.stringify(lectureOutline);
+
+  const answer = await callAgentChat(agent_data2, displayContent, userId, "详细讲解");
+  await insertChat(answer, spaceId, userId);
+
+  try {
+    await callMysqlProc("updatespaceisanalyze", {
+      sid: spaceId,
+      isl: 1,
+    });
+  } catch (err) {
+    console.error("[cocostudy] updatespaceisanalyze err", err.message || err);
+  }
+
+  console.log("[cocostudy] 错题分析完成", { userId, spaceId });
+}
+
+async function insertChat(answer, spaceId, userId) {
+  const params = {
+    userId,
+    userName: "系统",
+    groupId: spaceId,
+    answer: encodeURIComponent(answer),
+    problem: encodeURIComponent(""),
+    file_id: "",
+    session_name: spaceId,
+    alltext: answer,
+    type: "chat",
+    reasoning_content: "",
+    jsonData: "{}",
+  };
+
+  const res = await axios.post("https://gpt4.cocorobo.cn/insert_chat", params);
+  console.log("[cocostudy] insert_chat res", res.data);
+  return res;
+}
+
 
 // 定时任务:每10分钟触发一次
 schedule.scheduleJob("*/10 * * * *", async () => {