miloco-create-task
收到任务相关消息 → 第一步判 op,六条路径之一:
| op | 触发来源 | 例子 | 走哪条 |
|---|---|---|---|
| create | 用户消息 / [感知引擎] 语音指令系统消息 |
"每天喝 8 杯水" / "房间有人提醒我" / "明天 X" / "没人就 Y" | 本 skill 后续段(前置检查 → 两层维度判 → 装配映射) |
| list | 用户消息 | "我有哪些任务" / "查看任务列表" | references/crud-ops.md |
| logs | 用户消息 | "X 任务今天触发几次" / "看 X 触发记录" | references/crud-ops.md |
| disable / enable | 用户消息 | "暂停喝水任务" / "启用 X 任务" | references/crud-ops.md |
| update | 用户消息 | "把喝水改成 10 杯" / "条件改成阳台有人" | references/crud-ops.md |
| delete | 用户消息 | "删除喝水任务" / "把 X 任务去掉" | miloco-terminate-task |
输出协议
user-facing text 仅在两个时机出现:
- 终态:tool 链跑完发一段结果
- 反问:等用户答复发一句;只问本次需要的字段,不夹带已装配信息 / 进度同步 / 任何其他内容
其余时刻过程描述放 thinking 块,user-facing 静默。
person 引用规则:user-facing 文本(终态 + 反问)引用 person 时 role 优先——role 非空只写 role,不带 name 括号标注;role 为空才写 name。
终态必含项
内容反映本轮实际 CLI 装配参数,不增不减。格式不限,必含:
- 任务标识:task id + 简述
- 触发场景:什么时候启动
- 响应动作:触发后做什么 + 记录什么
- 生命周期:长期常驻 / 限时
- 装配提示:按 §装配提示元规则 逐条独立列出
以下内容仅覆盖 create 路径。判据走语义判断,不照字面词表死扣。下文给的词只作类别示例,不是穷举枚举。
装配后自检
工具链跑完到终态输出之间,必须 echo 下列格式做自检(任一 N → 修正后重过;echo 内容不输出到 user-facing):
[装配后自检]
1. 含 on-target-desc 时 task → record → rule 顺序完成装配:Y / N / NA
2. 终态 §响应动作 同时含「触发后做什么」+「记录什么」:Y / N
3. 每条装配提示「怎么改」字段给出用户可直接复述的短语:Y / N
装配提示元规则
凡装配过程中 agent 自主产生的对用户的提示性信息,统称「装配提示」。覆盖:默认猜测、偏离推荐、超阈值范围、需用户验证的常识默认等。
每条装配提示必产出,在终态独立成句出现,措辞自由。
每条必含 3 要素:
- 实际取值(用户可理解措辞,不暴露内部字段)
- 一句话原因
- 怎么改 / 怎么确认
子维度判据里只标 "→ 触发装配提示"。
多提示装配:N 条提示 → N 句,独立成句。
输入硬约束
- 多个独立 task → 拆成单 task 列表,各自走分类
- AND 型规则(同一 task 内多触发必须同时成立)→ 拒建
前置检查(早于第一层判定)
[输出协议 reload]:本章节及后续所有装配工具调用期间,过程描述走 thinking,user-facing 仅在终态与反问输出。
感知设备清单
调 miloco-cli scope camera list,取 in_use=true 的子集记为 N。结果给 §Rule.感知设备 装配阶段消费。与 §重复检查 / §person 清单 并行调用。
person 清单
调 miloco-cli person list 拿 role/name 列表,结果给 §Rule.condition.query 主语决策消费。与 §感知设备清单 / §重复检查 并行调用。
task_id 规范
agent 自己起,snake_case [a-z0-9_]{1,32},需含语义(drink_8_today / fall_alert / phone_time_daily),不用纯数字 / 简短缩写。
重复检查
调 miloco-cli task list --pretty 拿活跃 task 列表,按 task_id + description + rule_briefs 比对:
| 类型 | 判据 | 处理 |
|---|---|---|
| A 名字冲突 | 待建 task_id 命中现有活跃 task | 让用户换 task_id 或先 terminate 旧 |
| B 语义重复 | description 表意一致 / rule_briefs 触发+动作组合本质相同 | 反问三选一:替换 / 强制新建 / 取消 |
| C 触发冲突 | 同 source + 同 condition + 动作矛盾或参数改写 | 反问三选一:替换并删旧(旧历史不可恢复)/ 暂停旧建新(旧历史保留,可恢复)/ 取消 |
跳过条件:
- 命中 task_id 是 paused → 让用户先 enable 或 delete,不算冲突
- 作用对象不同(不同摄像头/房间/设备)→ 不算冲突
- 同 source + 同 condition 但 piid 不同(亮度 vs 色温)→ 不算冲突,并存
禁止主动 update / delete:A / B / C 类命中后等用户答复,不私自 update / delete 已有 rule / cron / record / task。
第一层 · task 维度判定
[输出协议 reload]:本章节及后续所有装配工具调用期间,过程描述走 thinking,user-facing 仅在终态与反问输出。
task 是聚合根,含:
- 0~N 个 rule(Rule 判存在性)
- 0~N 个 cron(Schedule 判存在性)
- 0~1 个 record(Record 判存在性)
- 1 个 lifecycle 属性(Lifecycle 判取值)
Rule/Schedule/Record 是子组件存在性(Y/N);Lifecycle 是 task 整体属性(permanent/temporary)。四者独立判,不互依赖。
Rule?(Y/N)
Y = 用户描述需要系统持续观察现实世界才能触发的场景,含以下任一语义:
- 环境状态变化:人在/不在、设备开关状态、温度湿度烟雾等环境量异常
- 人体可观测动作或行为:任何能被摄像头或麦克风识别的人体动作或姿态(瞬时如吃药/咳嗽/按门铃,持续如看书/写作业/玩手机)
- 人身安全异常:摔倒、入侵、求救、火灾相关
- 计数/累计数字目标:N 次/杯/个 或 累计 N 小时/分钟
N = 纯时间触发或纯查询,不依赖现实事件。
Rule 跟 Schedule 完全独立:句中含任何现实事件信号(动作/环境/状态变化/计数目标)就 Rule=Y,即使同句有定时提醒。
Schedule?(Y/N)
核心判据:触发是否依赖时钟锚点(在某个未来时间点 / 周期性时间点上由系统主动发起)。
- 时钟锚点触发:用户期望系统按时钟自动触发,无需现实事件配合 → Y
- 现实事件触发:触发由感知事件或时长持续状态发起,不依赖时钟 → N
Y 的语义(含以下任一):
- 单一未来具体时点(明天 12 点 / N 分钟后 / 一会儿 / 后天 等明确到小时/分钟/相对偏移)
- 周期性具体时点(每天早上 8 点 / 每周三 9 点 / 工作日 7 点 等明确含时段或时点的周期)
- 显式定时措辞(定时提醒 / 到点提醒 / 每 N 小时催一次 等含具体时间间隔的措辞)
- 周期+计数无具体时点 + 无显式定时措辞(每天/每周 N 杯/次/个 等周期目标) → 默认装周期定时提醒(按 §Schedule.频率默认 表取默认时点) → 触发装配提示
"每天累计/合计/总共/加起来 N 小时/分钟" → Schedule=N。区别于"每天 N 杯/N 次"离散计数 → Schedule=Y 定时提醒。
不归 Schedule:
- 时长阈值("持续/连续 N 分钟")
- 限时窗口("今天/这周/这个月")
- 语气词(一声/一下)
Record?(Y/N)
核心判据:用户语义里是否含"累计/计数"信号。
- 含累计/计数 → Y
- 不含 → N
累计/计数信号(含以下任一即 Y):
- 计数达标:够 N / 超过 N / 目标 N / 喝 N 杯 / 走 N 步 / 吃 N 次药 / 做 N 个
- 计数统计:"几杯/多少次/几个"等问句计数
- 时长累计:累计/总共/合计/加起来 N 小时/分钟
- 量化反查:算/统计/看/问 X 多久 / X 了多久 / 算 X 几次 / 看 X 用了多少时间
- 历史记录意图:记录/统计/跟踪/数 / 记下每次 / 看历史
- 周期重置:每天 N 杯 / 每周 N 次
N 的边界 case:
- 时长阈值 + 提醒(不含显式跨次词、不含历史记录词) → 默认按单次连续装(rule duration_seconds 表达,不建 record)→ 触发装配提示
Record=Y → Rule=Y(强约束):record 各 kind 必须由 rule 触发写入 —— progress = --action-desc「计数加一」/ duration = --on-enter-desc「开始计时」+ --on-exit-desc「结束计时」/ event = --action-desc「事件追加」。具体动词短语变体见 §Rule.action 自检表。
Lifecycle(permanent / temporary)
核心判据:用户的规则是长期常驻还是只在某个有限时间段内有效。
- 长期常驻:用户期望规则不限时一直生效,每次满足触发条件都响应 → permanent
- 限时有效:用户期望规则只在某个有限时间段内有效,过期或满足完成条件后失效 → temporary
按以下信号顺序判,命中第一个即停:
- 周期信号(每天/每周/工作日/周末/每月/以后/一直/总是/每次/都)→ permanent
- 时间窗信号(用户原话明确含时间段截止:今天/今晚/这周/本月/本月底前)→ temporary
- 达标完成信号("做够 N / 达到 N 个 / 满足 N 次"作为整个规则的终止条件)→ temporary
- 绝对一次性时刻信号("明天 X 点 / N 分钟后 / 后天 / 一会儿"作为唯一触发点)→ temporary
- 以上都无 → permanent
不归 temporary:
- 含具体地点(阳台/卧室)但无时间窗信号
- 含时长阈值("持续/连续 N 分钟")
- 含瞬时事件触发词(进入某场景、状态切换发生时)
- 语气词(一声/一下/就行)
- 活动名(看书/做饭/洗澡)
判出 temporary 时装 task create --lifecycle temporary(permanent 是默认值,不传);temporary 另按 §CLI 命令表 装到期销毁 cron。
temporary 且到期时刻确定(信号 2 时间窗 / 信号 4 绝对一次性时刻)→ 同一个 ISO 一并传 task create --expires-at,与销毁 cron 的 --at-iso、record 的 expires_at 取同一个值。信号 3 达标完成没有时刻,不传。
第二层 · 按取值展开子维度
Rule=Y 时填 Rule
Rule.direction(enter / exit / session)
一条 rule 只有一个 condition.query,只能表达一个观测。direction 决定这个观测成立时如何映射到 task 的进 / 出:
| direction | 观测成立时 | 动作槽 |
|---|---|---|
enter |
把 task 推进「进行中」 | on_enter |
exit |
把 task 推出「进行中」 | on_exit |
session |
成立 = 进,不成立 = 出 | 进 → on_enter,出 → on_exit |
名下没有 exit / session 规则的 task 不停留在「进行中」,每次 enter 都执行一次动作。
第 0 步:进入和退出是不是同一个观测的有 / 无?
| 判据 | 装配 |
|---|---|
| 是(人在 / 不在、手机拿着 / 放下、画面有人 / 无人) | 单条 rule,走第 1 步 |
| 不是(进退是两个独立观测:V 手势开灯 / 挥手关灯、开门进 / 关门出) | 两条 rule 挂同一 task,进的 --direction enter,出的 --direction exit |
判「不是」时禁止把退出观测写进 on-exit-*——session 的 on_exit 触发条件是进入观测消失,不是退出观测成立。两条的建立顺序:先 enter 后 exit(task 名下没有进路径时建 exit 会被拒)。
第 1 步(第 0 步判「是」时走):是否需要对"进入条件"和"离开条件"双向响应?
- 是 →
session - 否 →
enter(触发条件含持续时长用duration_seconds修饰,不改 direction)
按顺序判,命中即停:
- Record.kind=duration(跨次累计 record) →
session(on_enter 写 duration-start + on_exit 写 duration-end) - 人身安全/紧急异常 →
enter - 显式 session 信号 →
session- 同一观测的双向状态切换(进入做 X、退出做 Y,进退是同一条件的有 / 无)
- 激活/启动持续设备状态(开灯/开空调/拉帘/播放音乐 等"开 X"动作)→ 默认补
on_exit复位 - 开始/进入持续行为态("开始 X" / "进入 X" 语义,仅响应入态)→
duration_seconds取稳定窗(按推荐表)+ 长exit_debounce_seconds(按推荐表)+on_exit留空
- 到达/进入类瞬时事件(到家/到门/进门/进 X / 回家 等含强常识默认) →
session+ 长exit_debounce_seconds(按推荐表)+on_exit留空 - 通知/播报类一次性动作 + 陌生主体瞬时存在态触发(陌生人 / 任何人 来了 / 出现 等无强常识默认)+ 无明示触发频率 → 反问 A/B(详见 §事件触发频率反问)
- A 每次发生即响 →
enter - B 间隔一段时间才算 →
session+ 长exit_debounce_seconds(按推荐表)+on_exit留空
- A 每次发生即响 →
- 其他一次性判断 →
enter- 瞬时动作(< 10s 完成的可观测动作 / 计数型 N 个 N 次离散动作 / 喝水 / 咳嗽 / 按门铃 / 仰卧起坐)
- 关设备(关灯 / 关空调 / 关窗帘 等单向"关 X"动作;含"X 无人后关 X"持续条件触发)
- 持续行为 + 一次性通知(写作业的时候告诉我 / 久坐 N 分钟提醒 / 看电视 N 分钟提醒 等;行为持续但响应是一次性 desc 通知)
- 触发条件含持续时长 → 用
duration_seconds表达,不改 direction
Rule.condition.query
condition.query 是「判定 X 在发生」的视觉命题——主语 + 谓语,主语类型由命题语义决定(person / object / scene)。
- 只用视觉常识写命题,不读 family profile 习惯描述;profile 仅用于「主语是具体人物时」的 role/name 取值(见下)
- 必须是当下视觉信号的具体描述,禁止抽象动作词、禁止时间语义
- 不含字面 AND/OR 信号词
- 不含断言式措辞("检测到 / 识别到 / 已..." 等)
- 不含时长修饰(持续/累计 N 分钟 → 走 duration_seconds 或独立时长字段)
- 不含 SKILL 内部判据词
- 瞬时进入事件按摄像头视角填:视角覆盖关键动作 → 含动作的命题;视角不覆盖 → 退化为存在态命题(详见感知视角反问段)
主语取值规则(按命题语义分支):
| 命题语义 | 主语取值 |
|---|---|
| 人在做某事(具体姓名 / 家庭关系称谓 / 类别词如宝宝/老人 / 第一人称) | 跑 miloco-cli person list 解析(见下) |
| 多个具体人物明示列举("妈妈和爸爸" 等) | 见下方「多主语拆分」 |
| 抽象集合(家人 / 家庭成员 / 家里人 / 所有家人 / 全家) | 已注册成员 + 触发装配提示。禁展开为当前 person list 的具体成员 |
| 有人 / 任何人 / 谁 | 任何人 + 触发装配提示 |
| 陌生人 / 外人 / 非家人 | 陌生人(命题反例排除必含"不含家庭成员") |
| 物体异常(烟雾 / 火焰 / 积水 / ...) | 物体名词,取自用户原话 |
| 场景状态(无人 / 明暗 / ...) | 画面 或具体房间名 |
| 句中无明示主体("起床开窗帘" / "久坐提醒" 等) | 用户 + 触发装配提示 |
person list 解析(主语是具体人物 / 第一人称 共用,使用 §前置检查 已拿的列表):
- 0 命中 → 反问 A. 注册再建 / B. 退化兜底(按用户原话语义从主语取值规则挑合适的)
- 1 命中 → role 非空填 role;role 为空填 name(第一人称额外触发装配提示)
- ≥2 命中 → 反问 A. 选哪位(第一人称表述为"选哪位是自己")/ B. 退化兜底
多主语拆分(一条 query 只能写一个主语):按本命题的 §Rule.direction 取值分支。
| direction | 装配 |
|---|---|
session |
拆 N 个 task(session 必须独占 task),各自独立走全套判据 |
enter · N 人动作相同 |
N 条 enter rule 挂同一个 task,每条主语 = 一位 role/name;动作装在 task 上(见 §动作装在哪) |
enter · N 人动作不同 |
拆 N 个 task(一个 task 只有一份 on_enter 动作) |
动作类命题(含动作姿态的 query)必含三段。装 --condition 前先按三段拆解(- 动作姿态:... / - 关键物体:... / - 反例排除:...),对每个具象词标注来源「用户原话 / profile 习惯描述 / 视觉常识」,profile 习惯描述 → 删词。再按结构模板机械串接三段为 query 字符串装入 --condition:<主语><动作姿态><关键物体>;不含<反例1>,不含<反例2>[...]。串接规则:三段所有具象词必须 1:1 保留到 query 字符串,禁止简化、抽象化或省略下位词;串接后 query 字符串必须包含拆解出的所有具象词字面字符串。query 字符串必含分号 + ≥ 2 个「不含」前缀,缺任一视为未完成;关键物体段使用近邻易混结构时缺下位词或上位类别视为未完成:
颗粒度总则:每段提供视觉模型独立判别正例的最低必要信息,避免过粗(单动词 / 抽象集合)和过细(精确角度 / 品牌 / 型号)。
- 动作姿态:必含两类视觉特征——①身体部位与物体/环境的接触点关系,②动作完成时的姿态/状态变化描述。两类都要写,缺一不可;单动词(仅写抽象动作名)不算动作姿态描述
- 关键物体:动作涉及的物体(动作无固定物体的命题,如徒手运动,跳过本段)。类别单一明确(如"手机"/"电脑")直接写下位词;类别有近邻易混(如饮品容器、健身器械)用「<下位>/<下位>(/...)等<上位类别>」结构(≥ 2 个下位 + 上位类别);不含从 profile 习惯描述里读到的个体偏好
- 反例排除:≥ 2 组视觉极易混淆的相似情况,用具体物体/动作名词替代抽象否定。反例必须独立于正例成立——禁止以正例的反向、否定、缺失形式作为反例。每条反例必须与正例视觉判别互斥——同一帧只能是其中之一。装入前答一问:「这条反例和正例放在同一帧里,能不能并存?」能并存 → 属穿插行为,删该反例
Rule.action
每方向单独判(session 下 ENTERED / EXITED 独立选):
| 动作性质 | 装配 | fire 时谁执行 |
|---|---|---|
| 能 100% 写死(开关固定设备、播固定文本(音箱)) | action JSON | rule engine 直接执行 |
| 启动已有米家场景(用户原话是场景名或组合模式名) | action JSON(iid 固定 scene) |
rule engine 直接触发 |
| 需按运行时状态/上下文决定 | desc 文案(业务意图) | fire-agent 独立 turn 推理执行 |
按顺序命中即停:
- 涉及 Record 读写 → 响应方向 desc 首句必含 record 写操作动词短语:
- progress 用「计数加一」/「+1」/「<次数>加一」
- event 用「事件追加:<事件描述>」
- duration on_enter 用「开始计时」/「记录起点」/「记录计时起点」;on_exit 用「结束计时」/「记录终点」
- 激活型动作的 on_exit + 用户没明确说退出动作 → desc(默认)
- 动作内容要按上下文决策 → desc
- 动作是启动已有场景 / 组合模式(场景名,或"回家 / 观影 / 离家模式"这类一句话覆盖多设备的说法)→ 跑
scene list找同名:命中 → action JSON(scene 形态,见 §动作场景);0 命中 → 往下走第 5 条 - 动作是固定调一个 handler(开灯/调温/拉帘 等设备控制类)→ action JSON
同一个槽只能全 action JSON 或全 desc。 一个方向做多件事(触发场景 + 播报)→ 全部写成 action JSON(--action 重复传);其中任一件需按上下文决策 → 整槽退化成 desc。
通知类默认 desc;用户原话含引号台词且通道音箱 → action JSON(见例 10)。达标触发的通知按 §达标通知机制 三选一定位置。
desc 写法:业务语义,不贴 CLI 命令字面。文案以客观陈述事件为准,不含「检测到/识别到/感知到/察觉到」等系统视角词。通知类 desc 句式以「使用<通道>通知:<内容>」开头,<通道>取 §通道反问 A/B/C 三选项之一。desc 内禁用 {...} / ${...} / %s 等占位符语法。
Record 回写 desc 自检
涉及 Record 读写的 desc 首句必含动词短语:
| record kind | 响应方向 | 首句动词短语 |
|---|---|---|
| progress | action | 「计数加一」/「+1」/「<次数>加一」 |
| event | action | 「事件追加:<事件描述>」 |
| duration | on_enter | 「开始计时」/「记录起点」/「记录计时起点」 |
| duration | on_exit | 「结束计时」/「记录终点」 |
| duration | on_target | 业务通知文案 |
desc 结构:<首句动词短语>[;<业务通知文案>]。
on_exit 留空条件(不填任何 flag):
- 用户明确说"离开不动它"
- 进入动作是一次性 desc(TTS 播报 / 一次性通知 / 欢迎语)
达标通知机制
按业务语义对照:
| 业务语义 | 触发字段 | 通知装配位置 |
|---|---|---|
| 连续观测达标 | rule.duration_seconds |
action-desc(enter)/ on-enter-desc(session) |
| 跨次累计达标 | record.target_minutes(duration kind) |
on-target-desc(文案为抽象业务语义) |
| 计数达标 | record.target(progress kind) |
action-desc 末段含达标判断 |
| 跨次累计达标 + 退出复提醒 | 用户原话明示每次退出复提醒语义 | on-exit-desc 附加条件通知 |
session + duration record 三 desc 分工:
on-enter-desc仅放计时起点动词短语on-exit-desc仅放计时终点动词短语;用户原话明示每次退出复提醒语义时附加条件通知(按 §Rule.action 通知 desc 句式)on-target-desc仅放业务通知文案
on-target-desc 非空 → 必同时配 duration record + target_minutes,且必按 task → record → rule 顺序装配。
Rule.duration_seconds
含义:触发条件需要持续 N 秒才算成立。direction 无关修饰符(三个方向都可配)。
单位:CLI --duration-seconds 收秒整数。用户原话 N 分钟 → 装 N×60;N 小时 → 装 N×3600。同任务内 record.target_minutes 字段按分钟传,两者不混用。
单次时长跟踪场景:duration_seconds 直接表达用户业务时长(CLI 上限 86400 = 24h)。业务时长 > 24h 拒建,回话告知用户改用跨次累计 record(Record.kind=duration)。装 > 12h(43200s)→ 触发装配提示(内容:"本 rule 跟踪时长较长,若 Miloco 服务期间重启,计时窗口会清零重新累计")。
跨次累计场景(Record.duration):duration_seconds 退化为 rule 层姿态稳定窗(推荐值见下方推荐表),业务时长由 record.target_minutes 表达。
何时配:
- 人身安全/紧急 → 禁配
- 瞬时存在态 / 瞬时动作(< 10s,如喝水 / 咳嗽 / 按门铃 / 仰卧起坐 / 计数型离散动作)→ 不配
- 触发条件含"持续/连续 N 分钟"或语义上需要持续观测才能稳定判断 → 必配
推荐值(按动作类别取,不是字面词表):
| 动作类别 | 推荐 |
|---|---|
| 一般简单姿态动作 | 30 |
| 视线类前置确认 | 45 |
| 久坐/久站/久躺类前置确认 | 60 |
| 屏幕/读写沉浸类前置确认 | 90 |
| 运动/健身/锻炼类前置确认 | 180 |
| 睡眠/专注做事类前置确认 | 180 |
| 拿不准 | 60 |
取值流程:
- 先按上述「何时配」判定:命中 1 / 2 条 → 不传
--duration-seconds,跳过此项;命中第 3 条 → 继续取值 - 用户原话明示具体值 → 按用户值装;偏离推荐值较多 → 触发装配提示(中性陈述,不阻装、不劝改)
- 用户未明示 → 按上表推荐值装 → 触发装配提示
Rule.exit_debounce_seconds
session 防边沿抖动 / 防重复触发;enter 与 exit 不配(duration_seconds 已提供抖动保护)。
何时必配(session):
- on_exit 装了动作(desc 或 action JSON)
- 瞬时进入事件(到达/进入/到家 等)
| 场景 | 推荐 |
|---|---|
| session 双向边沿去重(默认) | 60 |
| session + duration record · 离散使用型(玩手机/看电视/看书 等) | 60 |
| session + duration record · 间断持续型(健身/做饭/打游戏/学习 等) | 180 |
| 瞬时事件防重复 / 持续行为入态 · 长窗(默认) | 1800 |
| 瞬时事件防重复 / 持续行为入态 · 长窗(用户指定 N 分钟) | N × 60(封顶 3600) |
取值流程:
- 用户原话明示具体值 → 按用户值装;偏离推荐值较多 → 触发装配提示(中性陈述,不阻装、不劝改)
- 用户未明示 → 按上表推荐值装 → 触发装配提示
Schedule=Y 时填 Schedule
Schedule.message:写业务意图,fire-agent fire 时独立 turn 推理执行;周期触发的唤醒次数 ≠ 提醒次数。
Schedule.触发器
| 类型 | 信号 |
|---|---|
at |
单一未来时刻 |
every |
固定间隔("每 N 分钟" / "每 N 小时" 等明示间隔但无具体时点的措辞) |
cron |
时钟模式 / 周期重复(具体时点 / 周中某天 / 工作日 / 周末) |
Schedule.时区(强制)
绝不创建不带家庭时区的 cron 定时任务。 openclaw 对未设 tz 的 cron 按宿主机时区求值,宿主机时区 ≠ 家庭时区时整点全偏。家庭时区(IANA 名,如 Asia/Shanghai)取自注入的「## 时间与时区」块,下方 <家庭时区> 一律替换成该真实 IANA 名。
- cron:时区是 cron tool
schedule对象里独立的tz字段,与表达式分开传:schedule={kind:"cron", expr:"0 21 * * *", tz:"<家庭时区>"}。expr只放裸 5 段 cron 表达式——绝不把时区写进 expr:cron list展示的0 21 * * * @ Asia/Shanghai是显示格式、不是合法输入,塞进 expr 会直接创建失败。 - every:固定间隔(everyMs),与时区无关,无需 tz。
- at:用
miloco-cli time-compute产出的 ISO 已带时区偏移(如+08:00),本身无歧义,直接传即可。
Schedule.频率默认
按顺序判,命中即停:
- 用户明示时点/频率 → 照用
- Record=Y(progress) 且 target > 0 → 多时点提醒:
- 时点数:
N = min(target_per_day, 6)取整;跨周期先归一target_per_day = target / 周期天数(day=1, week=7, month=30) - 时段:限清醒时段 06:00-22:00
- 时点分布按任务自然执行场景定(服药对齐三餐、晨练放早晨、课后活动放放学时段等);无强场景信号 → N 个时点均分 10:00-20:00
- 周期:window=day →
* * *;用户明示工作日/周末 →* * 1-5/* * 0,6 - 装配:N 个时点合并装单 cron
expr="0 H1,H2,...,Hn * * *"+tz="<家庭时区>",通过miloco-cli cron add --task-id <id> --kind cron --cron-expr ... --tz ...一步绑到 task(backend 内部走cron.task_idFK CASCADE,tz 必带,见 §Schedule.时区)
- 时点数:
- 其他(无 target / Record=event / Record=duration / 无 Record) → 每周期单时点:day →
0 9 * * *;week →0 9 * * 1;month →0 9 1 * *(均须配tz="<家庭时区>")
命中默认路径 → 触发装配提示(按 §装配提示元规则)
Record=Y 时填 Record
Record.kind
| kind | 用户语义 |
|---|---|
progress |
计数达标 + 明确目标正整数 |
duration(含阈值) |
累计/连续超过 N 小时/分钟,含明确时长阈值 |
duration(无阈值) |
记录/统计/追踪 X 时长,无目标数字 |
event |
累积事件流,记录/统计/跟踪/数 + 行为,无目标数字 |
歧义裁决(按顺序判):
- 记录对象是时长/多久("记录每次时长 / 看了多久 / 健身用了多久 / 记下时长" 等)→
duration(含阈值时填target_minutes;无阈值省略字段,禁写 0/1 占位) - 持续行为态 + "记录每次/记下来/统计"(看电视/玩游戏/写作业/久坐/看屏幕 等 ≥ 分钟级活动,无论是否含时长阈值) →
duration→ 触发装配提示 - 记录对象是瞬时动作(< 10s)的次数/事件流 →
event - 含明确正整数目标(喝 8 杯 / 走 10000 步 / 吃 3 次药 等)→
progress - 含计数但 target 缺失("几杯/多少次"问句)→ 强制
event,禁target=0/1
Record.task_type
| task_type | 判据 |
|---|---|
recurring |
含周期/习惯化语义 |
oneshot |
kind=progress/duration + 含限时窗口语义 |
longterm |
kind=event + 任意时间语义或无时间语义 |
特例(时长门槛模板):
- 单次连续追踪 →
longterm - 跨 session 累计追踪 →
recurring
Rule=Y + 动作含通知类语义 时填通道
触发条件:用户用了含糊动作词且未明示通道。
含糊动作词分两类:
- 通知类:提醒 / 告诉 / 通知 / 告知 / 喊我 / 让我知道 等
- 对人输出语音/文本类:夸 / 鼓励 / 表扬 / 安慰 / 欢迎 / 招呼 / 说一句 等
未明示通道 = 用户没说"音箱/喇叭/播报/朗读/手机/推送/消息"等任何通道线索。
必反问 A/B/C,等用户答复后装 desc:
- A. 使用音箱播报通知
- B. 使用手机推送通知
- C. 使用 AI 对话通知
跳过条件:user_intent 已明示通道 → desc 按用户指定通道写,不反问。
Rule=Y 时填触发歧义反问
用户描述的感知触发存在以下不明确 → 先反问再装配,不默认猜。
事件触发频率(短窗 / 长窗)
由 Rule.direction 判据第 5 条触发。按用户原话信号判:
- 明示即时("每次 / 一 X 就"等强调每发生一次都响)→ 短窗 60s,不反问
- 明示场景窗口(暗示间隔一段时间后才算,如"下班/外出/出门后")→ 长
exit_debounce_seconds(按推荐表),不反问 - 无修饰 → 反问 A/B 二选一
反问模板:
你说的"<原词>",我想确认下:
- A. 每次发生即触发(短时间内重复发生也响)
- B. 间隔一段时间后才算一次(默认 30 分钟以上)
映射:A → enter;B → session + 长 exit_debounce_seconds(按推荐表)+ on_exit 留空。
感知视角
触发条件:触发依赖主体进入或离开摄像头视野(vs 主体已在视野内的姿态/状态变化)。
跳过本反问:
- 持续行为态触发(久坐/写作业/看电视/玩手机 等单帧可判姿态)
- 音频/纯听感触发(咳嗽 / 哭 / 呼救 / 按门铃 等)
- 关设备 / 复位类一次性动作
按以下顺序判,命中即停:
- 用户原话明示视角(描述了摄像头位置或视角能力)→ 按字面装,不反问不提示
- 用户原话有房间名 + 房间名提供强常识默认(某类房间位置的摄像头按常识应当覆盖该视角)→ 按常识默认装 + 触发装配提示(让用户验证)
- 其他(无房间名 / 房间名无强常识默认)+ 触发依赖关键视角:
- 落到单台摄像头(房间名无强常识默认,或已锁
--source)→ 反问 A/B 二选一 - 无单一摄像头对象("家里 / 全屋"、不传
--source)→ 反问无对象 → 直接退化为存在态命题
- 落到单台摄像头(房间名无强常识默认,或已锁
反问模板:
"<房间名>" 那个摄像头能不能 <关键视角描述>?
- A. 看得到
- B. 看不到
<关键视角描述> 指向主体可见性切换发生的具体局部位置(出入口/家具点位/区域边界),不是房间整体。
按答复(或常识默认)选 condition.query 形式:视角覆盖 → 含触发动作的命题;视角不覆盖 → 退化为存在态命题。<X> 按命题主语决策替换。
路径 2 走常识默认 → 触发装配提示。
Rule=Y + 任一方向 action JSON 时填设备
感知设备(--source,可选)
消费 §前置检查 §感知设备清单 拿到的 N:
- 已锁
source_did[](非空)→ 直接当--source - N=0 → 反问 A. 开启摄像头感知再建 / B. 取消
- N≥1 → 按用户原话二分:
- 未指定房间 / 摄像头(含"家里" / "全屋")→ 不传
--source+ 触发装配提示(direction 无关,session 持续姿态触发不例外) - 指定房间 / 摄像头名 → 在 N 内按
name/room_name模糊匹配:- 命中 1 → 用该 DID
- 命中 ≥2 → 全部传(
--source <did1> --source <did2> ...)+ 触发装配提示 - 命中 0 → 反问 A. 改名字 / B. fallback 全屋
- 未指定房间 / 摄像头(含"家里" / "全屋")→ 不传
动作设备(--action JSON 的 did)
优先看 system context ## 设备目录 段。缺失或未覆盖 → device list --room + device spec <did> 拿 iid。iid 格式为 prop.<siid>.<piid>(属性直控)或 action.<siid>.<aiid>(method call,如 TTS),从 device spec 输出行首列直接复制真实数字。
cooldown_minutes 取值(idempotent:false 必配):紧急报警 1-5 / 日常提醒 5-30 / 欢迎播报 30-60;类别内下限=低频触发,上限=高频重复触发。
动作场景(--action JSON 的 scene 形态)
场景不在 ## 设备目录 里。跑 miloco-cli scene list --pretty 拿 scene_name → scene_id:
- 命中 1 → 用该
scene_id - 命中 ≥2 → 列出让用户选
- 命中 0 → 按具体设备动作拆(回 §Rule.action 判据第 5 条)
形态:{"did":"<scene_id>","iid":"scene","idempotent":false,"cooldown_minutes":<N>}
did放scene_id;iid是字面量scene,不是prop./action.idempotent必须false;cooldown_minutes必须 ≥ 1,取值同 §动作设备
目标设备消歧(TTS/语音播报/单点提示音/警报音/局部光效)+ 候选 ≥ 2:
- 用户原话含房间词 → 按房间词匹配,不反问
- 无房间词 → 默认装第一候选 + 触发装配提示(告知用户实际装到哪台,想换告诉我)
第三层 · 装配映射
[输出协议 reload]:本章节及后续所有装配工具调用期间,过程描述走 thinking,user-facing 仅在终态与反问输出。
按维度取值映射到 CLI 命令。多命令按顺序执行。
--name 必带 [<task_id>] 前缀(适用 rule create / cron job;task_id 规范见 §前置检查)。
装配执行规则
单任务串行装配:多 task 装配按 task 维度串行——上一个 task 装完所有相关命令、CLI 返回成功后,才能启动下一个 task。禁止同 model turn 中并发多 task 的 CLI 命令。
CLI 报错后重审全命令:任何 CLI 报错后,重装该命令前必须对所有参数逐项重新检查,不允许仅针对报错字段补丁式重发。
CLI 命令
| 维度 | CLI |
|---|---|
| 创建 task | miloco-cli task create --task-id <id> --description "<描述>"(permanent 是默认值不传;temporary 加 --lifecycle temporary,到期时刻确定再加 --expires-at <ISO>) |
| Rule=Y | miloco-cli rule create --task-id <id> <rule-flags> |
| Schedule=Y | miloco-cli cron add --task-id <id> --kind cron --name "[<id>] <描述>" --cron-expr "<expr>" --tz "<家庭时区>" --message "<业务意图>"(backend 强制 dispatch_owner=internal,经 cron.task_id FK 直接绑 task,无需 link;cron 类必带 tz,见 §Schedule.时区) |
| Record=Y | miloco-cli task record init <id> --kind <progress/duration/event> --content '<JSON>' |
| Lifecycle=temporary | miloco-cli cron add --task-id <id> --kind at --at-iso <expires_at> --name "[<id>] 到期销毁" --message "到期销毁 task <id>" --max-delay 0(<expires_at> 由 miloco-cli time-compute --anchor <kind> 直出裸 ISO,--max-delay 0 让 termination 无限补跑) |
动作装在哪
按同一个槽有几条 rule 想写选装法:
| task 形态 | 装法 |
|---|---|
| 单条 rule(enter / session) | rule 侧动作 flag |
| 一条 enter + 一条 exit | 各自 rule 侧动作 flag;出方向不做事 → 该条不传动作 flag |
| 同方向 ≥2 条 | miloco-cli task set-actions,rule 侧不传动作 flag |
task set-actions <task_id>:
| 槽 | 设备直控 | Agent 文案 |
|---|---|---|
| 进入 | --on-enter-action '<JSON>'(可重复) |
--on-enter-desc "<desc>" |
| 退出 | --on-exit-action '<JSON>' |
--on-exit-desc "<desc>" |
同槽 action 与 desc 互斥;清空用 --clear <槽名>。达标动作按 §达标通知机制 装。
同方向 ≥2 条的装配顺序:task create → task set-actions → 逐条 rule create。
Rule flag 映射
| 维度取值 | flag |
|---|---|
| name(必填) | --name "[<task_id>] <场景描述>" |
direction=enter |
--direction enter |
direction=exit |
--direction exit |
direction=session |
--direction session |
condition.query |
--condition "<query>" |
| enter / exit + action JSON | --action '<JSON>'(落哪个槽由 direction 定,不用 --on-exit-*) |
| enter / exit + desc | --action-desc "<desc>" |
| 动作装在 task 上 / 出方向不做事 | 不传动作 flag |
| session + on_enter action JSON | --on-enter-action '<JSON>' |
| session + on_enter desc | --on-enter-desc "<desc>" |
| session + on_exit action JSON | --on-exit-action '<JSON>' |
| session + on_exit desc | --on-exit-desc "<desc>" |
| on_exit 留空 | 不传 on_exit flag |
duration_seconds=N |
--duration-seconds N |
exit_debounce_seconds=N |
--exit-debounce-seconds N |
感知设备=<DID> |
--source <DID> |
| 感知设备=广播 | 不传 --source |
Record content JSON
| kind | content |
|---|---|
| progress(temporary) | {"target":N,"unit":"<次/杯/步>","window":"<day/week>","expires_at":"<ISO>"} |
| progress(recurring) | {"target":N,"unit":"<次/杯/步>","window":"<day/week>","recurring_pattern":{"window":"<day/week>"}} |
| duration 含阈值(temporary) | {"target_minutes":N,"expires_at":"<ISO>"} |
| duration 含阈值(recurring) | {"target_minutes":N,"recurring_pattern":{"window":"<day/week>"}} |
| duration 无阈值(longterm) | {"recurring_pattern":{"window":"longterm"}} |
| duration 无阈值(recurring) | {"recurring_pattern":{"window":"<day/week>"}} |
| event | {} |
装填规则:
- progress 顶层
window必填,决定 period 边界 - recurring 任务必填
recurring_pattern
装配失败回滚
多步执行(task create → record init → cron add → rule create)任一步失败 → 调 miloco-cli task delete <task_id> --reason abandoned 回滚。
- backend 一笔事务同步清 task / rule / cron / record(FK CASCADE),agent 不重复清
- 跑响应里的
agent_pending(仅含 cron kind),按顺序逐条cron remove - 本 turn 已建但尚未绑 task(
cron.task_id=NULL)的 cron → 按本 turn 已知的cron_id单独cron remove - task create 本身失败 → 无需调 delete
装配示例
example 展示判据→取值的推导过程,具体取值仅适用其原话场景。每个用户原话独立按 §第一层 / §第二层 判据推。
cron / at / termination 均由 miloco-cli cron add 单步装配:--task-id 直绑 task(backend 强制 dispatch_owner=internal,经 cron.task_id FK CASCADE),cron 类必带 --tz "<家庭时区>",at 类用 --at-iso 传带偏移 ISO(与 time-compute 输出、record expires_at 同格式)。
例 1
用户:"家里有人摔倒就报警"
推理:「摔倒」人身安全异常 → §Rule?=Y · §Rule.direction(人身安全)=enter;无累计/计数 → §Record?=N;现实事件触发 → §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;「报警」通知类 → §Rule.action=desc · §通道反问 触发(必反问 A/B/C);§Rule.感知设备 N≥1 + 「家里」未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=enter · action=desc
通道反问 → A 音箱
miloco-cli task create --task-id fall_alert --description "家里有人摔倒报警"
miloco-cli rule create --task-id fall_alert \
--name "[fall_alert] 摔倒报警" \
--direction enter \
--condition "任何人身体突然失去平衡倒地,呈仰面/侧卧/俯卧姿态躺在地面;不含主动卧倒、躺床睡觉等休息姿态,不含做仰卧起坐、瑜伽下犬式/平板支撑等贴地运动" \
--action-desc "使用音箱播报通知:有人摔倒了,立即报警"
例 2
用户:"起床后自动开窗帘"
推理:「起床」人体可观测动作 → §Rule?=Y;「开窗帘」"开 X" 动作 → §Rule.direction(激活持续设备)=session · 默认补 on_exit 复位;现实事件触发 → §Schedule?=N;无累计/计数 → §Record?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;session 默认 → §Rule.exit_debounce_seconds=60;§Rule.感知设备 N≥1 + 未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=session · 默认补 on_exit 复位 · exit_debounce_seconds=60
miloco-cli task create --task-id wakeup_curtain --description "起床开窗帘"
miloco-cli rule create --task-id wakeup_curtain \
--name "[wakeup_curtain] 起床开窗帘" \
--direction session \
--condition "用户从卧床躺姿切换到坐起或离床站立姿态;不含翻身、伸懒腰等床上小动作,不含坐起喝水后躺回等短暂起身动作" \
--exit-debounce-seconds 60 \
--on-enter-action '{"did":"<窗帘 DID>","iid":"prop.<siid>.<piid>","value":true,"idempotent":true}' \
--on-exit-desc "检查开窗帘是否仍有效,若已结束则复位"
例 3
用户:"明天 9 点提醒吃药"
推理:「明天 9 点」单一未来具体时点 → §Schedule?=Y(at);绝对一次性时刻信号 → §Lifecycle(绝对一次性时刻)=temporary;纯时间触发 → §Rule?=N · §Record?=N
Rule?=N · Schedule?=Y(at) · Record?=N · Lifecycle=temporary
AT_ISO=$(miloco-cli time-compute --anchor '{"kind":"tomorrow_at","time":"09:00:00"}')
miloco-cli task create --task-id med_tomorrow_9am --description "明天 9 点提醒吃药" --lifecycle temporary --expires-at "$AT_ISO"
miloco-cli cron add --task-id med_tomorrow_9am --kind at --at-iso "$AT_ISO" \
--name "[med_tomorrow_9am] 吃药提醒" --message "提醒用户吃药"
例 4
用户:"今天喝够 8 杯水提醒"
推理:「喝水」人体可观测动作 → §Rule?=Y;「8 杯」计数达标 → §Record?=Y · §Record.kind=progress · target=8 · window=day;「今天」时间窗信号 → §Lifecycle(时间窗信号)=temporary · expires_at=今日 24:00;瞬时动作 + 计数型 → §Rule.direction(瞬时动作)=enter;progress + target>0 → §Schedule?=Y(cron) · §Schedule.频率默认(progress+target>0 多时点) · N=min(8,6)=6 时点 · 无强场景均分 10-20;「提醒」通知类 → §Rule.action=desc · §通道反问 触发(必反问 A/B/C);句中无主体 → 主语=用户 + 触发装配提示;§Rule.感知设备 N≥1 + 未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=Y(cron×6 默认+装配提示) · Record?=Y · Lifecycle=temporary
Rule.direction=enter · Record.kind=progress · target=8 · window=day
通道反问 → B 手机
EXPIRES_AT=$(miloco-cli time-compute --anchor '{"kind":"end_of_day"}')
miloco-cli task create --task-id drink_8_today --description "今天喝够 8 杯水" --lifecycle temporary --expires-at "$EXPIRES_AT"
miloco-cli rule create --task-id drink_8_today \
--name "[drink_8_today] 喝水计数" \
--direction enter \
--condition "用户手持水杯/水瓶/茶杯/保温杯等饮品容器,杯口贴近嘴边并伴随仰头吞咽动作;不含手持牙刷/麦克风/纸盒/食物/餐盒等非饮品物品,不含举杯凑近鼻子闻、吹凉、展示等动作" \
--action-desc "喝水次数加一;首次达标时使用手机推送通知:恭喜达标"
miloco-cli task record init drink_8_today \
--kind progress \
--content "{\"target\":8,\"unit\":\"杯\",\"window\":\"day\",\"expires_at\":\"$EXPIRES_AT\"}"
miloco-cli cron add --task-id drink_8_today --kind cron \
--name "[drink_8_today] 喝水提醒" \
--cron-expr "0 10,12,14,16,18,20 * * *" --tz "<家庭时区>" \
--message "调 miloco-cli task record get drink_8_today,按 derived.remaining 决定是否催"
miloco-cli cron add --task-id drink_8_today --kind at --at-iso "$EXPIRES_AT" \
--name "[drink_8_today] 到期销毁" --message "到期销毁 task drink_8_today" --max-delay 0
例 5
用户:"每天累计玩手机超 1 小时提醒"
推理:「玩手机」人体可观测行为 → §Rule?=Y;「累计 1 小时」时长累计 → §Record?=Y · §Record.kind=duration · target_minutes=60;「每天累计 N 小时」→ §Schedule?=N;「每天」周期信号 → §Lifecycle(周期信号)=permanent · §Record.task_type=recurring · recurring_pattern={"window":"day"};跨次累计 record(duration kind) → §Rule.direction(跨次累计 record)=session;看屏幕姿态稳定窗 → §Rule.duration_seconds=90;session + duration record 离散使用型 → §Rule.exit_debounce_seconds=60;「提醒」通知类 → §Rule.action=desc · §通道反问 触发(必反问 A/B/C) · 跨次累计达标 → §达标通知机制 → on-target-desc;§Rule.感知设备 N≥1 + 未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=N · Record?=Y · Lifecycle=permanent
Rule.direction=session · duration_seconds=90 · exit_debounce_seconds=60
通道反问 → B 手机
Record.kind=duration · target_minutes=60 · task_type=recurring · window=day
miloco-cli task create --task-id phone_time_daily --description "每天累计玩手机超 1 小时提醒"
miloco-cli task record init phone_time_daily \
--kind duration \
--content '{"target_minutes":60,"recurring_pattern":{"window":"day"}}'
miloco-cli rule create --task-id phone_time_daily \
--name "[phone_time_daily] 玩手机累计时长" \
--direction session \
--condition "用户手持手机,屏幕亮起朝向脸部,目光低头注视屏幕;不含手持平板/书本/遥控器,不含手机贴耳通话" \
--duration-seconds 90 \
--exit-debounce-seconds 60 \
--on-enter-desc "记录计时起点" \
--on-exit-desc "结束计时" \
--on-target-desc "使用手机推送通知:今日累计玩手机已达目标时长,休息一下"
例 6
用户:"客厅没人就关灯"
推理:「客厅没人」环境状态变化 → §Rule?=Y;无累计/计数 → §Record?=N;现实事件触发 → §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;"关 X" 动作 → §Rule.direction(关 X 动作)=enter;持续条件「没人」需稳定观测 → §Rule.duration_seconds=300;「客厅」指定房间 → §Rule.感知设备 N 内按 room_name 匹配命中 1 台 → --source <客厅摄像头 DID>;「关灯」固定 handler → §Rule.action=action JSON
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=enter · duration_seconds=300 · action=action JSON
miloco-cli task create --task-id living_room_off --description "客厅没人关灯"
miloco-cli rule create --task-id living_room_off \
--name "[living_room_off] 客厅无人关灯" \
--direction enter \
--condition "画面中无人" \
--source <客厅摄像头 DID> \
--duration-seconds 300 \
--action '{"did":"<客厅灯 DID>","iid":"prop.<siid>.<piid>","value":false,"idempotent":true}'
例 7
用户:"久坐 30 分钟提醒一下"
推理:「坐」人体可观测姿态 → §Rule?=Y;「30 分钟」时长阈值 + 无跨次词 → §Record?=N · 单次连续 + 触发装配提示;现实事件触发 → §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;持续行为 + 一次性通知 → §Rule.direction(持续行为+一次性通知)=enter;30 分钟 → §Rule.duration_seconds=1800;「提醒」通知类 → §Rule.action=desc · §通道反问 触发(必反问 A/B/C);句中无主体 → 主语=用户 + 触发装配提示;§Rule.感知设备 N≥1 + 未指定房间 → 不传 --source + 触发装配提示
Rule?=Y(触发装配提示) · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=enter · duration_seconds=1800 · action=desc
通道反问 → B 手机
miloco-cli task create --task-id sit_30min --description "久坐 30 分钟提醒"
miloco-cli rule create --task-id sit_30min \
--name "[sit_30min] 久坐 30 分钟提醒" \
--direction enter \
--condition "用户臀部接触沙发/座椅,腰背靠近椅背或半弯曲,保持坐姿;不含蹲在地面双膝弯曲但臀部未接触座面,不含半靠扶手/桌沿臀部悬空的站姿,不含跪地或跪坐臀部触脚跟未接触座面" \
--duration-seconds 1800 \
--action-desc "使用手机推送通知:已久坐 30 分钟,建议起身活动"
例 8
用户:"妈妈回家就播放欢迎曲"
推理:「妈妈」具体人物 → person list 1 命中 → role/name;「回家」到达/进入类瞬时事件 → §Rule?=Y · §Rule.direction(到达/进入瞬时事件)=session · §Rule.exit_debounce_seconds=1800 · on_exit 留空;「欢迎曲」一次性 TTS → §Record?=N · §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;玄关无明示视角 → §感知视角 常识默认 + 装配提示;「玄关」隐含房间 → §Rule.感知设备 N 内按 room_name 匹配命中 1 台 → --source <玄关摄像头 DID>
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=session(到达事件)· on_exit 留空 · exit_debounce_seconds=1800
命题主语="妈妈"(跑 person list 唯一命中)
感知视角:玄关/门口走 agent 常识默认 + 装配提示
miloco-cli task create --task-id mom_arrival --description "妈妈回家欢迎播报"
miloco-cli rule create --task-id mom_arrival \
--name "[mom_arrival] 妈妈回家欢迎" \
--direction session \
--condition "妈妈伴随开门动作从户外走入玄关画面,正面或侧面对镜头;不含路过门口但未开门走入的短暂停留,不含其他家庭成员或来访客人进门" \
--source <玄关摄像头 DID> \
--exit-debounce-seconds 1800 \
--on-enter-desc "使用音箱播报通知:向妈妈说一段欢迎回家的话"
例 9
用户:"提醒我每天喝 8 杯水"
推理:「我」第一人称 → person list 1 命中 → role 非空填 role · 触发装配提示;「喝水」人体可观测动作 → §Rule?=Y;「8 杯」计数达标 → §Record?=Y · §Record.kind=progress · target=8 · window=day;「每天」周期信号 → §Lifecycle(周期信号)=permanent · recurring_pattern={"window":"day"};瞬时动作 + 计数型 → §Rule.direction(瞬时动作)=enter;progress+target>0 → §Schedule?=Y(cron) · §Schedule.频率默认(progress+target>0 多时点) · N=min(8,6)=6 时点 · 无强场景均分 10-20;「提醒」通知类 → §Rule.action=desc · §通道反问 触发(必反问 A/B/C);§Rule.感知设备 N≥1 + 未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=Y(cron×6 默认+装配提示) · Record?=Y · Lifecycle=permanent
Rule.direction=enter · 命题主语=<当前用户 role>("我" → 跑 person list 1 命中 → role + 装配提示;此例假设命中 role=妈妈)· Record.kind=progress · target=8 · window=day · recurring_pattern={"window":"day"}
通道反问 → B 手机
miloco-cli task create --task-id drink_8_daily --description "妈妈每天喝 8 杯水"
miloco-cli rule create --task-id drink_8_daily \
--name "[drink_8_daily] 喝水计数" \
--direction enter \
--condition "妈妈手持水杯/水瓶/茶杯/保温杯等饮品容器,杯口贴近嘴边并伴随仰头吞咽动作;不含手持牙刷/麦克风/纸盒/食物/餐盒等非饮品物品,不含举杯凑近鼻子闻、吹凉、展示等动作" \
--action-desc "喝水次数加一;首次达标时使用手机推送通知:恭喜达标"
miloco-cli task record init drink_8_daily \
--kind progress \
--content '{"target":8,"unit":"杯","window":"day","recurring_pattern":{"window":"day"}}'
miloco-cli cron add --task-id drink_8_daily --kind cron \
--name "[drink_8_daily] 喝水提醒" \
--cron-expr "0 10,12,14,16,18,20 * * *" --tz "<家庭时区>" \
--message "调 miloco-cli task record get drink_8_daily,按 derived.remaining 决定是否催"
例 10
用户:"家里来陌生人就用音箱说'请注意,有陌生人进入'"
推理:「陌生人」瞬时存在态触发 → §Rule?=Y;触发本属 §Rule.direction 判据第 5 条覆盖范围(陌生存在态+反问 A/B),但「进入」+「就」明示每次发生即响 → §事件触发频率(明示即时) → 跳过反问 → §Rule.direction=enter;无累计/计数 → §Record?=N;现实事件触发 → §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;主语=陌生人(存在态命题,反例排除必含"不含家庭成员");「家里」全屋无具体摄像头/房间词 → §感知视角(路径 3 · 无单一摄像头对象,反问无对象) → 退化为存在态命题;用户原话含引号台词「请注意,有陌生人进入」→ §Rule.action=action JSON(TTS 类:iid 走 action.<siid>.<aiid> 从 device spec 输出行首列复制,params 按 spec in_params 列填数组,idempotent:false,cooldown_minutes=5(紧急报警上限,陌生人识别高频重复));音箱候选 ≥ 2 且无房间词 → 默认装第一候选 + 触发装配提示;§Rule.感知设备 N≥1 + 「家里」未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=enter · action=action JSON(TTS 类)· 命题主语=陌生人
miloco-cli task create --task-id stranger_alert --description "陌生人进入提醒"
miloco-cli rule create --task-id stranger_alert \
--name "[stranger_alert] 陌生人进入" \
--direction enter \
--condition "画面中出现非家庭成员的人身影;不含家庭成员,不含快递员/物业人员等短暂停留在门外未入户的访客" \
--action '{"did":"<默认音箱 DID>","iid":"action.<siid>.<aiid>","params":["请注意,有陌生人进入"],"idempotent":false,"cooldown_minutes":5}'
例 11
用户:"我做 V 手势就开客厅灯,挥手就关"
推理:「V 手势」「挥手」人体可观测动作 → §Rule?=Y;进是 V 手势、出是挥手,两个独立观测 → §Rule.direction 第 0 步判「不是」→ 两条 rule 挂同一 task,进的 enter、出的 exit;「我」第一人称 → person list 1 命中 → role + 触发装配提示;无累计/计数 → §Record?=N;现实事件触发 → §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;「开灯」「关灯」固定 handler → §Rule.action=action JSON;一条 enter + 一条 exit → §动作装在哪 = 各自 rule 侧动作 flag;「客厅」指定房间 → §Rule.感知设备 N 内按 room_name 匹配命中 1 台 → --source <客厅摄像头 DID>
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction 第 0 步=不是同一观测 → enter + exit 两条同 task
命题主语=<当前用户 role>("我" → 跑 person list 1 命中 → role + 装配提示;此例假设命中 role=妈妈)
动作装在哪=各自 rule 侧(进出各占一个槽)
miloco-cli task create --task-id living_room_gesture_light --description "妈妈手势控制客厅灯"
miloco-cli rule create --task-id living_room_gesture_light \
--name "[living_room_gesture_light] V 手势开灯" \
--direction enter \
--condition "妈妈单手举至肩部以上,食指与中指伸直分开成 V 形、其余手指收拢,手掌正面朝向镜头;不含五指全张开的挥手或击掌,不含单指指向或握拳" \
--source <客厅摄像头 DID> \
--action '{"did":"<客厅灯 DID>","iid":"prop.<siid>.<piid>","value":true,"idempotent":true}'
miloco-cli rule create --task-id living_room_gesture_light \
--name "[living_room_gesture_light] 挥手关灯" \
--direction exit \
--condition "妈妈单手举至肩部以上,五指张开、手掌正面朝向镜头并左右摆动;不含食指与中指成 V 形的手势,不含举手静止不动或伸手拿取物品" \
--source <客厅摄像头 DID> \
--action '{"did":"<客厅灯 DID>","iid":"prop.<siid>.<piid>","value":false,"idempotent":true}'
例 12
用户:"比耶手势就开启观影模式,并用音箱说'观影模式已就绪'"
推理:「比耶手势」人体可观测动作 → §Rule?=Y;只有进、无退出观测 → §Rule.direction 第 0 步判「是」→ 第 1 步判据 6(瞬时动作)=enter;无累计/计数 → §Record?=N;现实事件触发 → §Schedule?=N;无信号兜底 → §Lifecycle(无信号兜底)=permanent;「观影模式」组合动作 → §Rule.action 判据第 4 条 → 跑 scene list 命中 1 → action JSON(scene 形态);用户原话含引号台词「观影模式已就绪」且明示音箱 → §Rule.action=action JSON(TTS 类)· §通道反问 跳过(已明示通道);同槽只能全 action JSON → 场景与播报两条都装 action JSON(cooldown_minutes=5:日常提醒下限,手势触发低频);句中无明示主体 → 主语=用户 + 触发装配提示;音箱候选 ≥ 2 且无房间词 → 默认装第一候选 + 触发装配提示;§Rule.感知设备 N≥1 + 未指定房间 → 不传 --source + 触发装配提示
Rule?=Y · Schedule?=N · Record?=N · Lifecycle=permanent
Rule.direction=enter · action=action JSON ×2(scene + TTS,同槽不混 desc)· 命题主语=`用户`
scene list 命中 1 → scene_id
miloco-cli task create --task-id movie_mode_gesture --description "比耶手势开启观影模式并播报"
miloco-cli rule create --task-id movie_mode_gesture \
--name "[movie_mode_gesture] 比耶开观影模式" \
--direction enter \
--condition "用户单手抬至胸部以上,食指与中指伸直并拢或微分、其余手指收拢,手背或手心朝向镜头;不含五指张开的挥手,不含单指指向屏幕或握拳" \
--action '{"did":"<观影模式 scene_id>","iid":"scene","idempotent":false,"cooldown_minutes":5}' \
--action '{"did":"<默认音箱 DID>","iid":"action.<siid>.<aiid>","params":["观影模式已就绪"],"idempotent":false,"cooldown_minutes":5}'