夜雨聆风学习资料网

ARTICLE · 1103770

我给 AI 管家做了个手机版:从想法到装进 iPhone 主屏

我给 AI 管家做了个手机版:从想法到装进 iPhone 主屏
我给 AI 管家做了个手机版:从想法到装进 iPhone 主屏

先说结论:从方案定稿到装进 iPhone 主屏、真机跑起来,中间迭代了 22 轮。

功能本身不复杂——一个薄薄的手机网页前端,能切会话、能发消息、能看历史。但它踩的坑,比功能本身多得多,而且一多半是只有真机才会暴露的。

本文由 AI 辅助创作,人工审阅后发布。

一、我想解决的不是功能,是「位置」

我的 AI 管家跑在家里那台机器上,能替我干活——查数据、跑脚本、写东西、盯任务。但有个前提:我得坐在电脑前。

一旦离开电脑,它就从「助手」变成了「黑盒」:我知道它在干活,却看不见它干到哪一步,也接不上话。

直接用手机浏览器访问它?适配很差,看着难受。用聊天软件发消息?看不到历史、切不了会话。

所以需求很清楚:一个手机能用的轻量前端,而且要在外面也能安全地用。

二、动手之前,先把底摸清

我克制住了「先写代码」的冲动,先花时间把环境摸了一遍。这一步省下了最多的返工。

它的接口是干净的 REST + 流式输出,意味着前端可以写得很薄;但它自己没有鉴权——谁连上端口,谁就能看到我的全部会话、也能替我发指令。

这一条直接决定了整件事的形态:前端页面可以随便做,但必须有一道门挡在前面,而且这道门不能做在页面里(页面里的密码框,等于把钥匙藏在门垫下面)。

好在之前做别的事情时,已经搭好了反向隧道和一套「nginx 层口令门禁」——没输口令,连页面都拿不到。于是方案很朴素:只写前端,剩下的全部复用。不新买机器、不开新域名、不动后端。

三、功能一次做全

切会话、发消息、看历史、传文件、语音输入、工具授权审批、模型切换——我是一次做全的。事后看这个决定对,因为这些功能互相咬合,分期反而更麻烦。

(会话名已打码。)

列表这块有两个坑。一个是过滤:接口返回的列表里混着定时任务和子代理会话,不滤掉的话列表会被系统任务刷满。

另一个更有意思——排序整个失效,而且一声不吭。

我最初的排序代码是「两个时间戳相减」。但服务端给的时间是字符串格式,字符串相减得到 NaN;而排序函数遇到 NaN 会当作「两者相等」处理——于是排序被静默跳过,列表就按最旧在前的顺序显示,不报错、不提示。我盯着看了半天,一度以为自己排序规则写反了。

教训是:静态类型语言里会报错的地方,弱类型语言里会安静地错。

四、真机才是考场

功能在电脑上全绿之后,发到手机上试,第一次反馈就三件事。

输入框不是「差点没对齐」,是被压成了 19 像素高(正常应是 42)。根因不在 CSS,在调用时机:内容先写进输入框、再显示页面,而写的时候容器还是隐藏的,没有布局,读出来的高度恒为 0。

顶栏被状态栏压住:安全区的内边距只加在了列表页,聊天页漏了。

键盘一弹,输入区被盖住:iOS 不会因为你固定了输入区就把它顶上去,它会直接盖住你,得自己算遮挡高度。

还有一条最值得说:输入区下方始终留着 47 像素空白。我量完发现,那不是内边距,是浏览器压根不给——页面的布局视口只有 797px,而屏幕是 844px,那块地页面画不到。

我的处理是承认这件事:不跟它争这块地,改底色——把页面画布颜色改成和输入区一样,缝隙就看不出来了。说句实话,这是「视觉补齐」,不是真铺满。

顺便埋了个诊断点:页面每次打开,都会把它实际拿到的视口高度回报到服务器。这样「是不是浏览器不给」不用猜,看日志就知道。

五、把它变成「App」

页面能用之后,下一个诉求是放到主屏上,像 App 一样点开。

