为了不再切回去看 Claude 跑没跑完,我 Vibe Coding 了个 Claude 的声音提醒

0 评论 89 浏览 0 收藏 27 分钟

当AI工具默默运行时,我们的注意力却被无声消耗。本文揭示了一个惊人现象:Claude用户看似在高效利用等待时间,实则沦为系统的「调度员」。作者通过亲手打造声音提醒系统,将AI交互从被动轮询转变为主动中断,最终夺回注意力的掌控权。这不仅是技术hack,更是一场关于数字时代注意力主权的深刻反思。

一、我以为我在并行处理,其实在做调度

故事是这样的。

那天我在 Claude 桌面客户端挂了一个任务让它跑,预计要十几分钟。我心想行,这十几分钟我也干不了正经事,但又不甘心啥都不干,于是顺手摸过手机,开始刷短视频。

刷着刷着,刷到了第 N 条。

我突然想起来 Claude,切回去看了一眼,有权限弹窗了。任务停在那,啥也没干。

我把它点掉,重新跑。

继续刷。

刷一会儿,又有点不放心,再切回去看一眼。还在跑。

回去刷。切回去。回去刷。切回去。

后来我发现,这一整个上午我什么也没干成。短视频 feed 推了我 200 条,Claude 那边因为我错过权限超时。我以为我在「等待时间充分利用」,实际上我做了一份叫「监控 Claude」的全职工作。

二、问题不是 Claude 慢,是它默默无声

你应该懂这种感觉。Claude 桌面端跑任务的时候,问题不在于它跑得慢,而在于它默默无声。

它不会跑完之后冒出来跟你说一声「老板我搞定了」,也不会在权限弹窗超时前喊你一嗓子。

它就静悄悄地待在那个窗口里,但你的脑子里始终有一根弦绑在那个窗口上。

绑着绑着,正经事干不了,只好选个最低门槛的娱乐来填空。

我刷短视频不是因为我多想刷,是因为这种碎片化的、随时可中断的内容,恰好匹配我那种「我不知道下一秒会不会要切回去」的待机状态。这是被动选择。

也就是说我哪怕花三分钟刷个短视频,回来一看,前面那十几分钟全白等了。

我后来一直琢磨这个事。

明明 Claude 是我用过最舒服的 AI 工具,能力强、上下文长、我让它干啥它就干啥,可为什么用着用着,我反而比以前更累?

想了一晚上,我意识到问题出在工作模式,不是工具本身。

现在大部分工作工具都是「轮询监控」模式,你得反复盯着才知道状态。

微信你得自己刷,Notion 你得自己点开,Claude 跑完它不出声。

所以我们一天到晚都在切窗口,其实是在给这些工具做主动 polling。

我们以为自己在工作,其实在做调度。

要打破这个,唯一的办法就是让工具自己来找我。

它有事敲我一下,没事别打扰我。

我决定给 Claude 加点声音。

三、最朴素的版本,从一行 afplay 开始

最朴素的方案,是用 Claude 自己留的口子。

很多朋友可能不知道,Claude Code 其实有个机制叫 hooks,每次发生权限请求、任务完成、子任务结束、会话开始这些事件,它会触发一个你预设好的命令。配置写在~/.claude/settings.json里。我第一反应就是,那不就直接 afplay 一个 macOS 系统音完事吗。

写出来大概是这个样子。

我先在 Claude Code(终端里那个 CLI)试了一下。

叮。

我笑了。

但这其实不是我想要的最终态。

我用 Claude Code 的时候反而不会去刷短视频,因为终端里它一行一行往下打字,我看着津津有味。

真正让我开小差的是桌面端,桌面端是 chat 形态,发完命令屏幕就一片安静,注意力立刻就漂走了。

四、桌面端这一关,没我想得简单

所以我打开桌面客户端,跑了一个任务。

没声音。

= =

我一开始的判断是,桌面客户端不触发 hook。

逻辑上也讲得通,hooks 配的是~/.claude/settings.json,是 Claude Code 这个 CLI 程序的配置,桌面客户端是另一个东西,桥归桥路归路。

