update for ragas

This commit is contained in:
wangwei
2026-07-01 17:53:00 +08:00
parent 2bb804b059
commit 4e74e1b247
15 changed files with 507 additions and 53 deletions
+3 -2
View File
@@ -5,8 +5,8 @@
const App = {
currentRunId: null,
activeView: null,
views: ["runs", "new", "report", "profiles", "scorejobs", "apidocs"],
titles: { runs: "运行列表", new: "新建评估", report: "报告详情", profiles: "LLM 配置", scorejobs: "评分记录", apidocs: "API 文档" },
views: ["runs", "new", "report", "profiles", "scorejobs", "dashboard", "apidocs"],
titles: { runs: "运行列表", new: "新建评估", report: "报告详情", profiles: "LLM 配置", scorejobs: "评分记录", dashboard: "指标看板", apidocs: "API 文档" },
// 初始化:绑定导航、从 URL/sessionStorage 恢复上次位置、启动健康检查。
init() {
@@ -73,6 +73,7 @@ const App = {
if (view === "report") Report.render(App.currentRunId);
if (view === "profiles") Profiles.load();
if (view === "scorejobs") ScoreJobs.load();
if (view === "dashboard") Dashboard.load();
},
// ----------------------------------------------------------------
+33
View File
@@ -69,9 +69,42 @@
return "#dc2626";
}
// 计算某指标本次相对上一次的涨跌信息,方向语义随指标而定
// 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.85lower-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);
}
globalObj.MetricPresenter = {
scoreClass,
describeMetric,
binColor,
isLowerBetter,
deltaInfo,
passThreshold,
meetsTarget,
};
})(window);
+60 -3
View File
@@ -283,7 +283,7 @@ const Report = {
const detail = document.createElement("div");
detail.className = "lowest-detail";
detail.hidden = true;
detail.innerHTML = Report._detailHtml(sample);
detail.innerHTML = Report._detailHtml(sample, metrics);
row.addEventListener("click", () => {
detail.hidden = !detail.hidden;
@@ -293,8 +293,8 @@ const Report = {
});
},
// 单条样本的展开详情:question / contexts / answer / ground_truth。
_detailHtml(sample) {
// 单条样本的展开详情:question / contexts / answer / ground_truth / 历史评分
_detailHtml(sample, metrics) {
const contexts = (sample.contexts || [])
.map((c, i) => `<div class="ctx-item">[${i + 1}] ${App.escape(c)}</div>`)
.join("");
@@ -320,6 +320,63 @@ const Report = {
<div class="detail-gt">${App.escape(sample.ground_truth || "—")}</div>
</div>
${errorBlock}
${Report._historyHtml(sample, metrics || [])}
</div>
`;
},
// 同一问题的历史评分小表格:本次 + 历次(按时间倒序),逐行标注较更早一次的涨跌。
_historyHtml(sample, metrics) {
const history = sample.history || [];
if (!history.length) return "";
// 只展示当前样本与历史中实际出现过的指标列,避免空列。
const cols = metrics.filter(
(m) =>
(sample.metrics && sample.metrics[m] !== undefined && sample.metrics[m] !== null) ||
history.some((h) => h.metrics && h.metrics[m] !== undefined && h.metrics[m] !== null),
);
if (!cols.length) return "";
// 组合行:[本次, 历次...],相邻两行做涨跌对比(行 r 对比更早的行 r+1)。
const rows = [
{ label: "本次", sub: "", metrics: sample.metrics || {}, current: true },
...history.map((h) => ({
label: App.escape(h.scenario_name || h.run_id || "历史"),
sub: App.escape(App.shortTime(h.finished_at)),
metrics: h.metrics || {},
current: false,
})),
];
let head = "<tr><th>评测</th>";
cols.forEach((m) => (head += `<th>${App.escape(App.shortMetric(m))}</th>`));
head += "</tr>";
let body = "";
rows.forEach((row, r) => {
const older = rows[r + 1];
body += `<tr class="${row.current ? "hist-current" : ""}">`;
body += `<td class="hist-when"><span class="hist-label">${row.label}</span>${row.sub ? `<span class="hist-sub">${row.sub}</span>` : ""}</td>`;
cols.forEach((m) => {
const v = row.metrics ? row.metrics[m] : null;
const cls = App.scoreClass(m, v);
const text = v === null || v === undefined ? "—" : Number(v).toFixed(2);
let deltaHtml = "";
const baseline = older && older.metrics ? older.metrics[m] : undefined;
const d = MetricPresenter.deltaInfo(m, v, baseline);
if (d.hasData && d.delta !== 0) {
deltaHtml = ` <span class="hist-delta ${d.cls}">${d.arrow}${d.magnitude}</span>`;
}
body += `<td><span class="score-badge ${cls}">${text}</span>${deltaHtml}</td>`;
});
body += "</tr>";
});
return `
<div class="detail-field">
<div class="detail-label">历史评分 history(同一问题,最近 ${history.length} 次,含本次对比)</div>
<table class="history-table">${head}${body}</table>
</div>
`;
},