本文档定义 BlinkLife 手表端应采集哪些数据、这些数据如何支撑复盘页、第一版复盘页的能力边界,以及数据缺失时的降级策略。
A. 产品结论与优先级判断
1. 为什么必须先明确手表采集能力,而不是直接堆复盘页 UI
结论:复盘页的价值上限 = 底层数据的可信度上限。
当前手表端仅支持打点事件(single_press/double_press/long_press → 高光/失误/撤销),没有心率、GPS、速度等任何传感器采集。如果直接画复盘页 UI,只能做出一个"打点统计面板",与现有回放页的 StatsCard 高度重复,无法体现复盘页的独立价值。
先定义采集能力,才能确定复盘页到底"有什么数据可以说话"。
2. 第一版复盘页为什么不能追求复杂报表化
结论:消费级运动产品的复盘不是教练报表,是"一眼看懂这场练了什么"。
三个硬约束:
- 数据约束:手表传感器数据第一版必然有断点、异常、缺失,不足以支撑精细报表
- 用户约束:目标用户是业余运动爱好者,不是专业教练,看不懂也不需要双 Y 轴曲线
- 竞争约束:Strava/佳明已经在专业报表上做到极致,BlinkLife 的差异化是"事件时间轴 + 视频回放 + AI 摘要",不是报表
3. 当前最合理的推进顺序
Step 1: 手表端补齐 P0 传感器采集(心率 + 基础运动量)
Step 2: 定义统一数据模型 + 落库方案
Step 3: 复盘页 MVP(总览 + 事件分布 + 心率趋势 + AI 摘要)
Step 4: 补齐 P1 数据(GPS 轨迹 + 强度区段)→ 复盘页增强
4. 必须先做 vs 必须延后
| 必须先做 | 必须延后 |
|---|
| 心率连续采集 | GPS 轨迹热图 |
| Session 生命周期管理 | 专业级冲刺分析 |
| 统一时间戳同步 | 高级 AI 战术结论 |
| 数据质量标记 | 多场次对比分析 |
| 传感器数据落库 | 社交排行榜 |
B. 采集能力分层
P0:必须先做稳
1. Session(会话管理)
| 维度 | 说明 |
|---|
| 采集内容 | 录制开始/暂停/恢复/结束的精确时间戳、运动类型、设备来源 |
| 为什么 P0 | 没有 Session,所有数据都是散点,无法形成"这场训练"的整体概念 |
| 不做的影响 | 复盘页无法回答"这场练了多久"、时间轴无法对齐 |
| 支撑复盘页 | 总览卡片(时长/运动类型)、时间轴骨架、AI 摘要的时间范围 |
2. Event Marker(事件打点)— 已实现
| 维度 | 说明 |
|---|
| 采集内容 | 时间戳、动作类型、输入源、置信度 |
| 为什么 P0 | 已实现且是 BlinkLife 核心能力,需要增强字段(confidence/weight/is_revoked) |
| 不做的影响 | 无法区分高质量事件和误触事件 |
| 支撑复盘页 | 事件分布图、AI 高光推荐、回放页联动锚点 |
3. Unified Timestamp(统一时间戳)
| 维度 | 说明 |
|---|
| 采集内容 | 手表与手机的时钟偏移量(在 Session 开始时交换一次 NTP 或本地时间差) |
| 为什么 P0 | 手表和手机的系统时间可能差几秒到几十秒,不同步就无法对齐 |
| 不做的影响 | 手表打的点和视频时间轴偏移,复盘页结论和视频画面对不上 |
| 支撑复盘页 | 所有时间轴对齐、所有"点击跳转回放"功能的基础 |
4. Heart Rate(心率)
| 维度 | 说明 |
|---|
| 采集内容 | 连续心率(建议 1Hz 采样)、静息心率、最大心率 |
| 为什么 P0 | 心率是消费级运动产品用户最关心的指标,采集成本最低(手表内置),可信度最高 |
| 不做的影响 | 复盘页缺少核心数据轴,只能展示打点统计,与回放页高度重复 |
| 支撑复盘页 | 心率趋势曲线、平均/峰值心率、强度区段划分、AI "这段时间你很拼" |
5. Quality State(数据质量状态)
| 维度 | 说明 |
|---|
| 采集内容 | 每种数据源的质量标记(good/degraded/unavailable)、断点区间、置信度 |
| 为什么 P0 | 没有质量标记,页面不知道哪些数据可信、哪些该隐藏,AI 不知道哪些结论该保守 |
| 不做的影响 | 展示错误数据 → 用户失去信任 → 功能报废 |
| 支撑复盘页 | 降级显示策略、AI 输出约束、"数据不完整"提示 |
P1:建议尽快补齐
6. Speed(速度)
| 维度 | 说明 |
|---|
| 采集内容 | 瞬时速度(m/s),来源于 GPS 或加速度计推算 |
| 为什么 P1 | 速度数据在室内场景(篮球馆/羽毛球馆)GPS 信号差时极不可信,需要 P0 心率先稳 |
| 不做的影响 | 复盘页无法展示冲刺片段,AI 无法判断"高强度跑动" |
| 支撑复盘页 | 速度趋势、冲刺区段识别、AI "第 25 分钟你冲了一次全速" |
7. Distance(距离)
| 维度 | 说明 |
|---|
| 采集内容 | 累计距离(米),基于 GPS 轨迹积分或步频估算 |
| 为什么 P1 | 依赖 GPS 或计步器,室内精度低,需和 Speed 一起做 |
| 不做的影响 | 无法显示"跑了多少公里",对跑步/足球场景影响大 |
| 支撑复盘页 | 总览数据(总跑动距离)、每节距离对比 |
8. Intensity Segment(强度区段)
| 维度 | 说明 |
|---|
| 采集内容 | 基于心率 + 速度的强度分段(热身/有氧/无氧/极限),客户端或服务端计算 |
| 为什么 P1 | 依赖 P0 心率数据稳定后才能计算,不是原始采集项 |
| 不做的影响 | 复盘页只有原始心率曲线,缺少"这段时间是高强度"的语义 |
| 支撑复盘页 | 强度分布饼图、时间轴色带、AI "上半场 60% 时间处于有氧区" |
P2:后续增强
9. Route / Position(轨迹 / 活动区域)
| 维度 | 说明 |
|---|
| 采集内容 | GPS 经纬度序列(1-5Hz),室内需 UWB/蓝牙定位替代 |
| 为什么 P2 | GPS 功耗高、室内不可用、轨迹渲染复杂度高,MVP 不需要 |
| 不做的影响 | 无热力图、无活动区域分析,对足球场景有影响但不阻塞 MVP |
| 支撑复盘页 | 轨迹回放、热力图、活动区域分析、AI "你主要活动在左路" |
10. Sprint Segment(冲刺区段)
| 维度 | 说明 |
|---|
| 采集内容 | 基于速度阈值(如 20km/h 以上)的冲刺检测 + 加速度峰值 |
| 为什么 P2 | 依赖 P1 速度数据可信后才能做,且阈值需按运动类型调参 |
| 不做的影响 | 无法自动标记冲刺片段,AI 无法分析爆发力表现 |
| 支撑复盘页 | 冲刺列表、冲刺速度排行、AI "全场最快冲刺出现在第 38 分钟" |
C. 采集能力 → 复盘页消费映射表
| 采集项 | 手表侧采集内容 | 复盘页用途 | 复盘页模块 | 回放页联动 | 优先级 | 主要风险 | 降级建议 |
|---|
| Session | 开始/暂停/恢复/结束时间 | 总览时长、节次划分 | 总览卡片 | 无直接联动(提供时间范围) | P0 | 暂停/恢复漏报 | 缺失时用首尾打点时间估算 |
| Event Marker | 时间戳+动作+来源+置信度 | 事件分布、高光推荐 | 事件分布图、AI 推荐 | 点击事件→回放页 seek 到对应时间 | P0 | 误触/重复打点 | confidence 低于 0.5 的事件灰显 |
| Timestamp Sync | 手表-手机时钟偏移 | 所有时间对齐的基础 | 不直接展示 | 保证跳转精度 | P0 | 运动中时钟漂移 | 首尾各同步一次取平均 |
| Heart Rate | 1Hz 连续心率 | 心率趋势、平均/峰值、强度判断 | 心率趋势卡片、总览数据 | 点击心率峰值→回放页 seek | P0 | 信号断点、异常跳变 | 断点超过 30s 显示虚线,异常值过滤 |
| Quality State | 各数据源质量标记 | 控制展示/隐藏/降级 | 全局影响 | 低质量区间禁止跳转 | P0 | 漏报质量问题 | 缺省为 degraded 而非 good |
| Speed | 瞬时速度 m/s | 速度趋势、冲刺标记 | 速度趋势卡片 | 点击冲刺区段→回放页播放区间 | P1 | 室内 GPS 漂移 | 质量差时隐藏速度模块 |
| Distance | 累计距离 | 总跑动距离 | 总览数据 | 无直接联动 | P1 | 室内不准 | 质量差时标注"估算" |
| Intensity Segment | 心率+速度→强度分段 | 强度分布、时间轴色带 | 强度分布卡片 | 点击区段→回放页播放区间 | P1 | 心率阈值因人而异 | 用通用公式,标注"参考值" |
| Route / Position | GPS 经纬度序列 | 轨迹回放、热力图 | 轨迹卡片 | 点击热区→过滤该区域事件 | P2 | 室内不可用、功耗高 | 无轨迹时完全隐藏该模块 |
| Sprint Segment | 速度阈值冲刺检测 | 冲刺列表和排行 | 冲刺分析卡片 | 点击冲刺→回放页 seek | P2 | 依赖速度可信 | 速度质量差时不生成冲刺 |
D. 统一数据模型
1. Session(训练会话)
Session {
id: string [必填] UUID
start_time: DateTime [必填] UTC 录制开始时间
end_time: DateTime? [必填] UTC 录制结束时间(进行中为 null)
pauses: List[TimePair] [增强] 暂停-恢复时间对列表
sport_type: string [必填] 运动类型
device_sources: List[str] [必填] 使用的设备 ['phone','watch','ble_ring']
status: enum [必填] active | paused | completed | interrupted
clock_offset_ms: int? [必填] 手表与手机的时钟偏移(毫秒)
recording_id: int? [增强] 关联的 RecordingRecord ID
video_path: string? [增强] 关联的视频路径
}
用途:复盘页的顶层容器,定义"这场训练"的时间范围和元数据。所有数据必须归属于一个 Session。
2. EventMarker(事件标记)
EventMarker {
id: int [必填] 稳定 ID(= timestamp 毫秒值)
session_id: string [必填] 所属 Session
timestamp: DateTime [必填] UTC 绝对时间
recording_time: Duration [必填] 相对 Session 开始的偏移
event_type: string [必填] 动作类型('射门','犯规'等)
event_subtype: string? [增强] 动作子类型('左脚射门')
source_device: string [必填] 'ble_ring' | 'gesture' | 'watch' | 'manual'
confidence: double [必填] 0.0-1.0,默认 1.0(手动=1.0, 手表单击=0.9, AI推断=0.6)
weight: int [AI预留] 重要性权重(AI 排序用)
is_revoked: bool [必填] 是否已撤销(长按撤销)
note: string? [增强] 用户备注
tags: List[string]? [AI预留] AI 标签('关键进攻','防守失误')
}
用途:复盘页事件分布图的数据源,AI 高光推荐的输入。与现有 DotRecord 兼容,增加 confidence/weight/is_revoked 字段。
与现有 DotRecord 的关系:EventMarker 是 DotRecord 的超集。迁移策略:DotRecord 保持不变用于回放页,EventMarker 用于复盘页消费,两者通过 id + timestamp 关联。
3. SensorSample(传感器采样)
SensorSample {
session_id: string [必填] 所属 Session
timestamp: DateTime [必填] UTC 采样时间
heart_rate: int? [P0] 心率(bpm),null 表示该采样无心率
speed: double? [P1] 瞬时速度(m/s)
distance_increment: double? [P1] 距离增量(m),累计求和得总距离
latitude: double? [P2] GPS 纬度
longitude: double? [P2] GPS 经度
altitude: double? [P2] 海拔(m)
quality_flag: enum [必填] good | degraded | interpolated | unavailable
}
用途:复盘页心率趋势、速度趋势的数据源。1Hz 采样,一场 90 分钟训练约 5400 条记录。
存储策略:本地 SQLite 独立表 sensor_samples(按 session_id 索引)。数据量可控,不需要时序数据库。
4. Segment(语义区段)
Segment {
id: string [必填] UUID
session_id: string [必填] 所属 Session
start_time: DateTime [必填] 区段开始
end_time: DateTime [必填] 区段结束
segment_type: enum [必填] intensity | sprint | rest | warmup | custom
intensity_level: enum? [P1] light | moderate | vigorous | max(心率区间)
avg_heart_rate: int? [P1] 区段平均心率
peak_speed: double? [P1] 区段最高速度
avg_speed: double? [P1] 区段平均速度
distance: double? [P1] 区段距离
linked_events: List[int] [增强] 区段内的 EventMarker ID 列表
source: enum [必填] auto_detected | user_defined | ai_suggested
confidence: double [必填] 0.0-1.0
}
用途:复盘页强度分布图、冲刺列表的数据源。由客户端或服务端根据 SensorSample 计算生成,不是原始采集。
5. ReplayAnchor(回放锚点)
ReplayAnchor {
id: string [必填] UUID
session_id: string [必填] 所属 Session
source_type: enum [必填] event | segment | ai_highlight | peak_hr | sprint
anchor_time: DateTime [必填] 锚定到的绝对时间
display_time: Duration [必填] 在视频中的显示时间(经过对齐)
linked_video_path: string? [必填] 关联的视频文件
offset_ms: int [必填] 跳转偏移(如事件前移 5 秒)
linked_event_ids: List[int] [增强] 关联的事件 ID 列表
title: string [必填] 显示标题("第 12 分钟 射门")
description: string? [AI预留] AI 生成的描述
}
用途:复盘页中所有"点击跳转到回放页"的载体。复盘页不直接操作视频,而是生成 ReplayAnchor,传递给回放页执行跳转。
6. QualityState(数据质量状态)
QualityState {
session_id: string [必填] 所属 Session
data_type: enum [必填] heart_rate | speed | gps | event | session
quality_level: enum [必填] good | degraded | unavailable
reason: string? [增强] 'signal_lost' | 'indoor_no_gps' | 'sensor_off'
missing_ranges: List[TimePair] [增强] 缺失的时间区间列表
confidence_score: double [必填] 0.0-1.0 综合可信度
sample_count: int [增强] 有效采样数
expected_count: int [增强] 应有采样数(sample_count/expected_count = 覆盖率)
}
用途:复盘页的全局降级控制器。页面渲染前先查 QualityState,决定哪些模块展示、哪些隐藏、哪些标注"数据不完整"。AI 生成前先查 QualityState,决定哪些结论可以给、哪些必须保守。
E. 第一版复盘页 MVP 边界
必须上线的模块
1. 本场总览卡片
| 维度 | 说明 |
|---|
| 展示内容 | 运动类型、总时长、总打点数、平均心率、峰值心率、总距离(如有) |
| 依赖数据 | Session + EventMarker 统计 + SensorSample 聚合 |
| 可信度 | 高(Session 和打点是 P0,心率采集稳定后可信) |
| 与回放页联动 | 无直接联动,点击时长可展开节次明细 |
| 为什么现在做 | 用户进入复盘页的第一眼,必须回答"这场练了什么" |
2. 事件分布图
| 维度 | 说明 |
|---|
| 展示内容 | 时间轴上的事件分布(按动作类型着色),支持按类型筛选 |
| 依赖数据 | EventMarker 列表 |
| 可信度 | 高(基于已有打点数据) |
| 与回放页联动 | 点击任意事件 → 生成 ReplayAnchor → 跳转回放页 |
| 为什么现在做 | 复盘页的核心差异化:全局视角看事件分布,回放页只能线性浏览 |
3. 心率趋势曲线(简版)
| 维度 | 说明 |
|---|
| 展示内容 | 时间轴上的心率折线图,叠加事件 marker,标注峰值点 |
| 依赖数据 | SensorSample.heart_rate + EventMarker |
| 可信度 | 中(心率断点用虚线表示,覆盖率低于 60% 时降级为仅数值) |
| 与回放页联动 | 点击心率峰值 → 生成 ReplayAnchor → 跳转回放页 |
| 为什么现在做 | 心率是用户最关心的运动指标,且手表采集成本最低 |
4. AI 摘要(保守版)
| 维度 | 说明 |
|---|
| 展示内容 | 2-4 句话的训练摘要(训练概况 + 强度评价 + 高光推荐 + 建议) |
| 依赖数据 | Session + EventMarker + SensorSample 聚合统计 |
| 可信度 | 中(基于统计数据生成,不做因果推断) |
| 与回放页联动 | 摘要中的"高光时刻"可点击 → 跳转回放页 |
| 为什么现在做 | AI 摘要是 BlinkLife 相对 Strava/佳明的核心差异化 |
5. 推荐片段列表
| 维度 | 说明 |
|---|
| 展示内容 | AI 推荐的 3-5 个值得回看的片段(缩略图 + 时间 + 原因) |
| 依赖数据 | EventMarker(按 confidence × weight 排序)+ 心率峰值 |
| 可信度 | 高(基于已有数据排序,不做复杂推理) |
| 与回放页联动 | 点击片段 → 跳转回放页播放,可一键剪辑 |
| 为什么现在做 | 帮用户快速找到"最值得看的几个瞬间",是复盘页到回放页的核心桥梁 |
建议延后的模块
| 模块 | 为什么延后 | 依赖条件 |
|---|
| GPS 轨迹热力图 | 室内不可用、GPS 数据 P2 优先级、渲染复杂 | P2 Route 数据 |
| 双 Y 轴心率+速度曲线 | 速度数据 P1、两轴对齐复杂、用户理解成本高 | P1 Speed 稳定 |
| 冲刺详细分析 | 依赖速度+加速度、阈值需调参 | P2 Sprint Segment |
| 节次对比雷达图 | 需要暂停/恢复机制稳定、数据量足够 | Session.pauses 稳定 |
| 高级 AI 洞察 | 需要足够数据积累和模型训练 | 多场次数据沉淀 |
| 多场次趋势对比 | 需要至少 5+ 场数据、后端存储 | 云端数据积累 |
F. 回放页与复盘页的联动规则
核心原则
复盘页 = 理解页(看全局、看数据、看结论)
回放页 = 操作页(看视频、做剪辑、细操作)
联动方向:复盘页 → 回放页(复盘页发出意图,回放页执行操作)
反向联动:回放页 → 复盘页(仅"查看复盘"入口,不传递操作状态)
1. 单点时间锚点联动
场景:点击复盘页上的某个事件/心率峰值/AI 推荐片段
跳转规则:
复盘页点击
→ 构造 ReplayAnchor {
anchor_time: 事件绝对时间,
offset_ms: -5000, // 前移 5 秒
linked_event_ids: [事件ID],
title: "第 12 分钟 射门"
}
→ Navigator.push(ReviewDetailPage, args: {
record: RecordingRecord,
replayAnchor: ReplayAnchor, // 新参数
})
→ 回放页 initState 检测 replayAnchor
→ session.seek(anchor.display_time, source: seekExternal, dotId: anchor.linked_event_ids[0])
→ EventsCard 自动滚动到对应事件并高亮
2. 区间联动
场景:点击复盘页的"第 20-30 分钟高强度区间"或某个 Segment
跳转规则:
复盘页点击区间
→ 构造 ReplayAnchor {
anchor_time: 区间开始时间,
offset_ms: 0,
linked_event_ids: segment.linked_events, // 区间内的所有事件
}
→ 跳转回放页
→ 回放页:
1. seek 到区间开始时间
2. TimelineToolbar 不自动筛选(保持全部可见)
3. EventsCard 高亮区间内的事件(通过 linked_event_ids)
4. 时间轴上显示区间高亮色带
3. 片段集合联动
场景:点击"AI 推荐高光 Top 5"或"全部射门事件"
跳转规则:
复盘页点击集合
→ 构造 ReplayAnchor 列表(多个锚点)
→ 跳转回放页,args: replayAnchors 列表
→ 回放页进入"集合浏览模式":
1. seek 到第一个锚点
2. 操作栏的"上一条/下一条"在锚点集合内导航(而非全部打点)
3. EventsCard 仅显示集合内的事件(临时筛选)
4. AppBar 标题显示"AI 推荐 · 1/5"
5. 右上角退出集合模式按钮
4. 剪辑联动
场景:复盘页推荐的片段直接进入剪辑
跳转规则:
复盘页长按推荐片段 → 弹出操作菜单
→ "剪辑此片段"
→ 跳转回放页,args: { replayAnchor, autoClip: true }
→ 回放页 initState 检测 autoClip=true
→ 自动调用 _clipSingleDot(anchor.linked_event_ids[0])
→ "剪辑全部推荐"
→ 跳转回放页,args: { replayAnchors, autoClipAll: true }
→ 回放页提交批量剪辑任务
页面间状态传递方式
class ReviewDetailPageArgs {
final RecordingRecord record;
final bool fromRecording;
final ReplayAnchor? replayAnchor;
final replayAnchors: List of ReplayAnchor?;
final bool autoClip;
}
G. 复盘页降级策略
场景 1:没有轨迹 / GPS 不可信
| 维度 | 策略 |
|---|
| QualityState | gps: unavailable 或 confidence 低于 0.3 |
| 页面展示 | 完全隐藏轨迹/热力图模块,不显示占位符 |
| 总览卡片 | 隐藏"总距离"字段,或标注"距离数据不可用" |
| AI 表达 | 不提及跑动距离、活动区域,不生成"你主要在左路活动"类结论 |
场景 2:心率数据断点较多
| 覆盖率 | 策略 |
|---|
| ≥80% | 正常显示曲线,断点用虚线连接 |
| 60%-80% | 显示曲线但标注"部分时段数据缺失",AI 仅使用有数据区间 |
| 40%-60% | 不显示趋势曲线,仅显示平均心率和峰值心率数值 |
| 低于 40% | 隐藏心率模块,AI 不引用心率数据 |
场景 3:速度数据异常跳变
| 维度 | 策略 |
|---|
| 检测规则 | 相邻采样速度差超过 15 km/h 视为异常 |
| 数据处理 | 异常点标记 quality_flag=degraded,不参与计算 |
| 页面展示 | 过滤异常点后显示,最高速度取过滤后的 P95 而非最大值 |
| AI 表达 | 使用"约"前缀("最高速度约 22km/h"),不给精确值 |
场景 4:Session 不完整 / 会话中断
| 维度 | 策略 |
|---|
| Session.status = interrupted | 总览卡片标注"本次训练记录不完整" |
| 时长计算 | 使用实际有数据的时间范围,不使用 start_time 到 end_time |
| 复盘页 | 允许查看,但 AI 摘要开头声明"本次记录不完整,以下分析仅基于已采集数据" |
| 跳转回放 | 正常允许,但无数据区间的锚点不生成 |
场景 5:外部视频时间未准确对齐
| 维度 | 策略 |
|---|
| 检测条件 | RecordingRecord.recordType == 2 或 3 且 alignOffsetMs 为 null |
| 复盘页 | 展示数据分析(不依赖视频对齐),但所有"查看片段"按钮灰显 |
| 提示文案 | "视频尚未对齐,请在回放页完成对齐后再查看相关片段" |
| AI 表达 | 正常生成摘要(基于打点数据),但不推荐"查看片段" |
H. AI 复盘的边界与约束
第一版 AI 可以做什么
| 能力 | 输入数据 | 输出示例 | 可信度 |
|---|
| 训练摘要 | Session + EventMarker 统计 | "本场足球训练 87 分钟,共标记 23 个事件,其中射门 8 次、犯规 3 次" | 高 |
| 强度评价 | 平均心率 + 心率区间分布 | "整体强度中等偏上,72% 时间处于有氧区" | 中(依赖心率可信) |
| 高光推荐 | EventMarker.confidence × 心率峰值关联 | "推荐查看第 12 分钟和第 38 分钟的射门,当时心率达到峰值" | 中 |
| 节奏总结 | 事件密度 + 心率趋势 | "上半场事件密集,下半场节奏放缓" | 中 |
| 保守型提示 | 心率异常高/事件密度异常 | "第 25-30 分钟心率持续偏高,建议关注该时段表现" | 低(仅提示) |
第一版 AI 不应该做什么
| 禁止行为 | 原因 | 替代方案 |
|---|
| "你的射门质量下降了" | 无动作质量数据,纯臆测 | "下半场射门次数减少" |
| "你应该加强左脚训练" | 无左右脚区分数据 | 不输出 |
| "你的防守位置不对" | 无轨迹数据,无法判断位置 | 不输出 |
| "你的体能不如上周" | 无多场次对比(MVP 阶段) | 不输出 |
| "这次犯规是因为注意力不集中" | 因果推断无数据依据 | "第 40 分钟出现犯规" |
AI 生成的数据依赖矩阵
| AI 结论类型 | 必须有的数据 | 缺失时行为 |
|---|
| 训练时长摘要 | Session | 不生成 AI 摘要 |
| 事件统计 | EventMarker ≥ 1 条 | "本场未记录到事件" |
| 强度评价 | 心率覆盖率 ≥ 60% | 跳过强度评价段落 |
| 高光推荐 | EventMarker ≥ 3 条 | "事件较少,建议下次更频繁标记" |
| 节奏分析 | Session 时长 ≥ 15 分钟 + EventMarker ≥ 5 条 | 跳过节奏段落 |
避免"AI 看起来很懂但没依据"的规则
- 每个 AI 结论必须标注数据来源:摘要末尾注明"基于 23 个打点事件和 87 分钟心率数据"
- 使用模糊量词而非精确数值:心率覆盖率低于 80% 时用"约"、"大致"
- 不使用因果连接词:禁止"因为...所以..."、"导致...",只用"同时..."、"期间..."
- 置信度低的结论用疑问句:不说"你累了",说"第 70 分钟后事件频率下降,是否感到疲劳?"
I. 推进顺序
Step 1:手表端 P0 数据采集(2-3 周)
| 维度 | 说明 |
|---|
| 目标 | 手表端能稳定采集心率 + 管理 Session 生命周期 + 时间戳同步 |
| 产出 | WearOS/watchOS 心率采集模块、Session 管理协议、时钟同步方案 |
| 为什么先做 | 没有数据就没有复盘页,心率是 ROI 最高的传感器 |
| 不做什么 | 不做 GPS、不做速度、不做复盘页 UI、不做 AI |
Step 2:统一数据模型 + 落库(1-2 周,与 Step 1 并行)
| 维度 | 说明 |
|---|
| 目标 | 定义 Session/EventMarker/SensorSample/QualityState 的 Dart 模型和 DB 表 |
| 产出 | 数据模型代码、DB v14 迁移、数据写入/查询 Service |
| 为什么先做 | Step 1 采集的数据需要有地方存,复盘页需要有数据源可读 |
| 不做什么 | 不做 Segment 自动计算、不做 ReplayAnchor 生成 |
Step 3:复盘页 MVP(2-3 周)
| 维度 | 说明 |
|---|
| 目标 | 上线 5 个模块:总览 + 事件分布 + 心率趋势 + AI 摘要 + 推荐片段 |
| 产出 | 复盘页 UI + ReplayAnchor 联动 + 降级策略实现 |
| 为什么先做 | P0 数据稳定后立即可验证复盘页的用户价值 |
| 不做什么 | 不做速度/轨迹相关模块、不做冲刺分析、不做多场次对比 |
Step 4:P1 数据 + 复盘页增强(持续迭代)
| 维度 | 说明 |
|---|
| 目标 | 补齐速度/距离采集、自动 Segment 计算、强度分布模块 |
| 产出 | 速度传感器模块、Segment 计算引擎、复盘页强度卡片 |
| 为什么后做 | 速度数据可信度依赖场景(室内 vs 室外),需要积累真实数据调参 |
| 不做什么 | 不做 GPS 轨迹热图(P2)、不做高级 AI 战术分析 |
J. 给研发团队的建议
1. 必须先抽象成统一层的模块
| 模块 | 原因 |
|---|
| SensorDataService | 统一管理所有传感器数据的写入/查询/聚合,不要让各页面直接操作 DB |
| QualityStateService | 统一计算和缓存数据质量状态,复盘页和 AI 共用同一个质量判断 |
| ReplayAnchorBuilder | 统一生成回放锚点,封装时间对齐逻辑,不要在复盘页到处拼参数 |
| TimeSync | 手表-手机时钟同步必须有独立模块,Session 开始时自动执行 |
2. 必须支持优雅降级的位置
- 复盘页的每个模块都必须检查 QualityState 再决定渲染
- AI 摘要生成必须接收 QualityState 作为输入,不是"生成完再过滤"
- 回放页的集合浏览模式必须处理"锚点指向无数据区间"的情况
3. 不要过度设计的地方
| 不要做 | 原因 |
|---|
| 不要做通用时序数据库 | SQLite 够用,5400 条/场足够 |
| 不要做实时流处理管线 | 1Hz 采样,定时批量写入即可 |
| 不要做传感器插件抽象层 | WearOS 和 watchOS API 差异大,各自实现更清晰 |
| 不要做复杂的 Segment 算法 | 第一版用心率区间简单分段,不要引入 ML |
4. 为未来 AI 留接口但现在不做重实现
| 留什么 | 怎么留 | 不做什么 |
|---|
| EventMarker.weight | 字段定义好,默认值 1 | 不做 AI 自动赋权 |
| EventMarker.tags | 字段定义好,默认空 | 不做 AI 自动打标 |
| Segment.source = ai_suggested | 枚举值预留 | 不做 AI 自动分段 |
| SensorSample 完整字段 | 模型定义全字段 | P2 字段暂不采集,值为 null |
最值得优先验证的 5 个关键假设
| # | 假设 | 验证方式 |
|---|
| 1 | 手表心率 1Hz 采集在运动中能稳定工作 60 分钟以上 | WearOS/watchOS 真机测试,记录断点率 |
| 2 | 手表-手机时钟偏移在一场训练中小于 2 秒 | Session 首尾各同步一次,对比偏移量 |
| 3 | 用户对"AI 摘要 + 推荐片段"的价值感知高于纯数据报表 | A/B 测试或用户访谈 |
| 4 | 1Hz 心率数据 5400 条/场在 SQLite 中查询聚合性能可接受 | 本地 benchmark,目标 200ms 以内 |
| 5 | 复盘页→回放页的跳转联动体验流畅,不会让用户迷失 | 原型测试,观察用户是否能顺利返回复盘页 |
当前阶段最容易做坏的 5 个风险点
| # | 风险 | 后果 | 缓解 |
|---|
| 1 | 心率数据不做质量标记就直接展示 | 异常值导致用户不信任 → 功能报废 | QualityState 必须与采集同步实现 |
| 2 | 复盘页做成回放页的复制品 | 两个页面高度重复,用户困惑 | 复盘页禁止放视频播放器,只通过锚点跳转 |
| 3 | AI 输出超出数据支撑的结论 | "这 AI 瞎说" → 信任崩塌 | 每条结论必须标注数据来源,可信度低时用疑问句 |
| 4 | 手表-手机时间不同步直接上线 | 点击"第 12 分钟射门"跳到视频第 15 分钟 → 体验崩溃 | Step 1 必须包含时钟同步,不是可选项 |
| 5 | SensorSample 表设计不预留 P1/P2 字段 | 后续加 GPS/速度需要再次迁移 DB | 一次定义全字段,P1/P2 字段 nullable |