语音播报约定(离屏状态感知 / Task Done & Status Reminder)
在每一轮顶层交互结束时用 Windows SAPI TTS 朗读一条语音,作为用户的离屏状态感知通道——用户确认自己多数时间没盯着屏幕,所以需要声音在不看屏时也知道当前状态。
本 skill 覆盖三类时刻:
- A. 任务完成:一轮交互已正常结束。
- B. 防静默卡死:坐在屏前也怕 agent 静默卡住,声音确认"它还活着/有结论了"。
- C. 待你处理:任何需要你整理 / 确认 / 选择才能继续的节点。
本 skill 是全项目唯一的语音播报约定来源——任何新建任务、新会话都应加载本 skill 并遵守下列规则,不要另起一套播报方式。
> 关键风险:因为用户多数离屏,音频若静默失败 = 用户永远不知道。所以"可见兜底"是硬规则,不是可选项。
标准文案(五条,全项目统一用语)
| 文案 | 触发场景 | 调用参数 |
|---|---|---|
任务已完成 |
整轮 turn 正常结束(含诊断、问答、规划、执行) | 不带参数(默认) |
有事项需要确认 |
turn 停在等用户确认/拍板(如"确认后落盘") | "有事项需要确认" |
需要你选择 |
已用 AskUserQuestion 把选项抛回给你 | "需要你选择" |
需要你整理 |
待你归纳/汇总/整理 | "需要你整理" |
执行出错 |
本轮以未解决错误结束、需你介入 | "执行出错" |
> 脚本接受任意自定义文案(如业务特定的「请刷卡」)。上表五条是约定好的标准项,避免同一含义多种说法造成混乱。改标准字只需改 scripts/task_done.py 的 DEFAULT_TEXT 或在调用时传参。
决策规则(每轮顶层结尾必须播,且只播一条)
- 仅顶层轮次:只有主代理给用户的那一轮结尾播;Agent 子代理(后台长任务)内部静默,防刷屏。
- 判断状态:本 turn 是"完成"还是"待你处理"?
- 待你处理 → 选有事项需要确认/需要你选择/需要你整理中动作类型明确的那条;
- 拿不准"完成 vs 待处理" → 默认有事项需要确认(宁可多提醒,也别漏掉等你拍板的节点)。 - 出错判定:仅当本轮以"未解决错误、需你介入"结束才播
执行出错;代理自行恢复则仍播任务已完成。 - 不允许多次播放;不在 turn 中途播报部分结果。
- 不分任务类型:诊断、问答、规划、执行,结尾都必须播。
如何播放
用 Bash 工具执行脚本,且必须 dangerouslyDisableSandbox: true(不然音频被沙箱吞掉,听不到)。
脚本本身不绑定任何机器路径(纯 Python + comtypes),只有下面 bash 里的 python / 脚本路径需要按你的环境填。已用 <你的Windows用户名> 占位符写成跨机器可移植形式(正斜杠 Windows 路径在本工具的 Bash 里可直接用,无需改源码):
# 1) python 解释器:优先用 WorkBuddy 自带的 managed venv(已含 comtypes);否则用你自己的 python(需 pip install comtypes)
PY="C:/Users/<你的Windows用户名>/.workbuddy/binaries/python/envs/default/Scripts/python.exe"
# 2) 脚本路径:skill 装在哪就是哪(用户级默认如下;项目级在 <仓库>/.workbuddy/skills/...)
SKILL="C:/Users/<你的Windows用户名>/.workbuddy/skills/task-done-reminder"
# 默认文案:任务已完成
"$PY" "$SKILL/scripts/task_done.py"
# 传入标准/自定义文案:
"$PY" "$SKILL/scripts/task_done.py" "有事项需要确认"
> 把上面两行的 <你的Windows用户名> 换成你 C:/Users/ 下的实际文件夹名(.workbuddy 在你的用户主目录里)。若 managed venv 路径不同,或改用 PY="python"(前提已 pip install comtypes)。换机器唯一要改的就是这两行路径,脚本无需动。
默认音色已锁中文 Microsoft Huihui、语速 0(常速),无需每次指定。
可见状态行(硬规则)
每轮都在聊天里打一行(纯文本,不用 emoji,遵守文件注释约定):
- 音频成功:[语音] 任务已完成
- 音频失败:[语音失败] 任务已完成
脚本本身也会向 stdout 打印这行([语音] … / [语音失败] …),作为工具结果里的双重保险。即便音频完全不可用,也至少有可见行,离屏时一瞥聊天记录即可知。
脚本能力(task_done.py)
- 默认文案常量:
DEFAULT_TEXT = "任务已完成",改标准字只动这一处。 - 自定义文案:传
argv[1]即可,无需改脚本。 - 音色:默认
TASK_DONE_VOICE="Huihui"(中文·简体);系统默认可能是英文 Zira,故必须锁中文嗓,否则中文被念歪。 - 语速/音量:
TASK_DONE_RATE(默认 0,范围 -10~10)、TASK_DONE_VOLUME(默认 100)。 - WAV 兜底:直读
Speak失败时自动改走 SpFileStream 写临时 WAV +winsound播放;TASK_DONE_WAV=1可强制走该通道(适用于直读静音但 winsound 能出声的沙箱)。 - 失败可感知:comtypes 缺失或 SAPI 异常会打印
[task-done] ...到 stderr 并退出非 0,且仍打印[语音失败] …可见行。
# 示例:指定中文音色 + 稍慢语速(沿用上面的 $PY / $SKILL 变量)
TASK_DONE_VOICE="Huihui" TASK_DONE_RATE="-1" \
"$PY" "$SKILL/scripts/task_done.py"
PowerShell 版(speak.ps1,分享/上架零依赖)
面向"分享给别人 / 上架市场"的场景:接收方无需 Python、无需 comtypes,只要他的机器是 Windows(自带 SAPI)就能用系统 PowerShell 直接播报。
# 外部用户的系统 PowerShell(无 WorkBuddy 拦截):
powershell -File speak.ps1 "任务已完成"
powershell -File speak.ps1 -Text "有事项需要确认" -Voice "Huihui" -Rate 0
- 中文嗓:
$Voice默认 "Huihui";系统默认嗓可能是英文 Zira,故锁中文。 - 失败兜底:任何异常打
[语音失败] 文案+ 错误明细,退出非 0,绝不静默。 - 编码要求(重要):文件必须保持 UTF-8 BOM,否则 Windows PowerShell 按 ANSI 读中文会乱码、引号配对失败(已踩坑并修复)。勿用无 BOM 编辑器覆盖。
> 重要限制:WorkBuddy 的 PowerShell 工具对 New-Object -ComObject 有安全策略硬拦截(即使放行沙箱也拦,提示 "COM object instantiation can run arbitrary code"),故无法在 WorkBuddy 会话内用 speak.ps1 出声。会话内播报请继续用 Python 版(task_done.py);speak.ps1 仅作对外分享/上架方案。
新建任务如何接入(一步到位)
任何新会话 / 新任务,只要涉及「需要让用户知道进展或结果」的交互,都应在结尾调用本 skill 的播放命令:
- 加载 skill:
Skill skill="task-done-reminder"(或直接按本约定执行 Bash)。 - 每次回复结尾,按「决策规则」选文案,执行上面的 Bash 命令,并在回复里也打一行可见状态行。
- 若项目有自定义业务文案(如「请刷卡」),直接把文案当参数传入即可,无需改脚本。
分享与跨机器适配(给接收方)
本 skill 提供两个实现,按场景选:
- WorkBuddy 会话内播报(你自己用):用 Python 版
task_done.py,见「如何播放」。需 managed venv + comtypes;路径已用<你的Windows用户名>占位符,仅换这一行即可。 - 分享 / 上架给别人(零依赖):把整个
task-done-reminder/文件夹发给对方,他在自己机器上用系统 PowerShell 跑speak.ps1即可,无需 Python、无需 comtypes。两点注意:① 文件夹解压到他自己的C:/Users/<他的用户名>/.workbuddy/skills/或<仓库>/.workbuddy/skills/;②speak.ps1必须保持 UTF-8 BOM 编码(已存好),否则中文乱码。
接收方验证:外部 Windows 跑 powershell -File speak.ps1 "任务已完成",能出声即正常;无声看 stdout 的 [语音失败] 行。
要求
- Windows 主机,自带 SAPI(现代 Windows 均内置)。
- Python(任意 3.x)+
comtypes(pip install comtypes)。推荐直接用 WorkBuddy 自带的 managed venv:C:/Users/<你的Windows用户名>/.workbuddy/binaries/python/envs/default(跨机器路径一致,实测含 comtypes 1.4.16)。
已知失效 / 被拦的替代方案(勿在会话内用)
- WorkBuddy PowerShell 工具内
New-Object -ComObject/Add-Type被安全策略硬拦截("COM object instantiation can run arbitrary code"),即使dangerouslyDisableSandbox也拦 —— 故会话内播报必须走 Python 版,勿在会话内调 speak.ps1。 - Bash 调
powershell.exe被拦截("bypasses PowerShell security checks")、wscript.exe被拦(LOLBin)—— 同理勿在 Bash 里启 PowerShell。 - 外部用户的系统 PowerShell 无此拦截,
speak.ps1可直接出声 —— 这是它作为分享/上架方案的立足点。 - PowerShell
.NET SpeechSynthesizer(System.Speech.Synthesis)在会话内退出 0 但无声,且外部也不如 SAPI COM 通用,未采用。 SystemSounds系统事件音 — 易静音,不可靠。- Python 版已内置更稳兜底:WAV 通道(SpFileStream + winsound),优先用它而非上述方案。