- AI 插件
- dsh-plugin
- 交互助手
- 桌面应用
【免费下载链接】dsh-pet
DSH 桌面宠物:一行命令装好即用的透明动画小桌宠,支持多开、大小位置随心配置;还内置 DIY 素材链,能用 AI 视频自造专属宠物
dsh-pet 是一款一行命令装好即用的透明动画桌宠,它最"贴心"的功能之一是工作状态联动:桌宠会实时监听 DSH 的会话事件流,把 agent 的思考、调工具、等确认、完成、出错等过程,压缩成 6 档动画档位 + 气泡文案。本文拆解这套工作状态状态机的设计——事件如何映射到档位、多会话如何聚合,以及"goal 自动续跑"时如何做到"真收尾才庆祝"。
为什么桌宠要"跟着会话干活"
平时你让 agent 跑一个长任务时,界面滚动、日志刷屏,很难一眼看出"它现在是在想、在干、还是在等我确认"。dsh-pet 的思路是:不做日志解析,也不调模型,只监听 DSH 的session/event事件流,用最轻的方式把"当前活动工作状态"聚合成一个快照,前端按档位切换预置动画。
开关就藏在宠物实例配置里:pets[].workStatusEnabled = true时,host 侧开始聚合会话事件,浏览器/桌面端轮询/state后按 counter 变化切动画。整个链路纯监听、零模型调用,配置见 config.jsonc。
6 档状态机:6 类会话事件 → 6 个动画档位
工作状态只有 6 个档位,顺序固定,与配置里animations.events.workStatus动画数组的索引严格一一对应(注释里专门叮嘱"勿在中间插入新档"):
| 档位索引 | 状态 | 触发事件 | 默认动画 |
|---|---|---|---|
| 0 | thinking | turn/start回合开始 | 工作状态-思考冒泡 |
| 1 | working | tool/call工具调用 | 工作状态-忙碌点按 |
| 2 | result | tool/result工具返回 | 工作状态-清点归档 |
| 3 | waiting | approval/asked/ 问用户选择题 | 工作状态-原地踱步张望 |
| 4 | success | turn/endreason=completed | 工作状态-雀跃庆祝 |
| 5 | error | turn/endreason=error/max-tokens/timeout | 工作状态-垂头叹气冒汗 |
核心压缩逻辑就一个纯函数 reduceWorkStatus,几行 switch 把所有事件收敛成档位或null:
tool/call有个特例:工具名是ask_user_question(agent 弹选择题问你)时归为waiting——"问你的题"不是"在干活";turn/end的blocked(回合被阻塞等确认)归waiting;- 其余 reason(如
aborted)返回null,含义是该会话回合已结束,清空回空闲。这是当年"这一步正在进行中哦"永久挂死的根因修复——回合被打断后绝不能残留上一档。
档位与索引常量在 shared/work-status.ts 中单一来源定义,气泡文案则完全由配置驱动:workStatusTexts 是一个二维数组,每档放多个备选句,播放时随机抽一句,想换文案只改配置不用碰代码。
goal 续跑判定:"真收尾才庆祝"
上面 6 档里有个隐藏矛盾:turn/end completed只代表"这一轮"结束,不等于"整个任务"完成。DSH 的 goal 机制会自动续跑:一轮跑完如果目标没达成,会带着source.kind === 'goal'的消息再开新一轮。如果每轮完成都播"雀跃庆祝",就会出现"任务还没干完,桌宠先撒花"的尴尬。
解法是给每个 turn 维护一对轻量标志(WorkStatusTurnContext),由 index.ts 在事件流里置位、在turn/start清零:
goalRound:收到user/message且source.kind === 'goal'时置true——本轮属于自动续跑;closing:本轮出现过update_goal工具调用且action为complete/blocked时记录收尾动作(解析函数 goalUpdateAction 对非法 JSON 一律按"未收尾"容错)。
终局判定收敛到 completedState 一个函数,规则非常干净:
| 场景 | turn/end = completed 时的档位 | 语义 |
|---|---|---|
| 非 goal 轮(普通问答) | success | 一轮答完即成功(原行为,向后兼容) |
| goal 续跑·中间轮(未收尾) | result | 本轮完成 ≠ 任务完成,只播"清点归档",不庆祝 |
收尾轮action: complete | success | 整个目标达成,才雀跃庆祝 |
收尾轮action: blocked | error | 目标被阻塞,诚实表达沮丧而不是假装成功 |
这套判定被单测逐条钉死,见 work-status.test.ts:"中间轮 completed → result(不庆祝);收尾轮 complete → success / blocked → error"。
多会话聚合:等待和错误优先浮出
桌宠常同时挂着多个会话(子代理、委派任务),展示时不能"先到先得"。WorkStatusStore 按会话各记一条state + task,对外用 pickDisplayed 挑出优先级最高的一条展示:
waiting(60) > error(50) > working(40) > thinking(30) > result(25) > success(20)这个优先级表里有两处刻意的取舍:
waiting永远最优先——只要有会话在等你确认,其他一切动画都该让位,你该去点那个"批准"按钮了;result高于success——任何一个会话的进行中过渡态,都不允许被别的会话的"已完成"盖过去,这正是"防中途庆祝"在多会话下的兜底。
另外两个细节让状态"不会赖着不走":
- 同档去重:同会话同状态不重复更新(签名比对防刷屏),状态或任务文案任一变化才刷新时间戳;
- 终态 60 秒窗口:
success/error展示约 60s 后自动清掉会话条目,陈旧完成态不会在后续无关活动里反复浮上来;若会话期间回到非终态,到期的清理定时器会被取消。
任务详情气泡("正在做第几步")也挂在会话条目上而非全局单值——条目一清,文案随之消失,不会再出现"A 会话的状态配 B 会话的文案",或任务结束后文案永久残留(issue #59 的根治点,详见 currentTaskFromTodo 的注释)。
渲染端:轮询 + counter 触发切档
host 侧聚合结果写进/state的sections.workStatus,浏览器与桌面端共用同一份纯逻辑 shared/work-status.ts(toWorkStatus把不认识的状态安全归一成"空闲",形状非法直接跳过这一拍、不抛错),前端按 counter 变化切到对应workStatus动画并弹出该档文案;终态气泡 10 秒自动收起,非终态常驻。
小结:把"状态机"做轻的三条经验
dsh-pet 的工作状态联动给自研状态展示功能提供了不错的参考:
- 压缩而不是解析:不碰日志和模型输出,只靠 6 类事件 → 6 档的固定映射,纯函数可单测,天然稳定;
- "完成"要分级:区分"本轮完成"与"整个任务完成"(goal 续跑的
goalRound + closing双标志),只在真收尾时庆祝,失败就诚实表达; - 终态要有生命周期:去重防刷屏、终态定时过期、文案与状态同生共死——状态机的下半身和上半身同样重要。
想深入源码,从 dsh-pet/src/host/work-status.ts 入手即可:6 档压缩、goal 收尾判定、多会话聚合全在这个文件里,配套的 work-status.test.ts 把每条边界行为都写成了用例。
- AI 插件
- dsh-plugin
- 交互助手
- 桌面应用
【免费下载链接】dsh-pet
DSH 桌面宠物:一行命令装好即用的透明动画小桌宠,支持多开、大小位置随心配置;还内置 DIY 素材链,能用 AI 视频自造专属宠物
相关推荐
窗口一失焦它就弹通知:dsh-pet 系统通知机制详解(5种DSH会话事件全覆盖)
窗口一失焦它就弹通知:dsh pet 系统通知机制详解(5种DSH会话事件全覆盖) dsh pet 系统通知 是桌面宠物 dsh pet 里一个「独立于宠物本体
AI 插件dsh-plugin交互助手桌面应用dsh-graded-mode(分级模式 v0.0.1-rc1)技术指南:DSH 生态下的会话级两级任务协议与规格化门控状态机
dsh graded mode(分级模式 v0.0.1 rc1)技术指南:DSH 生态下的会话级两级任务协议与规格化门控状态机 本指南以 graded/ 子模块
dsh-pluginAI 插件提示工程人工智能开发工具庆祝开源创新:`celebrate` - 动态生成庆祝动图的工具
庆祝开源创新: celebrate 动态生成庆祝动图的工具 在这个数字化的时代,我们经常需要为各种线上活动增添一些趣味性。 是一个由 Continuation
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考