女性运行时 × StopWatch 黑客松 Handoff

女性运行时 × StopWatch 黑客松 Handoff

更新日期:2026-08-25
用途:在新会话中继续产品、设计和技术实施。
比赛约束:项目必须在黑客松期间从 0 到 1 实现,不复制现成项目、仓库或 SDK 代码。赛前只做调研、需求、设计规范和探针测试;正式代码在比赛开始后重写。

1. 项目定位

项目暂定名

女性运行时:我怎么又这样了

一句话介绍

一个帮助女性低负担看懂身体规律的轻应用:用户不用填复杂表格,只需对挂脖 StopWatch 说一句话或完成五问,系统将主动感受与 Apple Health 客观数据放入同一条身体时间线,再通过本地知识库生成温和、非诊断的身体观察,并打印一张蓝色单色小贴纸带走。

核心价值

  • 不把所有问题归因于经期。
  • 不强调“哪里又不舒服”,而是提供可执行的小建议和简短安抚。
  • Apple Health 说明身体客观发生了什么;StopWatch 记录用户主观感受到了什么。
  • 本地知识库提供可信依据和安全边界,大模型只负责理解、调用工具与口语化表达。
  • 产品不是医疗诊断工具。

2. 最终演示故事

  1. 用户从 StopWatch 首页点击“说一说”。
  2. 用户说:“下午特别困,还想吃甜的,肩膀有点紧。”
  3. 系统完成语音转写,并提取困倦、想吃甜食和肩颈不适。
  4. 记录与 Apple Health 的睡眠、心率、步数等数据进入同一条身体时间线。
  5. Agent 查询当天身体摘要与本地知识库。
  6. Web App 展示主观记录、客观背景和温和建议。
  7. 用户确认后,打印蓝色单色“五行身体观察小贴纸”。
  8. 用户摇晃 StopWatch 时,可以额外揭晓一张“今日身体提示卡”;这是彩蛋,不阻塞核心流程。

3. 产品功能范围

StopWatch 首页

首页显示三个异形 Icon:

  1. 说一说:Typeless/语音输入。
  2. 看看自己:五大基本需求确认。
  3. 今日身体:今日数据摘要。

五大基本需求

  • 困
  • 饿
  • 渴
  • 情绪
  • 身体不适

支持简单等级或有/无选择,可以多选,保存为一次完整 checkin。

身体时间线

将两类数据按时间放在一起,但明确标记来源:

  • 自动数据:Apple Health。
  • 主动记录:StopWatch。

示例:

1
2
3
4
07:30  昨晚睡眠 6小时12分        Apple Health
11:40 饿 · 有点烦 StopWatch
14:20 困4/5 · 想吃甜食 StopWatch
14:25 心率 86 Apple Health

摇晃彩蛋

  • 使用 BMI270 检测明显摇晃。
  • 短震后展示一张身体提示卡。
  • 有记录时根据当天状态选卡;无数据时使用通用卡。
  • 失败或不摇晃不影响记录、摘要和打印。
  • 不做塔罗或运势,命名为“身体提示卡”或“今日身体签”。

蓝色身体观察小贴纸

打印机当前只能使用蓝色单色耗材。

五行结构:

1
2
3
4
5
今天的我:
身体在说:
我观察到:
可以试试:
今日身体签:

设计要求:纯蓝与留白;不用灰阶、透明度、渐变;用字号、粗细、边框和间距建立层级;IP 需提供蓝色单色线稿版。

4. StopWatch 设计规格

  • 官方真实屏幕:圆形 AMOLED,466 × 466 px。
  • Figma 主画板:466 × 466 px,单位为 px,不是 mm。
  • 物理屏幕圆心:(233, 233)。
  • 仓库参考方案:内部居中 450 × 450 px UI Frame,位置 x=8, y=8。
  • 内部 Frame 圆心:(225, 225)。
  • 重要内容建议放在半径 200 px 内。
  • 普通视觉边界建议不超过半径 209 px。
  • 装饰可以延伸到半径 225 px。

素材原则

  • 圆、线、按钮、进度条、波形优先由 C++ 绘制。
  • IP、插画等复杂内容使用最终显示尺寸的透明 PNG8。
  • 不在设备端解析 SVG;SVG/Figma 只作为设计源文件。
  • 不导出 2×/3× 大图再让硬件缩放。
  • 避免模糊阴影、照片纹理、大面积透明渐变。
  • 中文文字不要做成图片。
  • 动态元素必须能被一个独立的小矩形框住,矩形外内容进入页面时只画一次。

