1. 为什么我劝你用“从零开始”的思路搞AI工程先说个扎心的现实现在AI项目的门槛门槛不在算法而在“把东西跑起来”的能力。天天刷到GPT-4、Claude、Llama各种新模型刷榜知乎上一堆“三分钟搭建AI应用”的爽文真到自己动手的时候才发现文档看了三天连环境都没配好——这不是你笨是AI工程化这个领域的知识太碎了。我用了小半年时间走了一条挺笨的路不依赖任何一家云平台的一键部署不套用现成的AI应用模板从Python环境、数据清洗、模型选型、微调、评估到部署上线全程手工搭一遍。这个项目我给它起名叫“ai-engineering-from-scratch”听着费劲但走完一遍之后再看市面上那些AI项目一眼就能看懂它的架构、瓶颈和优化点再也不焦虑了。这篇文章就是把这条路复盘一遍把能直接用上的经验和踩过的坑都写出来给正在准备入局或者已经在这个领域摸爬滚打的朋友做一个参考。先说清楚适合谁看如果你是已经在用AI API做过一些小demo、但想把整个链路吃透的开发者或者是在做AI工程落地项目的同学这篇文章能帮你省掉大量查资料的时间。如果你纯零基础文章里也会把最底层的东西拆开讲跟得上。核心思路其实就一句话不要把AI工程当成一个“调API”的活要把它当成一套完整的产品研发流程来对待。数据、模型、评估、部署、监控每一环都决定了最终效果是80分还是30分。2. AI工程化到底是什么从“能跑”到“好用”之间隔着一整个技术栈2.1 AI工程和传统软件工程差的不是一点半点传统软件开发讲究的是逻辑确定性——输入A经过一连串if-else或者函数计算输出B每一次的结果都可预期。但AI工程最大的不同是核心逻辑是概率性的模型输出的结果不是必然正确而是“大概率合理”。这一下就把整个工程体系打乱了。我自己刚开始做的时候犯过一个特别蠢的错用传统软件工程的方式管理AI项目先写需求文档再搭代码框架最后调模型。结果做到一半发现需求文档里定义的“理解用户意图”压根没法在工程上直接落地因为模型怎么“理解”这件事本身就是一个黑盒。后来我调整了思路把AI工程拆成几个关键环节每个环节独立验证、独立迭代这才走上正轨。一个完整的AI工程链路大致是这么几个环节数据层数据的获取、清洗、标注、版本管理模型层基线模型选型、微调训练、量化压缩推理层模型推理服务化、并发控制、缓存策略应用层Prompt工程、RAG检索增强、业务逻辑编排评估层自动化评测数据集、人工评估反馈、bad case追踪部署运维层自动化部署、监控告警、版本回滚、成本控制任何一个环节出了短板整个系统就是跑得起来但不好用。很多人以为AI工程的核心是模型我走完一遍以后发现真正花时间的是数据清洗、评估体系和部署后的监控模型训练反而是整个链路里相对透明的一环。2.2 既然大模型这么强为什么还要工程化这是我被问得最多的一个问题“直接用Claude或者GPT工具不就行了为什么要自己搭”答案很简单通用能力不等于业务能力。你去饭店吃饭大厨什么菜都会炒通用大模型但你要吃的是一道特定口味的私房菜业务场景大厨得按你的方子调整火候、配料、时间——这个过程就是AI工程化要做的。举一个实际例子。我之前给一个电商客服场景做过一个问答机器人直接用开源大模型回答质量确实不错但它有两个致命问题第一没有专业知识库。用户问“你这件衣服的退换货政策是什么”模型只能给出一段模棱两可的官方话术因为它没有这个店铺的真实政策数据。第二没有稳定的服务保障。模型推理速度波动很大高峰期一个请求要5秒才能返回客服交互体验完全不可用。这两件事单靠换一个更强的API是解决不了的。你需要做RAG把知识库检索和生成结合起来、需要做推理加速、需要做并发控制、需要做缓存和降级方案——把这些东西组织起来把一个大模型变成一套真正能服务于业务的系统这个过程就是AI工程化。2.3 AI工程师到底需要哪些能力储备走完这个项目之后我总结了一个AI工程师的能力地图扎实的Python工程能力不是会写脚本就完了而是模块化、面向对象、代码规范、单元测试数据处理能力Pandas、SQL、常见的数据清洗操作不要只会用Excel机器学习/深度学习基本功理解梯度下降、反向传播、过拟合不然微调的时候完全是瞎调分布式和高并发的基础认知GPU服务会有并发问题不是写完就完了系统设计能力AI系统同样要考虑容错、降级、超时、重试运维技能Docker、K8s、日志系统AI模型最终要跑在服务器上而不是你的笔记本上听着吓人但不用一次性全学会。先把一条主链路跑通遇到问题再逐个补能力这是亲测最有效的路径。3. 技术栈选型详解各环节的关键工具和对比分析选对工具能少走很多弯路。这里把每个环节我自己对比过后最终选定的方案列出来附上选型理由。3.1 开发语言和基础环境Python不用犹豫生态最成熟。版本选择上建议直接用3.10以上太老的版本兼容不好太新的又有些深度学习库还没跟上。环境管理我用的是conda requirements.txt双重保险conda管Python解释器版本和CUDA相关的系统依赖requirements.txt管理项目内的Python包依赖。深度学习框架这边PyTorch基本是默认选择。Transformer生态的模型不管是原生还是HuggingFace上的都是PyTorch权重为主JAX、TensorFlow也有但它们转PyTorch权重成本高除非团队有明确技术积累否则没必要另起炉灶。需要说明的是这是基于我自己的实际项目经验做的选择如果你的团队已经在某个框架上有大量积累不用为了追新换。3.2 模型怎么选开源还是闭源多大参数量这是整个项目里最关键的决策点之一。我当时的判断标准决策项考虑因素我的选择开源 vs API数据隐私、成本、网络环境、可控性开源为主API作为备选参数规模显存成本、推理延迟、效果下限7B-13B为主力34B试过但成本太高中文支持词表大小、中文语料覆盖、中文指令效果优先选中文优化过的模型社区活跃度遇到问题能不能搜到答案选社区活跃的模型这个太重要了对于大多数项目7B-13B的开源模型微调RAG的路径性价比远远高于直接购买商业大模型API尤其在数据敏感和私有化部署的场景下。但如果是快速验证想法直接调用API是最快的这个要看你的项目阶段。3.3 数据工具栈数据清洗和存储数据处理这块我的选择是数据清洗Pandas 自定义规则引擎。Pandas处理结构化表格数据自定义规则引擎处理脏数据过滤比如URL、HTML标签、乱码符号、重复文本、敏感信息的正则匹配。数据存储SQLite存结构化元数据文件路径、标签、清洗状态、版本号Parquet文件存向量化前的原始语料。SQLite轻量但够用Parquet在读取大批量数据做批量向量化的时候效率很高。向量数据库Qdrant支持HNSW索引、Docker部署简单、API直观。对比过Milvus组件多配置复杂适合大规模生产小项目前期用不上、Chroma上手极快但索引性能和搜索质量一般Qdrant是折中方案单机性能足够我当前场景用。3.4 训练和微调全家桶微调这块我强烈推荐HuggingFace的Transformers库 PEFT库 Accelerate库的组合。Transformers就不用多说了加载模型和tokenizer的基础设施。PEFTParameter-Efficient Fine-Tuning管LoRA低秩适配微调只训练一小部分新增参数效果接近全参微调但显存占用和训练时间都小了一个数量级。Accelerate管分布式训练多个GPU的时候能自动分配训练策略。训练脚本我用的是一套DeepSeek团队在社区公开分享的带注释的LoRA训练代码作为基底然后自己改这个组合在社区里特别成熟遇到问题的时候搜索到的解决方案最多。3.5 部署侧推理框架和容器化推理这块我对比了vLLM、TGIText Generation Inference、原始Transformers的generate方法三个我都实测过结论很明确原始Transformers的generate稳定、可控但吞吐很低适合调试不适合线上。TGIHuggingFace官方出品功能全面支持持续批处理、量化推理但高度绑定HF生态。vLLM基于PagedAttention吞吐量和显存利用率明显更好。实测7B模型用vLLM跑QPS大约比原生generate高5-10倍。结论在线推理服务直接用vLLM作为调试工具用Transformers原生。容器化方面Docker docker-compose就足够了K8s如果你的项目流量没有达到一定规模前期不必上维护成本比收益高得多。4. 核心细节解析从数据清洗到微调的关键环节实操4.1 数据准备90%的AI项目死在数据这一步不是死在模型训练过一遍之后我彻底理解了那句老话“Garbage in, garbage out.” 数据质量直接决定模型效果天花板模型结构和训练技巧只是在逼近这个天花板。我做的是一个特定领域的问答系统整理数据的过程大概占了整个项目时间的40%。具体做了四件事**第一步数据收集与去重。**从公开数据集、行业资料、历史问答记录里收集原始语料然后用MinHash算法做去重。这个步骤非常关键因为重复数据会让模型过拟合我记得第一次去重前训练集里居然有大量完全相同的文本块导致模型反复输出类似的固定句子。**第二步清洗标准化。**写了一套清洗pipeline按顺序处理全半角统一、大小写统一中文场景下全角括号、逗号是重灾区去除HTML标签、URL、邮箱、乱码符号去除连续空白符、控制字符统一换行符Windows的\r\n和Linux的\n混在一起会很麻烦**第三步格式转换。**把数据统一成JSONL格式每行一条{instruction: 问题描述, input: 上下文信息, output: 标准回答}这里的格式设计很讲究instruction、input、output三字段齐全在训练的时候能支持更灵活的模板化拼接。很多开源模型的训练格式各不相同但基本都遵循类似的思路。**第四步质量筛选。**这一步很多人会忽略。我用了两种方式筛选一是写规则过滤比如长度过短小于5个字、过长大于2000字的样本直接剔除二是用分类模型给样本打质量分质量分低于阈值的直接剔除。第一次跑完之后训练集从3万条精简到1万条左右模型效果反而提升了。注意数据标注的一致性比数量重要得多。如果标注出来的标准答案本身就不一致模型学到的只会是混乱和矛盾。这一步宁可多花时间复核不要为了凑数量而牺牲一致性。4.2 RAG检索增强把模型不知道的知识补进去微调的局限性在于它只能把模型“学会”的知识嵌入到参数里一旦领域知识频繁更新每次都要重新训练成本太高。RAG的思路是知识不往模型里写而是存在数据库里回答问题时先检索再生成。我实现的RAG流程不复杂把领域文档切块chunk overlap策略每块最大长度512 token重叠128 token防止语义断裂用嵌入模型做向量化存入向量数据库。检索时把用户Query也用同一个嵌入模型向量化然后在向量库里做相似度检索取Top-K召回相关文本块。把召回文本块作为上下文拼接到Prompt里让模型基于上下文生成回答。这里面有两个细节对效果影响很大第一嵌入模型的选择。中英文混合场景我对比了BGE-M3、M3E、E5等几个主流嵌入模型最终选了BGE系列的中文模型在领域检索效果上明显好于通用的多语言模型。原因很简单嵌入模型是纯数据堆出来的中文语料的训练量直接决定了它的中文理解能力。第二Top-K召回参数的调优。K值太小召回的信息不够K值太大无关的噪声会干扰生成。我实测下来K5左右最稳妥但具体要看你的知识库文档粒度和业务场景建议写一个评测脚本批量测不同K值下的回答命中率用数据说话而不是靠感觉调。4.3 微调实操我从零跑通的LoRA训练流程完整跑通的微调流程我把关键命令和参数都贴在下面这套配置我在一张4090上跑通过显存占用大约14GB很适合个人实验室或小团队复现。# 1. 安装依赖 pip install torch transformers datasets accelerate peft bitsandbytes # 2. 加载模型和分词器以Qwen系列为例from transformers import AutoModelForCausalLM, AutoTokenizer from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training model_id Qwen/Qwen2.5-7B-Instruct tokenizer AutoTokenizer.from_pretrained(model_id, trust_remote_codeTrue) model AutoModelForCausalLM.from_pretrained( model_id, load_in_4bitTrue, # 4bit量化加载大幅降低显存占用 device_mapauto, trust_remote_codeTrue ) model prepare_model_for_kbit_training(model) # 对量化模型做训练前准备 lora_config LoraConfig( r8, # LoRA秩越大表达能力越强但显存占用也大 lora_alpha16, # 缩放系数一般设为r的2倍 target_modules[q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj], lora_dropout0.05, # 防止过拟合 biasnone, task_typeCAUSAL_LM ) model get_peft_model(model, lora_config)关键参数说明r8, lora_alpha16这是LoRA参数里最常用的一个比例。r控制低秩矩阵的维度太小模型学不进去太大会显著增加训练开销alpha控制缩放比例一般取r的两倍。target_modules不是所有线性层都要加LoRA根据社区经验attention层的q/k/v/o和MLP层的gate/up/down全部加上效果最稳定。一开始我只加了attention的部分训练loss看着挺低但实际生成效果差很多后来全加上才正常。4bit量化加载经典省钱操作。一段时期的经验告诉我7B模型全精度加载需要大约15GB显存而4bit量化后只需要6-7GB训练时再加上LoRA参数和优化器状态一张24GB的4090都能轻松跑。训练超参数这块我使用的是epoch3第一轮就出现loss收敛迹象了3轮足够再多容易过拟合batch_size1 gradient_accumulation_steps8模拟出8的batch size效果对显存很友好learning_rate2e-4LoRA训练常用值原模型参数不动只调新增参数的lr最大序列长度1024超过这个长度的样本会被截断太短会丢失信息太长显存又不够1024是个平衡点提示模型加载的时候务必加上trust_remote_codeTrueQwen这类模型的分词器代码是随权重发布的不信任执行会让代码直接加载失败。这个参数很多教程里没提第一次跑的人往往会在这里卡半天。4.4 评估体系不做评估 不做AI工程整个项目里我最想强调的就是评估这一环。很多开发者包括我自己前期的项目训练完模型肉眼看了几个例子觉得“不错”就部署上线了。这种操作在demo阶段没问题但到了生产环境一次效果劣化就能让你加班到凌晨排查原因。我搭了一套双层次评估体系第一层自动化评测脚本。准备了一批带标准答案的评测集至少200条覆盖不同场景类型写脚本批量跑模型用BLEU、ROUGE-L、语义相似度等指标自动化打分。第一次微调完以后我跑自动化评测发现BLEU分数只有0.1左右但肉眼看的几个例子都挺好——后来把评测集里的样本抓出来逐个人工看才发现模型生成的答案过于“自由发挥”缺乏规范性这个问题只靠肉眼看几个例子根本发现不了。第二层Bad Case追踪和人工回归。把自动化评测里得分最低的50个case拉出来一条条看模型的输出找规律。这个过程特别重要因为你会发现大量有共性的问题比如特定类型的question只会输出固定回答数据多样性不足长上下文的回答逻辑混乱序列长度不够信息丢失专有名词处理错误语料里术语上下文不够人工回归的结论再反过来指导数据优化和微调参数形成“评测-发现问题-改进-再评测”的闭环。AI工程落到实操层面本质上就是这样一个不断闭环迭代的过程。5. 部署和运维实战记录把模型真正推到线上模型训练完了评估通过了距离“能用”还差最后一步部署成线上服务让用户能通过HTTP接口请求模型能力。这一步我也踩了不少坑具体的过程值得单开一节讲。5.1 vLLM部署吞吐量从龟速到高铁一开始我用FastAPI包了一层Transformers的generate接口单卡4090跑7B模型单并发请求的延迟大约2秒但QPS超过2之后就开始积压请求延迟直接飙到10秒以上。后来换上vLLM同样一张4090吞吐量提升了差不多一个量级。vLLM的部署命令非常简单# 1. 启动vLLM推理服务加载训练好的LoRA权重合并后的模型 vllm serve /path/to/merged_model \ --served-model-name my_qa_model \ --tensor-parallel-size 1 \ --max-model-len 2048 \ --gpu-memory-utilization 0.9 \ --port 8000 # 2. 通过OpenAI兼容接口调用 curl http://localhost:8000/v1/chat/completions \ -H Content-Type: application/json \ -d { model: my_qa_model, messages: [{role: user, content: 你的问题}] }vLLM的几个关键参数值得细说--gpu-memory-utilization 0.9允许vLLM使用90%的显存做KV Cache。这个值设置得越高并发能力越强但太高容易OOM我一开始设0.95直接崩了后来降到0.9才稳定。--max-model-len 2048控制最大上下文长度。如果你的业务里用户输入的文本很短可以适当调小这个值给显存腾出更多空间来做KV Cache从而提升并发吞吐。--tensor-parallel-size单卡传1多卡可以传GPU数量vLLM会自动做张量并行。注意LoRA微调后的权重在部署前要先合并回模型权重文件否则vLLM加载LoRA权重会出现格式兼容问题。合并方法很简单用PEFT库的model.merge_and_unload()就能导出合并后的完整模型。5.2 服务架构除了模型你还得管这些模型推理服务只是整个系统的一半另一半是业务层的编排。我最终定的服务架构是这样的入口层Nginx做反向代理负责SSL终止和请求分发也可以挡掉一部分恶意流量业务编排层FastAPI写了一个BFF层Backend For Frontend负责把用户请求转换成模型输入格式拼装Prompt、调用RAG检索、处理超时和重试推理层独立的vLLM服务只接收处理好格式的请求不掺杂业务逻辑向量检索层Qdrant提供的服务给RAG用缓存层Redis存常用问题的答案缓存同样的用户问题在24小时内直接命中缓存返回可以省掉大量重复计算这层设计让我深刻体会到AI工程拼的不只是模型更是整体架构的可靠性。有一次我在测试阶段没有接入Redis缓存结果重复的压测请求全打到模型推理上QPS直接上不去加上缓存之后瞬时QPS轻松翻倍。5.3 成本控制算好每一条请求的账做AI工程的人最容易被忽视的就是成本。特别是GPU服务器的费用按小时计费一旦训练脚本有问题一天跑下来可能就是大几百块的无意义消耗。我在项目里做了三件事来控制成本所有训练任务加检查点checkpoint每200步自动保存一份防止跑了几小时后OOM中断一切重来。优先用4bit量化加载模型做实验确定性实验再上全精度。实践证明很多调参实验的结论在4bit和全精度下是一致的没必要每轮实验都烧全精度。部署环境用抢占式实例跑训练任务成本低用按量付费实例跑线上服务稳定性优先。这个组合策略很香适合预算有限的团队。6. 常见问题与排查技巧实录实际操作中遇到的问题特别多整理几个典型的高频问题做成速查表每个我都踩过坑照着排查能省很多时间。问题表现排查路径解决/避坑方案显存OOM训练或推理中途程序崩溃报CUDA out of memory先看加载模型的位宽再看max_length再看batch_size4bit量化 梯度累积 减小max_length三管齐下LoRA训练loss不下降训练了很长时间loss纹丝不动看学习率是否太小看target_modules是否漏了MLP层把学习率调到1e-4级别补全target_modules推理请求排队严重QPS一高延迟就爆炸检查是否用了原生generate检查gpu-memory-utilization换vLLM提高显存利用率到0.9微调后回答退化成复读机无论问什么模型只输出固定几句话数据重复度太高模型过拟合了去重降低epoch到1-2轮中文回答出现乱码生成内容出现[UNK]或者断裂tokenizer和训练时不一致微调和部署必须用同一个tokenizerRAG召回的全是无关内容生成回答和知识库完全不相关嵌入模型对领域语言理解不足chunk切得太碎换领域适配的嵌入模型调整chunk大小和重叠度6.1 最常被低估的问题训练和推理的tokenizer不一致这个问题非常隐蔽。微调的时候用的是训练数据的tokenizer配置部署的时候如果又用了全新的tokenizer对象两边的词表不一致生成的文本就经常出现乱码或者语义漂移。解决的办法很简单把tokenizer和模型一起保存部署时直接AutoTokenizer.from_pretrained(模型路径)加载同一个。6.2 Bad Case分析的正确姿势我强烈建议在项目一开始就搭一个简单的“bad case管理表”每一条bad case记录三样东西用户输入的原问题模型输出的答案你认为的理想答案然后每周固定的时间做一次回归分析把新发现的bad case和之前的对比看是否在变好。这比自己凭感觉看效果靠谱得多尤其是当模型迭代了好几版之后没有这个管理表你根本说不清上一版和这一版到底哪个更好。我曾经连续训练了三版模型主观感觉第三版明显不错但打开bad case表一看之前的60%的问题根本没被解决只是在其他方面表现得更好而已——这种可视化倾向驱动的判断偏差是AI项目里特别普遍但又没人愿意承认的事。6.3 压测工具模拟真实流量前先想想部署完之后一定要做压测但压测的方式有讲究。直接用ab压测工具打vLLM接口测出来的数字和真实场景差异很大因为用户的问题长度、并发模式都更复杂。我当时写了一个模拟脚本用真实用户日志里的问题分布去压测才暴露出一个有意思的问题长文本请求占并发总量10%左右的时候显存KV Cache会被长序列占满导致短文本请求反而排队。后来用PagedAttention的特性加了一个显存KV Cache上限控制才解决。提示如果压测时发现请求排队严重先检查vLLM有没有排队日志。vLLM在队列里积压的请求数超过一定阈值时说明你的模型服务吞吐已经到瓶颈了这时候该考虑加副本、加GPU而不是继续调代码。7. 一些真实经验总结和心态建议走完“ai-engineering-from-scratch”这个项目之后我最大的收获倒不是学会了一套具体的技术栈而是形成了对AI工程的整体认知——知道它复杂在什么地方也知道每个环节都有成熟的解决方案不会再被“AI很神秘”这种想法吓住。给正在上路的朋友几个实在的建议第一先用一个尽量小的闭环跑通。不要一开始就上一个庞大的多阶段pipeline。用一个小数据量级别的领域知识库配一个7B模型从数据清洗到微调评估部署把每个环节各跑通一遍你会发现整套流程没有想象中那么难难的是对每一个细节的理解。第二一定先建评测体系再优化模型。这句话我再重复一遍没有评测就没有优化方向。肉眼感觉的“效果不错”和Objective指标上的提升很多时候并不等价。第三多看看别人的踩坑记录。现在社区的氛围比前几年好很多很多开源项目的issue里都有人记录自己遇到的坑和解决方案这个信息源的价值被严重低估。我遇到好多次问题了都是先在GitHub issue里搜索三分钟就有答案比自己写半天调试代码快得多。第四也是我个人体会最深的一点不要等到“完全学会”了再动手。AI工程这个领域知识更新速度太快永远不可能有一个“学完”的状态。我自己的路径就是一边学一边做遇到什么新问题就查什么方案查完继续做。你需要的只是确定一个具体目标然后开始。希望这篇长文能帮正在折腾AI工程的你少走几步弯路。后面我还会把每个环节单独拆出来写更细的文章包括数据清洗的code细节、LoRA的超参搜索方法、vLLM压测观察等有感兴趣的具体问题也欢迎交流我踩过的坑大概率也能帮你避开。