2026-06-27 14:31:45 +08:00
|
|
|
|
// metric_presenter.js — 统一维护指标语义(高分好 / 低分好)、颜色阈值与简要说明。
|
|
|
|
|
|
|
|
|
|
|
|
(function attachMetricPresenter(globalObj) {
|
|
|
|
|
|
const METRIC_META = {
|
|
|
|
|
|
faithfulness: {
|
|
|
|
|
|
direction: "higher_better",
|
|
|
|
|
|
description: "回答是否被检索内容直接支持,越高越可靠。",
|
|
|
|
|
|
},
|
|
|
|
|
|
answer_relevancy: {
|
|
|
|
|
|
direction: "higher_better",
|
|
|
|
|
|
description: "回答与问题是否紧密相关,越高越切题。",
|
|
|
|
|
|
},
|
|
|
|
|
|
context_recall: {
|
|
|
|
|
|
direction: "higher_better",
|
|
|
|
|
|
description: "检索片段覆盖标准答案关键信息的程度,越高越完整。",
|
|
|
|
|
|
},
|
|
|
|
|
|
context_precision: {
|
|
|
|
|
|
direction: "higher_better",
|
|
|
|
|
|
description: "检索片段中有效信息的占比,越高越精准。",
|
|
|
|
|
|
},
|
|
|
|
|
|
noise_sensitivity: {
|
|
|
|
|
|
direction: "lower_better",
|
|
|
|
|
|
description: "对噪声上下文的敏感程度,越低说明抗干扰能力越强。",
|
|
|
|
|
|
},
|
|
|
|
|
|
factual_correctness: {
|
|
|
|
|
|
direction: "higher_better",
|
|
|
|
|
|
description: "回答与标准答案在事实层面的吻合程度,越高越准确。",
|
|
|
|
|
|
},
|
|
|
|
|
|
semantic_similarity: {
|
|
|
|
|
|
direction: "higher_better",
|
|
|
|
|
|
description: "回答与标准答案在语义上的相似程度,越高越接近。",
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
function isLowerBetter(metricName) {
|
|
|
|
|
|
return METRIC_META[metricName]?.direction === "lower_better";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function scoreClass(metricName, value) {
|
|
|
|
|
|
if (value === null || value === undefined || Number.isNaN(Number(value))) return "na";
|
|
|
|
|
|
const numeric = Number(value);
|
|
|
|
|
|
if (isLowerBetter(metricName)) {
|
|
|
|
|
|
if (numeric <= 0.15) return "good";
|
|
|
|
|
|
if (numeric <= 0.35) return "warn";
|
|
|
|
|
|
return "bad";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (numeric >= 0.85) return "good";
|
|
|
|
|
|
if (numeric >= 0.65) return "warn";
|
|
|
|
|
|
return "bad";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function describeMetric(metricName) {
|
|
|
|
|
|
return METRIC_META[metricName]?.description || "该指标用于衡量当前问答样本的评估表现。";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function binColor(metricName, lower) {
|
|
|
|
|
|
const numeric = Number(lower);
|
|
|
|
|
|
if (isLowerBetter(metricName)) {
|
|
|
|
|
|
if (numeric < 0.2) return "#16a34a";
|
|
|
|
|
|
if (numeric < 0.4) return "#84cc16";
|
|
|
|
|
|
if (numeric < 0.6) return "#eab308";
|
|
|
|
|
|
if (numeric < 0.8) return "#f97316";
|
|
|
|
|
|
return "#dc2626";
|
|
|
|
|
|
}
|
|
|
|
|
|
if (numeric >= 0.8) return "#16a34a";
|
|
|
|
|
|
if (numeric >= 0.6) return "#84cc16";
|
|
|
|
|
|
if (numeric >= 0.4) return "#eab308";
|
|
|
|
|
|
if (numeric >= 0.2) return "#f97316";
|
|
|
|
|
|
return "#dc2626";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 17:53:00 +08:00
|
|
|
|
// 计算某指标本次相对上一次的涨跌信息,方向语义随指标而定
|
|
|
|
|
|
// (noise_sensitivity 越低越好:下降=改善)。
|
|
|
|
|
|
function deltaInfo(metricName, current, previous) {
|
|
|
|
|
|
const isNum = (v) => v !== null && v !== undefined && !Number.isNaN(Number(v));
|
|
|
|
|
|
if (!isNum(current) || !isNum(previous)) {
|
|
|
|
|
|
return { hasData: false, delta: null, improved: null, arrow: "", magnitude: "", cls: "delta-flat" };
|
|
|
|
|
|
}
|
|
|
|
|
|
const delta = Number(current) - Number(previous);
|
|
|
|
|
|
const rounded = Math.round(delta * 10000) / 10000;
|
|
|
|
|
|
const arrow = rounded > 0 ? "▲" : rounded < 0 ? "▼" : "→";
|
|
|
|
|
|
const magnitude = Math.abs(rounded).toFixed(2);
|
|
|
|
|
|
const improved = isLowerBetter(metricName) ? rounded < 0 : rounded > 0;
|
|
|
|
|
|
const cls = rounded === 0 ? "delta-flat" : improved ? "delta-good" : "delta-bad";
|
|
|
|
|
|
return { hasData: true, delta: rounded, improved, arrow, magnitude, cls };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 返回指标的"达标阈值"(柱状图对比用,方向感知)。
|
|
|
|
|
|
// higher-better 指标:0.85;lower-better (noise_sensitivity):0.15。
|
|
|
|
|
|
function passThreshold(metricName) {
|
|
|
|
|
|
return isLowerBetter(metricName) ? 0.15 : 0.85;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 判断某指标的值是否达标。
|
|
|
|
|
|
function meetsTarget(metricName, value) {
|
|
|
|
|
|
if (value === null || value === undefined || Number.isNaN(Number(value))) return false;
|
|
|
|
|
|
const v = Number(value);
|
|
|
|
|
|
return isLowerBetter(metricName) ? v <= passThreshold(metricName) : v >= passThreshold(metricName);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-27 14:31:45 +08:00
|
|
|
|
globalObj.MetricPresenter = {
|
|
|
|
|
|
scoreClass,
|
|
|
|
|
|
describeMetric,
|
|
|
|
|
|
binColor,
|
2026-07-01 17:53:00 +08:00
|
|
|
|
isLowerBetter,
|
|
|
|
|
|
deltaInfo,
|
|
|
|
|
|
passThreshold,
|
|
|
|
|
|
meetsTarget,
|
2026-06-27 14:31:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
})(window);
|