动画与刷新

  • 动画不只限于逐帧:优先代码动画和局部部件动画。
  • 录音时间:1fps。
  • 波形:5–8fps。
  • 等待动画:2–4fps。
  • 成功动画:3–6帧,6–10fps,持续不超过1秒。
  • IP 动画优先只动眼睛、嘴巴或气泡。
  • 待机页面无变化时不刷新。
  • 交互结束后尽快降低亮度或熄屏。
  • AMOLED 待机尽量使用黑色背景,减少亮色面积。

参考仓库 isalicema/m5-stopwatch-dashboard 只用于理解圆屏安全区、扩大触摸热区、局部刷新和固件/Bridge 分层;不复制其代码。该仓库本身约有 6700 行固件和完整 Mac Bridge,不适合作为比赛起点。

5. 数据架构原则

四类数据

类型 示例 主要来源
measurement 心率、HRV、睡眠、步数、腕温 Apple Health
checkin 困、饿、渴、情绪、不适 StopWatch
activity 吃饭、喝水、运动、服药 StopWatch / HealthKit
insight “睡眠较少时下午更容易困” 系统推导

客观测量与主观感受不能互相覆盖。例如“睡眠6小时12分”和“下午困4/5”是两条事件,只在分析层建立关联。

统一事件模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"event_id": "stopwatch-a3f2-1724581200",
"event_type": "checkin",
"metric": "sleepiness",
"value": 4,
"unit": "level_1_5",
"start_at": "2026-08-25T14:20:00+09:00",
"end_at": null,
"source": {
"system": "stopwatch",
"device_id": "m5-001",
"app": "body-runtime"
},
"context": {
"raw_text": "下午特别困,还有点想吃甜的",
"tags": ["困", "想吃甜食"]
},
"version": 1,
"recorded_at": "2026-08-25T14:20:05+09:00"
}

防止数据打架

  • 每条数据永远保留来源。
  • 不使用 metric + time 作为唯一键。
  • 优先使用 source.system + source_sample_id。
  • StopWatch 在本地生成固定 event_id,失败重试时不能重新生成。
  • 心率、HRV、睡眠、步数以 Apple Health 为主来源。
  • 困、饿、渴、情绪、不适以 StopWatch 为主来源。
  • 不同时开启多个长期上传同一累计指标的链路,防止步数、距离、活动能量翻倍。
  • 原始事件只追加;结构化视图和每日摘要可以重新计算。
  • AI 推导结果不能冒充客观事实。

StopWatch 同步状态

1
2
pending → uploading → synced
↘ failed → retry

服务端应返回逐条 ACK:

1
2
3
4
5
{
"accepted": ["event-001"],
"duplicated": [],
"rejected": []
}

6. Apple Health 接入

参考 KKarsyline/Collar_watch 的思想,但比赛期间自行实现,不复制代码。

首版指标:

  • 睡眠时长
  • 心率
  • 静息心率
  • HRV
  • 步数
  • 运动时间

后置指标:腕温、呼吸频率、血氧。

同步规则:

  1. 使用 HealthKit Anchor 增量读取。
  2. 上传统一数据接口。
  3. 服务端返回成功后才提交 Anchor。
  4. 失败时不推进 Anchor,下次补传。
  5. 服务端根据源 Sample ID 幂等去重。
  6. 睡眠可以查询最近48小时完整片段并重新聚合,避免只得到半段睡眠。

待确定:Apple Health 最终由独立 watchOS App、iPhone App、简化上传程序还是比赛 Mock 接入。

7. 本地知识库与 Skill

定位

  • 本地知识库保存公共知识、来源、安全边界和提示卡。
  • Skill 规定如何查询知识、如何表达以及哪些结论不能推断。
  • 用户健康记录不进入公共知识库。
  • 首版不用向量数据库,采用确定性标签匹配和规则排序。

Monorepo 结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
packages/body-knowledge/
├── cards/
│ ├── sleep.json
│ ├── hunger.json
│ ├── hydration.json
│ ├── mood.json
│ ├── discomfort.json
│ └── cycle.json
├── sources/
│ └── sources.json
├── safety/
│ └── red-flags.json
└── index.ts

packages/body-observation-skill/
├── SKILL.md
└── references/

首版知识范围

准备约18–30张卡:

  • 困:3张
  • 饿/想吃东西:3张
  • 渴:2张
  • 情绪/压力:3张
  • 身体不适:3张
  • 女性周期:3张
  • 综合自查:1张以上

