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, };