Implement IIT quality workflow hardening across eQuery deduplication, guard metadata validation, timeline/readability improvements, and chat evidence fallbacks, then synchronize release and development documentation for deployment handoff. Includes migration/scripts for open eQuery dedupe guards, orchestration/status semantics, report/tool readability fixes, and updated module status plus deployment checklist. Made-with: Cursor
6.2 KiB
6.2 KiB
IIT / CRA Agent 指标口径与 SSOT 对照表(Phase 0)
文档版本:v1.0
创建日期:2026-03-08
适用范围:IIT 业务端大盘 / 报告与关键事件 / AI 实时工作流水 / AI 对话
目标:先统一“同名指标”的定义和数据源,再进入修复,避免反复打补丁
1. 背景与问题定义
当前线上/测试反馈的核心不是单点报错,而是同一指标在多个页面口径不一致,例如:
- 大盘通过率与趋势通过率不一致
- 报告页健康分与大盘健康分不一致
- 时间线问题数与待处理 eQuery 不一致
- 报告中 D1/D2/D3D4/D6 与下方维度评分对不上
这些问题在工程上属于口径漂移(metric drift),必须先冻结 SSOT(Single Source of Truth),再排查数据同步、规则执行、前端展示。
2. 指标分层模型(必须统一)
IIT 指标按 4 层定义,禁止跨层混算:
- 事实层(Raw Facts):REDCap 原始记录与事件数据
- 执行层(QC Results):
qc_field_status/qc_event_status的规则执行结果 - 聚合层(Project Stats):
iit_qc_project_stats、iit_record_summary的聚合快照 - 呈现层(UI/API):Dashboard/Reports/AiStream/AiChat
约束:
- 呈现层不允许重新发明计算公式,只消费聚合层或执行层
- 同一名称指标只能有一个“主口径”
- 不同用途的指标必须显式命名(例如“按受试者通过率”与“按事件通过率”)
3. 核心指标 SSOT 对照表(冻结版)
3.1 项目健康度与通过率
| 指标名 | 业务定义 | SSOT 来源 | 当前代码入口 | 备注 |
|---|---|---|---|---|
| healthScore | 健康度总分(0-100) | iit_qc_project_stats.health_score |
iitQcCockpitService.getStats() |
不允许前端 fallback 推导 |
| healthGrade | 健康度等级 | iit_qc_project_stats.health_grade |
iitQcCockpitService.getStats() |
同上 |
| passRate (大盘) | 按受试者通过率 | passed_records / total_records |
iitQcCockpitService.getStats() |
保留 1 位小数 |
| passRate (趋势) | 按日志条目通过率(旧) | iit_qc_logs 分组计算 |
iitQcCockpitController.getTrend() |
与大盘不是同一口径,必须重命名或替换 |
结论:
- 当前“通过率”至少有两种口径,UI 未标注,必然引发“100% vs 33%”类问题。
3.2 D1/D2/D3D4/D6 报表
| 报表 | 业务定义 | SSOT 来源 | 当前查询入口 |
|---|---|---|---|
| D1 筛选入选 | 入排规则是否通过 | qc_field_status(D1) + record_summary(受试者全集) |
iitQcCockpitService.getEligibilityReport() |
| D2 完整性 | 缺失字段率 | qc_field_status(D2) |
iitQcCockpitService.getCompletenessReport() |
| D3/D4 质疑跟踪 | eQuery 生命周期 | iit_equeries |
iitQcCockpitService.getEqueryLogReport() |
| D6 方案偏离 | 访视超窗等偏离 | qc_field_status(D6) |
iitQcCockpitService.getDeviationReport() |
结论:
- D 类报表与大盘维度评分来自不同聚合路径时,必须明确“时点一致性”(同一批次同一时刻)。
3.3 AI 实时工作流水
| 指标 | 业务定义 | SSOT 来源 | 当前入口 | 风险 |
|---|---|---|---|---|
| timeline total | 时间线受试者总数 | qc_field_status 聚合 + pass 补充 |
iitQcCockpitController.getTimeline() |
与 eQuery 总数不是同一概念 |
| red/yellow 问题数 | 每受试者 FAIL/WARNING 汇总 | qc_field_status |
getTimeline() |
应与 field-issues 可对账 |
| 事件中文名 | event display label | qc_event_status.event_label |
getTimeline() LEFT JOIN |
空值时会回退技术名 |
3.4 AI 对话
| 语义工具 | 主要数据源 | 适用问题 | 当前实现 |
|---|---|---|---|
read_report |
QcReportService 缓存报告 |
通过率、问题统计、趋势摘要 | ToolsService |
look_up_data |
REDCap 原始数据 | 单患者字段值、原始记录核查 | ToolsService |
check_quality |
QcExecutor 实时执行 |
用户明确要求“重跑质控” | ToolsService |
search_knowledge |
项目知识库(RAG) | 方案文本、入排标准文本 | ToolsService |
结论:
- 对话错误不等于“模型幻觉”,优先排查是否选错工具或工具查询不完整。
4. 现存口径漂移点(已识别)
-
趋势口径漂移
- 趋势接口仍从
iit_qc_logs计算通过率;大盘通过率来自iit_qc_project_stats。 - 导致“卡片 100%,趋势 33%”。
- 趋势接口仍从
-
健康分 fallback 风险
- 大盘前端在
healthScore缺失时回退为Math.round(passRate)。 - 数据未准备好时会把通过率误当健康分,造成“待处理质疑很多但仍 100 分”。
- 大盘前端在
-
报告缓存时点风险
read_report优先读缓存报告(默认有效期 24h),若未及时刷新会滞后于大盘/流水。
-
“总数”语义混淆
- 时间线问题数、eQuery 待处理数、D3D4 报表数本质是不同对象,UI 文案未区分。
-
展示字段名兼容风险
- 若
eventLabel/fieldLabel空,前端回退技术字段名;用户会误判为“事件名错误”。
- 若
5. 冻结规则(修复期强制执行)
-
页面上所有“通过率”必须标注口径:
- 按受试者(record)
- 按事件(record-event)
- 按规则检查条目(field-level checks)
-
健康度评分只允许来自
healthScoreEngine落库结果,不允许前端推算。 -
报告页、大盘、AI 对话必须显示“统计快照时间”,便于用户识别时点差异。
-
UI 文案新增说明:
- “待处理质疑”来自
iit_equeries - “时间线问题总数”来自
qc_field_status
- “待处理质疑”来自
-
涉及 D1/D2/D3D4/D6 的变更,必须附带“口径回归测试”记录。
6. Phase 1 进入条件
满足以下条件后,才进入根因排查与代码修复:
- 本文“SSOT 对照表”已被团队确认(产品/研发/质控)
- 每个页面核心指标已标注口径
- 已选定“最小复现项目”与冻结时间窗口
Phase 1 执行手册见:
docs/03-业务模块/IIT Manager Agent/09-技术评审报告/2026-03-08-IIT-CRA-最小复现项目对账执行手册-Phase1.md