上周我们团队把一个用了半年的BERT模型替换成DeBERTa-v3,原本计划两天完成回归测试,结果花了整整一周。不是代码重构难,而是原有的测试用例全部失效——同一个prompt,新模型给出的答案在语义上正确,但字符串不匹配,硬断言挂了300多个。后来我们不得不把所有基于文本比的测试改成基于embedding相似度加人工抽检。这件事让我彻底意识到:AI开发从头到尾都不是软件工程,硬套只会把自己玩死。
传统软件开发的本质是确定性逻辑:输入A,经过函数f,得到输出B,B是可预测、可断言、可回溯的。而AI开发,尤其是LLM应用,本质是概率映射:输入X,经过模型g,得到分布Y,每次采样都可能不同。你写不了单元测试,因为同样的问题换一种问法,答案可能结构完全不同;你写不了接口文档,因为模型的边界行为没人说得清。更致命的一点是,传统开发的Bug是代码里的逻辑错误,而AI的‘Bug’藏在训练数据里、藏在解码参数里、甚至藏在用户的措辞里。我见过太多从Java转过来的工程师,第一反应是给模型加try-catch,但模型不会throws Exception,它只会用更流畅的废话掩盖自己的无知。
如果你非要对比,我觉得最准确的类比不是‘软件’,而是‘炼金术’。传统工程师搞的是机械钟表,齿轮咬合,误差可控;AI工程师搞的是炼丹炉,配方是数据,火候是超参数,丹成是模型收敛,而开炉那一刻你永远不知道出来的是仙丹还是砒霜。我们做过一个文本分类项目,用同样的预训练模型、同样的数据清洗流程,只是随机种子从42换成2024,F1就从0.91掉到0.87。后来查了log,发现是数据shuffle顺序影响了少量对抗样本的分布。这种脆弱性在传统软件里是不可想象的——换一个随机数种子,不可能让排序算法多出几个Bug。所以AI开发里的‘质量’不是靠代码审查,而是靠数据治理和实验追踪。我们现在的做法是:每个实验必须记录data_version、model_version、prompt_version、seed、temperature、top_p,六个维度缺一不可,否则结果无法复现。这比Gradle依赖锁定要麻烦得多,但这是模型黑盒属性决定的唯一出路。
再说部署和运维,这个差异更是颠覆性的。传统服务你担心的是CPU峰值和内存泄漏,AI服务你担心的是模型漂移和数据漂移。上个月我们的用户意图识别系统准确率从92%掉到85%,代码一行没改,追查了两天才发现原因是用户群体在暑假期间发生了变化——学生用户占比升高,他们说话更口语化、更爱用缩略词,而训练数据里这部分样本只占8%。这种问题你没法通过增加服务器节点解决,只能建立监控体系,实时跟踪输入分布和输出置信度的变化,并制定定期的重训策略。我们现在的做法是每周自动抽取1000条线上数据,人工标注后混入训练集增量训练,然后跑一遍golden set,看关键指标有没有回退。这已经不是传统的CI/CD能覆盖的范畴了,更像是一种‘生物培养’——你得每天观察菌落的变化,而不是等它发臭了再排查。
所以我的核心观点很偏激:AI开发应该彻底抛弃‘软件工程’这个词,改叫‘模型养殖工程’。软件工程追求的是确定性、可验证性和可维护性,而AI开发追求的是统计有效性、鲁棒性和持续进化能力。你不能用编写单元测试的思路去评估一个概率系统,就像你不能用检查机械表齿轮的方式去给一头牛体检。真正适合AI开发的流程应该是数据驱动的实验循环:先定义业务可接受的最低指标,再构建数据流水线,然后做消融实验选模型和prompt,最后上线后持续监控漂移。其中最重要的能力不是写代码,而是设计实验的能力——你要能区分这次效果提升是模型改对了,还是数据恰好覆盖了更多易分样本。我个人建议所有AI开发团队至少配置一名有统计学背景的成员,而不是清一色的算法工程师。如果你还在用sprint排期、代码评审、版本发布那一套来管理AI项目,我赌你半年后必然陷入‘模型重构地狱’——因为新模型效果更好,但所有旧测试全挂,所有数据管线不兼容,而你根本不知道是哪个环节出了问题。