写这篇文章的起因很简单,我自己在推荐系统后端和数据平台两头都待过,见过太多算法模型在离线指标上表现亮眼、一上线上就被打回原形的案例。后来慢慢想明白一件事:算法和 infra 根本不是两个独立的团队,而是一条流水线上两端的工位。前者负责产出模型的“逻辑价值”,后者负责让这个价值在真实流量里“按预期兑现”。所谓“算法-infra协同”,说到底就是把这两端之间的缝隙填平,让模型从 notebook 到生产环境的每一站都有契约、有工具、有人兜底。
这篇文章不是理论科普,是我在真实项目里踩坑踩出来的实践总结。适合正在做推荐、广告、搜索、CV/NLP 推理服务,或者正在搭 AI 平台的算法工程师、infra 工程师、以及被迫两边都干的“全干工程师”参考。我不会列一堆高大上的架构图,只会讲清楚协同链路里的关键节点、常见故障和排查手段,以及我在实际项目中验证过的方案取舍。
1. 算法与infra协同,到底在协什么
1.1 协同的本质不是“配合”,而是接口与契约
很多人一听到“协同”,第一反应是团队之间多开会、多对齐、多互相理解。但以我的经验,这种理解基本是错的。团队之间的“态度”再端正,也解决不了线上环境不一致、模型文件命名混乱、特征口径对不上这类硬问题。
算法与 infra 的协同,本质是把两个团队各自掌握的“隐含知识”显性化成可验证的接口与契约。
拿我最常举的例子来说:算法工程师在离线训练时用 Python 3.8 + PyTorch 1.12,infra 团队线上推理服务跑的是 Python 3.6 + TensorFlow 2.5,中间没有模型转换层。结果模型一上线,预处理逻辑和训练时对不上,线上推出来的结果比离线差 20%。这件事无论开多少次对齐会都解决不了,但只要在模型交付时约定好“必须导出为 ONNX 格式 + 附带一份特征 schema 文件”,问题就从“靠人沟通”变成了“靠校验工具拦截”。
所以,做算法-infra协同,第一步不是招人也不是买平台,而是坐下来盘点:从模型训练完成到线上服务真正生效,中间要经过哪些环节,每个环节的输入输出是什么,谁能拍板定义这个接口。把这些定下来,协同就成功了一半。
1.2 现实中常见的三种协同形态
不同业务场景下,算法与 infra 的协同侧重点差异很大。我接触过的项目大致能分成三类,大家可以对照自己团队的情况看看属于哪一种。
第一种是离线链路协同,典型场景是广告点击率预估、商品推荐、用户画像这类需要海量样本训练的业务。协同的核心在数据管道和训练平台:特征怎么生产、样本怎么拼接、训练任务怎么在几百台机器上排队执行、模型训练完怎么进入模型仓库。这里的痛点往往是“算法想用的特征 infra 没上线,infra 上线的特征算法不买账”。
第二种是在线推理链路协同,典型场景是搜索排序、实时风控、图像识别这类对延迟和可用性有严格要求的服务。协同的核心在特征服务、推理引擎、资源调度和降级方案。这一趴也是最容易出线上事故的地方,因为在线服务对抖动的容忍度极低,哪怕只是一个线程池参数没调好,都可能让整体延迟从 20ms 飙到 500ms。
第三种是分布式与边缘协同,典型场景包括端边云协同的图像生成、多 agent 调度、无人集群通测一体化这类比较新的方向。协同的核心在模型分发、算力调度和结果汇聚。这里除了常规的模型部署,还要处理网络不稳定、节点异构、算力不均等问题,复杂度比前两种高一个量级。
不管哪种形态,底层逻辑是一样的:算法提需求,infra 提供能力,两者之间需要一层薄薄的、明确的“协同层”来管理数据、模型、资源和监控。
1.3 协同不好,谁来买单
协同出问题,表面上都是系统抖动、效果波动,实际上的损失非常具体。我在项目复盘时算过一笔账,大家可以感受下。
模型交付延期的损失最容易理解。算法团队花了两周训出一个离线指标提升 5% 的新模型,但因为模型仓库没有自动化验收流程,光等 infra 团队手动配环境就等了三天。这三天的机会成本不是一个简单的数字,在大促流量高峰期,可能值几十万甚至上百万的营收增量。
回滚事故的损失更直接。某一次线上推理服务发版后,因为新模型对某个特征的取值域假设错误,导致服务直接抛异常,整体可用性从 99.95% 跌到 98%。按当时每秒几百的 QPS 算,影响的范围不大,但后续排查和修复消耗了 5 个工程师半天时间,事情的真正成本就出来了。
还有一种是算力空转。训练集群的资源利用率常年只有百分之二三十,不是因为机器不够用,而是因为算法任务提交后要排队等 infra 工程师手动审核资源、安装依赖。机器在跑,但跑的都是垃圾任务;人很忙,但都在做本该自动化的操作。这种慢性损耗最容易被忽视,也最伤团队士气。
2. 协同链路上的关键环节与设计取舍
2.1 数据契约:把特征口径定死在配置里
我见过太多因为特征口径不一致导致的线上事故,几乎每一个都可以追溯到“这个特征应该长什么样”没有形成契约。特征到底用 T-1 的离线数据,还是用 T+0 的实时数据?用户年龄缺失时是填 0、填 -1 还是直接丢弃样本?这些细节如果只存在于某个算法工程师的 notebook 里,线上环境一定会出问题。
我的做法是建立一份“特征清单”,用 YAML 或者 JSON 格式维护在代码仓库里。每个特征记录名字、类型、来源、更新频率、缺失值处理方式、上线状态和负责人。这份文件同时被离线训练管线和在线特征服务读取,任何一方修改都必须走代码评审流程。
特征清单的维护成本不低,尤其是业务快速迭代时很容易落伍。但它的价值在于:每次模型上线前,infra 可以用脚本对比训练时使用的特征版本和线上特征服务的输出版本,一旦发现不一致就直接阻断发布。这个自动化校验比我见过的任何口头沟通都靠谱。实际落地时不用一步到位,可以从最关键的核心特征开始维护,慢慢扩。
2.2 环境对齐:训练与线上必须“二进制级”一致
模型训练环境和线上推理环境不一致,是导致效果衰减的头号原因。这里说的不一致不只是 Python 版本、依赖库版本,还包括 CPU 指令集、GPU 驱动、CUDA 版本这些“看不见”的底层差异。我在一个图像生成项目里就遇到过:离线训练用 A100,线上推理用 T4,结果同一个模型在 T4 上的显存占用比预估高了 40%,直接导致服务 OOM。
解决环境对齐问题,目前比较靠谱的方案是容器化 + 镜像版本管理。训练和推理共用一套基础镜像,镜像里锁定操作系统、CUDA 版本、Python 版本和所有核心依赖的精确版本号。模型交付时不仅交付模型文件,还要交付一个包含环境指纹的 manifest 文件,infra 团队发布时校验这个指纹是否和线上运行时一致。
有些团队觉得这样太麻烦,喜欢让算法工程师直接把模型文件丢给 infra 团队“自己看着办”。省事是真的省事,但后患无穷。一旦出现线上效果和离线不符的问题,两边会进入极其低效的“互相甩锅”模式。不如一开始就把环境对齐责任明确到机制上,而不是指望某个人记住。
2.3 推理服务资源画像:别等压测了才想起来算
很多算法工程师对推理服务的资源评估没有概念,默认“线上机器越大越好”。但资源给多了浪费,给少了线上抖动,资源画像其实是算法和 infra 协同中非常关键的一个环节。理想的做法是模型上线前,算法团队根据模型的计算图结构和输入输出尺寸,粗略估算一次推理的耗时和显存占用,然后和 infra 一起评估需要的副本数。
以我常用的一个估算方法为例:假设模型单次前向推理在 1 张 T4 GPU 上耗时 5ms,单副本可以处理的 QPS 大约是 1000ms / 5ms × GPU 利用率系数 0.6,也就是 120 QPS。如果线上预期峰值 QPS 是 2000,至少需要 2000 / 120 ≈ 17 个副本。这只是理论值,还要考虑特征拉取、预处理和后处理的耗时,通常要再乘 1.5 到 2 的冗余系数,也就是 25 到 34 个副本。
这种估算不要求非常精确,它的核心价值是让算法和 infra 在发布前就对“需要多少资源”达成共识,而不是等到线上被打爆了再临时扩容。而且有了这个基线,后续做弹性伸缩时也能有据可依,比如根据实际 P99 延迟和 CPU/GPU 利用率自动调整副本数。
2.4 可观测性:让算法团队自己就能看日志
线上服务出了故障,最麻烦的不是修,而是“为什么算法看不到日志”。很多公司的架构里,日志和监控都掌握在 infra 团队手里,算法工程师遇到问题只能靠“截图 + 描述”来找 infra 帮忙查。这个协作效率低到令人发指,一个简单的特征缺失问题,可能要来回沟通一小时才能定位。
我的建议是,算法和 infra 协同的一个重要目标,就是让算法团队能够自助完成线上问题的初步诊断。具体做法:至少在推理服务接入日志平台、指标监控和链路追踪时,给算法团队开只读权限;把模型输入输出、特征取值、耗时明细都打点记录下来;关键指标如推理耗时、错误率、特征命中率做成仪表盘。
这套能力建设起来之后,线上效果变差时,算法工程师可以自己先看特征分布有没有偏移,再看推理耗时有没有异常,最后才决定是否需要找 infra 排查。很多问题到了这一步就已经定位了,真正需要 infra 介入的只是少数底层故障。
3. 一次算法迭代上线,协同过程长什么样
3.1 从离线实验到灰度上线的完整链路
一次真实的算法迭代上线,涉及的环节远比大多数人想象中多。我按自己的项目经验大致梳理一下:首先是样本生成和特征工程,这一步由算法和数仓同学配合完成,产出训练集和验证集;然后是模型训练,训练平台会读取样本数据、启动训练任务,完成后把模型产物和评估指标写回模型仓库;接着是模型评估,infra 会跑一组预设的业务指标(比如推荐场景的 CTR、覆盖度),判断新模型是否满足上线门槛。
通过了评估,就会进入发布流程:配置中心里新增模型版本、调整分流比例、设置回滚策略,然后发布系统自动拉取新模型镜像,在灰度集群上启动新版本服务,逐步放量。这个过程中,任何一环节的自动化程度低,都会拉长整个迭代周期。所以我一直强调,协同的基础设施建设要优先于算法效果的优化,否则你再怎么调模型,也难以稳定跑出价值。
3.2 模型版本管理和配置下发的实操细节
模型版本管理是个容易被人忽略但极其重要的小事。很多人喜欢用“model_v2_final_final.onnx”这种命名,我强烈不建议。正确的做法是给每个模型分配一个全局唯一的版本号,比如“recall_v3.2.1”,并把这个版本号写进模型文件的元数据里,而不只是反映在文件名上。
配置下发方面,推荐用配置中心而不是改代码发版。模型的分数阈值、特征开关、候选集大小这些参数,都应该支持在线调整。我曾经遇到过一个场景:大促当天召回模型的分数阈值需要调低 10%,如果走发版流程,至少需要半小时,但在配置中心直接改参数,1 分钟内就能生效。这就是协同工具带来的实际效率提升。
另外,模型文件和配置文件最好分离存储。模型文件走对象存储,配置走配置中心,发布系统会把两者组合成一次完整的发布单。发布单里必须写清楚:版本号、变更说明、影响范围、回滚方案、操作人。这个看似繁琐的流程,在出问题的时候能帮你省下大量抢救时间。
3.3 压测与灰度回滚:预案要提前写好
压测不是 infra 团队单方面的事,算法必须参与制定压测目标。比如搜索排序服务,你需要知道新模型在 2 倍峰值 QPS 下的 P99 延迟是多少,以及特征服务被拖垮时,主服务能不能通过降级策略继续返回结果。
我在实际项目中常用的做法是:上线前先做离线压测,用录制好的线上流量回放,观察新旧模型在资源消耗、延迟分布上的差异;然后做影子流量验证,把线上真实请求复制一份打到新模型上,不直接影响线上结果,只做比对分析;最后才是按 1%、5%、10%、50%、100% 的阶梯灰度放量。每一级灰度都设置自动监控,如果错误率或延迟超过阈值,系统自动回滚到上一个稳定版本。
回滚预案必须在发布前写好,而且要和配置中心联动。建议在代码仓库里保留稳定的旧模型版本和相关配置,一旦需要回滚,只需一键切换。切不可等到事故发生时,再去找旧模型文件放在哪、重新构建镜像,那会让故障时间成倍拉长。
4. 常见问题与排查技巧实录
4.1 线上推理延迟突然抖动,先查冷启动和资源争抢
这是在线推理服务最典型的问题。现象是 P99 延迟平时 30ms,某天突然变成 300ms,但平均延迟变化不大。初次遇到这种情况,很多人会怀疑是模型变慢了,其实大概率是冷启动或资源争抢。
冷启动指的是服务扩容后新建的 Pod 首次接收请求时,需要加载模型权重、初始化 CUDA 上下文,这个时间可能长达几十秒。如果流量突发造成扩容,扩容期间新 Pod 的首次请求就会超时。排查方法很简单:看监控里是否有新建 Pod 的时间点和延迟峰值重合。解决思路包括预加载模型、提前预热线程池、对新建 Pod 做健康检查后才放流量。
资源争抢则是另一个隐藏的坑。多个推理服务共享同一台 GPU 或同一批 CPU,某个服务的流量高峰会拖慢其他服务。排查思路是看节点维度的 CPU/GPU 利用率,如果出现某个节点利用率飘到 90% 以上,大概率就是资源争抢导致的延迟抖动。这时候需要做的不是优化模型,而是调整 Pod 的资源配额、把高优服务隔离到独立节点池。
4.2 训练与线上特征不一致,问题往往藏在预处理里
模型离线效果正常、线上效果崩盘,十有八九是特征不一致。我自己遇到过最离谱的一次:离线训练用的是“用户过去 7 天点击商品类目分布”,但线上特征服务实现的是“用户过去 7 天点击商品类目计数”。分布特征做了归一化,计数特征没有,模型的输入分布完全变了,效果自然不可控。
排查这类问题,首先要做的是特征口径审计:逐个人工核对离线样本里的特征取值和线上特征服务返回的取值是否一致。这个工作很枯燥,但必须做。其次是建立一套特征一致性比对工具,选择一部分线上真实请求,把线上特征服务返回的特征值记录下来,拿到离线环境去和训练样本对比分布。
更系统的方案是,在训练时就使用与线上一致的“特征副本”。比如离线训练时的样本特征,直接调用线上特征服务批量生成。虽然会增加训练管线的复杂度,但能从根本上避免训练和线上特征代码逻辑分叉的问题。
4.3 模型效果与离线不符,先查样本选择和实时分布变化
排除特征一致性之外,模型线上效果远差于离线效果,还有两个常见原因:样本选择偏差和实时分布变化。
样本选择偏差很好理解。离线训练用的是历史日志,在训练时你采集到的样本已经是当时线上策略的结果。如果线上策略已经调整过,比如推荐列表只展示高热内容,那么训练样本里低热内容的样本量就会非常稀疏,模型自然学不好这类内容。排查方法:对比训练样本的标签分布和线上实时反馈的分布,看差异是否明显。
实时分布变化则是数据漂移问题。用户行为、内容热度、商品库存都会随时间变化。离线评估用的是过去的数据,线上面对的是当前的流量分布,两者不一致就会导致效果衰减。建议建立数据漂移监控,定期对比特征分布和标签分布的 PSI(Population Stability Index),一旦 PSI 超过阈值就触发告警,提示算法团队重新训练或调整特征。
4.4 资源冲突与故障逃逸,怎么快速定位到根因
最后讲一个比较容易被忽略的点。很多线上故障其实不是算法逻辑错了,而是基础设施层面出了问题,但故障表象却表现为算法效果异常。比如某个 GPU 节点出现了 ECC 错误,导致推理结果错误率升高;再比如某台宿主机的磁盘 IO 异常,导致特征服务读取慢,进而拖垮所有依赖特征的推理服务。
快速定位这类故障,关键在于架构设计时就要考虑“故障逃逸”。每个算法服务最好都能和底层资源解耦:比如推理服务通过服务网格调用下游,某个节点出问题时能自动剔除并摘流量;再比如特征服务做多副本部署,单副本故障时自动 fallback 到本地缓存。有了这层容错设计,底层资源故障的影响范围才能被控制住。
我汇总了一份排查速查表,供大家参考:
| 问题现象 | 可能根因 | 初查路径 | 常用解法 |
|---|---|---|---|
| P99 延迟突然上升 | 冷启动、资源争抢、下游依赖变慢 | 检查扩容事件、节点利用率、下游调用日志 | 预热、隔离节点池、超时降级 |
| 错误率升高 | 模型输入异常、推理算力出错、代码 bug | 看错误日志分布、对比新旧版本 | 快速回滚、增加输入校验 |
| 效果与离线不符 | 特征不一致、样本偏差、数据漂移 | 特征审计、分布对比、PSI 监控 | 统一特征代码、重建训练集 |
| 推理服务 OOM | 显存预估不足、内存泄漏 | 看显存/内存监控曲线 | 扩容、调 batch、优化模型结构 |
| 整体服务不可用 | 底层节点故障、网络分区 | 看基础设施监控、链路追踪 | 故障转移、多可用区部署 |
5. 工具选型解析
5.1 主流平台与框架怎么选
聊完理念和流程,说说实际工具。算法与 infra 协同涉及的工具链很长,从训练调度、特征平台到推理服务、实验系统,每一个环节都有成熟的开源方案。
训练调度方面,Kubernetes + Volcano 是目前比较主流的选择。Volcano 支持 gang scheduling,可以保证一组训练任务要么全部启动、要么全部不启动,避免因为部分 Pod 启动失败导致整个训练任务卡住。如果团队规模较小,Apache Ray 也是个不错的选择,特别是对于 Python 生态的强化学习和数据处理任务,Ray 的上手成本比 K8s 低很多。
特征平台方面,Feast 是目前知名度较高的开源方案,它提供特征注册、在线离线一致性校验、特征服务 API,基本能满足中小团队的需求。如果业务特征特别复杂,比如大量实时特征拼接、长期用户序列特征,建议在 Feast 基础上进行二次开发,或者参考它的架构做自研。
推理服务框架的选择,需要综合考虑模型类型、硬件资源和团队熟悉度。NVIDIA Triton 是我个人用得最多的,它支持多框架模型、动态 batching、并发模型加载,无论是在线推理还是离线批量推理都适用。如果团队深度绑定 PyTorch 生态,TorchServe 也很顺手。KServe 更像是一个完整的服务化平台,负责 K8s 上的自动扩缩容和流量管理,但灵活性相对低一些。
5.2 没有统一平台时,小团队怎么过渡
不是所有团队都有资源搭建完整的 AI 平台。我在创业公司带过团队,深知资源有限时不可能面面俱到。这时候我的建议是,不要一上来就追求“平台化”,而是先用轻量工具把最痛的地方补上。
模型存储可以先放在 Git LFS 或者 S3 上,配合一个简单的模型清单表格;发布流程先用脚本 + Webhook 半自动化,人工审批留一个环节即可;监控体系直接上 Prometheus + Grafana,把推理服务的 QPS、延迟、错误率、CPU/内存首先覆盖起来;配置管理用一个开源配置中心,比如 Apollo 或 Nacos,不用自研。
这套组合能覆盖 80% 的协同需求,而成本几乎是零。等到模型数量和业务复杂度上来了,再考虑往平台上迁移。关键在于,哪怕是最轻量的方案,也要保证“接口一致、流程可追溯、监控能发现问题”这三点。架构可以简陋,契约和闭环不能少,否则协同问题永远不会真正解决。
6. 一点心法,送给正在搭桥的同学们
6.1 协同的尽头,是让“边界感”从模糊变精确
做了这么多年团队协作,我最大的体感是:算法和 infra 之间的矛盾,大部分不是利益冲突,而是边界模糊。大家都觉得“这事应该是对方管的”,结果事就掉在缝里了。而一旦你愿意花大力气把这些边界定义清楚,很多冲突会自然消失。
什么叫边界清晰?就是模型文件由算法负责产出并上传到指定仓库,但发布到线上必须由 infra 统一管控;特征口径由算法定义,但特征服务由 infra 负责维护,任何一方变更都要通知另一方并走审批;线上监控由 infra 搭建,但算法团队必须对自己模型的健康度负责,收到告警要第一时间响应。规则可能会让人觉得很繁琐,但它是协同效率的基石。
6.2 我自己踩过最大的一个坑
最后说一个我自己真实踩过的坑。曾经在一个项目里,我们辛辛苦苦搭建了整套发布和监控体系,但唯独漏了“模型文件过大”这个细节。有一个多模态模型打包后超过 5GB,发布系统默认超时时间是 10 分钟,结果每次发版都失败,infra 和算法互相排查了很久才发现是超时设短了。
后来我们定了一个规矩:发布前先检查模型文件大小,超过 1GB 就要走专门的存储和加载方案,禁止直接打进镜像。这个规矩看起来很小,但它让我明白了,算法-infra协同不是靠一次大的架构设计就能一劳永逸的,它是在一件件小事里逐渐打磨出来的。
如果你现在正被算法和 infra 的协作问题折磨,我的建议是:不要指望找到一套完美的架构,而是从当前最痛的 1 到 2 个环节入手,把它们用最小的成本做扎实。先把模型发布流程、特征校验、监控告警这三件小事做自动化,你会发现团队之间的沟通成本、线上事故率、模型迭代速度都会有肉眼可见的改善。