1 line
18 KiB
JSON
1 line
18 KiB
JSON
{"repo": "chainrain7/examsysterm", "files": {"src/main/java/com/exam/util/AIClientUtil.java": {"d05590f4a50bc2653088de72fd3886ea2731e0eb": {"t": 1785746114.5731113, "n": 7988, "h": "660f6681967dba52", "c": "package com.exam.util;\n\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.io.InputStreamReader;\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.util.Properties;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\n/**\n * AI大模型API调用工具类\n * 用于主观题评分:传入参考答案与学生答案,获取AI评分建议与评语\n */\npublic class AIClientUtil {\n\n private static String apiUrl;\n private static String apiKey;\n private static String model;\n\n static {\n try {\n Properties props = new Properties();\n InputStream is = AIClientUtil.class.getClassLoader().getResourceAsStream(\"db.properties\");\n if (is != null) {\n props.load(is);\n apiUrl = props.getProperty(\"ai.api.url\");\n apiKey = props.getProperty(\"ai.api.key\");\n model = props.getProperty(\"ai.api.model\");\n }\n // Docker环境变量覆盖\n String envUrl = System.getenv(\"AI_API_URL\");\n String envKey = System.getenv(\"AI_API_KEY\");\n String envModel = System.getenv(\"AI_API_MODEL\");\n if (envUrl != null) apiUrl = envUrl;\n if (envKey != null) apiKey = envKey;\n if (envModel != null) model = envModel;\n if (apiUrl == null) apiUrl = \"https://api.deepseek.com/v1/chat/completions\";\n if (model == null) model = \"deepseek-chat\";\n } catch (Exception e) {\n e.printStackTrace();\n }\n }\n\n /**\n * 调用大模型对主观题进行评分\n * @param questionContent 题目内容\n * @param referenceAnswer 参考答案\n * @param studentAnswer 学生答案\n * @param maxScore 满分分值\n * @return 返回JSON格式的评分结果 {\"score\": 得分, \"comment\": \"评语\"}\n */\n public static AIScoreResult scoreSubjective(String questionContent, String referenceAnswer,\n String studentAnswer, int maxScore) {\n // 如果API Key未配置或为默认值,返回模拟评分结果\n if (apiKey == null || apiKey.equals(\"your-api-key-here\") || apiKey.isEmpty()) {\n return mockScore(referenceAnswer, studentAnswer, maxScore);\n }\n\n try {\n String prompt = buildPrompt(questionContent, referenceAnswer, studentAnswer, maxScore);\n\n URL url = new URL(apiUrl);\n HttpURLConnection conn = (HttpURLConnection) url.openConnection();\n conn.setRequestMethod(\"POST\");\n conn.setRequestProperty(\"Content-Type\", \"application/json\");\n conn.setRequestProperty(\"Authorization\", \"Bearer \" + apiKey);\n conn.setDoOutput(true);\n conn.setConnectTimeout(15000);\n conn.setReadTimeout(30000);\n\n // 构建请求体\n JsonObject requestBody = new JsonObject();\n requestBody.addProperty(\"model\", model);\n\n JsonArray messages = new JsonArray();\n JsonObject systemMsg = new JsonObject();\n systemMsg.addProperty(\"role\", \"system\");\n systemMsg.addProperty(\"content\", \"你是一位专业的课程教师,请根据参考答案对学生的答案进行评分。请严格按JSON格式返回:{\\\"score\\\": 数字, \\\"comment\\\": \\\"评语\\\"}\");\n messages.add(systemMsg);\n\n JsonObject userMsg = new JsonObject();\n userMsg.addProperty(\"role\", \"user\");\n userMsg.addProperty(\"content\", prompt);\n messages.add(userMsg);\n\n requestBody.add(\"messages\", messages);\n requestBody.addProperty(\"temperature\", 0.3);\n requestBody.addProperty(\"max_tokens\", 500);\n\n OutputStream os = conn.getOutputStream();\n os.write(requestBody.toString().getBytes(\"UTF-8\"));\n os.flush();\n os.close();\n\n // 读取响应\n int responseCode = conn.getResponseCode();\n if (responseCode == 200) {\n BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), \"UTF-8\"));\n StringBuilder response = new StringBuilder();\n String line;\n while ((line = br.readLine()) != null) {\n response.append(line);\n }\n br.close();\n\n // 解析响应\n JsonObject respJson = JsonParser.parseString(response.toString()).getAsJsonObject();\n String content = respJson.getAsJsonArray(\"choices\")\n .get(0).getAsJsonObject()\n .getAsJsonObject(\"message\")\n .get(\"content\").getAsString();\n\n // 从content中提取JSON\n content = content.trim();\n if (content.startsWith(\"```json\")) {\n content = content.substring(7);\n }\n if (content.endsWith(\"```\")) {\n content = content.substring(0, content.length() - 3);\n }\n content = content.trim();\n\n JsonObject scoreJson = JsonParser.parseString(content).getAsJsonObject();\n AIScoreResult result = new AIScoreResult();\n result.setScore(scoreJson.get(\"score\").getAsInt());\n result.setComment(scoreJson.get(\"comment\").getAsString());\n return result;\n } else {\n // API调用失败,返回模拟评分\n return mockScore(referenceAnswer, studentAnswer, maxScore);\n }\n } catch (Exception e) {\n e.printStackTrace();\n return mockScore(referenceAnswer, studentAnswer, maxScore);\n }\n }\n\n /**\n * 构建评分提示词\n */\n private static String buildPrompt(String questionContent, String referenceAnswer,\n String studentAnswer, int maxScore) {\n return \"请对以下学生答案进行评分:\\n\\n\" +\n \"题目:\" + questionContent + \"\\n\\n\" +\n \"参考答案:\" + referenceAnswer + \"\\n\\n\" +\n \"学生答案:\" + studentAnswer + \"\\n\\n\" +\n \"满分:\" + maxScore + \"分\\n\\n\" +\n \"请综合评估学生答案的准确性、完整性,给出0到\" + maxScore + \"之间的分数,并提供简短评语。\\n\" +\n \"请严格按JSON格式返回:{\\\"score\\\": 数字, \\\"comment\\\": \\\"评语\\\"}\";\n }\n\n /**\n * 模拟评分(当API不可用时使用)\n * 策略:基于学生答案与参考答案的相似度进行简单评分\n */\n private static AIScoreResult mockScore(String referenceAnswer, String studentAnswer, int maxScore) {\n AIScoreResult result = new AIScoreResult();\n\n if (studentAnswer == null || studentAnswer.trim().isEmpty()) {\n result.setScore(0);\n result.setComment(\"未作答,得0分。\");\n return result;\n }\n\n // 简单的关键词匹配相似度计算(使用Unicode标点类避免中文字符集问题)\n String[] refWords = referenceAnswer.replaceAll(\"[\\\\p{P}\\\\p{S}]\", \" \")\n .toLowerCase().split(\"\\\\s+\");\n String stuAnswer = studentAnswer.toLowerCase();\n int matchCount = 0;\n for (String word : refWords) {\n if (word.length() > 1 && stuAnswer.contains(word)) {\n matchCount++;\n }\n }\n\n double similarity = refWords.length > 0 ? (double) matchCount / refWords.length : 0;\n int score = Math.min((int) Math.round(similarity * maxScore), maxScore);\n\n result.setScore(score);\n if (score >= maxScore * 0.9) {\n result.setComment(\"回答很好,与参考答案高度一致。\");\n } else if (score >= maxScore * 0.6) {\n result.setComment(\"回答基本正确,部分要点已覆盖。\");\n } else if (score >= maxScore * 0.3) {\n result.setComment(\"回答不够完整,建议补充更多要点。\");\n } else {\n result.setComment(\"回答与参考答案差距较大,建议认真复习相关知识点。\");\n }\n result.setComment(result.getComment() + \"(注:当前为模拟评分,配置AI API Key后可启用大模型评分)\");\n\n return result;\n }\n\n /**\n * AI评分结果内部类\n */\n public static class AIScoreResult {\n private int score;\n private String comment;\n\n public int getScore() { return score; }\n public void setScore(int score) { this.score = score; }\n\n public String getComment() { return comment; }\n public void setComment(String comment) { this.comment = comment; }\n }\n}\n"}, "8fdac45db1fe2a415aea1b8410f2917ea2e7689d": {"t": 1785759971.23017, "n": 7988, "h": "660f6681967dba52", "c": "package com.exam.util;\n\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.io.InputStreamReader;\nimport java.io.BufferedReader;\nimport java.io.InputStream;\nimport java.util.Properties;\nimport com.google.gson.JsonArray;\nimport com.google.gson.JsonObject;\nimport com.google.gson.JsonParser;\n\n/**\n * AI大模型API调用工具类\n * 用于主观题评分:传入参考答案与学生答案,获取AI评分建议与评语\n */\npublic class AIClientUtil {\n\n private static String apiUrl;\n private static String apiKey;\n private static String model;\n\n static {\n try {\n Properties props = new Properties();\n InputStream is = AIClientUtil.class.getClassLoader().getResourceAsStream(\"db.properties\");\n if (is != null) {\n props.load(is);\n apiUrl = props.getProperty(\"ai.api.url\");\n apiKey = props.getProperty(\"ai.api.key\");\n model = props.getProperty(\"ai.api.model\");\n }\n // Docker环境变量覆盖\n String envUrl = System.getenv(\"AI_API_URL\");\n String envKey = System.getenv(\"AI_API_KEY\");\n String envModel = System.getenv(\"AI_API_MODEL\");\n if (envUrl != null) apiUrl = envUrl;\n if (envKey != null) apiKey = envKey;\n if (envModel != null) model = envModel;\n if (apiUrl == null) apiUrl = \"https://api.deepseek.com/v1/chat/completions\";\n if (model == null) model = \"deepseek-chat\";\n } catch (Exception e) {\n e.printStackTrace();\n }\n }\n\n /**\n * 调用大模型对主观题进行评分\n * @param questionContent 题目内容\n * @param referenceAnswer 参考答案\n * @param studentAnswer 学生答案\n * @param maxScore 满分分值\n * @return 返回JSON格式的评分结果 {\"score\": 得分, \"comment\": \"评语\"}\n */\n public static AIScoreResult scoreSubjective(String questionContent, String referenceAnswer,\n String studentAnswer, int maxScore) {\n // 如果API Key未配置或为默认值,返回模拟评分结果\n if (apiKey == null || apiKey.equals(\"your-api-key-here\") || apiKey.isEmpty()) {\n return mockScore(referenceAnswer, studentAnswer, maxScore);\n }\n\n try {\n String prompt = buildPrompt(questionContent, referenceAnswer, studentAnswer, maxScore);\n\n URL url = new URL(apiUrl);\n HttpURLConnection conn = (HttpURLConnection) url.openConnection();\n conn.setRequestMethod(\"POST\");\n conn.setRequestProperty(\"Content-Type\", \"application/json\");\n conn.setRequestProperty(\"Authorization\", \"Bearer \" + apiKey);\n conn.setDoOutput(true);\n conn.setConnectTimeout(15000);\n conn.setReadTimeout(30000);\n\n // 构建请求体\n JsonObject requestBody = new JsonObject();\n requestBody.addProperty(\"model\", model);\n\n JsonArray messages = new JsonArray();\n JsonObject systemMsg = new JsonObject();\n systemMsg.addProperty(\"role\", \"system\");\n systemMsg.addProperty(\"content\", \"你是一位专业的课程教师,请根据参考答案对学生的答案进行评分。请严格按JSON格式返回:{\\\"score\\\": 数字, \\\"comment\\\": \\\"评语\\\"}\");\n messages.add(systemMsg);\n\n JsonObject userMsg = new JsonObject();\n userMsg.addProperty(\"role\", \"user\");\n userMsg.addProperty(\"content\", prompt);\n messages.add(userMsg);\n\n requestBody.add(\"messages\", messages);\n requestBody.addProperty(\"temperature\", 0.3);\n requestBody.addProperty(\"max_tokens\", 500);\n\n OutputStream os = conn.getOutputStream();\n os.write(requestBody.toString().getBytes(\"UTF-8\"));\n os.flush();\n os.close();\n\n // 读取响应\n int responseCode = conn.getResponseCode();\n if (responseCode == 200) {\n BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), \"UTF-8\"));\n StringBuilder response = new StringBuilder();\n String line;\n while ((line = br.readLine()) != null) {\n response.append(line);\n }\n br.close();\n\n // 解析响应\n JsonObject respJson = JsonParser.parseString(response.toString()).getAsJsonObject();\n String content = respJson.getAsJsonArray(\"choices\")\n .get(0).getAsJsonObject()\n .getAsJsonObject(\"message\")\n .get(\"content\").getAsString();\n\n // 从content中提取JSON\n content = content.trim();\n if (content.startsWith(\"```json\")) {\n content = content.substring(7);\n }\n if (content.endsWith(\"```\")) {\n content = content.substring(0, content.length() - 3);\n }\n content = content.trim();\n\n JsonObject scoreJson = JsonParser.parseString(content).getAsJsonObject();\n AIScoreResult result = new AIScoreResult();\n result.setScore(scoreJson.get(\"score\").getAsInt());\n result.setComment(scoreJson.get(\"comment\").getAsString());\n return result;\n } else {\n // API调用失败,返回模拟评分\n return mockScore(referenceAnswer, studentAnswer, maxScore);\n }\n } catch (Exception e) {\n e.printStackTrace();\n return mockScore(referenceAnswer, studentAnswer, maxScore);\n }\n }\n\n /**\n * 构建评分提示词\n */\n private static String buildPrompt(String questionContent, String referenceAnswer,\n String studentAnswer, int maxScore) {\n return \"请对以下学生答案进行评分:\\n\\n\" +\n \"题目:\" + questionContent + \"\\n\\n\" +\n \"参考答案:\" + referenceAnswer + \"\\n\\n\" +\n \"学生答案:\" + studentAnswer + \"\\n\\n\" +\n \"满分:\" + maxScore + \"分\\n\\n\" +\n \"请综合评估学生答案的准确性、完整性,给出0到\" + maxScore + \"之间的分数,并提供简短评语。\\n\" +\n \"请严格按JSON格式返回:{\\\"score\\\": 数字, \\\"comment\\\": \\\"评语\\\"}\";\n }\n\n /**\n * 模拟评分(当API不可用时使用)\n * 策略:基于学生答案与参考答案的相似度进行简单评分\n */\n private static AIScoreResult mockScore(String referenceAnswer, String studentAnswer, int maxScore) {\n AIScoreResult result = new AIScoreResult();\n\n if (studentAnswer == null || studentAnswer.trim().isEmpty()) {\n result.setScore(0);\n result.setComment(\"未作答,得0分。\");\n return result;\n }\n\n // 简单的关键词匹配相似度计算(使用Unicode标点类避免中文字符集问题)\n String[] refWords = referenceAnswer.replaceAll(\"[\\\\p{P}\\\\p{S}]\", \" \")\n .toLowerCase().split(\"\\\\s+\");\n String stuAnswer = studentAnswer.toLowerCase();\n int matchCount = 0;\n for (String word : refWords) {\n if (word.length() > 1 && stuAnswer.contains(word)) {\n matchCount++;\n }\n }\n\n double similarity = refWords.length > 0 ? (double) matchCount / refWords.length : 0;\n int score = Math.min((int) Math.round(similarity * maxScore), maxScore);\n\n result.setScore(score);\n if (score >= maxScore * 0.9) {\n result.setComment(\"回答很好,与参考答案高度一致。\");\n } else if (score >= maxScore * 0.6) {\n result.setComment(\"回答基本正确,部分要点已覆盖。\");\n } else if (score >= maxScore * 0.3) {\n result.setComment(\"回答不够完整,建议补充更多要点。\");\n } else {\n result.setComment(\"回答与参考答案差距较大,建议认真复习相关知识点。\");\n }\n result.setComment(result.getComment() + \"(注:当前为模拟评分,配置AI API Key后可启用大模型评分)\");\n\n return result;\n }\n\n /**\n * AI评分结果内部类\n */\n public static class AIScoreResult {\n private int score;\n private String comment;\n\n public int getScore() { return score; }\n public void setScore(int score) { this.score = score; }\n\n public String getComment() { return comment; }\n public void setComment(String comment) { this.comment = comment; }\n }\n}\n"}}}} |