diff --git a/webapp/static/css/app.css b/webapp/static/css/app.css
index 5661e8b..15f70ef 100644
--- a/webapp/static/css/app.css
+++ b/webapp/static/css/app.css
@@ -270,6 +270,24 @@ table.history-table td { font-variant-numeric: tabular-nums; }
.hist-delta.delta-bad { color: #dc2626; }
.hist-delta.delta-flat { color: var(--slate-light); }
+.advisor-comparison-panel { border-left: 3px solid #0ea5e9; }
+.advisor-comparison-header {
+ display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
+ margin-bottom: 10px;
+}
+.advisor-comparison-row {
+ display: flex; align-items: center; gap: 10px;
+ padding: 6px 0; border-bottom: 1px solid var(--line);
+ font-size: 13px;
+}
+.advisor-comparison-row:last-of-type { border-bottom: none; }
+.advisor-comparison-status { font-weight: 600; white-space: nowrap; }
+.advisor-comparison-metric { flex: 1; color: var(--slate); }
+.advisor-comparison-scores { font-variant-numeric: tabular-nums; white-space: nowrap; }
+.advisor-comparison-status.delta-good { color: #16a34a; }
+.advisor-comparison-status.delta-bad { color: #dc2626; }
+.advisor-comparison-status.delta-flat { color: var(--slate); }
+
.empty { text-align: center; padding: 60px 20px; color: var(--slate); }
.empty p { margin-bottom: 8px; }
diff --git a/webapp/static/index.html b/webapp/static/index.html
index b48180b..6ac4748 100644
--- a/webapp/static/index.html
+++ b/webapp/static/index.html
@@ -174,6 +174,17 @@
⑤ 优化建议 OPTIMIZATION ADVICE
diff --git a/webapp/static/js/report.js b/webapp/static/js/report.js
index 49b80a9..966b081 100644
--- a/webapp/static/js/report.js
+++ b/webapp/static/js/report.js
@@ -31,6 +31,7 @@ const Report = {
Report.renderDistribution(detail.report);
Report.renderGroupings(detail.report);
Report.renderLowest(detail.report);
+ Report.renderAdvisorComparison(detail.report);
Report.renderAdvice(detail.summary, detail.report);
Report.renderTokenUsage(detail.report);
content.style.opacity = "1";
@@ -275,6 +276,61 @@ const Report = {
wrap.innerHTML = `
`;
},
+ // 相比上次运行的顾问诊断对比(同 scenario_name 的最近一次前序运行,自动匹配)。
+ renderAdvisorComparison(report) {
+ const section = document.getElementById("advisor-comparison-section");
+ const meta = document.getElementById("advisor-comparison-meta");
+ const body = document.getElementById("advisor-comparison-body");
+ if (!section || !meta || !body) return;
+
+ const comparison = report.advisor_comparison;
+ if (!comparison || !comparison.entries || comparison.entries.length === 0) {
+ section.hidden = true;
+ return;
+ }
+
+ section.hidden = false;
+ meta.textContent = `对比:${comparison.previous_run_id}(${App.shortTime(comparison.previous_finished_at)})`;
+
+ const STATUS_LABEL = {
+ resolved: "✅ 已改善",
+ regressed: "⚠️ 新触发",
+ still_triggered: "❌ 仍未解决",
+ new_metric: "🆕 新增指标",
+ };
+ const STATUS_CLASS = {
+ resolved: "delta-good",
+ regressed: "delta-bad",
+ still_triggered: "delta-bad",
+ new_metric: "delta-flat",
+ };
+
+ const fmt = (v) => (v === null || v === undefined ? "—" : Number(v).toFixed(2));
+
+ let rows = "";
+ comparison.entries.forEach((entry) => {
+ const label = STATUS_LABEL[entry.status] || entry.status;
+ const cls = STATUS_CLASS[entry.status] || "delta-flat";
+ const d = MetricPresenter.deltaInfo(entry.metric, entry.current_score, entry.previous_score);
+ const deltaHtml = d.hasData && d.delta !== 0
+ ? `
${d.arrow}${d.magnitude}`
+ : "";
+ rows += `
+
+ ${label}
+ ${App.escape(App.shortMetric(entry.metric))}
+ ${fmt(entry.previous_score)} → ${fmt(entry.current_score)}${deltaHtml}
+
`;
+ });
+
+ let caveat = "";
+ if (comparison.judge_model_changed) {
+ caveat = `
⚠️ judge_model 不同(${App.escape(comparison.previous_judge_model)} → ${App.escape(comparison.current_judge_model)}),对比仅供参考。
`;
+ }
+
+ body.innerHTML = rows + caveat;
+ },
+
// ④ 最低分样本逐条复核表(点击展开)。
renderLowest(report) { const wrap = document.getElementById("lowest-table");
const samples = report.lowest_samples || [];