第7章 初版完成,测试漏洞百出
许文远把手机倒扣在桌上,屏幕还亮着十几个未读消息的红点。
“第一批测试名单拉好了,”他搓了搓脸,“二十个,都是论坛里信得过的老哥,有做财务的,有自由职业的,还有俩人天天记账打卡,堪称人形Excel。”
陈默没抬头,手指在键盘上敲得飞快,终端里滚动着刚跑完的压力测试日志。
“上传量撑得住吗?”
“目前看,五百并发没问题,但再往上,数据库就开始抽风。”
“抽风?”
“就是卡顿、延迟、偶尔直接502。”许文远抓过鼠标,切到监控面板,“你看这儿,请求堆积在队列里出不去,像是厕所堵了下水道。”
陈默盯着那条飙升的曲线,眉头一跳。
“不是说加了缓存?”
“加了,但缓存命中的时候像开挂,不中的时候像断网。”
两人对视一眼,同时开口:
“模型太重。”
“OCR拖后腿。”
说完又沉默。
初版是跑通了,可真让用户用起来,怕是三分钟就卸载。
***
他们从头开始拆问题。
第一轮测试反馈很快汇总过来,清一色差评刷屏。
>“拍照识别慢得像老年机”
>“买包盐都给我归类成‘奢侈品’”
>“昨天记的账,今天打开全没了!”
最后这条让陈默猛地坐直。
“数据丢了?”
“不是全丢,是部分用户的历史记录查不到。”许文远调出日志,“看起来像是同步机制出了岔子,前端显示提交成功,后端其实没写进去。”
“事务没回滚?”
“回滚了,但有个时间窗口——用户点击确认和系统落盘之间,如果服务重启,就会掉单。”
陈默摘下眼镜,捏了捏鼻梁。
这问题不大,可一旦发生,用户体验直接崩盘。谁愿意辛辛苦苦记一天账,结果一场白干?
“得加个本地缓存兜底。”
“加是能加,但手机存储有限,图片+结构化数据一起存,用户迟早嫌占空间。”
“那就分层。”陈默重新戴上眼镜,“高频访问的数据放本地,冷数据自动上传归档,删图不留痕。”
“行,我这就改。”
许文远正要敲代码,忽然“嘶”了一声。
“怎么?”
“你猜我刚在GitHub看到啥?”他转过屏幕,“有人把咱们的commit记录扒下来,做了个对比分析帖,标题叫《从v0.3.0看草台班子如何造火箭》。”
陈默凑过去看了一眼。
帖子图文并茂,把他俩三天前写的每行关键代码都标了注释,有夸的,也有喷的。
>“异步任务没加超时控制,纯属埋雷”
>“分类靠关键词匹配?建议改名叫人工智障”
>“但……这帮人真敢想”
最后一句被顶到了热评第一。
陈默嘴角动了动。
“被人当笑话看,难受吗?”
许文远笑了:“难受个屁,他们骂得越狠,说明越在意。要是没人理,那才叫完蛋。”
“那就别让他们失望。”
***
修复工作从晚上八点持续到凌晨两点。
他们先重构了文件上传链路,把原本串行的“识别→分类→存储”改成并行处理,中间加了个消息队列做缓冲。
接着优化OCR模型,砍掉冗余层,换成轻量级网络结构。
“现在识别速度提升四倍,准确率掉了五个点。”许文远看着测试结果,“不过用户手动修正的数据我们还在收,等积累够了,拿来做增量训练。”
“够用。”陈默点头,“先让用户能用,再让用户爱用。”
最难搞的是数据丢失问题。
他们试了三种方案:本地双写、事务日志备份、分布式锁。
最后一个太重,前两个各有漏洞。
“要不加个强制同步开关?”许文远提议,“用户提交时弹个提示:‘立即同步会耗电,是否等待Wi-Fi?’”
“不行。”陈默摇头,“普通人不懂这些,只会觉得烦。我们要做的不是教用户适应系统,而是让系统跪着伺候人。”
许文远愣了两秒,笑出声:“这话要是被以前老板听见,非开了你不可。”
“所以我现在自己当老板。”
最终他们决定采用“影子写入”机制——每次用户记账,数据先写本地,再异步推送到服务器,成功后标记为“已同步”,失败则保留离线记录,下次联网自动补传。
“有点像谈恋爱。”许文远边敲边说,“你说你会回来,我不信,但我先把行李留在你家。”
“那你这比喻挺渣。”
“现实本来就很渣。”
***
第三轮测试开始于清晨六点十七分。
许文远趴在桌上睡了不到两小时,被邮箱提示音吵醒。
他揉着眼睛点开新邮件,是第一位测试用户发来的截图。
一张早餐摊的收据照片,系统五秒内完成识别:
>时间:2025-04-10 06:08
>商户:老张煎饼果子
>类别:餐饮外卖
>金额:8元
>备注:加蛋加肠
下面还附了一行手写备注:“这次没卡,也没丢,牛逼。”
许文远把屏幕转向陈默。
陈默正在喝第四杯速溶咖啡,听到动静看了过来。
他没说话,只是伸手点了点截图右下角的时间戳。
“六点零八分下单,六点十三分识别完成,六点十四分同步成功。”
“响应时间五分半钟,还是偏高。”
“但至少没崩。”
“没崩只是及格。”陈默放下杯子,“我们要的是碾压。”
许文远深吸一口气,重新打开代码编辑器。
“接下来干哪块?”
“算法。”
“你想上模型融合?”
“单一模型总有盲区,人眼都能看错,何况机器。”
“你是说,让多个小模型投票决策?”
“对。比如一个专攻手写字体,一个专攻打印小票,一个负责商户联想,最后综合判断。”
“资源占用会翻倍。”
“那就压缩。”
“怎么压?”
“砍掉那些永远用不上的特征。”
“比如?”
“比如‘发票章的颜色’。”
许文远愣住,随即大笑:“谁他妈关心章是红是黑!”
笑声在狭小的办公室里撞来撞去,惊飞了窗外一只麻雀。
他们继续改。
前端界面简化操作路径,后端服务拆分成独立微模块,数据库加上读写分离。
中午饭是楼下便利店的饭团,凉的。
许文远咬了一口,发现馅料漏了,糊了半手。
他没擦,直接拿纸巾包住手继续敲。
下午三点,第五轮测试启动。
二十名测试用户同时上传历史账单,总数据量超过一千条。
监控大屏上,CPU使用率一度冲到91%,但没有崩溃。
所有请求在八分钟内处理完毕,错误率低于百分之二。
许文远长出一口气,往后一仰,椅子发出不堪重负的吱呀声。
“活了。”
陈默盯着屏幕,手指无意识地敲击着桌面,节奏和代码提交的频率一致。
“还不够。”
“你还想咋样?”
“我要它快到让用户感觉不到延迟,准到连他自己都怀疑记忆。”
“你这是要造神。”
“我不是造神,我是造工具。”
“工具也得讲科学。”
“科学也是人定的。”
许文远翻了个白眼,正要反驳,忽然发现日志里跳出一条异常记录。
“等等……这个IP地址,怎么看着眼熟?”
陈默立刻凑近。
那是一条来自外部的批量爬取请求,伪装成普通用户,试图下载大量已识别的账单数据。
“有人在偷训练集。”
“手法很专业,带随机延时,避开频率限制。”
“查来源。”
许文远快速反向追踪,路由跳转了七层,最后一站在某云服务商的匿名节点。
“典型的商业侦察。”陈默冷笑,“等不及正面打,先来摸底牌。”
“要不要反制?”
“不急。”
“为啥?”
“让他们看。”
“啊?”
“看清楚点。”陈默嘴角微扬,“我们到底有多不一样。”
他打开后台管理界面,在数据出口处加了一道动态混淆逻辑——所有被爬取的数据都会混入一定比例的干扰项,看似真实,实则错乱。
“等他们拿这些脏数据去训模型,结果只会越来越傻。”
许文远反应过来,笑得差点从椅子上摔下去。
“高,实在是高。”
两人相视一笑,又低头继续干活。
夜色再度降临,城市灯火次第亮起。
屋里的显示器依旧闪烁,一行行代码如流水般划过屏幕。
陈默忽然停下,看向许文远。
“你觉得,咱们做的到底是个啥?”
许文远想了想:“一个记账的?”
“不止。”
“那是什么?”
陈默没有回答,只是重新敲下一行命令:
git commit -m “v0.7.0,漏洞清零,准备上线“

