Files
siemens_ragas/webapp/static/index.html
wangwei 4fd515d2d9 feat: async score jobs — POST /api/score/async + 评分记录 page
Each async score job:
- Runs InlineScorer.score() in thread pool
- Writes standard run artifacts (metadata.json, scores.csv, summary.md)
- Runs optimization_advisor => optimization_advice.md
- Result appears in 运行列表 and 报告详情 with full report

New endpoints:
- POST /api/score/async  (202, job_id immediate)
- GET  /api/score/jobs   (list all jobs)
- GET  /api/score/jobs/{id} (single job status)

Frontend:
- 评分记录 nav page with card list
- 5s auto-polling for queued/running jobs
- 查看报告 button navigates to existing 报告详情 page

Dify: change /api/score -> /api/score/async, no response parsing needed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-24 17:24:22 +08:00

277 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Siemens RAGAS 评估平台</title>
<link rel="stylesheet" href="/static/css/app.css" />
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
</head>
<body>
<div class="app">
<!-- 左侧导航(布局 A -->
<aside class="sidebar">
<div class="brand">
<div class="brand-mark">Siemens RAGAS</div>
<div class="brand-sub">评估平台</div>
</div>
<nav class="nav">
<button class="nav-item" data-view="runs">
<span class="nav-ico"></span><span>运行列表</span>
</button>
<button class="nav-item" data-view="new">
<span class="nav-ico"></span><span>新建评估</span>
</button>
<button class="nav-item" data-view="report" data-requires-run="1" disabled>
<span class="nav-ico"></span><span>报告详情</span>
</button>
<button class="nav-item" data-view="profiles">
<span class="nav-ico"></span><span>LLM 配置</span>
</button>
<button class="nav-item" data-view="scorejobs">
<span class="nav-ico">📋</span><span>评分记录</span>
</button>
<button class="nav-item" data-view="apidocs">
<span class="nav-ico"></span><span>API 文档</span>
</button>
</nav>
<div class="sidebar-foot">
<span class="dot" id="health-dot"></span>
<span id="health-text">连接中…</span>
</div>
</aside>
<!-- 主内容区 -->
<main class="main">
<header class="topbar">
<h1 id="view-title">运行列表</h1>
<button class="btn btn-ghost" id="refresh-btn">刷新</button>
</header>
<!-- 运行列表视图 -->
<section class="view" id="view-runs">
<div id="runs-container" class="runs-grid"></div>
<div class="empty" id="runs-empty" hidden>
<p>暂无评估运行。</p>
<p class="muted">从「新建评估」触发一次,或运行示例数据生成脚本:<code>python scripts/seed_sample_run.py</code></p>
</div>
</section>
<!-- 新建评估视图 -->
<section class="view" id="view-new" hidden>
<div class="panel">
<h2>选择场景并运行</h2>
<p class="muted"><code>scenarios/</code> 下选择一个场景配置,点击运行后在下方查看实时状态与日志。</p>
<div class="scenario-list" id="scenario-list"></div>
<div class="run-actions">
<button class="btn btn-primary" id="run-btn" disabled>运行评估</button>
<span class="selected-scenario muted" id="selected-scenario">未选择场景</span>
</div>
</div>
<!-- LLM 角色配置面板(选中场景后显示) -->
<div class="panel llm-assignment-panel" id="llm-assignment-panel" hidden>
<h2>LLM 角色配置 <span class="muted" style="font-size:13px;font-weight:400">(可选)</span></h2>
<p class="muted" style="margin-bottom:14px">为不同任务角色选择已保存的 LLM 配置,留空则使用场景文件中的原始配置。</p>
<div class="llm-role-rows">
<div class="llm-role-row">
<label class="llm-role-label">评测打分 Judge LLM</label>
<select class="select llm-role-select" id="role-judge">
<option value="">— 使用场景原始配置 —</option>
</select>
</div>
<div class="llm-role-row">
<label class="llm-role-label">生成答案 Answer LLM</label>
<select class="select llm-role-select" id="role-answer">
<option value="">— 使用场景原始配置 —</option>
</select>
</div>
<div class="llm-role-row">
<label class="llm-role-label">生成题库 Dataset LLM</label>
<select class="select llm-role-select" id="role-dataset">
<option value="">— 使用场景原始配置 —</option>
</select>
</div>
</div>
</div>
<!-- ??????????????? -->
<div class="panel weight-config-panel" id="weight-config-panel" hidden>
<h2>???? <span class="muted" style="font-size:13px;font-weight:400">???????????????</span></h2>
<div class="weight-section">
<div class="weight-section-title">???? <span class="muted" style="font-size:12px">???????????????????</span></div>
<div id="metric-weight-rows" class="weight-rows"></div>
</div>
<div class="weight-section" style="margin-top:16px">
<div class="weight-section-title">???? <span class="muted" style="font-size:12px">?? PDF ???????????????????????</span></div>
<div id="doc-weight-rows" class="weight-rows"></div>
<button class="btn btn-sm" id="add-doc-weight-btn" style="margin-top:8px">? ??????</button>
</div>
</div>
<div class="panel" id="task-panel" hidden>
<div class="task-head">
<h2>评估进度</h2>
<span class="badge" id="task-status">queued</span>
</div>
<pre class="log-box" id="task-log"></pre>
<div class="task-actions">
<button class="btn btn-primary" id="view-report-btn" hidden>查看报告</button>
</div>
</div>
</section>
<!-- 报告详情视图 -->
<section class="view" id="view-report" hidden>
<!-- 历史报告切换下拉(顶部,始终可见) -->
<div class="report-switcher no-print" id="report-switcher">
<label class="report-switcher-label">切换报告</label>
<select class="select report-switcher-select" id="report-switcher-select">
<option value="">— 加载中… —</option>
</select>
</div>
<div class="empty" id="report-empty">
<p>请先从「运行列表」选择一次运行。</p>
</div>
<div id="report-content" hidden>
<!-- 顶部元信息条 -->
<div class="report-meta" id="report-meta"></div>
<div class="report-actions no-print">
<button class="btn btn-ghost btn-export-pdf" id="export-pdf-btn" onclick="Report.exportPdf()">
📄 导出 PDF
</button>
</div>
<!-- ① 指标均值卡片 -->
<div class="section-label">① 指标均值 OVERVIEW</div>
<div class="metric-cards" id="metric-cards"></div>
<!-- ② 分布 + ③ 分组 并排 -->
<div class="report-row">
<div class="panel report-half">
<div class="panel-head">
<div class="section-label tight">② 分数分布</div>
<select id="dist-metric-select" class="select"></select>
</div>
<canvas id="dist-chart" height="160"></canvas>
<p class="muted tiny">暴露长尾失败样本</p>
</div>
<div class="panel report-half">
<div class="section-label tight">③ 分组均值</div>
<div id="grouping-tabs" class="grouping-tabs"></div>
<div id="grouping-table"></div>
<p class="muted tiny">定位薄弱类别</p>
</div>
</div>
<!-- ④ 最低分样本逐条复核 -->
<div class="section-label">④ 最低分样本(点击展开逐条复核)</div>
<div class="lowest-table" id="lowest-table"></div>
<!-- ⑤ 优化建议optimization_advisor: true 时显示) -->
<div id="advice-section" hidden>
<div class="section-label">⑤ 优化建议 OPTIMIZATION ADVICE</div>
<div class="panel advice-panel">
<div class="advice-header">
<span class="advice-badge">AI 诊断报告</span>
<span class="advice-model" id="advice-model-label"></span>
</div>
<div class="advice-body" id="advice-body"></div>
</div>
</div>
</div>
</section>
<!-- LLM 配置视图 -->
<section class="view" id="view-profiles" hidden>
<div class="panel">
<div class="panel-head">
<h2>LLM 配置管理</h2>
<button class="btn btn-primary" id="add-profile-btn"> 新建配置</button>
</div>
<p class="muted">保存常用 LLM 连接参数,在运行评估时按角色选择。</p>
</div>
<!-- 新建 / 编辑表单(默认隐藏) -->
<div class="panel" id="profile-form-panel" hidden>
<h2 id="profile-form-title">新建 LLM 配置</h2>
<div class="profile-form">
<input type="hidden" id="edit-profile-id" />
<div class="form-row">
<label class="form-label">配置名称 <span class="req">*</span></label>
<input class="form-input" id="pf-name" placeholder="例DeepSeek Flash内网" />
</div>
<div class="form-row">
<label class="form-label">模型名称 <span class="req">*</span></label>
<input class="form-input" id="pf-model" placeholder="例deepseek-v4-flash" />
</div>
<div class="form-row">
<label class="form-label">Base URL <span class="req">*</span></label>
<input class="form-input" id="pf-base-url" placeholder="例http://6.86.80.4:30080/v1" />
</div>
<div class="form-row">
<label class="form-label">API Key <span class="req">*</span></label>
<input class="form-input" id="pf-api-key" type="password" placeholder="sk-…" />
</div>
<div class="form-row">
<label class="form-label">超时(秒)</label>
<input class="form-input form-input-sm" id="pf-timeout" type="number" value="30" min="5" max="300" />
</div>
<div class="form-actions">
<button class="btn btn-primary" id="save-profile-btn">保存</button>
<button class="btn btn-test" id="test-profile-btn">测试连通性</button>
<button class="btn" id="cancel-profile-btn">取消</button>
<span class="form-error muted" id="profile-form-error"></span>
</div>
<div class="profile-test-result" id="profile-form-test-result" hidden></div>
</div>
</div>
<div id="profile-cards" class="profile-grid"></div>
<div class="empty" id="profiles-empty" hidden>
<p>尚未添加任何 LLM 配置。</p>
<p class="muted">点击「新建配置」添加第一个。</p>
</div>
</section>
<!-- 评分记录视图 -->
<section class="view" id="view-scorejobs" hidden>
<div class="panel">
<div class="panel-head">
<h2>评分记录</h2>
<span class="muted" style="font-size:13px">来自 Dify 异步评分任务POST /api/score/async</span>
</div>
<p class="muted">评分完成后自动生成完整报告(含指标得分与 LLM 优化建议),点击「查看报告」跳转报告详情页。</p>
</div>
<div id="scorejobs-list"></div>
<div class="empty" id="scorejobs-empty" hidden>
<p>暂无评分记录。</p>
<p class="muted">在 Dify 工作流中调用 <code>POST /api/score/async</code> 后,记录将在此显示。</p>
</div>
</section>
<!-- API 文档视图 -->
<section class="view" id="view-apidocs" hidden>
<iframe
id="apidocs-frame"
src="/docs"
class="apidocs-frame"
title="API 文档"
allowfullscreen>
</iframe>
</section>
</main>
</div>
<script src="/static/js/api.js"></script>
<script src="/static/js/report.js"></script>
<script src="/static/js/profiles.js"></script>
<script src="/static/js/runner.js"></script>
<script src="/static/js/score_jobs.js"></script>
<script src="/static/js/app.js"></script>
</body>
</html>