我跟群里几个朋友说过这个结论。

结果一个朋友把他的桌面客户端用我的初版工具跑了一下,过了一会儿在群里发了句「有声音啊」。

我当场愣住。

回去查了半天才搞清楚,桌面客户端实际上是通过启动一个 Claude Code 子进程来跑命令的,所以它说到底还是 CLI 在响应 hooks。

但桌面端跑的子进程没法直接 afplay,因为它没接到正确的运行环境,所以 command 模式响不出来。

它倒是会向外发请求,所以正确的做法是起一个 HTTP server 接收事件,由它来播声音。

也就是把 hooks 从 command 模式改成 http 模式,把afplay xxx.aiff改成curl -X POST http://127.0.0.1:3737/api/play-hook?type=Stop。

五、整个工具的骨架,其实就三段

讲到这里,整个工具的骨架其实就清楚了。

它说到底就三段。

第一段,接收。本地127.0.0.1:3737跑一个 Hono HTTP server,监听/api/play-hook这个端点。事件来了它就 afplay 一下。

第二段,触发。把 Claude 的 hooks 配置改成 curl POST 上面那个端点,每次有事 Claude 自动叫一下 server。

第三段,守护。用 macOS LaunchAgent 把 server 接管过去,菜单栏 App 关掉、电脑重启,server 都还活。

整个数据流画出来大概是这样。

桌面客户端那条线和 Claude Code 那条线最后会汇到 hooks 这一层,因为桌面端是靠启动一个 Claude Code 子进程来跑命令的,两边走同一套 hooks 配置。这也解释了我前面那个经典误判,CLI 一开始能响、桌面端不响,不是hooks 没触发,是触发的子进程跑命令的时候 afplay 的 command 模式不通,必须走 HTTP。

OK 骨架清楚了,回头说我撞过的几堵墙。

六、第一堵墙,Clash Verge 拦截了 localhost

这玩意我得专门讲讲,因为它会成为绝大部分国内开发者用类似工具的拦路虎。

我开着 Clash Verge 用系统代理,这是我大部分时间的默认配置。Claude 桌面客户端发起的那个 curl 请求,被 Clash 当成普通 HTTP 流量,一脚踢到代理服务器。

代理一看,127.0.0.1,这地址没法路由到外部,502 给你。

localhost 被代理转发出去就是 502,这事第一次撞到的人都得花点时间反应过来。

打开 Clash 的连接面板才看明白,所有 localhost 的请求全被代理走了。

解决方法是在 Clash profile 的 merge 模板里加一段 prepend-rules,让 localhost 走 DIRECT,不经过代理。

加完这四行,重启 Clash。

叮。

七、第二堵墙,我盯错了仪表盘

最离谱的一堵,因为它纯粹是我自己绕进去的。

我把这个 server 拓展成菜单栏 App 之后,用 Electron 跑。

同时为了让用户关掉 App 之后后台还能继续响应,我加了个 LaunchAgent,关掉 App 也能由 launchd 接管,重启电脑也活。

LaunchAgent 跑的时候是无头进程,所以我让它把 server 的 console.log 重定向到/tmp/claude-sound.log。

问题来了。

用户双击 App 启动的时候,server 是跑在 Electron 主进程里的,console.log 走 Electron 的系统日志,根本不写/tmp这个文件。

我搞混了。

测试桌面客户端的时候,App 是我刚双击启动的(也就是 GUI 模式),但我去看的是/tmp/claude-sound.log(这玩意只有 LaunchAgent headless 模式才用),文件空的,于是我得出结论,桌面客户端没触发 hook。

我跟一个朋友说过这个结论。

然后他用我的工具试了一下,告诉我桌面端有声音。

我当场愣住。

回去重新捋了一遍 log 写入路径,发现是我蠢。GUI 启动的 server 没写那个 log 文件。

它一直在响应,是我看错了仪表盘。

这事我后来反复想。

用户的耳朵和 curl 直连测试,永远比你预设的诊断路径可信。

