老办法(四项打总分)的 prompt 只写了 "criteria_details":{},模型换成 deepseek-flash
之后直接给 {"逻辑正确性": 32} 这样的裸数字,详情页读不到 score / max。prompt 里把
四项的形状写全,存之前按 { score, max, comment } 收好(缺满分按 40/30/20/10 补);
0036 把线上已经存进去的 4 条改回来。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d8e0efd05e
commit
67b13a9345
4 files changed
+3898
-2
No files matched your search
@@ -0,0 +1,32 @@
|
|||||||
|
-- 流程图老办法(四项打总分)的明细,模型有时给成 {"逻辑正确性": 32, ...} 这样的裸数字,
|
||||||
|
-- 提交详情读不到 score / max,四项全显示「0 / 0」。2026-10-19 重新评分老提交时线上撞上 4 条
|
||||||
|
-- (模型换成 deepseek-flash 之后)。裸数字改成 { score, max, comment },满分按 40 / 30 / 20 / 10;
|
||||||
|
-- 本来就是对象的项原样不动。新评的由 flowchart/run.ts 的 normalizeCriteria 收好再存。
|
||||||
|
UPDATE "flowchart_submission" AS target
|
||||||
|
SET "ai_criteria_details" = fixed.details
|
||||||
|
FROM (
|
||||||
|
SELECT f."id",
|
||||||
|
jsonb_object_agg(
|
||||||
|
e.key,
|
||||||
|
CASE
|
||||||
|
WHEN jsonb_typeof(e.value) = 'number' THEN jsonb_build_object(
|
||||||
|
'score', e.value,
|
||||||
|
'max', CASE e.key
|
||||||
|
WHEN '逻辑正确性' THEN 40
|
||||||
|
WHEN '完整性' THEN 30
|
||||||
|
WHEN '规范性' THEN 20
|
||||||
|
WHEN '清晰度' THEN 10
|
||||||
|
ELSE 100
|
||||||
|
END,
|
||||||
|
'comment', ''
|
||||||
|
)
|
||||||
|
ELSE e.value
|
||||||
|
END
|
||||||
|
) AS details
|
||||||
|
FROM "flowchart_submission" f, jsonb_each(f."ai_criteria_details") e
|
||||||
|
WHERE NOT f."ai_criteria_details" ? 'version'
|
||||||
|
AND jsonb_typeof(f."ai_criteria_details") = 'object'
|
||||||
|
GROUP BY f."id"
|
||||||
|
HAVING bool_or(jsonb_typeof(e.value) = 'number')
|
||||||
|
) AS fixed
|
||||||
|
WHERE target."id" = fixed."id";
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -253,6 +253,13 @@
|
|||||||
"when": 1792130722120,
|
"when": 1792130722120,
|
||||||
"tag": "0035_recount_solved_metrics",
|
"tag": "0035_recount_solved_metrics",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 36,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1792415463343,
|
||||||
|
"tag": "0036_fix_flowchart_criteria_numbers",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -163,11 +163,36 @@ function evaluationPrompt(problem: Problem) {
|
|||||||
return `你是专业的编程教学助手,负责评估学生的 Mermaid 流程图。
|
return `你是专业的编程教学助手,负责评估学生的 Mermaid 流程图。
|
||||||
评分:逻辑正确性40分、完整性30分、规范性20分、清晰度10分。
|
评分:逻辑正确性40分、完整性30分、规范性20分、清晰度10分。
|
||||||
不要评价系统生成的节点ID。feedback不超过100字;suggestions最多3条且只针对真实问题。
|
不要评价系统生成的节点ID。feedback不超过100字;suggestions最多3条且只针对真实问题。
|
||||||
返回纯 JSON:{"score":85,"grade":"A","feedback":"...","suggestions":"...","criteria_details":{}}。
|
返回纯 JSON:{"score":85,"grade":"A","feedback":"...","suggestions":"...","criteria_details":{"逻辑正确性":{"score":34,"max":40,"comment":"..."},"完整性":{"score":26,"max":30,"comment":"..."},"规范性":{"score":17,"max":20,"comment":"..."},"清晰度":{"score":8,"max":10,"comment":"..."}}}。
|
||||||
等级:S=90-100,A=80-89,B=70-79,C=0-69。
|
等级:S=90-100,A=80-89,B=70-79,C=0-69。
|
||||||
题目:${problem.title}\n${problem.description.slice(0, 2000)}`
|
题目:${problem.title}\n${problem.description.slice(0, 2000)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CRITERIA_MAX: Record<string, number> = { 逻辑正确性: 40, 完整性: 30, 规范性: 20, 清晰度: 10 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四项明细收成 `{ 项名: { score, max, comment } }`,提交详情和统计都按这个形状读。
|
||||||
|
* 原来 prompt 里只写了 `"criteria_details":{}`,模型有时直接给 `"逻辑正确性": 34`,
|
||||||
|
* 存进去页面上四项全是「0 / 0」(2026-10 重新评分老提交时撞上的)。缺 max 的按满分表补
|
||||||
|
*/
|
||||||
|
function normalizeCriteria(raw: unknown) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(asRecord(raw)).map(([key, value]) => {
|
||||||
|
const item = asRecord(value)
|
||||||
|
const score = typeof value === "number" ? value : Number(item.score)
|
||||||
|
const max = Number(item.max)
|
||||||
|
return [
|
||||||
|
key,
|
||||||
|
{
|
||||||
|
score: Number.isFinite(score) ? score : 0,
|
||||||
|
max: Number.isFinite(max) && max > 0 ? max : (CRITERIA_MAX[key] ?? 100),
|
||||||
|
comment: typeof item.comment === "string" ? item.comment : "",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function parseEvaluation(value: string): Evaluation {
|
function parseEvaluation(value: string): Evaluation {
|
||||||
const block = value.match(/```(?:json)?\s*([\s\S]*?)```/)?.[1]
|
const block = value.match(/```(?:json)?\s*([\s\S]*?)```/)?.[1]
|
||||||
const json = block ?? value.match(/\{[\s\S]*\}/)?.[0]
|
const json = block ?? value.match(/\{[\s\S]*\}/)?.[0]
|
||||||
@@ -181,7 +206,7 @@ function parseEvaluation(value: string): Evaluation {
|
|||||||
grade: gradeForScore(score),
|
grade: gradeForScore(score),
|
||||||
feedback: typeof data.feedback === "string" ? data.feedback : "",
|
feedback: typeof data.feedback === "string" ? data.feedback : "",
|
||||||
suggestions: typeof data.suggestions === "string" ? data.suggestions : "",
|
suggestions: typeof data.suggestions === "string" ? data.suggestions : "",
|
||||||
criteria: asRecord(data.criteria_details),
|
criteria: normalizeCriteria(data.criteria_details),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user