
从 0 到 1 做大模型平台这件事听起来像是一个“重投入、大工程”的故事但真正落地之后回头看最核心的问题反而不是模型本身而是工程化的坑。我过去几年一直在做 AI 基础设施相关的工作这类项目最真实的起点往往不是“我们要搞个大平台”而是业务方跑过来问“我这边有一个 70B 的模型要微调你能不能给我几块卡还有一个对话场景要在 2 秒内返回你能不能让模型服务稳定上线”问的人多了你才发现必须从零搭建一套通用的训练和推理平台而不是今天帮 A 组配环境、明天帮 B 组手动部署容器。这篇文章就以得物这类电商业务为背景聊一聊从算力层到调度层、从训练到推理、从模型上线到持续观测到底要经历哪些关键环节踩过哪些坑以及为什么某些方案在选型时“非它不可”。无论你是在企业里负责 AI 基础设施还是打算自建大模型平台的算法工程师这篇文章应该都能给你一份可以照着做的参考。1. 项目背景与整体设计思路1.1 为什么要做“通用”的训练和推理平台得物的业务场景里有大量需要大模型能力的环节商品信息理解、拍照搜同款、个性化推荐文案、客服自动应答、内容安全审核、多模态图片识别等。这些需求并不属于某一个团队而是分散在多个业务线里。如果没有一个通用的平台最常见的局面就是每个业务团队各搞一套训练环境各推理各的模型结果就是 GPU 利用率低下、镜像重复构建、模型上线流程混乱一个模型从训练到上线往往要花几周甚至一个月。“通用”这个词在平台设计里意味着两件事第一训练和推理要解耦业务方不关心底层用了哪些 GPU、有没有分布式训练框架只要提交任务、拿到结果第二不同业务的需求要能在同一个平台上满足比如一个有 7B 模型的微调任务和一个 70B 模型的全量训练任务可以跑在同一套资源池上只是调度策略和资源配置不同。这个平台的定位不是做一个“大而全”的 AI 中台而是把大模型落地的最短路径打通。要做到这一点架构上有几个关键点必须提前想清楚训练和推理共用底层的 K8s 集群但是通过 namespace、ResourceQuota 做资源隔离。模型统一存到对象存储和模型仓库训练完成后直接“注册”而不是“拷贝”。所有在线推理服务统一走一套网关方便做灰度、限流和回滚。对照这个思路我们的整体架构就是最底层是算力池上面是资源调度与任务编排再上面是训练服务和推理服务最上层是对业务开放的 API 与控制台。1.2 平台的核心技术选型训练框架、推理框架与调度器技术选型是整个项目最容易反复纠结的阶段。训练框架现在基本没得选PyTorch 已经是大模型训练的事实标准配合 DeepSpeed 或 FSDP 做分布式训练也有人会用 Megatron-LM 做大规模预训练但考虑到得物的主要场景是微调和增量训练PyTorch DeepSpeed/FSDP 的组合足够通用社区资料多排查问题也快。推理框架是另一个关键决策点。vLLM 和 SGLang 是当前最主流的两个开源推理引擎两者都支持 continuous batching连续动态批处理能把 GPU 的利用率拉高很多。相比早期一次只处理一个请求的方案continuous batching 的思路是不再等一个请求完整生成完再处理下一个而是每个 token 生成完就释放显存、立刻插入新请求。效果就是单卡吞吐量可以提升数倍。我们在实际压测中7B 模型在 A100 上配合 vLLM吞吐量能到 1500 tokens/s 以上完全够支撑内部多个对话和内容生成业务。最核心的调度层我们还是落在 Kubernetes 生态里训练任务用 Volcano 或自研的 controller 来管理推理服务用 KServe 来做模型服务化。K8s 本身不擅长直接跑分布式训练任务比如多机多卡任务的启动顺序、rank 分配、容错机制都需要额外的一层来编排所以训练和推理的调度必须是分开的。1.3 平台的总体架构和各模块职责平台按模块可以分成四层算力资源层GPU 集群、对象存储、并行文件系统、镜像仓库。这一层负责“有什么”。平台服务层任务管理提交、排队、调度、监控、推理服务管理部署、扩缩容、灰度、模型管理注册、版本、发布。能力开放层提供统一 API、SDK 和可视化控制台让算法同学能自助完成训练、评估、上线。业务应用层各业务线的算法应用接入进来调用训练服务和推理服务。这种分层方式带来的好处是任何一层都可以独立演进。比如后面需要加入国产算力卡的支持只要在资源层适配驱动和运行环境平台层不需要改动。再比如说某天业务方突然需要一套全新的推理框架也只需要在平台服务层新增一个适配器不需要动到底层 K8s 集群。2. 训练平台建设从资源池化到分布式训练加速2.1 算力池化与资源调度策略训练平台要解决的第一件事是让算法工程师“想用卡就能用卡”但又不至于让资源被无效占用。我们当时的做法是把所有 GPU 节点收编进一个大的 K8s 集群通过 Node 的 label 区分 GPU 型号A100、V100、H800 等然后按业务线做 ResourceQuota 和 PriorityClass 的分级。调度策略上我们踩过一个比较大的坑一开始用了默认的 K8s 调度器遇到多卡任务它会很“随意”地把 Pod 分散到不同节点上。如果节点间的 GPU 卡是走 PCIe 互联跨节点通信带宽可能远低于节点内部 NVLink直接导致多卡训练效率暴跌。后来我们给调度器加了拓扑感知策略优先把同一个任务的 Pod 调度到同一个节点尽量保证用满节点内的 NVLink 带宽。训练任务的优先级也需要认真设计。我们分成三个优先级高优先级给线上模型热修和紧急训练任务中优先级给日常迭代微调低优先级给实验性任务或 preemptible 任务。低优先级任务可以被高优先级抢占回收的 GPU 立刻给高优任务用这样既能保证紧急任务及时跑起来又不至于让 GPU 空闲。GPU 资源碎片化也是一个常见问题。70B 模型用 DeepSpeed ZeRO-3 微调可能需要 8 张 80G 或者 16 张 40G 的卡但如果集群里刚好只剩 6 张空闲任务就永远排不进去。我们后来在调度器里加了“Binpack 优先”的策略也就是尽量“塞满”已有节点减少碎片。同时在任务侧也允许算法同学把模型并行度调小、梯度累积步数调大适配更少卡数的小资源池。2.2 镜像、数据与代码版本管理的工程化训练平台不像本地 PyCharm 里按一下运行它在云端跑的是一套不可变的环境。镜像要解决的核心问题是“环境一致性”今天跑通的环境三个月后还要能原样重建出来。我们的做法是把 PyTorch、CUDA、驱动版本、常用工具库都固化成一套“基础镜像”再给不同团队打上各自依赖的 tag平台不直接运行裸的 Python 脚本一律要求先构建镜像。基础镜像的构建不是一次搞定的最初团队经常遇到“训练跑到一半说缺库”的问题。后来我们规定所有训练任务必须先在平台指定的镜像列表里选一个基础镜像然后通过 requirements.txt 或 environment.yaml 安装依赖平台在提交任务时做一次镜像构建和缓存。镜像缓存的关键是把不常变的层放在 Dockerfile 前面把频繁变的代码和依赖放在后面这样能大幅降低构建时间。数据这块得物的训练数据以图片和文本为主总量不小。平台必须避免每次训练都把数据拷贝到本地磁盘的做法否则光等数据就浪费半小时。我们统一走对象存储 并行文件系统的组合大数据集先放对象存储训练任务启动时通过 JuiceFS 或 Alluxio 做缓存加速第一次训练时读冷数据会慢一些之后同一份数据再跑就直接走缓存。文本类和表格类的小数据集则直接做成数据集版本跟模型版本一一对应。代码和实验版本管理同样重要。我们让平台的“训练任务”绑定一个 git commit id 和一份数据集版本号配合 Experiment Tracking 记录超参数、loss、指标。这样任何一个模型产物都能追溯到“哪个代码版本 哪份数据 哪组参数”训练出来的出了问题可以直接回滚到上一个可用版本。2.3 分布式训练的加速与容错大模型训练的效率问题主要在三个地方数据加载、通信、Checkpoint 保存。数据加载的瓶颈很隐蔽。很多团队在用 DataLoader 时设了 num_workers但在大规模训练里如果数据是在远端对象存储上每个 step 都可能触发网络读取GPU 只能干等。我们当时用了一个很“土”但有效的办法先把数据打散成大量小文件预取到本地 NVMe 缓存盘再在训练启动前做一次数据预热训练过程中用独立的进程持续预取下一批数据保证 GPU 永远有数据可算。通信优化方面最常见的问题是网络拥塞。多机训练时不同机器之间的梯度同步非常依赖网络的稳定性。如果机间网络带宽不够或者交换机配置了流控导致 TCP 重传训练速度会急剧下降。我们的做法是核心训练集群之间用 RDMA 网络而普通任务走 TCP 网络调度器根据任务规模和优先级决定走哪套网络平面。Checkpoint 是训练容错的核心。大模型的 checkpoint 经常有几十 GB全量保存一次要几分钟如果每 10 分钟存一次光保存就占了大量时间和磁盘空间。我们做了几层优化一是异步保存主训练过程不用等 checkpoint 写完二是只保存优化器状态和模型权重的增量定期做一次全量快照三是把 checkpoint 直接写到对象存储本地节点故障后新节点可以直接从远端恢复。我特别想强调断点续训的验证。有一次我们一个 7B 模型训练到第 120 个 epoch集群节点硬件故障了调度器自动把任务调度到了新节点上但训练进程起不来。排查了半天才发现 checkpoint 里保存的 rank 信息和节点 hostname 绑死了恢复时匹配不上。后来我们把 checkpoint 里的元数据改成只依赖 world_size、rank 等逻辑信息才彻底解决这个问题。3. 推理平台建设服务化、弹性伸缩与高可用3.1 把大模型封装成标准服务从 vLLM 到 SGLang训练好的模型最终要变成线上可用服务第一步是把模型跑在推理引擎上。我们的标准做法是用 vLLM 或 SGLang 做推理后端通过 OpenAI 兼容的 API 暴露给上层业务。SGLang 是最近很多团队会考虑的方案它的核心优势是 RadixAttention也就是缓存前缀树特别适合我们这种有很多 prompt 带固定系统提示词和有少量 few-shot 样本的场景。同一个前缀的请求可以直接复用 KV Cache不用从头计算延迟和吞吐的提升非常明显。我们内部推理服务的启动命令长这样这是一个常见的实践模板python -m sglang.launch_server \ --model-path /models/qwen2.5-14b-instruct/ \ --host 0.0.0.0 \ --port 8000 \ --mem-fraction-static 0.85 \ --tp-size 2 \ --max-running-requests 256 \ --max-total-tokens 65536这里有几个参数要重点看--mem-fraction-static 0.85预先为 KV Cache 预留 85% 的显存。调太高可能会导致同时服务多个模型副本时 OOM调太低会减少并发批处理的能力吞吐上不去。根据我们的经验单模型专用节点可以设到 0.9混合部署建议 0.7~0.8。--tp-size 2设置张量并行度。当单卡显存放不下模型时用多卡切分模型。比如 14B 模型 fp16 权重大概要 28GB单张 40G 的卡也能放但如果你同时还想要比较长的上下文建议设置 tp-size 2留出更多 KV Cache 空间。--max-running-requests 256控制同时处理的请求数。这个值不宜设得太大否则大量请求排在 GPU 上会互相拖慢单个请求的响应时间会恶化。上线推理服务时我们不是直接暴露裸的 SGLang 或 vLLM 端口而是通过平台生成一个 K8s Service外层挂一层推理网关统一负责鉴权、限流、灰度路由和 QPS 监控。业务方拿到的就是一个标准 REST API endpoint不需要关心背后是哪种推理引擎。3.2 推理服务的弹性伸缩与流量管理大模型推理服务和传统 Web 服务最大的不同是它的资源消耗不是线性的。一个请求可能只需要十几 token也可能一上来就把上下文拉满到 32K显存占用波动非常大。如果只看 RPS 来做弹性伸缩很容易误判。我们的弹性伸缩核心指标是“显存利用率和平均排队长度”而不是单纯的 QPS。在每个推理副本内部外层的 Prometheus 会采集请求排队数当排队数超过副本数的一定阈值时HPA 就会拉新副本同时我们设置了冷却时间防止流量抖动导致频繁伸缩。举一个实际例子假设一个副本能同时并发处理 8 个请求平均每个请求耗时 2 秒那么单副本的容量大约就是 4 QPS。如果线上流量从 10 QPS 涨到 30 QPSHPA 会在 1~2 分钟内把副本数从 3 拉到 8 个流量高峰期结束后再逐步缩容到 4 个。这套机制在经历几次大促流量冲击时表现稳定核心的保障就是“扩容靠指标缩容靠冷却”。流量管理上我们采用每个模型独立部署一套服务而不是好几个模型混在一个服务里。这样做的好处是模型 A 的流量突增不会把模型 B 的显存挤爆故障域也更小。不同的模型服务之间通过 K8s 的 PodDisruptionBudget 和拓扑分布约束确保同一模型至少有两个副本分布在不同的节点或可用区这样单节点故障不会导致服务整体不可用。另外还有一个容易忽略的点长连接与流式输出。对话类场景普遍用 SSE 或者 WebSocket 做流式返回这意味着网关不能简单地把连接挂在单个 Pod 上就不管。如果某个推理副本 OOM 重启正在进行的流式连接会被打断用户体验很差。我们的做法是在网关层做请求级别的重试并且让模型服务支持“断点续传”语义——客户端重新请求时带上之前已返回的上一轮上下文服务端只需要继续生成新内容即可。3.3 推理链路的可观测性不止是延迟和 QPS推理平台的可观测性比传统服务要复杂得多。传统服务只需要看 QPS、延迟、错误率但大模型服务还必须关注“输入 token 数”“输出 token 数”“首 Token 延迟”“显存占用”“Batch 大小”这些维度。“首 Token 延迟”Time to First Token是我们在实际调优中最敏感的一个指标。大模型推理的时候用户感知到的“快”并不只是完整返回所有内容有多快而是第一个字出来的时间。TTFT 太长用户会以为服务卡死了。目前我们要求对话场景的 TTFT 小于 800ms长文档摘要场景可以放宽到 2~3 秒。如果 TTFT 超标一种常见的原因就是请求排队太多GPU 上积压的请求把 Prefill 阶段占了。和传统请求不同Prefill 阶段计算量非常大短请求如果排在一个长请求后面TTFT 就会明显劣化。我们的做法是在排队逻辑里增加“短请求优先”的策略算一下请求的输入长度短的请求可以插队到长请求前面这样能显著改善短对话场景的用户体验。可观测性里还有一个容易被忽略的点模型输出的质量监控。推理平台不只是保证服务不宕机还要保证模型输出没跑偏。我们会在推理网关层做随机采样把部分请求的输入和输出自动同步到日志中心算法团队定期抽样检查。如果发现某个版本的模型回答风格不对或者内容安全上有漏洞可以快速触发“紧急回滚”。4. 模型生命周期管理从训练到上线的闭环4.1 训练、评测、准入、上线的标准流程早期没有统一流程的时候模型上线靠的是“某个算法同学说自己模型训好了”然后手工拷贝权重、手工部署、手工测试。这个流程在小规模时勉强能用一旦模型数量和迭代频率上来就一定会出问题。我们在平台上把模型生命周期抽象成了四步注册、评测、准入、发布。第一步“注册”很简单算法同学训练完成后把 checkpoint 上传到模型仓库同时填写模型名称、版本号、训练数据版本、评测指标。模型仓库里会记录所有历史版本支持随时回溯。第二步“评测”是关键。平台针对不同类型的模型内置了评测任务比如对话模型跑公开的评估集MMLU、GSM8K、C-Eval 等电商文本模型则跑内部的问答对和指令集。评测不是只看一个总分数还要看每类样本上的表现避免模型提分了但某些硬场景反而变差。第三步“准入”对应的是业务方验收。模型评测通过后并不是直接上生产而是先进入 staging 环境由业务方跑一批真实请求做回归测试和人工评估。第四步“发布”才真正把模型推上线。发布的时候有两种方式一种是新版本直接替换老版本另一种是“金丝雀发布”先让 5% 的流量走新模型观察一段时间无异常后再全量切换。我强烈建议所有模型发布都走金丝雀哪怕是对自己的模型很有信心因为线上真实数据分布的复杂程度远超出离线评测集的覆盖能力。4.2 模型仓库与版本管理的最佳实践模型文件和代码一样需要版本管理但模型文件通常有几 GB 到几十 GB没法用 Git 直接存。我们的模型仓库就是基于对象存储做的一个元数据服务每次注册模型时把权重文件的对象存储路径、文件大小、校验和MD5、训练超参数、评测指标一并记录。这里要特别注意“模型文件的不可变性”。一个模型版本的文件一旦注册就不允许任何人去改动哪怕是修复一个明显的问题也得新发一个版本。在实践中我们发现如果允许直接覆盖模型文件最终会引发非常难排查的线上问题——你根本不知道线上跑的模型到底是什么样的权重。模型安全也需要考虑。大模型本身可能包含敏感的训练数据模型文件直接暴露在对象存储里存在泄露风险。我们对模型文件的访问做了严格的权限控制只有经过模型仓库服务签发的临时凭证才能下载文件业务线之间也不能互相访问对方的模型。4.3 评测体系与模型准入标准评测体系是连接“训练”和“上线”之间的质量闸门。我们内部定了一个“双 95”的准入标准新模型在公开评测集上的得分不能比上一个线上版本低超过 5%在内部业务评测集上的得分不能低超过 5%。如果新模型的某项指标比老版本差即使整体得分更高也建议重新训练或者回滚。不过光看平均分是不够的。大模型在分布边缘的样本上可能表现极差所以我们在评测时还关注最低分样本的分布。比如一个商品问答模型如果对“玻璃材质”相关的问题准确率特别低哪怕整体准确率是 99%也不能上线因为相关业务可能正好对材质识别要求很高。这种通过“弱势场景”卡准入的方式确实帮我们挡掉过好几次事故。评测还需要有人工抽检环节。机器评估只能告诉你对不对但大模型生成的文本是否通顺、是否符合平台调性很多时候还得靠人看。我们在评测流程中加入了“人工抽检队列”按比例随机抽取评测结果由产品和运营同学做一对一的质检。5. 平台化过程中的常见问题与排查经验5.1 训练侧问题GPU 利用率低、分布式训练卡住、Checkpoint 损坏训练侧最常见的问题是“GPU 利用率上不去”。很多人一看到 GPU 利用率低于 50% 就怀疑是显存不够实际上更多时候卡在数据加载或 CPU 预处理。排查路径一般是先看训练日志里每个 step 的耗时确认是“数据等待”还是“计算等待”然后在训练脚本里加上简单的耗时打点分别统计 data_loader 耗时和 forward/backward 耗时。如果 data_loader 耗时占比超过 20%优先优化数据预取和缓存。分布式训练“卡住不动”也经常遇到。现象就是训练日志停在某个 epoch但是进程没有退出GPU 利用率变成 0。最常见的原因是多机多卡任务中某个节点的某个进程挂了其他节点一直在等它同步。遇到这种情况第一件事是看nvidia-smi显示每个节点的 GPU 利用率哪一个节点利用率异常就是问题节点然后在那个节点上查 dmesg 看有没有 GPU 报错或者驱动问题。Checkpoint 损坏是所有人都不希望遇到但迟早会遇到的事。推荐的做法是checkpoint 写入的时候不要直接覆盖旧文件而是写到临时目录等整体写完后再做一次原子 rename。同时在训练脚本里加上“校验和检查”读取 checkpoint 时先做一次 MD5 校验不通过就直接告警。我们因为 checkpoint 损坏丢过一次接近两天的训练进度后来强制要求所有保存操作都走临时文件 rename 的流程。5.2 推理侧问题显存不够、首 Token 延迟高、长上下文会话中断推理侧的高频问题排第一的是“显存不够”。常见的情况是模型权重本身放得下但加载几个请求后上下文变长KV Cache 把显存打满了。这时候一般有两种解法一是调低--mem-fraction-static给临时分配留出余量二是对长上下文做长度限制例如超过 16K token 的请求放到专门的“长文本推理服务”上处理避免它抢占普通场景的显存和并发能力。首 Token 延迟高优先排查是不是请求排队过多。可以用vllm或sglang的 metrics 接口看一下当前 running 和 waiting 的请求数。如果 waiting 数长期大于 0最简单的解法就是扩容如果这种状态只出现在高峰就需要和业务方约定限流策略。还有一个特别容易踩的坑是“长上下文会话中断”。很多团队在对话场景中会把多轮历史记录全部塞进 prompt随着对话轮数增加输入 token 数不断膨胀最终触发模型的最大上下文长度限制。我们的经验是在网关层做上下文裁剪超过长度阈值就自动丢弃最早的几轮对话但要在接口返回信息里告知前端“上下文已精简”避免用户和模型“失忆”后表现异常。5.3 平台工程侧问题资源配额冲突、镜像构建慢、模型回滚救急资源配额冲突经常发生在多个业务线共用同一批 GPU 节点时。某个团队提交了大型训练任务把集群空闲卡全占了另一个团队的推理服务扩容排队等了十分钟。我们最终的解决方式是训练集群和推理集群物理分离推理集群预留 30% 的空闲余量训练集群允许打满。因为推理服务的稳定性直接关系到线上业务而训练任务可以排队等待。镜象构建慢也是一个常见的吐槽点。base 镜像太大比如带 CUDA 的 PyTorch 镜像动辄 5~8GB每次构建都卡半天。我们后来优化了两点一是用镜像缓存和分层构建尽量让基础依赖层不变二是给所有算法同学提供一份“精简镜像模板”去掉不必要的包让镜像瘦身到 2~3GB。构建时间从原来的十几分钟降到三分钟以内。关于模型回滚我强烈建议平台提前做好“一键回滚”脚本。有一次我们在灰度发布一个客服大模型时发现它回答中出现了重复文案业务方要求立刻回滚到上一个版本。因为我们提前把上一个模型的权重和部署配置都打成了标准 Release回滚操作只需要一条命令两分钟内就完成了流量切换。这件事之后我们给平台加了一个硬性要求任何一个模型版本发布前必须验证回滚路径可用。6. 实操总结与经验沉淀回头看这段从 0 到 1 的平台建设过程我最深的体会是不要试图一开始就做一个完美的平台而是先解决最痛的 80% 问题再在迭代中逐步打磨。当时我们第一批接入的业务只有两个平台功能也简单得可怜甚至没有完整的任务排队功能但正因为先把这两个业务跑通了才让平台有了“初始用户”后续完善功能时有了真实反馈。等到第三、第四个业务接入时平台已经沉淀出一套相对稳定的流程新增一个业务线的工作量被压到非常低。还有一点平台建设最怕的不是技术难点而是“组织协同”。训练、推理、模型管理、业务接入每一层都会牵扯到不同的团队如果大家在目标上不一致项目推进会非常痛苦。比较好的做法是让平台的每个模块都有明确的负责人同时设立一个“平台满意度”指标定期拉业务方一起评审把“平台好不好用”这件事放到台面上来。最后分享一个小技巧训练和推理平台的文档一定不要写成“API 说明文档”而要写“问题导向文档”。比如“我要训练一个模型应该怎么做”“我要上线一个对话服务应该怎么做”把每一步操作和常见的报错都写清楚。这样能极大减少平台团队“接客”的负担也能让算法同学更愿意使用平台而不是绕过平台自己搭环境。这个项目做到现在距离“完美”还有很长一段路但总体框架已经稳定后续无论是接入更大的模型、支持国产算力还是做多模态推理都只是在这个框架上做增量扩展。对于一个从 0 到 1 的项目来说能走到这一步我觉得已经算是可以达到目标了。