很多人一看“ai-engineering-from-scratch”这名字以为又是那种从零手写神经网络的教程或者某个“三天入门大模型”的速成课。其实不是这个项目说的是更实在的一件事从一张白纸开始把一个AI想法真正做成一个能扛住线上流量的工程系统。我去年就用这个思路完整搭过一套从数据采集到推理接口上线再到监控告警闭环整个过程踩了不少坑也积累了很多只有动手做过才会懂的经验。这篇就把它掰开揉碎讲讲AI工程这条链路上到底有哪些环节、每个环节怎么设计、哪些地方最容易翻车。1. 项目整体设计与思路拆解1.1 到底什么是“从零开始的AI工程”先把概念捋清楚。AI工程和前几年流行的“AI算法”是两码事。算法阶段很多人关心的是模型结构、损失函数、训练技巧甚至一篇文章就能复现一个SOTA但工程阶段模型只是链路里一个环节数据、训练、部署、服务和监控组成了一个完整闭环。我理解“from scratch”有两层意思。第一层是技术栈全部从零搭不用现有的一体化平台第二层是心智上从零建立链条意识不只是把模型训练好就结束。实际动手之前我先画了一条主线数据怎么来、怎么清洗、怎么喂给模型模型怎么训练、怎么评估、怎么迭代训好的模型怎么服务化、怎么扛并发上线之后怎么观测、怎么告警、怎么持续优化。这条主线听上去不难但每环拆开都有讲究。比如数据不是越多越好脏数据会直接拖垮训练效果模型离线指标再好看上线后线上分布一变照样拉胯服务化不光是包个接口就完事还涉及GPU显存管理、请求排队、超时熔断这些基础设施问题。1.2 为什么选择按链路分模块而不是搞大一统方案搭建时我特意避开了“一个框架全搞定”的方案。市面上确实有很多端到端的ML平台但从零开始做工程我更推荐按模块拆分。原因很实际。第一模块化方便定位问题。线上出了故障如果是单体系统排查效率极低拆开之后是数据管道挂了、训练任务崩了还是推理服务超时一眼就能看出来。第二每一环的技术选型可以独立演进。今天数据量大了可以单独扩数据管道明天换更好的模型不用动服务层代码。第三也方便团队协作。数据工程师、算法工程师、后端工程师各管一段职责清晰。当然模块化也带来了额外的通信开销和部署复杂度这要通过规范来约束比如每个模块定义清晰的接口、统一日志格式、共享配置中心我会在后面的实施环节具体讲。1.3 这套系统适合谁能解决什么问题这个项目最适合两类人看。一类是已经做了几年后端或算法想往AI工程方向拓展的工程师。你手里可能有一些零散的技能点会写Python、调过模型、用过Docker但缺少把整条链路串起来的能力。这篇的步骤和踩坑记录能把这块拼图补上。另一类是团队的技术负责人或架构师需要从0到1搭建AI能力中台。你不需要亲自写每个模块但必须知道系统长什么样、关键决策点在哪里、什么节点该做什么事情。这个项目里的设计思路和选型理由可以直接拿来做方案参考。它解决的问题也很明确在一个算力有限、人力和时间都不富裕的真实环境里如何用工程手段把AI能力稳定落地。2. 模型选型与训练落地细节2.1 模型选型不能只看效果还要算成本账很多人在这一步就掉坑里了。看到某个模型榜单效果好就直接上完全不考虑推理时要花多少卡、延迟能不能接受、显存够不够。我当时的做法是先把候选模型拉个表从三个维度打分任务效果、推理开销、生态成熟度。评估维度说明考量重点任务效果离线评测集上的准确率/召回率等不代表线上表现只作为参考推理开销单次推理所需GPU显存、延迟直接决定服务成本和并发上限生态成熟度是否有预训练权重、社区资料、部署示例影响开发效率和排障速度比如做文本分类任务当时有两个候选大模型和百亿参数小模型。效果上两者接近但小模型的显存占用只有大模型的五分之一单次推理延迟低一个数量级。线上实际跑起来小模型单卡可以扛住每秒几十个请求大模型要上多卡还得做切分成本和复杂度完全是两个量级。所以最终选了实用性更强的小模型作为主干同时在架构上留了一个“模型后门”将来如果效果跟不上了可以通过替换模型文件来升级不需要动服务端代码。2.2 训练环境的搭建与资源配置训练环节我的配置思路是“恰到好处就行不追求顶级装备”。因为整个项目是从零搭建算力有限所以要尽量发挥每一块GPU的价值。我用的是单机双卡方案一张负责常规训练一张作为备用跑验证集和特殊任务。显存管理上我提前算好每一步所需的批量大小和序列长度用公式推导出峰值显存避免训练中途显存溢出崩溃。训练时开了混合精度这能让显存占用减少约一半同时计算速度提升不少。代价是数值精度降低但在我们这种任务场景下不影响效果。如果你做的是高精度数值相关的任务建议慎重开启混合精度。同时要注意数据加载的并行度设置。训练初期我遇到一个典型问题GPU利用率只有30%数据加载线程却跑满。后来把数据读取方式从逐条读取改成批量预读同时开了多个加载进程GPU利用率才升到90%以上。2.3 数据质量比模型结构更决定成败这是整个项目里我体会最深的一点。模型结构再花哨喂进去的数据有问题效果一定好不了。训练初期我用了一批从网上爬来的公开数据没做仔细清洗结果loss怎么都降不下去看起来模型学不到东西。排查后发现问题出在近三成的数据上有的标签标错了有的文本和标签完全不相关还有大量重复样本。标签错误会让模型学到错误映射重复样本会让训练偏向这些重复内容直接拉低泛化能力。后来我花了整整三天重做数据清洗流程去重通过计算文本的哈希值把近似重复的样本剔除标签修正设计了一套规则加人工抽检的复合体系对可疑样本自动标记再人工确认处理长尾让模型先跑一轮预测把低置信度样本挑出来人工重点核查清洗完之后同样的模型、同样的训练参数离线准确率提升了接近8%。这说明一个道理与其换更强的模型不如先把数据弄干净。2.4 离线评估与线上效果的差距控制离线评估是训练之后的一个必要检查站。我在评估集上跑出了不错的分数但上线前的压测发现实际线上表现明显下滑。这里要提醒大家一个概念离线数据分布和线上数据分布几乎不可能完全一致。用户真实产生的数据往往比测试集更复杂、更脏、更具长尾特性。我的处理策略是构建“线上仿真评估集”。在训练完成之后抽取一段线上历史请求数据模拟线上真实输入来测试模型而不只是用标准的离线评测集。这个过程帮我找到了很多离线测不出来的问题比如某些特定人群的表达习惯模型完全没覆盖到。另一个细节是阈值选择。离线调优时模型输出的置信度阈值定在了0.5效果看着不错但上线后发现,这个阈值会放过很多原本该被拒绝的样本导致误报率升高。后来根据线上仿真评估重新调整了阈值才把误报率压下来。这一系列的检查做完模型才算真正达到了“可上线”的状态而不是“训练完了”就结束。3. 推理服务化与API设计实战3.1 从模型文件到线上服务的桥接模型训练完之后只是产出了一个权重文件无法直接被外部系统调用。需要一个服务把它包起来对外提供HTTP接口。这一步的任务就是要把模型封装成API服务并且要处理并发、延迟和异常等工程问题。我选了FastAPI来做服务框架主要因为它异步支持好、性能不错、自动生成接口文档调试和对接起来都很方便。再用Uvicorn作为web服务器跑起来。服务设计上我划分了两个标准接口推理接口接收业务数据返回模型预测结果健康检查接口供负载均衡和监控系统探测服务是否存活健康检查接口非常关键它能方便监控系统判断服务状态是否正常一旦接口不响应就能自动触发重启或告警避免出现“服务假死但没人发现”的尴尬状况。3.2 输入校验与数据预处理这一步是新手最容易忽略的。模型的输入格式有严格要求如果让脏数据直接进模型推理轻则报错重则产出荒谬结果。我的做法是在API层做两道校验。第一道是格式校验用数据模型检查字段是否存在、类型是否正确。比如文本分类接口要求传入一个字符串如果调用方传来一个列表直接返回参数错误不进入后续流程。第二道是内容校验比如非空校验、长度限制。文本过长会被直接截断或者拒绝避免超出模型的最大输入限制。预处理环节也不能省。原始文本进入模型前要经过分词、token化、映射到ID等步骤这些操作虽然基础但性能影响很大。我的经验是把分词器和模型一起加载到内存避免每次请求都重新初始化。实际压测中我发现预处理逻辑写得不合理会导致性能下降好几倍。比如每次请求都做正则替换、重复编译正则表达式这些都是可以提前优化掉的细节。3.3 性能优化GPU显存管理与并发控制推理服务上线前我最担心的是性能和稳定性。模型推理要占用GPU显存如果并发请求同时涌入显存可能瞬间被打爆。我的方案是模型常驻显存加载一次推理时反复使用而不是每来一个请求再加载一次控制并发数在服务里加信号量限制同一时刻进入推理的请求数量超出限制的请求进入排队设置合理的超时时间防止单个慢请求拖死整个服务使用批量推理同一时刻把多个请求拼成一个batch喂给模型大幅提高吞吐批量推理这个优化收益特别明显。单条推理时GPU利用率很低大量算力被浪费拼成batch之后GPU利用率可以到达80%以上。优化方式优化前优化后收益说明批量推理每次1条每次8条GPU利用率从40%提升到80%显存常驻每次加载常驻单请求延迟降低一个量级并发控制无限制队列限制避免显存溢出和超时雪崩3.4 服务发布与容器化部署部署上我用的容器化方案。先把服务打包成镜像再通过容器编排工具来管理。整个过程走下来最大的感受是“环境一致性”特别重要。Python依赖版本混乱是常见的坑。本地跑通的服务换个环境就跑不起来多半是依赖版本不一致。写依赖清单时我不仅锁定了主版本号连小版本号也一起锁定尽量保证可复现性。镜像构建时还有一些小技巧。基础镜像尽量选择精简版本而是不装一堆用不到的组件能有效减小镜像体积加快分发和启动速度。代码和数据文件分开处理减少镜像层数提升构建效率。模型文件比较大我用的方式是在容器启动时从共享存储拉取而不是构建进镜像里这样业务代码更新时不需要重新传递巨大的模型文件。记住一个原则发布过程要可重复环境要完全一致这样出现问题才能快速回滚和排查。4. 链路联调、监控告警与持续迭代4.1 端到端联调把整条链路真正串起来模型服务上线之后真正的工作才刚开始。端到端联调是整个项目最折腾、也最考验全局观的一个阶段。我遇到的一个典型问题是数据链路脱节。上游数据管道产出的数据进到模型服务里之后字段格式不匹配导致全链路报错。这个问题的原因在于开发各做各的接口约定虽然有了但实际实现时理解不一致。解决方式是在联调阶段使用真实业务数据走全流程而不是各模块自己用假数据测一遍就认为万事大吉。从数据接入、清洗、模型推理、结果返回到最终写入业务系统一条龙测下来问题暴露得特别快。联调阶段我做了一个“问题清单”表记录遇到的所有问题、原因和解决方式。这些问题清单后来成了非常有价值的踩坑资料后续排查同类问题时能直接检索。4.2 监控指标选择与告警阈值设定服务上线只是起点必须让系统运行状态对人是可见的。没有监控系统出问题只能靠用户反馈才发现这是灾难。我的监控体系分三层。第一层是基础设施监控看CPU使用率、内存占用、显存占用、磁盘IO这是判断机器有没有问题的基本指标。第二层是应用层监控看请求量、接口延迟、错误率、排队长度。第三层是模型效果监控看线上预测结果的置信度分布和输入分布变化。告警阈值怎么设这里有个经验阈值设得太敏感容易每天告警轰炸设得太宽松真出问题了又发现不了。我的做法是先搜集一周的基线数据再根据正常波动范围设定告警边界。以推理请求错误率为例正常情况在0.5%以下我把告警阈值设在1.5%连续触发3次才真的告警这样既避免偶然抖动误导又能及时发现问题。4.3 服务扩容与稳定性保障监控告警做起来之后我开始考虑如何保障稳定性。线上的流量不可能一成不变遇到业务高峰或者突发活动时单机服务可能扛不住。弹性伸缩策略是根据服务请求量指标比如CPU使用率和请求数自动增加或减少服务实例。刚开始部署时只做了一个服务实例后来压测发现高峰期跟前端服务联动后请求量会翻倍就配置了按CPU使用率超过70%时扩展实例数的策略。扩张实例时要注意模型加载的时间成本。模型较大加载耗时较长扩容时新实例需要时间加载模型才能提供服务。为了解决这个问题我给健康检查设置了更合理的初始化时间避免服务还没就绪就被打入流量。另一个稳定性细节是优雅退出。服务下线时要确保正在处理的请求处理完再退出而不是直接杀掉进程不然用户请求会在发布瞬间出现大量失败。4.4 模型漂移的监控与重训策略模型上线后效果会慢慢变差这是AI系统面临的一个特殊性挑战。用户的语言习惯在变业务场景在变输入数据的分布在变模型是静态的它的表现自然会被磨损。我设计了一套简单的模型漂移检测机制。每隔一段时间用当前线上输入和模型训练集的输入做分布对比如果差异超过设定阈值说明模型已经部分失效需要重新训练。重训触发之后整个训练到上线的流程要能自动化或半自动化运行。我在项目里搭建了一条定时训练流水线每隔一段时间自动触发重训并在重训之后跑评估任务评估通过才尝试上线。这里我遇到过一个问题自动重训跑出来的模型评估集分数很低但仔细排查发现是评估集数据顺序被搞乱了和数据质量无关。所以自动化链路里也必须有数据完整性校验否则会产生误判和无效训练。5. 常见问题与排查技巧实录5.1 典型故障速查表把项目里踩过的、问过的典型问题整理成了一张速查表后面排查时直接对着表找思路。现象可能原因排查思路GPU显存溢出并发过高、批量过大减少并发数降低batch size开启混合精度模型加载极慢没做常驻加载确认模型是否常驻内存/显存避免每次请求加载接口超时严重并发排队过长优化推理速度开启批量推理增加实例数预测结果全一样模型没有加载权重检查模型权重路径是否正确加载训练loss降不下去数据标签错误抽检数据集统计标签是否准确服务假死无报错健康检查不好用检查健康检查接口是否返回正常状态扩容后仍打满新实例未就绪就接流量延长就绪检测时间确保模型加载完全5.2 排查问题的方法论排查问题不分领域一套稳定的方法论十分关键。我的习惯是先看监控面板不直接猜原因。CPU打满还是显存打满错误是5xx还是超时这些信息能快速缩小排查范围。接下来看近期的变更。很多时候线上问题不是突发的而是某次代码更新或配置变更引入的。回退到上次稳定版本测试往往能最快确认是否是变更导致。最后再看日志。日志里不要只打印一行错误要打印完整的调用链信息包括请求ID、参数摘要、耗时数据。这样排查问题时才知道是入参问题还是模型推理问题。5.3 独家避坑小技巧最后分享几个实用小技巧这些是我在项目中踩过坑之后总结出来的常规文档里很少写。**第一GPU任务一定要处理OOM重试机制。**显存溢出是推理服务最常见的问题之一即便做了并发控制偶发的峰值请求也可能导致内存超限。我在客户端加了一个失败的自动重试机制遇到OOM会自动排队重试而不是直接返回失败。这个小改动让服务的可用性提升了不少。**第二模型文件不要和应用镜像绑定。**把模型文件放在对象存储或共享文件系统里应用镜像只关注代码和数据这样模型更新时不用重新构建镜像直接替换模型文件生效即可。发布时代码版本和模型版本分离能极大减少升级过程的复杂性。**第三日志一定要有请求级的trace标识。**每条请求进入服务时生成一个唯一的请求ID所有模块打印日志都带上这个ID排查问题时可以按ID拉出整条链路的痕迹极大提高定位问题的效率。**第四压测要提前做透不要上线后再验证性能。**我在上线前搭建了一套压测环境模拟了高并发流量跑了一轮提前发现并解决了大量的性能和稳定性问题。如果等到真实用户流量进来才出问题产生的负面反馈代价远比压测成本高。5.4 写给小白的上手建议如果你完全没有这方面的经验我建议先不要一上来就做大而全的系统。找一个小而实际的问题比如“给公司内部留言板做个自动分类”把一个完整的AI工程链路跑通。跑通的意思是数据有了模型能训练训练出的模型能做成接口接口能在容器里跑起来服务能被监控出问题能恢复。这一圈走下来你基本上就掌握了AI工程的核心骨架。然后再去做优化换更大的数据尝试更好的模型加入更高并发场景加自动重训流程。每一步的边际困难都是可控的。还有一个建议一定要把你的过程记录下来。AI工程的知识很大部分藏在踩坑细节里今天不记两个月后就只有模糊印象了。做这个项目最大的感受是AI工程是“技术管理”的问题不只是“算法”的问题。数据、模型、部署、监控哪一个环节都缺一不可。很多团队训练模型很厉害但上线之后问题频发就是因为工程化能力没有跟上。我个人更倾向于把这条链路当成一个稳定体系来打磨每个环节规范清晰、风险可见、失败可恢复。比起比谁训练出一个更高分的模型我觉得把一套端到端的体系稳稳运行三个月不闹脾气更符合“ai-engineering-from-scratch”这个项目名字里“工程”二字的重量。