工具开发者最容易掉进的坑,不是写不出功能,是用错了观测手段,然后基于错误的观测得出错误的结论,再基于错误的结论做错误的设计决策。

一路下来,看起来很专业,其实什么都没诊断对。

八、第三堵墙,Gatekeeper

这事我没装作没坑。

我没买苹果开发者证书签名(99 美元一年,为一个个人项目,我一时还没想好要不要掏),所以用户首次打开 App 会撞红框「无法验证开发者」。我在 README 里给了一行xattr -dr com.apple.quarantine /Applications/Claude Sound.app解封。

一行的事,但我知道这行命令对没用过终端的人不算友好。

后续如果这工具用的人多了,我会把签名搞了。

九、v0.2.0 长这样

讲完坑,回来说效果。

我把 6 类事件全部支持上,每个都能挂自己的声音。

具体说就是 PermissionRequest(权限申请)、Notification(系统通知)、Stop(任务完成)、StopFailure(任务失败)、SubagentStop(子任务完成)、SessionStart(会话开始)。

每个事件可以独立选系统声音、独立调音量、也可以上传自定义的音频文件。

界面我做了中英双语 + 亮暗主题,跟随系统切换。

菜单栏点一下有总开关,一键关掉所有声音;下次想开的时候再点一下就回来,配置不会丢。

十、装上之后,我把那篇拖了三周的论文读完了

装上之后,我重新跑了开头那个让我刷了 200 条短视频的任务。

Claude 跑的时候,我打开了一直想读但拖了三周没读的一篇论文。

15 分钟左右听到 Glass,那是我给 PermissionRequest 设的声音,知道权限要批,cmd+tab 过去点 Allow,再切回论文。20 多分钟听到 Blow,知道全跑完了,过去看结果。

整个过程我没有主动切过一次窗口,也没有刷一条短视频。

听起来好像没什么大不了。

但你认真回想一下,过去你跑长任务的时候,是不是隔几分钟就会下意识 cmd+tab 一次?或者下意识摸过手机?那个动作不需要你做决策,是你大脑在偷偷帮你 polling 的下意识反射。

装了声音之后,那个反射没有发生,因为你的大脑知道有事会有人喊。

这就是「轮询监控」和「中断驱动」的差别。

电话和微信也是这个差别。微信你得自己刷,刷十次九次没事;

电话响了你才接,没响你不用想它。

Claude 加上声音通知,一下子从微信变成了电话。

不是「有声音了所以我开心」,是终于不用我自己 poll 了,我可以真的去深耕另一件事。

我现在重新跑长任务的时候,能真的把那篇论文读完,能写一段稿子,能跟人开个不被打断的会。

Claude 那边有事了,它自己会来叫我。

我才意识到,过去几个月我以为自己在「并行处理」,其实哪边都没沉下去。

所谓多任务,说到底就是多次中断单任务,每次中断都要付出重新进入心流的成本。

一个上午切 30 次窗口加上摸 30 次手机,相当于让大脑做了 60 次冷启动。

更扎心的是,我之所以总是去刷短视频不是去读书,不是因为我不爱读书,是因为我那个「随时可能要切回去」的待机状态根本不允许我读书。

读书需要一段连续的、不被打断的注意力,而我那个状态最长只能撑 30 秒。

在那个状态下,能匹配的娱乐方式,结构上就只剩下短视频。

十一、注意力是这个时代最贵的东西

不过我后来又往下挖了一层。

我刷短视频本身其实不是问题。

碎片时间里你想刷就刷,想放空就放空,想做家务就做家务,都行,没毛病。

问题是这个「碎片」是你切的,还是工具的沉默逼出来的。

这个时代注意力是最贵的东西。

比时间还贵,因为时间你睡一觉它会回来,注意力被切碎之后是真的回不来。

我们用 AI 用得越多,等待状态就越多。

AI 跑一个长任务的时候,那段时间到底是「我主动决定的休息」,还是「被工具的不出声偷偷决定的发呆」,听起来好像差不多,结果完全不一样。前者是我对自己时间的主导权,后者是我把方向盘交给了工具。

