|
|
const fs = require('fs');
|
|
|
const path = require('path');
|
|
|
|
|
|
/**
|
|
|
* 解析 config/evaluation-dimensions.yaml(仅支持本仓库使用的扁平 dimensions 列表)
|
|
|
* @returns {{ id: string, name: string, weight: number, rubric: string }[]}
|
|
|
*/
|
|
|
function loadEvaluationDimensions(rootDir) {
|
|
|
const p = path.join(rootDir, 'config', 'evaluation-dimensions.yaml');
|
|
|
if (!fs.existsSync(p)) return [];
|
|
|
const raw = fs.readFileSync(p, 'utf8');
|
|
|
const dims = [];
|
|
|
let cur = null;
|
|
|
for (const line of raw.split('\n')) {
|
|
|
const t = line.trim();
|
|
|
if (t.startsWith('- id:')) {
|
|
|
if (cur && cur.id) dims.push(cur);
|
|
|
cur = { id: t.replace(/^-\s*id:\s*/, '').trim(), name: '', weight: 1, rubric: '' };
|
|
|
} else if (cur && t.startsWith('name:')) {
|
|
|
cur.name = t.replace(/^name:\s*/, '').trim();
|
|
|
} else if (cur && t.startsWith('weight:')) {
|
|
|
const w = parseFloat(t.replace(/^weight:\s*/, '').trim());
|
|
|
cur.weight = Number.isFinite(w) ? w : 1;
|
|
|
} else if (cur && t.startsWith('rubric:')) {
|
|
|
cur.rubric = t.replace(/^rubric:\s*/, '').trim();
|
|
|
}
|
|
|
}
|
|
|
if (cur && cur.id) dims.push(cur);
|
|
|
return dims;
|
|
|
}
|
|
|
|
|
|
/** 写入 Agent 提示词:教师可改 YAML 即改维度与 rubric */
|
|
|
function formatDimensionsForPrompt(dimensions) {
|
|
|
if (!dimensions.length) return '(未找到 evaluation-dimensions.yaml)';
|
|
|
return dimensions
|
|
|
.map(
|
|
|
(d, i) =>
|
|
|
`${i + 1}. id=${d.id} name=${d.name} weight=${d.weight}\n rubric: ${d.rubric}`,
|
|
|
)
|
|
|
.join('\n');
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 将大模型返回的 ability 与 YAML 维度对齐:顺序、id、name 以 YAML 为准;缺项用启发式或占位。
|
|
|
*/
|
|
|
function alignEvaluationAbilityToDimensions(evaluation, dimensions, heuristicEvaluation) {
|
|
|
if (!evaluation || !dimensions.length) return;
|
|
|
const raw = evaluation.ability || evaluation.abilities || [];
|
|
|
const byId = new Map();
|
|
|
for (const a of raw) {
|
|
|
if (a && typeof a.id === 'string' && a.id.trim()) byId.set(a.id.trim(), a);
|
|
|
}
|
|
|
const fb = heuristicEvaluation?.ability || heuristicEvaluation?.abilities || [];
|
|
|
const fbMap = new Map();
|
|
|
for (const a of fb) {
|
|
|
if (a && a.id) fbMap.set(String(a.id).trim(), a);
|
|
|
}
|
|
|
|
|
|
evaluation.ability = dimensions.map((d) => {
|
|
|
const m = byId.get(d.id);
|
|
|
const h = fbMap.get(d.id);
|
|
|
const val = (() => {
|
|
|
if (m != null && m.value != null && !Number.isNaN(Number(m.value))) {
|
|
|
return Math.max(0, Math.min(100, Math.round(Number(m.value))));
|
|
|
}
|
|
|
if (h != null && h.value != null && !Number.isNaN(Number(h.value))) {
|
|
|
return Math.max(0, Math.min(100, Math.round(Number(h.value))));
|
|
|
}
|
|
|
return 32;
|
|
|
})();
|
|
|
const comment =
|
|
|
(typeof m?.comment === 'string' && m.comment.trim() && m.comment.trim()) ||
|
|
|
(typeof h?.comment === 'string' && h.comment.trim() && h.comment.trim()) ||
|
|
|
`请结合任务说明与本维度 rubric 核验:${d.rubric}`;
|
|
|
return { id: d.id, name: d.name, value: val, comment };
|
|
|
});
|
|
|
delete evaluation.abilities;
|
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
|
loadEvaluationDimensions,
|
|
|
formatDimensionsForPrompt,
|
|
|
alignEvaluationAbilityToDimensions,
|
|
|
};
|