# Miloco Notify

> 需要主动触达家庭成员时使用——感知告警、危险预警、任务到期、定时播报、设备反馈、关怀提醒等一切「主动告知 / 通知 / 触达」的场景。负责决策：通知给谁 → 走哪个渠道（音箱 TTS / IM / 米家推送）→ 写什么文案 → 交付执行。只要不是被动回答用户当前的提问、而是系统或 agent 要主动找人，就加载本 skill。用户想配置 / 绑定 / 解绑通知接收渠道时也走本 skill（见 references/channel-config.md）。

- Skill: `xiaomi/miloco-notify` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add xiaomi/miloco-notify`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xiaomi/miloco-notify/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: xiaomi (https://skillmd.com/u/xiaomi)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/xiaomi/miloco-notify

---


# miloco-notify

凡是 agent **主动找人**（而非回答当前对话）的场景，都经由本 skill 决策「**给谁 → 走哪 → 说什么**」，再交对应渠道执行。

## 何时激活

- 要主动向家庭成员传递信息 → 激活（感知告警、危险预警、任务到期、定时播报、设备反馈、关怀提醒）
- 用户正在对话、你在直接回答他的提问 → **不激活**（那是普通回复）
- 用户要配置 / 绑定 / 解绑通知接收渠道 → 激活，转 [references/channel-config.md](references/channel-config.md)

典型触发源：感知引擎 suggestion、任务 fire、cron 定时、巡检关怀、含「异常 / 危险 / 提醒 / 通知」语义的系统事件。

## 重要原则

1. **渠道优先级：TTS 现场 > IM 备选 > 米家推送兜底。** TTS 是当面说话，最自然、最及时；IM 是 TTS 够不着时的备选；米家推送适用于系统通知+危险警报。
2. **TTS 的前置条件：目标房间有在线音箱，且有人能听到（在场或在邻近房间）。** 没音箱、或**确认**全家无人，对着空房间播报没意义 → 降级 IM / 推送。但**在场无法确认**、或需引人前往处置时，宁可播报也别漏（见下「特例」与「异常处理」）。
3. **一次只用一个音箱。** 多个音箱在线时只挑**目标房间那一个**播报；目标房间无在线音箱时，按「工作流」和「异常处理」的级别规则决定是退选其他音箱还是放弃 TTS；多台齐鸣会此起彼伏，制造混乱。
4. **三条渠道都是即时真发、无 dry-run，发出即不可撤回；工具默认可用，无需自测。** 每次调用都真的送到家人面前，**绝不为"看能不能跑通"先发一条试探**——有配置 / 参数问题调用时自会报错，不必用真实推送探路；先按「工作流」决策清楚再交付。这条同样适用于一切有真实副作用的不可逆操作。


## 工作流

```
解析 → 分级 → 选人 → 选渠道 → 写文案 → 交付执行
```

### 1. 解析

从触发源提取：**通知内容**、**接收人线索**、**事发房间**、**紧迫度**、**是否危险**。

### 2. 分级

| 级别 | 含义 | 例子 |
| ---- | ---- | ---- |
| **L1 危险** | 已发生的人身 / 财产险情 | 老人跌倒、明火、燃气泄漏、破门闯入 |
| **L2 预警** | 危险前兆 / 监护对象异常（还来得及干预） | 儿童靠近危险物、烟雾初判、水浸初判 |
| **L3 日常** | 提醒 / 督促 / 信息播报 | 吃药、久坐、天气、任务完成、陌生人逗留 |

L1 / L2 = 危险类 → 走「危险通知」；L3 = 普通类 → 走「普通通知」。

> **陌生人 ≠ 入侵**：门口 / 院内出现陌生人，默认按 **L3 平和告知**，不要替家人脑补成入侵。**只有伴随明确威胁行为**（破门、翻墙、持械、暴力撬锁、翻箱倒柜等）才升 L1。

### 3. 选人

触发源已指定接收人 → 直接用。未指定 → `miloco-cli person list` 取成员，按下表判定：

| 情况 | 接收人 |
| ---- | ------ |
| L1 危险 | 全家 |
| L2 / L3 当事人是能自理的成人 | 当事人本人 |
| L2 / L3 当事人是儿童 / 老人 | 当事人 + 成人监护人 |
| L2 / L3 涉及家庭公共事务 | 全家 |

> 监护人：儿童 → role 含「爸爸 / 妈妈 / 父亲 / 母亲」的成员；老人 → role 含「儿子 / 女儿」的成员。

**身份不确定时**：
- 感知给了 person_id 且匹配 person list → 用真名称呼；
- 标注「陌生人」/ 未识别 / 无 person_id → 文案只说「有人」，**绝不猜身份**。

### 4. 选渠道 

选择通知渠道前需要确认 2 件事：

- **谁在家、在哪个房间** —— 来自触发源的感知上下文（拿不到时见「异常处理」）。
- **那个房间有没有在线音箱** —— 交 **miloco-devices skill** 查 `category=speaker` 的在线设备，本 skill 不直接拼设备命令。

#### 危险通知（L1 / L2）—— 目标：让能处理的人立刻注意到

**家里有人：**
- 事发房间有在线音箱 → **事发房间 TTS**（现场播报，引起家人注意）**＋ IM ＋ 米家推送**（三渠道全覆盖，确保不漏）
- 事发房间没在线音箱 → 改用**其他成人 / 监护人所在房间的音箱 TTS ＋ IM ＋ 米家推送**

**全家无人：**
- 没人能听见，TTS 无意义 → **IM ＋ 米家推送**

#### 普通通知（L3）—— 目标：把信息平静地递到对的人

- 接收人所在房间有在线音箱 → **该房间 TTS**
- 接收人不在家，或所在房间没在线音箱（TTS 不可达）→ **IM**

> **特例**：需要引导人前往某房间处理的事（厨房干烧 / 溢锅但厨房没人）——即使该房间当前无人，也优先用**该房间音箱 TTS**，目的是让家里听得到的人注意去处理。

### 5. 写文案 —— 你是家人，不是监控系统

三渠道**共享一套核心表述**，只按长度 / 形式微调，不要刻意改写成三套话。统一语气：

- 温暖自然，像家人之间的关心
- **客观陈述事实**，不催促、不指挥、不反复追问——用户看到事实自会判断（L1 紧急除外，可用「马上来」这类引导）
- 不用「检测到 / 系统发现 / 感知引擎 / 根据数据」这类机器话，用户不关心底层
- 已识别成员用称呼（爸爸、奶奶、豆豆）；未识别只说「有人 / 来了个人」，不猜身份
- 对儿童温和引导，别命令、别吓到孩子

| 渠道 | 长度 | 形式 |
| ---- | ---- | ---- |
| TTS | 一句话、尽量短 | 口语、现场播报——长了打断不了、听着烦 |
| IM | 1～2 句 | 客观陈述，可补一点细节 |
| 米家推送 | 精炼一句 | 系统通知口吻 |

**示例**（同一事件、三渠道）：

| 场景 | TTS | IM | 推送 |
| ---- | --- | -- | ---- |
| 老人跌倒 L1 | 奶奶摔倒了，家里人马上来 | 奶奶在客厅摔倒了，15 秒没起来 | 奶奶在客厅摔倒，15 秒未起身 |
| 儿童危险 L2 | 豆豆，那个不能玩，先放回去 | 豆豆在厨房拿了把菜刀，旁边没大人 | 豆豆在厨房拿菜刀，无成人在场 |
| 陌生人逗留 L3 | 门口来了个人，不认识 | 门口来了个陌生人，待了好几分钟了 | 门口有陌生人逗留 |
| 吃药提醒 L3 | 该吃药了 | 该吃降压药了 | 该吃降压药了 |

### 6. 交付执行

| 渠道 | 怎么发 |
| ---- | ------ |
| TTS | **交给 miloco-devices skill**：在目标房间定位 `category=speaker` 的在线设备并播报。本 skill 不直接拼设备命令。 |
| IM | 调 **agent 工具** `miloco_im_push(message="<文案>")`——这是 agent 内置工具（function call），直接调用；**不是 miloco-cli 命令，别用 shell / miloco-cli 去执行**。已绑定多个 IM 通道时，工具会向**所有已绑定通道**广播；返回 `needsBind=true` → 见 [references/channel-config.md](references/channel-config.md)，**立即带 bindHint 重发**（这是继续操作的信号，不是回复给用户的内容）。 |
| 米家推送 | 命令行 `miloco-cli notify push --text "<文案>"`（这条才是 CLI 命令，与上面的 agent 工具区分）。 |

> **上表是"决定要发之后怎么发"的语法参考，不是可以照着跑一遍的自检脚本。** `<文案>` 是占位符，交付时必须换成本轮真正要说的话；**绝不**照抄示例、也绝不用「测试」「测试通知」这类字样先发一条来验证——渠道都无 dry-run、探一次就真骚扰一次，命令 / 工具本就可用、有问题调用时自会报错（见「重要原则」）。只在走完上面「工作流」、确认该发之后才执行。

## 异常处理

| 异常 | 处理 |
| ---- | ---- |
| 接收人识别不出 | 按「家庭公共」处理，发全家 |
| 事发 / 目标房间识别不出 | 跳过 TTS，走 IM |
| 目标房间无在线音箱 / TTS 失败 | **L1/L2**：改用其他有人房间的音箱（全家无在线音箱才放弃 TTS），IM + 米家推送照常必发；**L3**：降级 IM |
| IM 返回 `needsBind=true` | 见 [references/channel-config.md](references/channel-config.md)，带 bindHint 重发 |
| IM 返回 `ok=false`（非 needsBind） | 降级米家推送 |
| 通知工具报错 / 调用失败 | **绝不对同一条通知反复重推**（会造成死循环、疯狂骚扰用户）。规则：单次发送 + 至多一次降级（TTS→IM→米家推送）；`needsBind=true` 补 bindHint 重发是**唯一**允许的重试（只一次）。其余失败按级别降级一次；若已无可降级渠道、或降级仍失败 → **立即结束本轮通知、不再重试**，交由下个触发周期重新决策。 |
| 在家 / 在场、人在哪个房间无法确认 | 宁可播报别漏。**TTS 选哪台**：优先事发房间，无在线音箱 → **任选一台在线音箱**，全家都无在线音箱才放弃 TTS。危险类（L1/L2）无论 TTS 是否发出，IM + 米家推送照常必发；普通类（L3）放弃 TTS 后才降级 IM |

## 注意事项

1. **主动触达统一入口**——凡 agent 主动找人都走本 skill；多条通知逐条独立决策，不合并。
2. **一次只用一个音箱、且是目标房间那一个**——不多台齐鸣；TTS 要短，长了打断不了惹人烦。
3. **渠道分层**：危险类（L1/L2）三渠道全覆盖；普通类（L3）按可达性单选 TTS 或 IM；米家推送只用于系统通知（兼作危险类和 IM 通知失败兜底）。
4. **系统触发的通知直接执行**，不主动追问。
5. **TTS 执行交 miloco-devices**；**渠道配置见 references/channel-config.md**——本 skill 只管决策，不重复造轮子。
6. **绝不循环推送 / 发完即止。** 一条通知本轮只走一遍决策链、只发一次；发出（或已尽力降级）后**立即结束**，不要回头重查 profile / 重读 skill / 反复自证「是不是只发了一次」——那会绕回来再次触发推送，形成死循环。工具调用失败也不自主重试（`needsBind` 补 bindHint 重发除外）。