知识卡示例:

1
2
3
4
5
6
7
8
9
10
11
12
{
"id": "sleep-energy-001",
"topic": "sleep",
"title": "睡眠不足与白天困倦",
"triggers": ["困", "疲惫", "睡眠不足"],
"summary": "睡眠时间较短时,白天更容易出现困倦和注意力下降。",
"suggestions": ["降低今天的任务强度", "短暂活动或休息", "今晚提前收尾"],
"avoid_claims": ["不能据此判断睡眠障碍", "不能断定困倦只由睡眠不足导致"],
"source_ids": ["source-001"],
"evidence_level": "general_guidance",
"version": 1
}

安全边界

  • 不提供诊断。
  • 不解释异常心率的具体病因。
  • 不根据单次数据判断内分泌问题。
  • 不把所有情绪和食欲变化归因于月经周期。
  • 使用“可能”“同时出现”“可以试试”,不使用“说明你”“一定是……导致”。
  • 胸痛、呼吸困难、晕厥、意识异常、持续剧烈疼痛、严重过敏等情况停止普通提示,改为明确就医提醒。

8. 大模型、Function Calling 与 MCP

模型选择

用户计划使用 DeepSeek 或豆包,不使用 OpenAI 作为默认方案。

推荐选择:

  • Typeless 已输出文字:只接一个文本模型,DeepSeek 或豆包二选一。
  • StopWatch 上传音频:豆包语音识别 + DeepSeek 文本,或全豆包方案。
  • 比赛期间不要同时让 DeepSeek 与豆包分析同一条记录。

建议实现供应商抽象:

1
2
3
4
5
6
7
interface LLMProvider {
extractCheckin(text: string): Promise<BodyCheckin>;
runObservation(
input: ObservationInput,
tools: ToolDefinition[]
): Promise<ObservationResult>;
}
1
2
3
4
providers/
├── deepseek.ts
├── doubao.ts
└── mock.ts

现场通过环境变量切换:

1
LLM_PROVIDER=deepseek

必须准备 mock 降级,用预置数据保证现场演示。

Function Calling

适合的工具:

  • health_now:获取当天紧凑健康摘要。
  • health_timeline:查询一段时间内的客观数据和主动记录。
  • knowledge_search:检索本地知识卡和安全边界。
  • generate_sticker:生成贴纸结构与预览。

不交给模型的内容:鉴权、去重、事件ID、HealthKit Anchor、同步重试、累计指标计算、最终安全拦截、直接打印。

MCP

MCP 不用于 StopWatch 上传,也不代替 REST API。它负责把健康数据和知识库作为可复用的 Agent 工具暴露出去。

1
2
3
4
5
6
apps/mcp-server/
└── src/tools/
├── health-now.ts
├── health-timeline.ts
├── knowledge-search.ts
└── sticker-preview.ts

即使 DeepSeek 或豆包不原生直连 MCP,也可以在 TypeScript Agent Runtime 中把 MCP 工具转换成 Function Calling Schema,执行工具后再把结果返回模型。

Skill、Function Calling、MCP 分工

层 责任
Skill 分析步骤、语气、安全边界、输出格式
Function Calling 决定当前调用哪些业务工具
MCP 提供可复用的数据与能力
本地知识库 保存事实卡、来源和规则
REST API 设备与应用的数据通信

9. 代码结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apps/
├── web/ # 身体时间线 Web App
├── api/ # 数据、Agent、打印接口
├── mcp-server/ # Agent 可复用工具
└── firmware/ # StopWatch C++ 固件

packages/
├── shared-schema/ # 全项目唯一数据类型
├── body-knowledge/ # 本地知识库
├── body-observation-skill/ # Agent 流程与表达规则
├── feature-sdk/ # 功能模块接口
└── ui/ # 共享 UI 与主题

plugins/
├── voice-input/
├── body-checkin/
├── health-dashboard/
├── knowledge-cards/
├── daily-summary/
├── sticker-print/
└── shake-card/

这里 plugins/ 是团队并行开发的 Feature Modules,不等于 MCP 插件。

10. 非技术团队的插件式分工

用户本人:架构与集成

  • 初始化 monorepo。
  • 定义 shared-schema 与 feature-sdk。
  • 建立 API 空壳和 Mock 数据。
  • 管理环境变量与根配置。
  • 负责固件和真实通信。
  • 审核与合并 PR。

