feat: admin端UI

This commit is contained in:
Blizzard
2026-06-27 12:06:29 +08:00
parent d2662a1f37
commit 2d9695e940
9 changed files with 2117 additions and 85 deletions
+258
View File
@@ -0,0 +1,258 @@
import { useState } from "react";
// Mock 评测趋势数据
const QUALITY_TREND = [0.82, 0.84, 0.79, 0.81, 0.85, 0.88, 0.89, 0.87, 0.86, 0.91, 0.92, 0.88, 0.87, 0.88];
const HALLUCINATION_TREND = [18, 15, 22, 19, 14, 11, 10, 12, 13, 8, 7, 11, 12, 10]; // % 比例
const DATES = ["06-14", "06-15", "06-16", "06-17", "06-18", "06-19", "06-20", "06-21", "06-22", "06-23", "06-24", "06-25", "06-26", "06-27"];
// Mock 低分评测记录(错题本)与纠偏轨迹
const MOCK_POOR_RUNS = [
{
id: "task_e8f2a1b9",
time: "11:47:05",
user: "Bob",
agentName: "法律合同审查 Agent",
overall: 0.42,
ruleScore: 0.60,
llmScore: 0.50,
faithful: 0.15,
level: "poor",
reason: "幻觉严重。模型声称合同中包含‘三年内无条件退款限制条款’,但所附 RAG 参考材料中仅提及‘按比例折算退款规则’,属于严重的知识库脱轨和无中生有(无立足依据)。",
trace: {
initialAnswer: "根据合同第 4 条,本合同包含三年内无条件全额退款条款,客户可随时申请解除合作。",
critique: "【评测发现异常】RAG 知识块[文档: 退款政策_2026.pdf]中明确规定退款须‘按合作月份比例折算,扣除已产生渠道服务费后退还余款,且需提前30天书面申请’。模型回答‘无条件全额退款’属严重事实性捏造,忠实度(Faithful)分值判定为 0.15。",
refinePrompt: "你是一个严肃的合同审查纠偏助手。在前一次生成中,模型产生了事实性幻觉。请根据参考材料【退款政策_2026.pdf】:‘退款须按合作月份比例折算,扣除已产生渠道服务费后退还余款,且需提前30天书面申请’,对前次答案【根据合同第 4 条...】进行修改纠正,必须忠实于材料,杜绝捏造无条件条款。",
refinedAnswer: "根据退款政策附件规则,退款非无条件全额,而是必须按合作月份比例折算,且扣除已产生渠道服务费后退还余款。此外,客户申请退款需提前30天提交书面申请,合同第4条仅规定了申请路径,而非‘无条件退款’。",
newScore: 0.85
}
},
{
id: "task_ff3c0b12",
time: "10:12:30",
user: "Alice",
agentName: "医学文献总结 Agent",
overall: 0.55,
ruleScore: 0.50,
llmScore: 0.60,
faithful: 0.55,
level: "poor",
reason: "引用缺失。用户要求‘回答时必须在事实断言处标注 RAG 参考的文档来源’,但模型生成的总结文本中未包含任何形如 [1] 或 [doc_xxx] 的引用锚点,违反了输入 DSL 的强制规范规则。",
trace: {
initialAnswer: "该临床研究表明,使用该抗体偶联药物能提高 15% 的无进展生存期(PFS),且中位缓解期达到了 12.4 个月。",
critique: "【评测发现异常】模型得出了准确的结论,但未按照强制指令将数据来源指向知识块 [文献_ADC研究_Phase3.pdf]。违反强制引用约束,规则分降低为 0.50。",
refinePrompt: "前次生成中未包含数据来源引用。请在前次总结中,为‘提高 15% 的无进展生存期’与‘中位缓解期达到 12.4 个月’数据,在行内添加形如 [文献_ADC研究_Phase3.pdf] 的数据来源引用标签。",
refinedAnswer: "最新临床三期研究表明,使用该抗体偶联药物能提高 15% 的无进展生存期(PFS[文献_ADC研究_Phase3.pdf],且中位缓解期达到了 12.4 个月[文献_ADC研究_Phase3.pdf]。",
newScore: 0.92
}
},
{
id: "task_a5fd2510",
time: "09:30:15",
user: "Alice",
agentName: "财务审计核对 Agent",
overall: 0.48,
ruleScore: 0.40,
llmScore: 0.55,
faithful: 0.50,
level: "poor",
reason: "计算口径不一致。知识库给出的 2025 Q4 营业收入为‘1.2 亿元(含未结算款项)’,但财务报表核查中模型漏计了未结算款,算出了 1.05 亿元,与账面发生额不符。",
trace: {
initialAnswer: "核对完毕,Q4 累计核算收入为 1.05 亿元,与系统申报一致。",
critique: "【评测发现异常】少计算了未结算款项 1500 万元。知识库规定必须将未结算款项计入 Q4 营业收入内。规则评分降为 0.40。",
refinePrompt: "计算遗漏。请把财务参考中的‘1500万未结算款项’加进 Q4 营业收入核对中,重新计算总营收并核对金额。",
refinedAnswer: "重新核算后,Q4 累计核算总收入为 1.2 亿元(已包含 1500 万元未结算款项),与系统申报发生额完全吻合。",
newScore: 0.89
}
}
];
export function EvalsPage() {
const [runs, setRuns] = useState(MOCK_POOR_RUNS);
const [expandedId, setExpandedId] = useState<string | null>(null);
const toggleExpand = (id: string) => {
setExpandedId((prev) => (prev === id ? null : id));
};
// SVG 趋势图宽高
const chartW = 260;
const chartH = 70;
const pad = 10;
// 1. 质量曲线点计算
const maxValQ = 1.0;
const pointsQ = QUALITY_TREND.map((val, idx) => {
const x = pad + (idx * (chartW - pad * 2)) / (QUALITY_TREND.length - 1);
const y = chartH - pad - (val * (chartH - pad * 2)) / maxValQ;
return { x, y };
});
const pathQ = pointsQ.reduce((p, pt, i) => p + `${i === 0 ? "M" : "L"} ${pt.x.toFixed(1)} ${pt.y.toFixed(1)}`, "");
// 2. 幻觉率曲线点计算
const maxValH = 30; // 最大 30% 刻度
const pointsH = HALLUCINATION_TREND.map((val, idx) => {
const x = pad + (idx * (chartW - pad * 2)) / (HALLUCINATION_TREND.length - 1);
const y = chartH - pad - (val * (chartH - pad * 2)) / maxValH;
return { x, y };
});
const pathH = pointsH.reduce((p, pt, i) => p + `${i === 0 ? "M" : "L"} ${pt.x.toFixed(1)} ${pt.y.toFixed(1)}`, "");
return (
<div className="space-y-6">
{/* 顶部大盘指标与微缩趋势图 */}
<div className="grid grid-cols-1 gap-6 md:grid-cols-3">
{/* 指标 1:综合评测均分 */}
<section className="rounded-xl border border-gray-100 bg-white p-4 shadow-sm flex items-center justify-between gap-4">
<div>
<span className="text-xs font-medium text-gray-400"></span>
<h3 className="mt-1 text-2xl font-bold text-gray-800">0.88</h3>
<span className="text-[10px] text-emerald-600"> 3%</span>
</div>
{/* 微型折线图 */}
<div className="w-36 h-12 bg-gray-50/50 rounded border p-1">
<svg viewBox={`0 0 ${chartW} ${chartH}`} className="w-full h-full overflow-visible">
<path d={pathQ} fill="none" stroke="#7c3aed" strokeWidth="2" strokeLinecap="round" />
</svg>
</div>
</section>
{/* 指标 2:忠实度评测与幻觉率 */}
<section className="rounded-xl border border-gray-100 bg-white p-4 shadow-sm flex items-center justify-between gap-4">
<div>
<span className="text-xs font-medium text-gray-400"></span>
<h3 className="mt-1 text-2xl font-bold text-rose-600">10%</h3>
<span className="text-[10px] text-emerald-600"> 8%</span>
</div>
{/* 微型折线图 */}
<div className="w-36 h-12 bg-gray-50/50 rounded border p-1">
<svg viewBox={`0 0 ${chartW} ${chartH}`} className="w-full h-full overflow-visible">
<path d={pathH} fill="none" stroke="#f43f5e" strokeWidth="2" strokeLinecap="round" />
</svg>
</div>
</section>
{/* 指标 3:纠偏系统效能 */}
<section className="rounded-xl border border-gray-100 bg-white p-4 shadow-sm">
<span className="text-xs font-medium text-gray-400"> ()</span>
<div className="mt-2 flex items-baseline gap-2">
<h3 className="text-2xl font-bold text-gray-800">84.2%</h3>
<span className="text-xs text-gray-500"> 228 </span>
</div>
<div className="mt-2 h-1.5 w-full bg-gray-100 rounded-full overflow-hidden">
<div className="h-full bg-violet-600 rounded-full" style={{ width: "84.2%" }} />
</div>
</section>
</div>
{/* 自动纠偏错题本(失败记录) */}
<section className="rounded-xl border border-gray-100 bg-white p-5 shadow-sm">
<div className="mb-4">
<h3 className="text-sm font-semibold text-gray-700"> ()</h3>
<p className="text-[11px] text-gray-400">poorRefinement</p>
</div>
<div className="space-y-3">
{runs.map((r) => {
const isExpanded = expandedId === r.id;
return (
<div key={r.id} className="rounded-lg border border-gray-100 overflow-hidden">
{/* 简要行 */}
<div
onClick={() => toggleExpand(r.id)}
className={`flex flex-col md:flex-row md:items-center justify-between gap-4 p-3.5 cursor-pointer hover:bg-gray-50/50 transition-colors ${
isExpanded ? "bg-violet-50/20 border-b border-violet-100" : ""
}`}
>
<div className="flex items-center gap-3">
<span className="text-xs font-mono font-bold text-gray-400">[{r.time}]</span>
<div>
<div className="text-xs font-semibold text-gray-800">{r.agentName}</div>
<div className="text-[10px] text-gray-400">: {r.user} | ID: {r.id}</div>
</div>
</div>
{/* 分数指标组 */}
<div className="flex items-center gap-3">
<div className="text-center">
<div className="text-[9px] text-gray-400"></div>
<div className="text-xs font-bold text-rose-600">{r.overall.toFixed(2)}</div>
</div>
<div className="text-center border-l pl-3">
<div className="text-[9px] text-gray-400"></div>
<div className="text-xs font-semibold text-gray-600">{r.ruleScore.toFixed(2)}</div>
</div>
<div className="text-center border-l pl-3">
<div className="text-[9px] text-gray-400">RAG忠实度</div>
<div className={`text-xs font-semibold ${r.faithful <= 0.3 ? "text-rose-600" : "text-gray-600"}`}>
{r.faithful.toFixed(2)}
</div>
</div>
<div className="text-center border-l pl-3">
<div className="text-[9px] text-gray-400"></div>
<div className="text-xs font-bold text-emerald-600"> {r.trace.newScore.toFixed(2)}</div>
</div>
{/* 展开折叠箭头 */}
<span className="text-gray-400 text-xs pl-2 font-mono">{isExpanded ? "▲" : "▼"}</span>
</div>
</div>
{/* 展开详细信息(纠偏对齐轨迹详情) */}
{isExpanded && (
<div className="p-4 bg-gray-50/30 text-xs space-y-4 animate-fadeIn">
{/* 1. 问题定位 */}
<div className="border-l-2 border-rose-500 pl-3">
<h5 className="font-bold text-gray-800"> (Evaluator Diagnosis)</h5>
<p className="mt-1 text-gray-600 leading-snug">{r.reason}</p>
</div>
{/* 2. 纠偏流转卡片组 */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* 初次答案 */}
<div className="rounded border bg-white p-3">
<div className="text-[10px] font-bold text-rose-600 flex items-center gap-1.5">
<span className="h-1.5 w-1.5 rounded-full bg-rose-500" />
/ Score: {r.overall.toFixed(2)}
</div>
<p className="mt-2 text-gray-500 font-mono leading-relaxed">{r.trace.initialAnswer}</p>
</div>
{/* 初次评测评语 */}
<div className="rounded border bg-white p-3">
<div className="text-[10px] font-bold text-amber-600 flex items-center gap-1.5">
<span className="h-1.5 w-1.5 rounded-full bg-amber-500" />
(Evaluator Critique)
</div>
<p className="mt-2 text-gray-500 leading-relaxed">{r.trace.critique}</p>
</div>
{/* 纠偏 Prompt 注入 */}
<div className="rounded border bg-white p-3 md:col-span-2">
<div className="text-[10px] font-bold text-violet-600 flex items-center gap-1.5">
<span className="h-1.5 w-1.5 rounded-full bg-violet-500 animate-pulse" />
(Refinement Rewrite Prompt)
</div>
<p className="mt-2 text-gray-500 font-mono leading-relaxed bg-gray-50 p-2 rounded border border-gray-100">
{r.trace.refinePrompt}
</p>
</div>
{/* 纠偏后最终答案 */}
<div className="rounded border bg-white p-3 md:col-span-2 border-emerald-200 bg-emerald-50/10">
<div className="text-[10px] font-bold text-emerald-600 flex items-center gap-1.5">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
Score: {r.trace.newScore.toFixed(2)}
</div>
<p className="mt-2 text-gray-700 font-mono leading-relaxed">{r.trace.refinedAnswer}</p>
</div>
</div>
</div>
)}
</div>
);
})}
</div>
</section>
</div>
);
}