当用户说"帮我看/守护某物品"时,用视觉检测持续巡查该物品,发现被拿走时本地语音播报、挥手报警,并通过 channel_reply 向消息平台发送远程报警。
当用户说"帮我看/守护某物品"时,用视觉检测持续巡查该物品,发现被拿走时本地语音播报、挥手报警,并通过 channel_reply 向消息平台发送远程报警。
# 物品守护 — 稳定执行脚本
严格按此逻辑运行,不得额外发挥。
## 一、触发门禁
```text
IF 用户消息包含 ("不用看了" OR "停止守护" OR "结束守护"):
执行 STOP_FLOW
ELIF 用户消息包含 ("帮我看" OR "看着" OR "守护" OR "盯着")
AND 包含具体物品名:
执行 GUARD_FLOW
ELIF 用户消息包含 ("帮我看" OR "看着" OR "守护" OR "盯着")
BUT 不包含具体物品名:
TEXT_REPLY "请告诉我要守护什么物品,比如说帮我看手机"
ELSE:
按正常对话处理
# 不启动、停止或修改守护任务
# 不因为普通对话调用 channel_reply
```
普通对话不能中断正在运行的守护任务。
## 二、允许使用的工具
本脚本只允许使用以下工具或动作:
```text
vop
tts.speak
speaker.play
channel_reply
camera_rgb
TEXT_REPLY
SLEEP
```
`camera_rgb` 仅作为 `vop` 的输入源。
禁止调用 `arm_gesture` 或任何手部动作工具。
## 三、channel_reply 核心规则
`channel_reply` 是一次性事件通知工具,不是循环播报工具。
只允许在以下四种状态事件首次发生时调用:
```text
guard_started
item_missing
guard_completed
guard_stopped
```
每个事件在一次守护任务中最多通知一次。
禁止在以下情况下调用 `channel_reply`:
```text
每次巡查
每次调用 vop.read
物品仍然存在
首次暂时未检测到物品
等待下一次确认
普通对话
重复进入同一状态
守护任务已经结束
没有状态变化
```
所有 `channel_reply` 必须通过下面的统一通知逻辑执行,不得直接在其他位置调用:
```text
FUNCTION SEND_EVENT_ONCE(EVENT_NAME, CONTENT):
IF EVENT_NAME NOT IN notified_events:
notified_events.ADD(EVENT_NAME)
channel_reply(content=CONTENT)
ELSE:
RETURN
```
`TEXT_REPLY`、`tts.speak` 和 `channel_reply` 都只能在对应事件真正发生时执行一次,不得因为循环仍在运行而重复执行。
## 四、物品名称转换
从用户消息中提取物品名,按以下翻译表转换为 `vop` 英文类名:
```text
手机→cell phone
包/背包→backpack
杯子→cup
电脑/笔记本→laptop
书→book
遥控器→remote
伞→umbrella
瓶子→bottle
行李箱→suitcase
剪刀→scissors
钟→clock
花瓶→vase
毛绒熊→teddy bear
香蕉→banana
苹果→apple
披萨→pizza
蛋糕→cake
碗→bowl
刀→knife
叉子→fork
勺子→spoon
鼠标→mouse
键盘→keyboard
电视→tv
椅子→chair
沙发→couch
相机→camera
```
```text
IF 物品在翻译表中:
TARGET = 翻译后的英文类名
ELSE:
TARGET = 用户说的物品名称
# 使用 vop 开放词汇检测
ITEM_CN = 用户说的原始物品名
```
## 五、GUARD_FLOW
### STEP 1:检查现有任务
```text
IF 已经存在 guard_status == "running" 的守护任务:
TEXT_REPLY "当前正在守护" + ITEM_CN + ",请先结束当前守护"
RETURN
```
禁止同时创建多个守护循环。
### STEP 2:解析守护时间
```text
IF 用户明确指定守护时长:
DURATION_SECONDS = 将用户指定的时长转换为秒
DURATION_TEXT = 用户指定的原始时长
ELSE:
DURATION_SECONDS = 120
DURATION_TEXT = "2分钟"
```
### STEP 3:初始化任务状态
每次启动新任务时必须重新初始化:
```text
guard_status = "starting"
miss_count = 0
notified_events = EMPTY_SET
start_time = 当前时间
end_time = start_time + DURATION_SECONDS
```
旧任务留下的 `miss_count`、`notified_events`、`end_time` 或状态不得复用。
### STEP 4:启动检测
必须先配置并启动检测,再宣布守护已经开始:
```text
vop.stop()
vop.set_classes([TARGET])
vop.start()
guard_status = "running"
```
如果用户未指定守护时长:
```text
TEXT_REPLY "好的,我帮你守护" + ITEM_CN + ",默认守护2分钟,拿走的话我会报警"
SEND_EVENT_ONCE(
"guard_started",
"物品守护已开始:" + ITEM_CN + ",默认守护2分钟。"
)
```
如果用户指定了守护时长:
```text
TEXT_REPLY "好的,我帮你守护" + ITEM_CN + ",持续" + DURATION_TEXT + ",拿走的话我会报警"
SEND_EVENT_ONCE(
"guard_started",
"物品守护已开始:" + ITEM_CN + ",守护时长为" + DURATION_TEXT + "。"
)
```
开始通知只发送一次。
### STEP 5:循环巡查
```text
WHILE guard_status == "running":
IF 当前时间 >= end_time:
guard_status = "completed"
BREAK
SLEEP(不超过5秒)
# 睡眠后重新检查状态,避免停止后继续检测
IF guard_status != "running":
BREAK
IF 当前时间 >= end_time:
guard_status = "completed"
BREAK
result = vop.read()
found = TARGET IN result.objects
IF found:
miss_count = 0
ELSE:
miss_count += 1
IF miss_count == 1:
TEXT_REPLY "暂时没看到" + ITEM_CN + ",再确认一次"
# 此处禁止调用 channel_reply
# 此处禁止报警
ELIF miss_count >= 2:
guard_status = "alarm"
BREAK
```
循环内禁止调用 `channel_reply`。
普通对话不会修改 `guard_status`、`miss_count` 或 `end_time`。
## 六、ALARM
仅当状态首次从 `running` 变为 `alarm` 时执行:
```text
IF guard_status == "alarm":
vop.stop()
tts.speak("警报!" + ITEM_CN + "不见了!")
speaker.play()
TEXT_REPLY ITEM_CN + "被拿走了!"
SEND_EVENT_ONCE(
"item_missing",
"警报!" + ITEM_CN + "不见了,疑似被拿走,请立即查看。"
)
guard_status = "finished"
RETURN
```
报警通知只能发送一次。
进入 `finished` 后不得重新报警,不得继续巡查,不得发送完成通知。
## 七、GUARD_COMPLETE
仅当状态首次从 `running` 变为 `completed` 时执行:
```text
IF guard_status == "completed":
vop.stop()
tts.speak("已完成" + ITEM_CN + "的守护")
speaker.play()
TEXT_REPLY "已完成" + ITEM_CN + "的守护"
SEND_EVENT_ONCE(
"guard_completed",
"物品守护结束:" + ITEM_CN + "已完成指定时长的守护。"
)
guard_status = "finished"
RETURN
```
完成通知只能发送一次。
进入 `finished` 后不得继续巡查,也不得再次发送完成通知。
## 八、STOP_FLOW
停止请求的优先级高于启动请求。
```text
IF guard_status == "running" OR guard_status == "starting":
# 先改变状态,确保巡查循环立即失效
guard_status = "stopping"
vop.stop()
tts.speak("已结束守护")
speaker.play()
TEXT_REPLY "已停止守护"
SEND_EVENT_ONCE(
"guard_stopped",
"物品守护已提前结束:" + ITEM_CN + "。"
)
guard_status = "finished"
RETURN
ELSE:
TEXT_REPLY "当前没有正在进行的物品守护"
RETURN
```
没有运行中的守护任务时,禁止调用 `channel_reply`。
提前停止通知只能发送一次。
## 九、终态互斥规则
一次守护任务只能以以下一种方式结束:
```text
item_missing
guard_completed
guard_stopped
```
三个结束事件互斥。
如果已经发生其中一个结束事件:
```text
不得执行另外两个结束流程
不得再次调用 channel_reply
不得继续调用 vop.read
必须保持 guard_status == "finished"
```
## 十、允许的 channel_reply 调用位置汇总
整个脚本只能出现以下四种远程消息:
```text
开始:
"物品守护已开始:{物品名},守护时长为{时长}。"
物品消失:
"警报!{物品名}不见了,疑似被拿走,请立即查看。"
正常结束:
"物品守护结束:{物品名}已完成指定时长的守护。"
提前结束:
"物品守护已提前结束:{物品名}。"
```
除此之外,任何分支、循环、普通对话或重复状态都禁止调用 `channel_reply`。
# ── 结束。无其他步骤。──