AgentEvalTool/.scratch/v0.6/spec-ux.md
sinohqb 8bc5aa6979 feat(campaign): add per-Run timeline seam + endpoint
build_campaign_timeline flattens a campaign's child Runs into offset-sorted
per-Run entries (distinct from the report's 12-bucket aggregation), reusing a
shared _run_window_offset口径 so both views place a run identically. Exposes
GET /campaigns/{id}/timeline and the api.ts type/call. (v0.6 ticket 06)
2026-07-31 16:49:04 +08:00

9.7 KiB
Raw Blame History

v0.6 SpecUX 升级)— 评估活动页 UX 升级

状态: ready-for-agent 领域词汇: 见 CONTEXT.md「周期评估」章节评估活动 / 服务周期窗口 / 活动计划 / 可用性) 关键决策: ADR-0002通过率含执行失败、ADR-0003活动分期本批不改判定/调度/报告聚合语义 批次说明: 本 spec 是 v0.6 评估活动功能线的 UX 升级批次(①倍速反向输入 ②计划时间轴预览 ④展开行时间轴),与 spec.mdv1 静态地基)并存。线③「活动分析 Agent」属新功能另立 v0.7 spec。 工单: 本 spec 派生 v0.6/issues/0610。

Problem Statement

评估活动Campaign的能力在 v0.6 静态地基已落地,但新增/查看活动的页面对用户不友好

  1. 时间倍速是裸数字:新增活动时「时间倍速」是一个 InputNumber(默认 1仅一句 tooltip用户得自己心算「填多大倍速能让 24h 窗口几分钟跑完」。用户真正关心的是「加速后多久跑完」,而不是抽象的倍速值。
  2. 活动计划不直观:计划是一组 场景 + 偏移小时 + 次数 的表单行,看不出「在这个窗口的哪些时段、跑哪个场景、跑几次」的时间分布全貌。
  3. 看不到活动过程:活动列表每行不可展开;跑完只能进报告抽屉看聚合曲线,无法直观回看「整个活动过程中,各个子 Run 在时间上如何依次发生、各自成败」。

Solution

对活动页做三项 UX 升级,不触碰判定、调度、报告聚合的任何语义:

  • ① 倍速反向输入:新增活动时用户填「窗口长度」+「希望加速后多久跑完」系统自动派生并只读展示时间倍速正式线用「实时」开关×1。用户从此按「耗时」思考而非「倍速」。
  • ② 计划时间轴预览:保留(优化后的)行编辑器做精确录入,其上方增加一条只读时间轴预览,把计划条目按偏移画在代表窗口的水平轴上、颜色区分场景、标注次数,随表单实时刷新。
  • ④ 展开行时间轴:活动列表每行可展开为一条横向窗口时间轴,把该活动的子 Run 按「加速后窗口偏移」定位成节点、颜色表状态、hover 显详情、点击跳该 Run 报告;进行中活动随现有轮询实时长出新节点。②与④共用同一套时间轴视觉语言。

User Stories

  1. 作为质量负责人,我想在新增活动时直接填「希望这个 24 小时窗口在多久内跑完」(如 1 小时),系统替我算好倍速,以便我不必心算倍速。
  2. 作为质量负责人,我想在填写时实时看到「加速后约 X 跑完」,以便确认压缩节奏符合预期。
  3. 作为质量负责人,我想在正式线一键选「实时」(真实墙钟、倍速 ×1以便采到真实的凌晨/高峰表现。
  4. 作为质量负责人,当我把目标耗时填得比窗口还长(意味着倍速 <1、比真实还慢我想被拦下或纠正以便不产生无意义的减速活动。
  5. 作为质量负责人,我想在编辑活动计划时看到一条时间轴预览,把每个计划条目按其窗口偏移画出来,以便一眼看清「哪些时段跑哪个场景、跑几次」的分布。
  6. 作为质量负责人,我想计划预览用颜色区分不同场景、并标注每个条目的次数,以便区分密集与稀疏时段。
  7. 作为质量负责人,我想计划预览随我编辑表单实时更新,以便边填边看。
  8. 作为质量负责人,我想在活动列表里展开任意一行,看到这个活动的完整过程时间轴,以便回看整个服务周期里发生了什么。
  9. 作为质量负责人,我想过程时间轴把每个子 Run 按其「加速后窗口偏移」定位成节点,以便看清子 Run 在窗口内的时间分布。
  10. 作为质量负责人,我想每个节点用颜色表示状态(完成/失败/运行中/取消),以便一眼识别问题时段。
  11. 作为质量负责人,我想 hover 节点看到场景、通过率、时延,以便无需展开报告即可速览。
  12. 作为质量负责人,我想点击节点直接跳到该子 Run 的报告,以便下钻排查。
  13. 作为质量负责人,当我展开一个进行中的活动时,我想时间轴随轮询长出新派生的子 Run以便实时观察活动推进。
  14. 作为质量负责人,我想活动列表/详情处也用「加速后约 X 完成」而非裸倍速展示,以便和新增表单的心智一致。

Implementation Decisions

① 倍速反向输入前端为主API 契约不变)

  • 新增活动表单把「时间倍速」输入替换为两项:窗口长度(沿用预设下拉 6/12/24/48/72h+ 目标加速耗时(数值 + 单位,默认分钟)。
  • 前端派生 time_scale = window_seconds / target_real_seconds,作为只读展示(如「倍速 ×24 · 加速后约 1 小时跑完」),提交时仍在 payload 里发 time_scale后端 POST /campaigns 契约与 Campaign.time_scale 语义完全不变
  • 「实时」开关:选中即 target_real_seconds = window_secondstime_scale = 1.0,并禁用目标耗时输入。
  • 约束:目标耗时必须 ≤ 窗口长度(即 time_scale ≥ 1);违反时表单校验拦截(对应 US-4
  • 派生与反算(由 scale+window 反推「加速后耗时」用于列表/详情展示)抽成纯前端 util(如 campaignTime.tsderiveTimeScale / acceleratedDuration),集中口径、便于复用。

② 计划时间轴预览(纯前端)

  • 新增一个只读时间轴预览组件,输入 = 表单当前的 window_seconds + plan 条目 + 场景名映射,输出 = 一条水平窗口轴,条目按 offset_seconds 定位,颜色按 scenario_id 稳定分配,标注 count
  • 不涉及任何后端改动;数据完全来自表单本地状态,随编辑实时重渲染。
  • 与④的过程时间轴共享底层展示(见下「共享组件」)。

④ 展开行时间轴(新后端纯接缝 + 前端展开行)

  • 新后端纯函数接缝 build_campaign_timeline(campaign, runs) -> list[CampaignTimelineEntry](放 evaluation/report.py 或紧邻的模块),每条含:run_id / scenario_id / scenario_name / offset_seconds加速后窗口偏移clamp 到 [0, window]/ status / pass_rate / avg_latency_ms / started_at
    • 偏移计算复用现有报告口径:offset = (run.started_at - campaign.started_at).total_seconds() * time_scaleclamp 到 [0, window_seconds];未开始的子 Run无 started_at与取消 Run 的处理在此接缝内明确定义。
    • pass_rate 直接取子 Run summary.pass_rate(权威,只读不重算,沿 v0.5/v0.6 约定)。
  • 新端点 GET /campaigns/{id}/timeline{entries: CampaignTimelineEntry[]},返回逐 Run 扁平数组(区别于报告的 12 桶聚合曲线)。
  • 前端:活动列表行改为 expandable,展开区渲染横向窗口时间轴:子 Run 节点按 offset_seconds 定位、颜色=状态、hover 显场景/通过率/时延、点击跳 /reports?run={run_id}。展开进行中活动时,随现有 5s 轮询刷新时间轴数据。

共享组件

  • ②的计划预览与④的过程时间轴共用一个底层横向时间轴展示组件(窗口轴 + 定位标记 + 颜色 + hover两处用不同数据适配②喂「计划条目」④喂「子 Run」。保证视觉语言一致避免两套实现。

API 契约(新增项)

  • GET /campaigns/{id}/timeline{ "entries": [ { "run_id", "scenario_id", "scenario_name", "offset_seconds", "status", "pass_rate", "avg_latency_ms", "started_at" } ] }
  • 其余活动端点、Campaign 模型、time_scale 语义均不变。

Testing Decisions

  • 好测试只测外部行为:接缝喂构造好的输入、断言输出,不测内部实现细节。
  • 主接缝:build_campaign_timeline(纯函数) —— 唯一新增的可测后端接缝。单测覆盖:正常子 Run 的偏移计算(含 time_scale 压缩、clamp 到窗口边界、未开始/取消/失败子 Run 的处理、字段映射pass_rate 取自 summary、场景名映射、空活动。Prior arttests/unit/test_report.py / test_campaign_report.py(同样喂 campaign+runs 断言聚合 dict 的模式)。
  • 端点集成测试GET /campaigns/{id}/timeline 的 200/404、字段形状。Prior arttests/integration/test_runs_api.py、现有 campaigns router 测试。
  • 前端无测试框架:①倍速反向输入(派生/反算/实时开关/≤窗口校验)、②计划预览、④展开时间轴,均以 npx tsc --noEmit + npm run build + 浏览器实操验证(含进行中活动的轮询刷新)。deriveTimeScale/acceleratedDuration 写成纯函数,便于将来接入前端测试。

Out of Scope

  • 线③ 活动分析 Agent + 服务质量改善建议:新功能,另立 v0.7 spec含新增「分析」模型岗位、全局默认分析模型 + 活动可覆盖、多步分析 Agent、结构化分析报告、按需生成+缓存)。
  • 计划的拖拽编辑:本批时间轴预览为只读;拖拽调偏移/点击空白新增等交互不做。
  • 跨活动对比、活动列表分页/筛选增强,均不在本批。

Further Notes

  • ②预览与④过程时间轴的共享底层组件是本批的关键复用点,也是唯一较有设计含量的前端接缝——先把它的输入接口设计干净(喂「带窗口偏移的标记数组」),两处适配即可。
  • 列表/详情的「加速后约 X 完成」展示与新增表单的派生口径必须走同一个纯 util避免两处算法漂移呼应 v0.6 起「口径收敛到一处」的做法)。