ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从零搭建AI工程能力:模型选型、推理服务化与评估监控全链路实战

2026/9/30 3:44:52 拓冰建站 浏览量
从零搭建AI工程能力:模型选型、推理服务化与评估监控全链路实战 从零搭建AI工程能力这件事我前前后后折腾过好几轮。最早的时候我也觉得搞AI嘛会调个API、能跑通一个demo不就行了后来真正在项目里踩了一圈坑才发现从能跑到能扛住真实流量、能持续迭代、能被人接手维护中间隔着的距离比想象中大得多。ai-engineering-from-scratch这个标题说的其实就是这条从零到一的路——不是教你怎么训练一个大模型而是教你怎么把AI能力真正工程化地落地到系统里。这篇文章适合那些已经会写代码、但对AI工程化还没有完整认知的开发者也适合带团队做AI项目的技术负责人。我会把这条路上最关键的几个环节拆开讲清楚包括每个决策背后的逻辑、我实际踩过的坑以及可以直接抄作业的做法。1. 先搞清楚AI工程到底在工程什么很多人一上来就急着装环境、跑模型结果做到一半发现方向都偏了。我觉得有必要先把AI工程这个概念本身掰开揉碎讲清楚不然后面全是白费功夫。1.1 AI工程和传统软件工程的分界线在哪传统软件工程的核心假设是给定同样的输入系统应该产生确定的、可预期的输出。你写一个订单金额计算函数输入100块打八折输出永远是80块。测试用例写死了CI跑通了上线就放心了。AI工程打破了这个假设。模型输出本质上是概率性的同样的输入可能产生不同的输出而且正确本身往往没有唯一标准。这就带来一系列传统工程里不存在的问题你怎么测试一个没有确定输出的系统你怎么监控一个看起来正常但实际在退化的服务你怎么在模型更新后确保线上效果没有回退我刚开始做AI项目的时候习惯性地用传统单元测试的思路去测模型输出写了个assert output expected结果当然是天天挂。后来才明白AI工程的测试思路得换成评估集指标阈值的模式关注的是统计意义上的表现而不是单次输出的精确匹配。这个认知转变是AI工程的第一道门槛。你得接受不确定性是系统的固有属性然后围绕这个属性去设计你的工程方案而不是试图消灭它。1.2 一个完整的AI工程链路包含哪些环节从零搭建的话我习惯把整条链路拆成这么几层每一层都有它独立要解决的问题层级核心职责常见误区数据层数据采集、清洗、标注、版本管理以为数据准备好就一劳永逸忽略持续迭代模型层模型选型、微调、评估、版本管理盲目追新忽略推理成本和延迟服务层推理服务封装、批处理、并发控制直接裸调模型没有做资源隔离应用层Prompt编排、上下文管理、结果后处理把业务逻辑塞进Prompt里运维层监控、日志、告警、成本追踪上线后就不管了出问题才发现这五层不是严格的瀑布关系实际做的时候经常要来回跳。但心里有这张图你就知道当前在解决哪个层次的问题不会把不同层次的事情搅在一起。比如有人把业务规则硬编码进Prompt里这就是典型的层次混淆——业务逻辑应该放在应用层用代码控制Prompt只负责跟模型沟通。1.3 为什么从零反而是一种优势市面上有很多封装好的AI平台拖拖拽拽就能搭出一个应用。但我一直建议真正想做AI工程的人至少完整地从零走一遍。原因很简单封装层帮你屏蔽了复杂度但也屏蔽了你对系统行为的理解。当线上出问题的时候平台只会告诉你请求失败了但不会告诉你失败发生在tokenization阶段还是推理阶段是显存不够还是输入超长。你自己从零搭过一遍就知道每个环节可能出什么幺蛾子排查起来心里有底。而且从零搭建的过程本身就是在建立你对整个链路的手感。这种手感是看多少篇教程都换不来的。你知道一个请求从进入到返回中间经过了多少次序列化、多少次网络往返、多少次内存拷贝你才能做出合理的优化决策。2. 环境搭建那些教程不会告诉你的细节环境搭建看起来是最没技术含量的部分但实际上它是新手翻车最集中的地方。我见过太多人卡在环境问题上好几天热情直接磨没了。2.1 Python环境管理的正确姿势先说一个反直觉的建议不要用系统自带的Python也不要在全局环境里装任何AI相关的包。AI领域的依赖冲突是出了名的严重不同框架对同一个底层库的版本要求经常打架。我的标准做法是用conda或者uv来管理独立环境。uv是这两年新起来的工具速度比pip快一个数量级我现在的项目基本都切到它了。创建一个干净环境的命令大概是这样# 用 uv 创建虚拟环境 uv venv ai-eng --python 3.11 # 激活环境 source ai-eng/bin/activate # Linux/Mac # 或者 Windows 下 ai-eng\Scripts\activate # 安装核心依赖 uv pip install torch transformers fastapi uvicorn为什么推荐Python 3.11而不是最新的3.12或3.13因为AI生态里很多库对最新版Python的支持总是滞后的你装个3.13可能一半的包都编译不过。3.11是目前兼容性和性能平衡得最好的版本我实测下来最稳。提示如果你用的是Apple Silicon的Mac装PyTorch的时候注意选对版本MPS后端的支持和CUDA不完全一样有些算子会fallback到CPU性能差距很大。装之前先确认你的模型用到的算子在MPS上有没有实现。2.2 依赖版本锁定别让昨天还能跑变成常态昨天还能跑今天就不行了——这句话在AI项目里出现的频率高得离谱。根本原因就是依赖版本没有锁定。你今天pip install装的是最新版明天别人clone你的代码再装装到的可能就是另一个版本了。我的做法是在项目根目录维护一个requirements.txt并且把所有依赖的精确版本都写死torch2.1.2 transformers4.36.2 fastapi0.109.0 uvicorn0.27.0 numpy1.26.3注意这里用的是而不是。很多人图省事写觉得这样能自动获取更新但实际上这是给自己埋雷。AI库的API变动非常频繁一个小版本升级就可能改掉你正在用的接口。更严格的做法是用pip-compile或者uv pip compile生成带哈希校验的锁文件这样连包的完整性都能验证。团队协作的话这一步基本是必须的。2.3 GPU环境的坑CUDA版本匹配如果你要用GPU跑推理或训练CUDA版本匹配是绕不过去的坎。PyTorch、CUDA驱动、cuDNN这三者之间有严格的版本对应关系错一个就跑不起来。我整理了一个常见的对应关系供参考PyTorch版本推荐CUDA版本推荐cuDNN2.0.x11.7 / 11.88.52.1.x11.8 / 12.18.72.2.x12.18.9装的时候最省心的方式是直接用PyTorch官网给的安装命令它会自动帮你选对CUDA版本。别自己去手动装CUDA Toolkit然后指望PyTorch能认出来那个匹配过程能让你怀疑人生。验证GPU是否可用跑这一行就够了import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0))如果第一个输出是False别急着怀疑显卡坏了先检查驱动版本和CUDA版本是否匹配。我遇到过好几次都是驱动太老导致的。3. 模型选型不是越大越好而是越合适越好模型选型是AI工程里最容易被参数崇拜带偏的环节。很多人一上来就想用最大的模型觉得参数越多效果越好。实际做项目的时候这个思路会让你在成本和延迟上吃大亏。3.1 先明确你的任务类型再选模型不同任务对模型的要求差异很大我一般按这几个维度来分类文本生成类对话、写作、摘要需要较强的语言理解和生成能力文本分类类情感分析、意图识别小模型微调往往就够了信息抽取类实体识别、关系抽取对结构化输出要求高多模态类图文理解、图像描述需要专门的视觉语言模型我见过有人用几十B参数的模型去做一个简单的文本分类任务效果还不如一个几百M的微调小模型而且推理成本高了两个数量级。这就是典型的选型错配。选型的时候先问自己三个问题任务复杂度有多高对延迟的要求是多少预算能承受多大的推理成本这三个问题的答案基本就框定了你的选择范围。3.2 开源模型和闭源API的取舍逻辑这是每个AI项目都要面对的选择。我的判断逻辑是这样的如果你的数据敏感度不高、对成本不敏感、追求快速上线闭源API是更省事的选择。你不需要管GPU、不需要管部署、不需要管扩容调用就完事了。但如果你对数据隐私有要求、调用量很大导致API成本过高、或者需要对模型行为做深度定制那就得考虑开源模型自己部署。我做过一个粗略的成本对比假设每天100万次调用每次平均500 token方案前期投入月度成本可控性闭源API几乎为零较高随量线性增长低自部署开源模型GPU服务器成本相对固定高这个表只是帮你建立量级概念实际数字要根据你的具体情况算。关键是要意识到调用量到一定规模后自部署的边际成本优势会非常明显。3.3 模型量化用精度换成本的关键手段如果你决定自部署量化是必须掌握的技能。简单说就是把模型参数从高精度比如FP16压缩到低精度比如INT8或INT4从而减少显存占用和提升推理速度。量化的代价是精度会有一定损失但很多时候这个损失在可接受范围内。我实测过一个7B模型INT8量化后显存占用减少一半推理速度提升约40%而在我的任务上效果下降不到2%。这个交换比是很划算的。常见的量化方案有GPTQ、AWQ、GGUF等各有适用场景。GGUF适合CPU推理和消费级显卡GPTQ和AWQ更适合GPU上的高吞吐场景。选哪个取决于你的部署环境。注意量化不是万能的。有些对精度极其敏感的任务比如代码生成或者数学推理量化后的效果下降可能会超出预期。上线前一定要在你的评估集上验证量化前后的效果差异。4. 推理服务化把模型变成能扛流量的服务模型能在本地跑通和模型能作为一个稳定服务对外提供能力是两码事。这一章讲怎么把模型包装成一个真正能用的服务。4.1 推理框架的选择别自己造轮子自己用Flask或者FastAPI裸包一个模型推理接口是很多人的第一反应。但这样做有几个问题没有做请求批处理、没有做显存管理、没有做并发控制稍微上点量就会崩。我建议直接用专门的推理框架。目前主流的几个选择vLLM吞吐量极高支持PagedAttention适合高并发场景TGIText Generation InferenceHuggingFace出品部署简单功能全面TensorRT-LLMNVIDIA官方性能极致但配置复杂我大部分项目用的是vLLM它的连续批处理continuous batching机制能显著提升GPU利用率。简单说就是它不会等一个请求处理完再处理下一个而是动态地把新请求塞进正在进行的批次里让GPU始终处于忙碌状态。启动一个vLLM服务的命令大概是这样python -m vllm.entrypoints.openai.api_server \ --model /path/to/your/model \ --tensor-parallel-size 1 \ --max-model-len 4096 \ --gpu-memory-utilization 0.9--gpu-memory-utilization 0.9这个参数控制显存使用上限设太高容易OOM设太低浪费显存。0.9是个比较稳妥的起点具体要看你模型大小和显卡容量。4.2 请求队列与限流保护你的服务不被打垮推理服务最怕的就是突发流量。模型推理是计算密集型任务一个请求可能要占用GPU几百毫秒如果瞬间涌进来几百个请求没有队列和限流机制的话服务直接雪崩。我的做法是在推理服务前面加一层请求队列用Redis或者内存队列实现。请求进来先入队推理worker按自己的能力从队列里取任务。队列满了就返回429让客户端重试而不是让请求堆积到把服务压垮。限流策略我一般按这几个维度来设单用户QPS限制防止单个用户刷爆服务全局并发数限制根据GPU能力设定上限请求超时时间超过一定时间没处理完就丢弃这些参数没有标准答案要根据你的硬件配置和业务特点来调。我的经验是先从保守的值开始观察一段时间再逐步放宽。4.3 流式输出提升用户体验的关键细节对于文本生成类任务流式输出几乎是标配了。用户不需要等整个回答生成完才看到内容而是可以一个字一个字地看到模型在思考。这个体验差异非常大。实现流式输出服务端要用SSEServer-Sent Events或者WebSocket把生成的token逐步推给客户端。vLLM和TGI都原生支持流式输出你只需要在调用的时候打开streamTrue就行。但流式输出也带来一些工程上的复杂性。比如你需要在客户端做增量渲染、需要处理流中断的情况、需要在服务端记录完整的输出用于日志。这些细节在demo阶段可以忽略但上线前必须处理好。我踩过的一个坑是流式输出的时候如果客户端提前断开连接服务端如果不做处理推理还会继续跑完白白浪费算力。后来我在服务端加了连接状态检测一旦发现客户端断开就立即中止推理。5. 评估与监控让AI系统的表现可观测AI系统最让人头疼的地方就是它看起来在正常工作但实际效果可能在悄悄退化。没有一套完善的评估和监控体系你根本不知道系统什么时候开始出问题。5.1 构建你的评估集这是AI工程的命根子评估集是你判断模型好坏的唯一依据。没有评估集你所有的优化都是盲人摸象。构建评估集的原则是覆盖你的真实使用场景包含各种边界情况并且要有明确的评判标准。规模不用很大几百到几千条就够但质量一定要高。我一般会从这几个来源收集评估数据线上真实请求的采样脱敏后人工构造的边界case已知的历史bad case竞品或基线系统的输出对比评估指标的选择取决于任务类型。分类任务看准确率、召回率、F1生成任务看BLEU、ROUGE但更重要的是人工评估或者用更强的模型做裁判LLM-as-a-Judge。提示评估集要版本化管理每次模型更新都要在固定的评估集上跑一遍记录指标变化。我习惯用表格记录每次迭代的评估结果这样能清晰看到优化是否真的有效。5.2 线上监控关注哪些指标线上监控和离线评估是互补的。离线评估告诉你模型应该表现如何线上监控告诉你模型实际表现如何。我重点监控这几类指标指标类别具体指标异常含义性能指标延迟P50/P95/P99、吞吐量服务性能退化质量指标输出长度分布、拒答率、格式错误率模型行为异常业务指标用户采纳率、二次追问率实际效果下降成本指标token消耗、GPU利用率成本失控其中输出长度分布这个指标特别有用。如果模型突然开始输出很短的回复或者回复长度分布发生明显偏移往往意味着模型行为出了问题可能是输入分布变了也可能是模型本身出了问题。5.3 数据漂移检测提前发现问题的信号数据漂移是指线上输入数据的分布随着时间推移偏离了模型训练或评估时的分布。这是AI系统效果退化的最常见原因之一。检测数据漂移我一般监控输入文本的长度分布、关键词分布、以及embedding空间的分布变化。如果发现明显偏移就要警惕了可能需要重新评估模型或者更新评估集。举个实际例子我之前做过一个客服场景的AI助手上线几个月后发现效果慢慢变差。排查后发现是用户的问题类型发生了变化早期用户问的都是简单问题后来逐渐开始问一些复杂的、多轮的问题而我的评估集里这类问题很少。这就是典型的数据漂移。解决办法是定期用线上数据更新评估集保持评估集和真实分布的同步。这个工作要持续做不是一次性的。6. 迭代与优化AI工程没有终点AI工程和传统软件工程最大的区别之一就是它没有完成这个状态。模型会更新、数据会变化、用户需求会演进你必须持续迭代。6.1 Prompt工程最廉价的优化手段在所有优化手段里Prompt工程的投入产出比是最高的。改几行Prompt可能就能让效果提升一大截而且不需要重新训练模型。但Prompt工程也有它的边界。我见过有人试图用Prompt解决所有问题把Prompt写得越来越长、越来越复杂最后变成了一个难以维护的怪物。我的原则是能用代码逻辑解决的不要塞进Prompt能用简单Prompt解决的不要写复杂Prompt。Prompt的版本管理也很重要。我习惯把Prompt当成代码一样管理每次修改都记录变更原因和效果对比。这样出问题的时候能快速回滚。6.2 微调什么时候值得做微调不是万能的也不是必须的。我一般在这几种情况下才会考虑微调Prompt工程已经优化到极限效果还是不够任务有很强的领域特殊性通用模型理解不了需要模型输出严格遵循某种格式或风格有足够的标注数据且数据质量有保证微调的成本不只是训练成本还有数据标注成本、评估成本、以及后续维护成本。做之前一定要算清楚这笔账。如果决定微调我建议先用LoRA这类参数高效的方法试试。LoRA只训练一小部分参数成本低、速度快效果在很多任务上已经接近全量微调了。全量微调留到LoRA确实不够用的时候再上。6.3 缓存策略省钱又提速的实用技巧AI推理很贵但很多请求其实是重复的或者高度相似的。缓存能帮你省下大量成本。我一般做两层缓存精确匹配缓存和语义缓存。精确匹配就是输入完全一样直接返回缓存结果实现简单。语义缓存是把输入做embedding如果和之前某个请求的embedding相似度超过阈值就返回那个请求的结果。语义缓存的效果取决于你的场景。如果是FAQ类的场景语义缓存命中率会很高如果是开放式对话命中率就低一些。但不管怎样能省一点是一点。注意缓存要设置合理的过期时间并且要在模型更新后主动失效。否则用户可能拿到旧模型的结果造成困惑。7. 我踩过的几个印象深刻的坑理论讲了一堆最后分享几个我实际踩过的坑都是血泪教训。第一个坑是显存泄漏。我早期写推理服务的时候没有注意及时释放中间变量跑了一段时间后显存慢慢被占满服务就挂了。后来养成了习惯在每个请求处理完后手动清理缓存并且用torch.cuda.empty_cache()释放未使用的显存。这个问题在长时间运行的服务里特别常见一定要重视。第二个坑是输入长度没有限制。有一次线上突然来了一批超长输入直接把模型的max length撑爆了服务报错。后来我在入口处加了输入长度校验超过限制的直接截断或者返回错误提示。这个防护是必须的不能指望用户永远输入正常长度的文本。第三个坑是模型版本管理混乱。早期我没有做模型版本管理直接覆盖式更新结果有一次新模型效果不好想回滚发现旧模型已经被覆盖了只能重新训练。后来我改用版本化目录管理每次更新都保留旧版本回滚就是改个配置的事。第四个坑是忽略了冷启动问题。模型服务刚启动的时候第一次推理会特别慢因为要加载模型、初始化CUDA上下文。如果这时候正好有流量打进来用户体验会很差。解决办法是服务启动后先跑几个预热请求把模型热起来再对外提供服务。这些坑说起来都不复杂但没踩过的人就是想不到。AI工程这个领域很多经验都是踩坑踩出来的。希望这篇内容能帮你少走一些弯路。从零搭建AI工程能力最重要的不是掌握某个具体工具而是建立起对整个链路的系统认知知道每个环节在解决什么问题、可能出什么错、怎么排查和优化。有了这个认知框架具体工具的学习就是水到渠成的事。