所以装一个声音提醒,做的事其实不是让我不偷懒。

我装上之后该刷短视频还是会刷,该放空还是会放空。它做的事是把方向盘抢回来。

让我可以选沉浸做事,也可以选主动放松,但这个选择是我做的,不是工具的默认状态偷偷给我做的。

把这个工具装上之后,我每天 cmd+tab 的次数粗略估了一下,至少少了七成。

摸手机的次数我没数,但晚上睡前看屏幕使用时间,抖音从两小时多掉到了 40 分钟。掉下去的不是「我变自律了」,是「我重新有了选择权」。

十二、把自己做的 App 拆了,又重做了一遍

上面那段我本来以为是收尾。但 v0.2.0 真用了几天之后,又遇到了新问题。

某天我电脑重启,开机后打开 Claude 桌面客户端,发了个长任务,转头去做别的。

半小时后回来,发现任务老早跑完了,完全没听到声音

我以为又是哪个环节坏了。

重启 App、重启 Claude、重启电脑,全试了一遍。看 server 的 status 接口,enabled。看 hooks 配置,正常。直接 curl 调 server 的 play-hook 接口,能播。

但只要让 Claude 自己触发 hook,就是没声音。

盯着代码看了好一会儿才反应过来。

http 模式有个我之前没想清楚的空窗期。

App 关闭到新进程把 server 起来,中间有 2-5 秒 server 完全不在线。Claude Code 触发 hook 是 fire-and-forget 的 HTTP 请求,失败就失败了,不会重试

如果你正好在 App 重启或者电脑重启那个窗口里跑了一个长任务,这次的 Stop 事件就被永久错过。

更要命的是,Claude Code 进程一旦经历过 hook 失败,状态可能被卡住,之后 server 即使恢复也不再触发。

这点我没法拍胸脯肯定,但实测就是这样。

也就是说,我前面那一通工程化操作,Electron + LaunchAgent + 单实例锁 + 总开关持久化 + 中英双语主题,全都建立在一个隐藏前提上,server 永远在线。

这个前提一旦破,整个工程化都跟着塌。

最讽刺的是临时修复方法。

我把整个 Claude Sound App 关掉,LaunchAgent 禁用,回到最朴素的方案。

直接把 hooks 写成下面这样。

afplay 是 macOS 内置命令,永远在。

Claude Code 触发 hook 直接 spawn afplay 子进程,整个链路里不需要任何 server,不需要任何 Electron 进程,不需要任何后台守护。

电脑重启不影响。

App 重启不影响。cmd+Q 也不影响。因为根本没有 App 这一层。

这事我反复想了好久。

我做的整个工具,最有价值的部分其实是前半段那个核心洞察,用声音把 Claude 从「轮询监控」变成「中断驱动」。这个洞察拆解到执行层,就是「让 Claude Code 触发事件时播一声系统音」。

这件事 Claude Code 自带的 hooks 机制 + macOS 内置的 afplay 命令,本来就完全够用。

我之所以又做了个 Electron App,是因为我想要 GUI 配置面板、想要总开关、想要中英双语主题、想要自定义声音上传、想要 LaunchAgent 守护进程保证它永远活着。

所有这些听起来都很合理,但合在一起,就是过度工程化。

更扎心的是,我加的每一层守护进程,都是新的故障源

LaunchAgent 偶尔不会正确接管。

Electron 重启有空窗。

server spawn afplay 在沙盒下偶尔失败。

每多一层,整个系统的可靠性就掉一个台阶。

KISS 原则说做加法容易做减法难。

我以为我学过这个原则,这次轮到我自己被自己加的工程化坑了几天,才真正咽下去这口气。

但停用只解决了我一个人的问题。

每个用 v0.2.0 的人都会在同样的 App 重启 / 电脑重启场景下踩同样这个坑。

我又不能一个一个跑过去说「兄弟你把 App 关掉,自己改 settings.json」。这跟我前面那段说的「让工具学会主动叫人」南辕北辙。

一个本来要省你注意力的工具,现在反而要你天天惦记着它有没有挂。

