Alias Archive档案更新中
档案更新中

把浏览器当显示器:宿主无关的舞台架构与三场基建战役

数字伴侣建造记04 / SERIES

让同一个 AI 伴侣同时以浏览器标签、独立窗口和桌面壁纸存在的架构方法:逻辑全部下沉服务端,页面退化为显示器。附语音识别、音频播放与全局热键三场战役的完整记录。

归档日期
归属板块
其他
语言版本
ZH / 阅读版本

本文是「数字伴侣建造记」的第四篇。她需要一个“身体”——最初是浏览器标签,后来想要无边框的独立窗口,再后来想让她成为桌面壁纸本身。三种形态若各自实现,维护成本会失控。本文记录让三者共存的架构决定,以及为此打的三场硬仗。

原则:页面是显示器,不是应用

架构图:浏览器标签、无边框窗口、桌面壁纸三种宿主共用同一份页面,全部连接到本机服务端;服务端内含大脑、本地语音识别、服务器直出音频、阀门与事件广播;宏键盘通过 HTTP 调用服务端
一切逻辑住在服务端;页面改一次,三端同变。

规则只有一条:任何状态、任何决策、任何有副作用的动作都发生在本机服务端,页面通过 WebSocket 收事件、通过 HTTP 发请求,自身不保存真相。由此获得的性质:

  • 三种宿主加载同一个 URL,互为备份;
  • 页面全关,系统照常运转(语音照说、热键照用);
  • 每个“宿主特有的缺陷”都可以在服务端绕过——这句话是下面三场战役的共同主题。

战役一:耳朵搬进服务端

语音识别最初用浏览器的 Web Speech API。壁纸宿主上它死于一个无解的交互:麦克风权限气泡是原生控件,壁纸软件只把鼠标转发给网页内容,气泡永远点不到。且 WebView2 的语音后端本身不可靠。

解法是釜底抽薪:麦克风属于机器,不属于页面。服务端直接录音,本地 whisper 模型转写(16GB 显卡上 45 秒音频约 0.7 秒转完),文字走与打字输入相同的通道。页面从此不需要任何权限,三种宿主平等获得语音输入;隐私上,私密语境的语音永不出本机。

两个配套设计值得记录:

自听抑制。开着持续识别时,她自己的声音会从音箱进入麦克风——不处理的话她会跟自己对话。方案:播放侧上报“她的播放窗口”,服务端记一个绝对截止时刻而非布尔标志——上报方中途死掉,截止时刻自然过期,标志位永远不会卡死。识别段落若在窗口内开始则丢弃;按住说话不受抑制(按着键说话就是想插话,这是意图,不是回声)。

CUDA 缺库要到推理时才暴露。模型构造成功不代表能用——缺 cuBLAS 时构造照样成功,第一次转写才崩。因此加载后必须用半秒静音做一次“热身转写”来验真,失败则降级 CPU。另一个坑:动态库搜索走的是普通 LoadLibrary,只认 PATH,Python 侧的 add_dll_directory 不够。

战役二:喉咙搬进服务端

触发事件:壁纸软件在其他窗口获得焦点时会冻结壁纸进程——她正说到一半,整个人被暂停。在宿主设置里与各种暂停策略搏斗数轮后放弃,回到原则:声音属于机器,不属于页面

服务端直接解码音频、写系统音频流。收益清单比预期长:任何宿主冻结都不再影响说话;多宿主同开时的重复出声永久消失(单一声源);浏览器“点击解锁自动播放”的遮罩不再需要;打断精度提高到约 40 毫秒(按块写流,随时可停);自听抑制的时间窗从“页面上报”变成服务端自己掌握的精确值。

一个环境陷阱在此期间杀过整条语音链路:日文区域设置下,重定向到文件的日志流回落到 cp932 编码,打印一句简体中文直接抛异常——而那行打印恰好在语音转写完成的必经之路上。修复是强制 UTF-8 并让打印永不抛错。教训一句话:日志不该有能力弄死它观察的东西。

战役三:手

全局快捷键最初用 Win+字母,与系统快捷键全线冲突。转向一块可编程宏键盘,让实体键发送 F13–F19——这段 USB HID 规范里存在、实体键盘上不存在的键位,永不冲突。监听端用系统原生 RegisterHotKey(发现依赖的第三方热键软件其实从未安装成功之后,索性用 Python 原生重写并入启动脚本,从此常驻)。

真正的对手是按键抖动:宏键盘一次按压可能抖出多个事件。它先后击穿三处,各自需要不同的对策:

现场症状对策
按住说话键按住期间“接触不良”式反复启停松键去抖:抬起后 120ms 内重新按下视为抖动
模式切换键密码框关了一个又弹一个串行化:互斥锁保证同时只有一个对话框 + 关闭后冷却
壁纸上的网页按钮开关类按钮“点了没反应”(双击自我抵消)捕获层去重:同目标 250ms 内的第二次点击视为回声

同一块硬件的同一个毛病,在三个不同的抽象层需要三种不同的修法——这大概是本篇最工程的一课。

可复用的原则

  • 宿主越多,逻辑越要下沉;“页面是显示器”这条纪律的回报随宿主数量线性增长。
  • 属于机器的能力(麦克风、扬声器、热键)不要借道页面实现,宿主的每个限制都会变成你的限制。
  • 超时与截止时刻优于布尔标志:持有者死亡时,前者自愈,后者永久卡死。
  • 对“构造成功”保持怀疑,用最小真实调用验真。

关联阅读

02 / LINKS
C01

数字伴侣建造记:一份流水账与目录

两周时间,从一个开源桌宠项目出发,最终造出一个完全自有的数字伴侣系统。这是全程的心路流水账,也是系列五篇技术记录的目录。

阅读全文 ↗
C02

自研大脑:四层记忆、快慢双模型与一场口头禅战争

从借用开源项目的对话引擎到完全自研:无 session 的关系时间线、四层记忆架构、口头禅自增强污染的完整病理与机制化解法,以及按延迟敏感度分工的双模型设计。

阅读全文 ↗