其他人禁止直接修改共享 Schema、核心 API、固件和根配置。

队友 A:Web App

  • 今日摘要。
  • 主客观身体时间线。
  • 数据卡片。
  • 打印入口。
  • 仅通过 Mock API 开发。

队友 B:五大需求交互

  • 五问选择页面。
  • 状态与等级。
  • 保存成功反馈。
  • 输出固定 BodyCheckinInput。

队友 C:本地知识库

  • 知识卡。
  • 来源。
  • 安全红线。
  • 提示卡与贴纸文案。
  • 测试输入样例。

队友 D:贴纸与提示卡

  • 五行蓝色单色贴纸。
  • 圆屏彩色提示卡。
  • 单色线稿版。
  • HTML/SVG 打印预览。

队友 E:语音与结构化提取

  • 接收音频或文本。
  • 转写。
  • 结构化提取。
  • 10组测试语料。

每个模块目录包含:

1
2
3
4
5
6
plugins/<module>/
├── README.md
├── SPEC.md
├── src/
├── mock/
└── tests/

统一约束:一个插件一个分支、一个 PR;不改其他目录;不增加第二套字段;不升级全局依赖;必须提供 Mock、截图和修改清单。

11. API 草案

1
2
3
4
5
6
7
POST /api/events/batch
GET /api/today
GET /api/timeline
POST /api/transcribe
POST /api/extract
POST /api/sticker/preview
POST /api/sticker/print

贴纸打印必须由用户显式点击确认,Agent 不得自动执行真实打印。

12. 黑客松优先级

P0:必须完成

  • StopWatch 三入口首页。
  • 至少一种真实主动记录方式。
  • 文本/语音转写和结构化提取。
  • Apple Health 至少提供睡眠、心率、步数;若真链路来不及,准备可信 Mock。
  • 统一事件结构、来源标记与幂等去重。
  • Web App 今日时间线。
  • 本地知识卡检索。
  • 身体观察输出。
  • 蓝色单色小贴纸预览和打印。
  • 模型 Mock 降级。

P1:时间允许

  • 完整五问。
  • 主客观时间关联。
  • 卡牌模板匹配。
  • 记录编辑。
  • 断网队列和自动补传。
  • MCP Server。

P2:展示加分

  • 摇晃身体提示卡。
  • IP 局部动画。
  • 更完整睡眠阶段。
  • 更多 HealthKit 指标。
  • Skill 完整封装。
  • 向量检索(比赛后优先)。

13. 实施顺序

  1. 创建 monorepo、Schema、插件模板和 Mock。
  2. 先用文本跑通:结构化提取 → 知识检索 → 身体观察 → 贴纸。
  3. 并行开发 Web、五问、知识卡和贴纸预览。
  4. 实现统一事件 API 和本地存储。
  5. 接 StopWatch 真实上传。
  6. 接 Apple Health 或可靠演示数据。
  7. 最后加入 MCP、摇晃彩蛋和动画。

14. 当前待确认的技术问题

  1. Apple Health 最终从 watchOS App、iPhone App、简化上传程序还是 Mock 读取?
  2. StopWatch 语音是直接 Wi-Fi 上传音频、USB 传 Mac,还是只触发 Typeless?
  3. Typeless 只负责转文字,还是还负责把文字输入 Web?
  4. 打印机具体型号、连接方式和接受的数据格式是什么?
  5. 服务端首版是否采用 TypeScript + SQLite + Drizzle?
  6. 比赛现场是否有稳定 Wi-Fi?
  7. 文本模型最终选择 DeepSeek 还是豆包?
  8. 是否需要在比赛现场真实展示 Apple Health 权限授权和增量同步?

15. 新会话启动提示词

1
2
3
4
5
6
7
8
9
10
请阅读这份 handoff,并继续帮助我完成“女性运行时 × M5Stack StopWatch”黑客松项目。

请遵守以下原则:
1. 比赛项目必须从零实现,不复制参考仓库代码。
2. 优先保证核心演示链路,不提前扩张功能。
3. Apple Health 是客观数据,StopWatch 是主观记录,两者不互相覆盖。
4. 本地知识库负责依据和安全边界,大模型负责理解、Function Calling 和口语化表达。
5. 输出非诊断、温和、积极、低焦虑。
6. 每次提出开发任务时标注 P0/P1/P2、输入输出、验收标准和允许修改的目录。
7. 如果关键技术条件尚未确认,先指出它会影响哪些架构决策。