所以我又把 App 拆开,照着临时方案的样子重做了一版,叫 v0.3.0。

具体改了三件事。

第一,App 启动后写进 settings.json 的 hooks,从 http 模式默认改成 command 模式。

出来就是一行afplay /System/Library/Sounds/Glass.aiff,server 退出 hot path。

第二,老用户升级 v0.3.0,App 第一次启动会自动检测 settings.json 里残留的 http hooks,原地改写成 command。不需要他们手动改 JSON,不需要他们读这篇文章里的「临时方案」。

第三,LaunchAgent 整个删了。App 启动时如果发现老版本的 plist 还躺在 LaunchAgents 目录里,主动卸载。后台守护进程这一坨东西彻底退场。

App 的角色也跟着变了。从「runtime」变成「纯 GUI 配置工具」。打开它选声音、调音量、切总开关,配置完直接退出,声音永远在响,跟 App 是不是开着无关。

退一步看,整个 v0.2.0 → v0.3.0 的迭代其实是在做减法。

删 server 的 hot path,删 LaunchAgent 守护,删进程间通信。每删一层,可靠性升一档。

这不是什么炫技的架构。但对我来说是一次带痛感的 KISS 复习。

光读 medium 上那种「make it work, make it right, make it fast」的箴言没什么用,被自己的工程化坑了几天再亲手拆掉自己加的中间层,那个味道才记得住。

现在我电脑里那个 Claude Sound 是 v0.3.0。settings.json 里就是几行 command 模式 JSON。

重启什么都不影响。Glass / Blow 雷打不动。

十三、下载入口

工具叫 Claude Sound,v0.3.0 版本已经发到 GitHub,4 个二进制全在 release 里,Apple Silicon 和 Intel 各有 DMG 和 ZIP 两种格式,挑你用的下载就行。MIT License,免费,没广告,没埋点,没账号系统,纯本地跑。

仓库地址 https://github.com/vanci444-hue/claude-sound

下载地址 https://github.com/vanci444-hue/claude-sound/releases/tag/v0.3.0

如果你用 Claude 桌面客户端,或者用 Claude Code 做开发,强烈建议装一下。装上之后给 Stop 事件挂个 Blow 或者 Glass,PermissionRequest 挂个 Hero,跑一个任务,你就懂我前面在说什么了。

如果你之前装过 v0.2.x,第一次开 v0.3.0 它会自动把老的 http hooks 改写成 command 模式,老的 LaunchAgent 也会自动卸掉。你只需要装一遍,配置都跟着走。

如果你不用 Mac,抱歉,这个工具暂时只有 macOS 版。Windows 和 Linux 不在我个人 daily 路径上,做了我自己也没法长期维护。如果有人愿意 fork 一份做 Windows 版,仓库我已经设公开。

十四、最后说几句

最后讲一件我做完之后才想明白的事。

我们这一代用户用 AI 工具的姿势其实还停留在「我下指令,它执行,我盯着」。

这个姿势跟 90 年代用 Windows 跑安装程序进度条没区别。

但 AI 工具的能力级别已经远远超过装个软件,它能帮你跑半小时甚至一两个小时的任务。如果一个能跑两小时的工具需要你两小时盯着它,那它的实际净价值就被你的注意力消耗抵消了一大半。

工具能跑多久,决定了它的能力上限。

但工具会不会主动找你,决定了它的能力上限有多少能真正变成你的产出。

这两件事不是一回事。

声音只是一个最小可行方案。

再往下走,可能是更聪明的状态推送,可能是 Claude 自己判断什么时候该打断你什么时候不该,可能是跨设备的接力(比如 Claude 跑完了我的 Apple Watch 震一下)。

但起步先走小步,先让它能叫我一声,先把那个被工具偷走的方向盘抢回来。

仓库 https://github.com/vanci444-hue/claude-sound

本文由 @JZNext 原创发布于人人都是产品经理。未经作者许可,禁止转载

题图来自Unsplash,基于CC0协议

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
评论
评论请登录
  1. 目前还没评论,等你发挥!