主屏图标丑的真因,不是审美问题:这个页面当时既没有主屏图标、也没有应用清单,iOS 只能拿网页截图当图标——它不是设计得丑,是它压根不是一个图标。

补齐之后,图标做了三版让用户挑,最后定的是「满底橙点阵花」那版。

这里有个我挺喜欢的做法:用户给的源图很小,点直径才 3~8 像素,直接放大会糊成马赛克。所以没有拉伸原图,而是把那 49 个点逐个量出来(中心 1 点 + 8 层环,每层比内层旋 33°,这就是源图那种漩涡感的来源),再用矢量圆重画成高清母版——与源图逐点误差 0.39 像素,等于复刻,但边缘干净。

还有个必须知道的规矩:iOS 是在你点「添加到主屏幕」那一刻抄下图标的,之后服务器怎么改都不会自动更新,得手动移除重加。

六、用起来之后,才出现的三个真问题

到这儿,「做出来」的部分结束了。真正有意思的从这里开始。

① 锁屏回来,屏幕上只剩一句报错。看起来像任务挂了,但我核对了后端代码:客户端断开只是取消订阅,任务本身照跑、结果照样落库。也就是说那一轮其实早就跑完了,只是页面没去取。现在断线会给一张卡片说清「任务还在跑,接回就能补齐」+ 一个按钮,而且多数情况根本不用点,回前台会自动接回。

② 语音发完,输入框里还留着字。原因是识别引擎没停,迟到的一次结果把已经发出去的文字又写回了输入框。这条我没凭感觉改:先写复现脚本,拿未修复的版本跑——真的红了,再改,再跑绿。

③ 重进正在跑的会话,手机端毫无反应。三个 bug 叠在一起。修完加了兜底:12 秒没动静就自查一次。效果也很直观——修复前 90 秒一个字没有,修复后 1~2 秒就看到字在滚。

七、最后一段,是关于「通知该在什么时候响」

需求是:只有一定要审批、而且真卡住了,才通知到我手机上。

我第一版做得很直白:一看见「待审批」就推。然后被自己的手机教育了——某天深夜收到一条「任务卡在等你确认」,点进去一看,它 20 秒后自己就被批准了,纯属虚惊。

于是我去翻数据,看审批到底通常要等多久(当天可配对的 259 笔):

批准耗时中位数 8 秒;75% 在 32 秒内完成;90% 在 102 秒内完成;超过 120 秒才被批的只有 23 笔。

数据说得很清楚:绝大多数审批几秒内就完成了,因为人通常就在电脑前,顺手点了。

所以我加了一个等待门槛:默认 120 秒。满了才推,而且正文里写清「已经等了约 N 分钟」「再没人应它就会被自动拒绝」。

这次修正背后那句话,我觉得比代码值钱:

❝ 通知不该报「有事件发生」,该报「真的卡住了」。 前者只是把系统的忙碌转嫁给人,后者才是「需要人」。 ❞
八、代价,也一起说清楚
  • 后端接口没有鉴权,所以那道口令就是那把锁。
    它不是账号体系——谁拿到口令,谁就能看到全部会话。这点我接受(它是单用户设计),但口令本身就是最高机密。
  • 它跑在一个「可写层」上,不是持久卷。
    重启没事,但把容器删掉重建,数据会一起没——要升级先备份。
  • 常驻进程的配置,冷启动时可能被重写掉。
    我加的通知进程一度只存在于运行时配置里,容器一重启就静默消失。补进启动模板之后,断电恢复才真正可靠。
  • 真机验证不可替代。
    安全区、键盘、视口这几个 bug,在无头浏览器里一个都复现不出来。
九、一句话总结

功能部分其实不复杂,真正花时间的全是「用起来之后」才暴露的东西。

❝ 「能用」和「好用」之间,隔着的不是功能清单,是一堆只有真用起来才会撞上的坑。 ❞

而这些坑,没有一个是靠「想清楚」能提前绕开的——它们只会在你把它放进兜里、锁上屏、走出门之后,才一个个冒出来。

(本文由 AI 辅助创作,人工审阅后发布。文中截图均为真实使用界面,会话名称与账号信息已打码脱敏。)

相关学习资料