Blog
BLOG42
Notes on building, AI, and whatever I'm thinking about.
- 事后抽检,而不是提交前拦人:一个只读质检面板的取舍 5 min read
质检规则最初被做成了提交前的一道闸——后来摘掉了。同样一套规则,放在「挡住正在干活的人」和「事后给主管看」两个位置,误伤的代价差着数量级。
- 一张 changelogs 表服务多个产品:版本号、更新日志,和「漏了会静默」的两道闸门 5 min read
任何靠人维持同步的副本,迟早会漂。README 里手写的版本号落后了五个版本,更新日志被抄成三份——把它们收成单向的真源关系,再给「漏写不报错」的地方装上闸门。
- 不发版地给所有人推一条消息:云端公告的四个决策和一条不能改的 id 5 min read
公告看起来是最简单的功能——一条记录、一个消息条。真正要想清楚的是:它和业务提示抢不抢位置、已读存在哪、什么算读过,以及为什么公告的 id 发出去就不能再动。
- 采集纠偏样本:一个埋点盲区,和「高分而自信地错」的通用叶子 3 min read
拿埋点数据做 A/B 归因,得出「新功能上线后准确率掉了一大截」——被当场指出口径有问题。那个字段从头到尾只记录了其中一方的输出。
- 让模型查分类树,而不是背它——然后发现整棵子树根本塞得下 7 min read
分类链路先是把菜单塞进 prompt 让模型背,然后改成给模型三个检索 tool 让它查。查准了,但要来回好几轮。最后量了一下体积:整棵子树直接给它就行。
- 把聊天里的截图喂给模型:直接传 URL,以及「文档和视频别伪装成图片」 5 min read
客户发来的截图常常是唯一的故障证据。接入多模态时差点白写一整条取图链路;上线后又发现,PDF 和录屏在模型眼里全是「一张图片」——不是模型认不出,是取数层先把信息毁了。
- 用 Native Messaging 在本机跑语音转写:三道消息上限,和「搬代码要搬判据」 6 min read
扩展里做语音转写,云端要每人一个 API key,浏览器内 WASM 又太慢。最后落到 Native Messaging + 本机宿主——路上撞到的每一道墙,都是消息通道的尺寸与形状。
- 一条命令发版:打包、部署、更新日志、公告、群通知,以及「HTTP 200 也可能是失败」 5 min read
发版被拆成五步串在一条 npm 命令里。每一步都有自己的闸门——而最容易出事的,是那些失败时不报错的地方。
- 打包与图标:一个「成功」但装不上的 zip,和一张不该被放大的 128px 3 min read
打包脚本一路绿灯、还打印了体积和哈希,产出的却是一个装不上的包。以及为什么图标永远要从最大的原稿往下生成。
- 和 AI 结对写一年代码:精瘦的 CLAUDE.md、按域拆的文档、git 纪律,和压缩后的幻觉 7 min read
这个项目的大部分代码是和 AI 编码助手一起写的。真正决定协作质量的不是模型,是那些写给它看的文档、给它设的闸,以及几次它(和我)被自己的记忆骗到的经历。
- 「对方回复过了吗」这个判据的四代史:字段被删,语义被误读,异步化之后种子种不上 5 min read
一个看似只需要一行代码的判断,被宿主系统的改版打穿了三次。每一代都有一个看起来天经地义的依据,而每一个依据都在某天悄悄失效。
- 服务端粗筛 + 本地精排:给一个没有模糊搜索的列表接口做相似检索 5 min read
过滤器写错不报错,而是「看起来查到了全量」。在这样的接口上做相似检索,第一件事是验证你的过滤条件真的生效了。
- 一个全局键位管理器:所有快捷键走同一个监听器 4 min read
快捷键散在各个模块里各自监听,很快就会出现两个功能抢同一个键、改键要改代码、焦点在 iframe 里就不灵。收成一个监听器之后,这几件事都有了唯一的答案。
- 用扩展的 Service Worker 替掉一台 Puppeteer 机器人:纯 HTTP 轮询的监控看板 5 min read
一台常驻服务器跑着无头浏览器抓监控数据。搬进扩展之后服务器可以关掉了——前提是想清楚哪些能搬、数据以谁为准,以及为什么面板会卡。
- 先分流再匹配:一个关键词分类引擎的三次失效 6 min read
三级分类树里 38% 的叶子重名,加分改变不了「同名无法区分」。于是先分流再打分——然后树被改了层级,锚点淹没了小系统,分词器切不开连写的中文。
- 「用某个 CSS 框架」意味着用它的组件类:一份 beercss 陷阱清单 5 min read
第一版只把框架当变量字典,控件样式全部重写——被一眼看穿「根本没用上」。改成真用之后,又踩到了全局样式框架会悄悄改写你以为自己控制的属性。
- 扩展的入口放哪:从一颗会眨眼的浮动小球,到宿主顶栏里一枚安静的图标 5 min read
入口迭代了三次。最后删掉的那颗小球做得很精致——贴边三档、换位手柄、呼吸动画。问题恰恰在于它看起来太像一个「东西」了。
- 给 agent 的 tool 分级:read / write / send,以及为什么 send 永远不给 LLM 6 min read
把已有能力函数包成可编排的 tool 只是第一步。真正的设计在于副作用分级,以及「流程可以自主提交」和「LLM 可以自主调用提交」之间那条容易被抹平的界线。
- all_frames: true 的代价:一个页面五个实例,模块级变量锁不住任何东西 5 min read
内容脚本注入每个 frame,于是你的模块级变量每个 frame 各一份。五份定时器一起改同一块 DOM,会产出一类极难复现的 bug——以及一条很反直觉的排查口诀。
- 一个 <base href> 引发的 CORS:我的相对路径 fetch 跑到 CDN 去了 3 min read
内容脚本注入每一个 frame,而富文本编辑器的那个 iframe 带着自己的 base href。相对路径按 baseURI 解析,于是同源请求悄悄变成了跨域请求。
- 排查方法论:错误信息会撒谎,日志会缺一段,你的仪器本身可能是坏的 8 min read
一年里最贵的那些 bug,没有一个是因为不懂某个 API。全都是因为相信了一件不该相信的东西——八条原则,每条配一个真实案例。
- 让 React 受控组件「看见」你写的值:native setter、mousedown,和按 label 找字段 5 min read
操控一个没有源码的 React 表单,四件事各有一个「想当然的写法」会失败。附一个可以亲手点出结论的靶页。
- 扩展配置的存储与迁移:deep-merge 能做什么,不能做什么 3 min read
一个 slice 一个 key、deep-merge 自动补新字段,听起来就够了。但数组不逐元素合并——这一条会在你完全想不到的地方炸。
- 从 DOM 节点摸到 fiber,再摸到表单实例:比点击稳得多的直填 4 min read
组件库的 Form 实例挂在 fiber 上。拿到它就能 setFieldsValue 一步到位,不用再走「开下拉→等选项→点击」那一套。代价是跨 frame 找到「真正那张表单」。
- 固定流程 + AI 判断节点,而不是 function calling 自主决策 5 min read
同一件事,写死一条链和交给模型自己编排,差别不在能力上,在于出问题时你能不能定位到是哪一步。
- 往别人页面里注入 UI 的三条纪律:幂等、别污染文本、别让 observer 自激 5 min read
在一个你控制不了的 SPA 上挂自己的界面,会遇到三类问题。每一类都会以「完全不像是 UI 引起的」的方式暴露出来。
- 接一个 OpenAI 兼容网关的实战清单:强制流式、函数名不能含点、模型下架不报错 5 min read
「OpenAI 兼容」只保证形状兼容。真正会咬你的是那些它没说、而你也不会去试的地方——包括一个模型被下架后静默路由到别的模型。
- MV3 扩展工程骨架:五个入口、一个 manifest,和一条「build 过了但类型是错的」 4 min read
Vite + crxjs + TypeScript 的扩展工程长什么样,五个入口各管什么,以及为什么 npm run build 通过不代表类型没问题。
- MV3 Service Worker 不肯活着:alarms 有下限,Port 才是保命绳 5 min read
把一台常驻的 Puppeteer 机器人搬进扩展的 Service Worker,要先接受一件事:它三十秒没事干就会被杀掉。定时、状态、重连全都得重新设计。
- 内容脚本的三个世界:为什么 DevTools 里能跑,扩展里就是拿不到 React fiber 6 min read
隔离世界、主世界、扩展页面共享同一棵 DOM 却不共享 JS 堆。这个边界会制造两种方向相反的误判,以及一个必须自己造的跨世界桥。
- 把 AI 助手放进 Chrome 原生 Side Panel:瘦客户端、per-tab 绑定、360px 5 min read
从网页内注入面板改成浏览器级侧栏,换来的是不抢页面空间和不被宿主重建冲掉。代价是三个必须先知道的约束。
- irm | iex 一条命令装浏览器扩展:免管理员的 load-unpacked 分发 5 min read
不能上架、用户没有管理员权限、很多人不用命令行。最后落到一条 PowerShell,以及一堆「顺序不能换」的细节。
- 用 PocketBase 给客户端做远程配置:按域拆 key,和两种下发模型 7 min read
一条记录装所有参数,改一处要动整块 JSON。拆开之前得先想清楚:这个参数是「管理员说了算」还是「用户也能改」——选错了会得到很难查的 bug。
- PowerShell 5.1 的编码地狱:BOM 救不了你,而且它在下载阶段就已经乱了 6 min read
同一个中文脚本,四个不同阶段会以四种方式坏掉。加 BOM 修好一个会弄坏另一个。附 .cmd 引导器的三条铁律和一份可执行的验证清单。
- 富文本编辑器有三个源:我写对了两个,第三个只在关页那一刻才用 7 min read
显示层、提交源、组件状态——同一个富文本字段背后是三份数据。写漏一份,你会得到「提交正常但草稿丢内容」这种最难复现的 bug。
- 339 处裸 console 之后:给扩展做一个带 scope 的日志层 5 min read
「调试日志」开关立项就有,但它只管得住两个文件——其余三百多处裸 console 拨不拨都照样刷屏。收口的过程里有四条规矩和一个环形缓冲。
- 在别人的页面里做自己的 UI:shadow DOM、设计 token,和一个构建期闸门 5 min read
一份 CSS 要同时服务四种消费方:独立页面、被框架接管的设置页、和一堆 shadow DOM 注入组件。它们的取值方式完全不同,而失败方式一个比一个安静。
- 云端功能开关 + Ed25519 离线兜底:服务器挂了,也不能让人自己提权 5 min read
明文的本地白名单等于零鉴权。用签名把兜底名册钉死,再配一套静默退避的健康探测——服务器挂了不影响干活,也不会有人给自己开权限。
- 【Udesk 系列】导读:一个人维护一个内测产品,要凑齐哪些零件 11 min read
一个不能上架、不能开源的浏览器扩展,靠一个人做完分发、更新、鉴权、公告、更新日志、统计和排障。这是整个系列的地图。
- tbx: Compressing a Remote Support Session into One Command 8 min read
An online toolbox for an IT service desk — take over a machine remotely, run one command, and get installs, driver deployment, and repairs. Plus every pit I fell into along the way.
- About This Site: From AI Blog to Portfolio 1 min read
- From Scratch: Turning a Blog into a Portfolio 1 min read