AgentEvalTool/.scratch/v0.8/spec.md
sinohqb dd3b9a5e91 refactor(comparison): 评审修复 — 共享 gateway_chat_client、指标元表、对比区块组件化
- analysis/comparison 重复的 _gateway_chat_client 提取为共享 gateway_chat_client
- Campaigns.tsx 周期对比区块抽为 PeriodComparisonSection 组件,指标格式化
  收敛为单一 METRICS 元表(消除三处 metric 分支级联)
- 基线下拉排除无 completed_at 的终态活动(选中必 400)
- spec/issue 03 追认 delta 按好坏着色口径与 GET 生效基线合并口径
2026-08-03 14:26:20 +08:00

95 lines
7.7 KiB
Markdown
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.

# v0.8 — 周期对比Period Comparison
## Problem Statement
运营者用评估活动对同一对象做周期性考察,每期末尾拿到一份智能分析(总体结论、问题诊断、改善建议)。但单期分析是孤立的:这一期的问题到底是新冒出来的、持续未愈的、还是已经消解的?上一期的改善建议落实了吗?要回答这些,人得自己打开两期报告逐条对照自由文本——而这正是平台该替人做的元层研判。
## Solution
在相邻两期活动之间提供**周期对比**:机械 diff 给出两期聚合指标的客观变化(整窗与分场景的通过率/可用性/时延 deltaADR-0004 口径确定性计算LLM 在两期分析结论之上产出结构化演进叙述(趋势判断、问题演化、建议追踪)。报告抽屉内新增「周期对比」区块,与智能分析并列。正式线活动全链自动(活动完成 → 分析 → 对比),加速调试线手动按需。
## User Stories
1. 作为运营者,我希望打开本期活动报告时看到"与上一期相比"的指标变化表(整窗通过率/可用性/时延及分场景 delta以便快速判断走势。
2. 作为运营者,我希望看到一个趋势徽章(改善/平稳/退化),以便不看数字也能感知方向。
3. 作为运营者,我希望看到问题演化列表(新增/持续/消解,含涉及场景与说明),以便知道该担心什么、可以放下什么。
4. 作为运营者,我希望看到上期建议的追踪状态(已落实/部分落实/未落实/新增),以便评估改进动作的有效性。
5. 作为运营者,我希望正式线活动的对比在分析完成后自动生成,以便零操作拿到结论。
6. 作为调试者,我希望加速线活动能手动点「生成对比」,以便验证功能而不等完整周期。
7. 作为运营者,当本期活动计划与历史不一致(无自动基线)时,我希望能手动挑选一个历史活动作基线,以便换计划后仍能对比。
8. 作为运营者,我希望对比生成失败时看到原因并可重试,以便区分模型问题与数据问题。
9. 作为运营者,我希望对比叙述与指标 diff 分开呈现(叙述是 LLM 判断、数字是确定性计算),以便知道哪部分可以较真。
10. 作为管理员,我希望对比复用「分析默认」模型(活动覆盖 ?? 全局默认),以便不引入新的配置面。
11. 作为运营者,我希望对比中引用的场景是真实存在的(白名单过滤),以便不被模型虚构误导。
## Implementation Decisions
### 配对(计划指纹 + 自动基线)
- **计划指纹** = 评测对象 + 计划条目集合(场景 id、偏移秒数、次数+ 窗口秒数。指纹相等的正式线活动构成活动串
- **自动基线** = 同活动串中、完成时间早于本期、且已有 completed 分析的最近一期活动
- 指纹不一致 → 无自动基线;`POST /comparison` 接受可选 `baseline_campaign_id` 手动指定(跨串允许,可比性由用户负责)
- 基线活动被删或分析被重置 → 视为无基线
### 产出(混合:机械 diff + LLM 叙述)
- **机械 diff**:纯函数,输入两期 `generate_campaign_report` 结果,输出整窗 + 分场景的通过率/可用性/时延 delta读时现算不存储
- **LLM 叙述**:单次调用(不做两阶段——输入只有两份已消化的分析 JSON + 机械 diff无原始样本输入上限轻量输出结构化 JSON
```json
{
"trend": "improving | stable | regressing",
"summary": "总体演进结论",
"problem_evolution": [{"status": "new | persisting | resolved", "title", "detail", "scenario_ids": []}],
"suggestion_tracking": [{"text", "status": "addressed | partial | unaddressed | new", "note"}]
}
```
- 叙述中的 `scenario_ids` 落库前过白名单(同 v0.7);非法 `trend`/`status` 枚举归一trend→stablestatus→persisting / unaddressed
- LLM 解析失败 → 行落 failed + error
### 存储与生命周期
- 新表 `campaign_period_comparisons`:当前活动 id 唯一 upsert、baseline_campaign_id、generating/completed/failed、result叙述 JSON、model_config_id 快照、error、triggered_byauto/manual、时间戳——镜像 `campaign_analyses` 模式
- 模型解析复用 v0.7 `resolve_analysis_model`(活动覆盖 ?? 全局分析默认);不可解析 → 自动链静默跳过 / 手动 400 引导
- 自动链:`execute_campaign_analysis` 成功落 completed 后,活动为正式线且存在自动基线 → enqueue 对比生成;任一前提不满足静默跳过
- 重新生成(手动 POST= 同活动 upsert 覆盖,可借此更换基线重算
### API
- `GET /api/campaigns/{id}/comparison`:无行 → 空态 `{"status": "none"}`;有行 → 机械 diff现算+ 叙述行合并返回;附 `baseline_campaign_id` 与自动配对结果,前端据此渲染或引导选基线。`metric_diff` 跟随生效基线:有对比行按行内基线算,否则按自动基线算
- `POST /api/campaigns/{id}/comparison`body 可选 `{"baseline_campaign_id": "..."}`;非终态活动 400当前活动无 completed 分析 400先生成分析无模型 400 引导;成功 → 后台任务 + `{"status": "generating"}`
- 两期都有 completed 分析是生成前提(基线无分析 → 400 提示先生成基线分析)
### 前端(报告抽屉「周期对比」区块)
- 位置:「智能分析」区块之下,复用区块状态行范式(生成中 5s 轮询 / 失败重试 / 未生成按钮)
- 已生成:趋势徽章 + 总结 → 指标变化表(整窗 + 分场景 delta按好坏着色——比率升为绿、时延降为绿反之红→ 问题演化(新增红/持续橙/消解绿 Tag→ 建议追踪(已落实绿/部分橙/未落实灰/新增蓝)
- 无自动基线:基线选择器(同对象历史终态活动下拉)+ 生成按钮
- 展示基线活动名与完成时间、所用分析模型名与生成时间
## Testing Decisions
好测试只测外部行为,不测实现细节;全部落在既有最高层 seam
1. **机械 diff 纯函数**:手搓两份报告 dict断言 delta 结构与数值、空桶/None 处理(先例如 `test_report_render.py`
2. **计划指纹与自动基线**:内存 SQLite指纹相等/不等、跳过无分析活动、跳过非正式线、取最近一期(先例如 `test_campaign_analysis.py` 的仓储测试)
3. **LLM 叙述编排**FakeChatClientv0.7 seam覆盖成功编排、解析失败→AnalysisError 式失败、白名单剔除虚构 scenario_ids、非法枚举归一
4. **API 集成**:镜像 `test_campaign_analysis_api.py`——GET 空态、POST 非终态 400、无分析 400、无基线 400、手动指定基线成功、重跑 upsert 不新增行
5. **自动链**spy 对比入口,断言分析完成后正式线自动触发、加速线不触发、无基线不触发(镜像 `test_campaign_analysis_auto_trigger.py`
6. **迁移测试**:表存在 + campaign_id 唯一约束(先例如 `test_campaign_analyses_migration.py`
## Out of Scope
- 跨活动对比(多对象横切)——词汇表中另一个轴,本规格不涉及
- 活动列表趋势标记(↑↓)——后续增强
- 对比结果纳入 Markdown 导出——看使用情况再定
- 基线活动的选取 UI 优化(如按指纹分组展示)
- 三期以上的趋势序列(只对比相邻两期)
## Further Notes
- 设计决策源自 `/grill-with-docs` 访谈6 问 6 决):配对 A、产出 C 混合、呈现 A 抽屉区块、生命周期 A 新表+联动触发、输入 A 轻量、输出 A 结构化
- 「周期对比」「正式线/加速调试线」已入 `CONTEXT.md` 词汇表
- 对比不重新消化失败样本:样本已被两期分析消化,对比是元层研判