
1. 从零搭建AI工程体系为什么我劝你别一上来就调包“ai-engineering-from-scratch”这个标题第一次看到的时候我愣了一下。不是因为陌生恰恰相反是因为太熟悉了——过去两年里我至少带过五六个从零起步的AI工程项目有做推荐系统的有做文档问答的也有做多模态检索的。每一次都是从“装环境”开始到“模型上线”结束中间踩过的坑能写满一个笔记本。所以当我看到这个标题脑子里第一反应不是“又一个教程”而是“终于有人把这件事当成一个工程问题来聊了”。先把话说清楚这篇文章不是教你从零训练一个大模型。那件事的算力门槛和工程复杂度不是一篇文章能覆盖的也不适合绝大多数团队。这里说的“from scratch”指的是从零搭建一套能跑通、能迭代、能上线的AI工程体系——包括数据管道、特征处理、模型选型、推理服务、监控告警这一整条链路。换句话说你手里可能有一个预训练模型也可能调用外部API但你要做的是把“模型能力”变成“产品能力”的那套工程骨架。这件事适合谁看三类人。第一类是有后端或数据工程背景想转AI工程方向的开发者你们有工程底子缺的是AI链路的特殊性和踩坑经验。第二类是算法出身但没怎么碰过生产环境的同学你们模型调得溜但一到部署、并发、监控就头大。第三类是小团队的技术负责人你们没有大厂那种成熟的MLOps平台需要自己从零搭一套够用、不贵、能维护的方案。我自己的经历比较典型最早做AI项目的时候我也是“notebook一把梭”模型在本地跑得飞起一上线就各种问题——内存泄漏、推理延迟抖动、数据漂移没人发现。后来被逼着把整个链路重新设计了一遍才慢慢摸清楚哪些环节是真正关键的哪些是看起来重要其实可以后补的。这篇文章就是把这些经验整理出来按“从零搭建”的顺序讲一遍每个环节都告诉你为什么这么做、不这么做会怎样、以及我实际踩过的坑。2. 整体架构设计先画数据流再选组件2.1 为什么我坚持“数据流优先”而不是“模型优先”很多团队做AI项目第一件事是选模型——是调GPT还是用开源Llama是微调还是RAG。这个思路不能说错但顺序有问题。我自己的习惯是先把数据从输入到输出的完整路径画出来再决定每个节点用什么组件。原因很简单模型只是链路中的一环而且往往是可替换的一环。真正决定系统能不能跑、好不好维护的是数据怎么进来、怎么处理、怎么存、怎么出去。举个例子。我之前做一个合同审查的AI助手最初方案是“上传PDF→调模型→返回结果”。画完数据流才发现中间缺了太多东西PDF解析出来的文本质量参差不齐表格和扫描件经常乱码模型返回的结果需要结构化不能是一段自由文本用户可能上传几十页的合同需要分块处理再合并。这些问题跟模型选型没关系但如果不提前想清楚上线后就是无尽的返工。所以我的建议是拿一张白纸从“用户输入”开始画一直画到“用户看到结果”中间每一个数据形态的变化都标出来。比如原始文件→解析后文本→分块后的chunk→向量化后的embedding→检索结果→模型输入→模型输出→结构化结果→前端展示。画完这张图你自然就知道需要哪些组件了。2.2 组件选型的三个原则够用、可换、能观测画完数据流接下来是选组件。市面上的工具太多了向量数据库有十几家推理框架也有七八种很容易陷入“选型焦虑”。我自己的原则就三条第一够用就行别为未来过度设计。很多团队一上来就上Kubernetes、上分布式向量库、上特征平台结果日请求量不到一千。我一般建议日请求量低于一万、数据量低于百万级用单机方案完全够。向量检索用FAISS或者pgvector推理用FastAPI包一层数据库用PostgreSQL这套组合能撑很久。等真的到瓶颈了再换迁移成本远低于你一开始就搭复杂架构的维护成本。第二每个组件都要有可替换的备选。这不是说你要写抽象层而是说选型时尽量选接口标准的。比如向量检索如果直接用某个厂商的SDK以后想换就麻烦了但如果用LangChain或者自己封一层换起来就是改配置的事。推理服务也一样用OpenAI兼容的接口格式本地模型和外部API可以无缝切换。第三从第一天就要能观测。这是我最想强调的一点。AI系统跟传统后端最大的区别是它的输出是不确定的。同样的输入模型可能给出不同的结果数据分布变了效果会悄悄下降。如果你没有日志、没有指标、没有追踪出了问题根本不知道从哪查。所以我在搭任何AI项目时第一周就会把日志和基础监控接上哪怕只是打印到文件。2.3 一个最小可行架构的参考基于上面的原则我给一个我实际用过的最小可行架构适合日请求量几千到几万的场景层级组件选型建议备注接入层API网关FastAPI / Flask轻量够用业务层编排逻辑自己写Python模块别过早引入复杂框架模型层推理服务OpenAI兼容接口本地/外部可切换检索层向量检索pgvector / FAISS数据量小用pgvector存储层关系库PostgreSQL元数据、日志都放这缓存层结果缓存Redis相同请求直接返回观测层日志指标结构化日志Prometheus第一天就接这套架构的好处是所有组件都是成熟的、有大量文档的、招人好招的。你不需要一个专门的MLOps团队来维护一个后端工程师加一个算法工程师就能跑起来。等业务量上来了再把检索层换成专用向量库、把推理层换成GPU集群都是平滑的。3. 数据管道AI工程里最脏最累但最重要的部分3.1 数据清洗别信“原始数据直接喂模型”那套我见过太多项目死在数据上。模型选得再好数据一塌糊涂效果就是上不去。而数据问题里最常见的就是清洗没做够。什么叫清洗没做够举几个我实际遇到的例子。做文档问答时PDF解析出来的文本里混着页眉页脚、页码、乱码字符这些噪声会严重干扰检索效果。做用户评论分析时评论里有大量表情符号、重复字符、无意义的口语直接向量化后相似度计算完全不准。做多轮对话时历史对话里夹杂着系统提示、错误信息模型会被带偏。我的做法是针对每种数据源写一个专门的清洗函数并且把这个函数当成核心代码来维护。清洗不是“预处理一下”而是工程链路里的一等公民。具体来说清洗至少包括这几步格式归一化统一编码、统一换行符、去掉控制字符。噪声过滤去掉页眉页脚、广告、导航栏这些模板内容。如果是网页数据用Readability之类的库提取正文。文本规范化全角转半角、繁简统一、数字和单位统一。这一步看场景不是必须但做了会减少很多意外。分块策略长文本必须分块但怎么分有讲究。按固定长度分最简单但会切断语义按段落分更合理但段落可能很长按语义分效果最好但需要额外模型。我一般用“递归分块”先按段落分段落太长再按句子分句子还长再按字符分同时保留一定的重叠。注意清洗逻辑一定要写单元测试。我吃过亏改了一行清洗代码结果把某个关键字段过滤掉了线上跑了三天才发现。现在我的习惯是每个清洗函数都配几个典型样本的测试用例。3.2 数据版本管理别再用文件名区分了“data_v2_final_真的最终版.csv”这种命名我相信很多人都见过。小项目凑合能用但只要超过两个人协作或者需要回溯“上周的效果是用哪版数据跑的”就彻底乱套。数据版本管理不需要多复杂但必须有。我的做法是每次数据变更都生成一个内容哈希用哈希作为版本号同时记录变更说明。具体实现可以很简单把数据文件算个MD5存到数据库里关联上时间、操作人、变更描述。这样任何时候你都能回答“线上模型用的是哪版数据”这个问题。如果数据量不大比如几GB以内可以直接用DVC或者Git LFS来管理。如果数据量大就用对象存储加元数据表的方式。关键不是工具而是习惯任何进入训练或索引的数据都必须有版本号。3.3 特征工程AI工程和传统ML的分水岭虽然现在大模型时代很多人不提“特征工程”了但在实际工程里特征处理依然关键。只不过形式变了以前是手动设计特征现在是设计“怎么把原始数据转成模型能用的输入”。我举几个实际场景。做搜索排序时除了向量相似度还需要考虑时间衰减、点击率、内容质量分这些特征这些都需要在工程链路里计算和存储。做推荐时用户的历史行为需要聚合、编码变成模型可用的输入。做风控时各种统计特征需要实时计算。这些特征处理逻辑我建议独立成一个模块不要散落在业务代码里。原因有两个一是特征逻辑经常需要迭代独立模块方便测试和替换二是训练和推理需要用同一套特征逻辑如果散落各处很容易出现“训练时用的特征和线上不一致”这个经典问题。具体做法写一个FeatureProcessor类输入原始数据输出特征字典。训练时用它生成训练数据推理时用它生成实时特征。保证两边调用的是同一个函数就不会出现不一致。4. 模型层选型、封装与推理优化4.1 模型选型别只看榜单要看你的场景模型榜单上的分数跟你的实际效果往往差得很远。我见过太多团队照着榜单选了个“最强模型”结果在自己的场景上效果还不如一个小模型。原因很简单榜单测的是通用能力你的场景是特定的。我的选型流程是这样的先定义评估集再跑候选模型最后看成本和延迟。评估集不需要很大一两百条真实场景的样本就够但必须覆盖你的核心用例和边界情况。然后拿几个候选模型跑一遍看准确率、召回率这些指标。最后结合成本和延迟做决策——如果两个模型效果差不多选便宜的那个如果贵的那个效果好5%但成本高10倍那要仔细想想这5%值不值。还有一个容易被忽略的点模型的输出格式稳定性。有些模型有时候返回JSON有时候返回带解释的文本这在工程上很麻烦。选型时一定要测一下看模型能不能稳定按你要求的格式输出。如果不能要么换模型要么在工程上加一层解析和重试。4.2 推理服务封装为什么我推荐OpenAI兼容接口不管你用的是外部API还是本地部署的模型我都强烈建议用OpenAI兼容的接口格式来封装。原因有三个第一生态好。大量工具和框架都支持OpenAI格式你封装成这个格式以后想接LangChain、LlamaIndex这些直接就能用。第二可切换。今天用外部API明天想换本地模型只要本地模型也提供兼容接口现在很多推理框架都支持业务代码一行不用改。第三标准化。请求和响应的结构是固定的日志、监控、缓存都好做。具体实现上如果你用FastAPI可以写一个简单的路由把请求转成内部模型的调用再把结果转成OpenAI格式返回。如果是本地模型用vLLM或者TGI这些框架它们本身就提供兼容接口直接起服务就行。# 一个极简的推理服务封装示例 from fastapi import FastAPI from pydantic import BaseModel app FastAPI() class ChatRequest(BaseModel): model: str messages: list temperature: float 0.7 app.post(/v1/chat/completions) async def chat_completions(req: ChatRequest): # 这里替换成你实际的模型调用 result call_your_model(req.messages, req.temperature) return { choices: [{message: {role: assistant, content: result}}], model: req.model }这段代码很简单但它是整个推理层的基础。有了它你后面加缓存、加限流、加日志都是在这个基础上扩展。4.3 推理性能优化几个立竿见影的手段推理性能是AI工程里最容易被低估的问题。模型在本地跑一次可能只要几百毫秒但上线后并发一上来延迟就飙到几秒甚至超时。我总结几个实际有效的优化手段批处理Batching这是最有效的。单个请求推理GPU利用率很低把多个请求攒一批一起推理吞吐量能提升几倍到几十倍。vLLM这类框架自带连续批处理效果很好。如果自己写可以用一个队列攒请求攒到一定数量或者等一小段时间就一起推理。量化Quantization把模型权重从FP16降到INT8甚至INT4显存占用大幅下降推理速度也有提升。代价是效果可能略有下降需要评估。对于大多数场景INT8量化的效果损失可以接受。缓存Caching相同的请求直接返回缓存结果。这在问答类场景特别有效因为很多用户问的问题是重复的。缓存key可以用请求的哈希注意要把temperature0的请求才缓存有随机性的不缓存。流式输出Streaming虽然不降低总延迟但能显著改善用户体验。用户看到字一个个出来感觉上快很多。实现上用SSE或者WebSocket都行。实操心得优化之前一定要先测量。我见过团队花一周做量化结果发现瓶颈其实在数据预处理上。先用 profiling 工具找到真正的瓶颈再针对性优化。5. 检索层RAG系统的核心战场5.1 向量化模型选择比你想的重要做RAG系统向量化模型的选择直接决定检索效果。我试过七八种embedding模型差距真的很大。有些模型在通用语义相似度上表现好但在特定领域比如法律、医疗就差很多。我的建议是优先选在你的领域数据上表现好的模型而不是榜单上分数最高的。怎么判断拿你的真实查询和文档人工标注一批“相关/不相关”然后测不同模型的召回率。这个工作量不大但能避免后面大量返工。另一个关键点是维度和成本的权衡。高维向量比如1536维效果通常更好但存储和计算成本也高。如果数据量不大百万级以内高维没问题如果数据量上亿可能要考虑降维或者用低维模型。还有一点同一个系统里查询和文档必须用同一个模型向量化。这个听起来是废话但我真的见过有人查询用A模型、文档用B模型然后纳闷为什么检索不准。5.2 分块策略检索效果的一半在这里分块Chunking是RAG里最被低估的环节。很多人随便按500字切一刀就完事结果检索出来的内容要么不完整要么包含大量无关信息。我实际用下来比较好的策略是按语义边界分块同时保留上下文。具体来说优先按段落分段落是天然的语义单元。如果段落太长超过模型最大输入按句子分但保留前后各一句作为上下文。如果句子还长按字符分但尽量在标点处切。块之间保留10%-20%的重叠避免边界信息丢失。另外给每个块加上元数据很重要。比如来源文档、章节标题、页码。检索时可以按元数据过滤也能在返回结果时给用户展示来源。5.3 混合检索向量不是万能的纯向量检索有个问题它对关键词匹配不敏感。比如用户搜一个特定的产品型号“XR-2000”向量检索可能返回一堆语义相似但型号不对的内容。这时候就需要关键词检索BM25来补充。我的做法是向量检索和BM25各跑一遍然后用RRFReciprocal Rank Fusion融合结果。RRF很简单就是把两个排序列表按排名倒数加权求和不需要调参效果稳定。实测下来混合检索比纯向量检索的召回率能提升10%-20%。如果还想更进一步可以加一个重排序Rerank模型。先粗召回几十条再用cross-encoder精排取top几。这个能显著提升精度代价是增加一点延迟。对于质量要求高的场景值得加。6. 监控与迭代上线只是开始6.1 必须监控的三个指标AI系统上线后有三类指标必须盯着第一性能指标延迟、吞吐量、错误率。这些跟传统后端一样用Prometheus加Grafana就能搞定。要注意的是AI系统的延迟分布往往很长尾P99可能比P50高一个数量级所以一定要看分位数不能只看平均值。第二质量指标这个最容易被忽略但最重要。模型输出质量怎么监控我的做法是定期抽样人工评估加上自动化的启发式检查。比如对于问答系统可以检查答案是否包含引用来源、是否为空、长度是否异常。对于分类系统可以监控各类别的预测分布如果某个类别的比例突然变化可能是数据漂移了。第三业务指标用户满意度、点击率、转化率这些。AI系统最终是要服务业务的技术指标再好业务指标不涨也是白搭。6.2 数据漂移检测别等效果崩了才发现数据漂移是AI系统特有的问题。用户行为变了、输入数据分布变了模型效果就会下降。但这个过程往往是渐进的不监控就发现不了。我的做法是定期计算线上输入数据的统计特征跟训练数据对比。比如文本长度分布、词汇分布、向量分布。如果差异超过阈值就告警。具体实现可以用PSIPopulation Stability Index或者KL散度都不难算。发现漂移后怎么办不一定要马上重新训练。先分析原因如果是暂时的波动可能不用管如果是持续的趋势那就要考虑更新数据、重新训练或者微调。6.3 迭代闭环从反馈到改进一个健康的AI系统应该有一个从用户反馈到模型改进的闭环。用户点了“不满意”这个信号要能被收集、分析、用于改进。具体来说我会做这几件事记录所有请求和响应包括用户反馈定期分析bad case看是数据问题、模型问题还是工程问题把确认的bad case加入评估集防止以后回归根据分析结果决定下一步动作——是改prompt、换模型、补数据还是调检索策略。这个闭环不需要很复杂但必须存在。我见过太多团队上线后就不管了效果慢慢下降最后用户流失了才反应过来。7. 常见问题与排查技巧实录7.1 效果类问题排查问题检索出来的内容不相关。排查顺序先看查询向量化是否正常有没有报错、维度对不对再看文档向量化是否用了同一个模型然后看分块是否合理块太大或太小都不行最后看是否需要加关键词检索或重排序。问题模型回答胡编乱造。排查顺序先看检索结果里有没有正确信息如果没有是检索问题再看prompt里有没有明确要求“基于给定内容回答”然后看模型是否适合这个任务有些模型指令遵循能力差最后考虑加few-shot示例或者换模型。问题相同问题每次回答不一样。这个通常是temperature设置的问题。如果希望稳定输出把temperature设为0。但要注意即使temperature0有些模型因为浮点计算的原因也可能有微小差异。如果要求完全一致加缓存。7.2 性能类问题排查问题推理延迟高。先看是模型推理慢还是前置处理慢。用profiling工具分段计时。如果是模型慢考虑量化、批处理、换更小的模型。如果是前置处理慢看是不是向量化或者检索拖了后腿。问题并发上不去。检查是不是每个请求都新建了模型实例或者数据库连接。这些资源必须复用。另外看GPU利用率如果利用率低但延迟高可能是批处理没做好。问题内存泄漏。Python里常见的是全局变量不断增长、缓存没有淘汰策略、循环引用。用memory profiler定期检查。另外注意有些模型加载后会占用大量内存如果反复加载不释放很快就会OOM。7.3 我的避坑清单坑后果避坑方法训练和推理特征不一致效果断崖式下降特征逻辑独立成模块两边共用没做数据版本管理无法复现结果每次数据变更记录哈希和说明没监控质量指标效果下降发现不了定期抽样评估自动化检查缓存没设过期返回过时结果缓存加TTL重要数据不缓存没做限流被刷爆API层加限流和配额prompt硬编码改一次要发版prompt模板化配置化忽略P99延迟少数用户体验极差监控分位数不只看平均8. 一些关于“从零搭建”的个人体会做AI工程这几年我最大的体会是这件事的难点不在AI在工程。模型能力是现成的调包就能用但怎么让它在生产环境稳定、高效、可维护地跑起来才是真正考验人的地方。另一个体会是别追求一步到位。我见过太多团队想一开始就搭一个“完美”的架构结果三个月过去了还没上线。正确的做法是先跑通最小闭环哪怕很粗糙然后根据实际遇到的问题逐步优化。很多你一开始担心的问题实际上根本不会发生而真正的问题往往是你没想到的。最后说一个心态上的事。AI系统跟传统软件不一样它没有“完成”的那一天。数据在变、用户在变、模型在更新你需要持续地监控、评估、迭代。这不是负担而是这个领域的常态。接受这一点你就能做得更从容。如果非要给一个建议那就是从第一天就开始记录。记录你的决策、记录你踩的坑、记录效果的变化。这些记录不仅帮你复盘也是团队知识沉淀的基础。我现在的习惯是每个项目都维护一个“决策日志”每次重要选择都写清楚背景、选项、理由。这个习惯帮我省了无数次“当初为什么这么设计”的困惑。