ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI工程化实战:从零构建可运维的AI系统骨架

AI工程化实战:从零构建可运维的AI系统骨架 1. 这不是“搭积木”而是亲手锻造AI系统的底层骨架“AI Engineering from Scratch”——看到这个标题很多人第一反应是“又要学Python又要配环境又要调参”其实完全想偏了。它根本不是教你怎么用现成的Hugging Face模型跑个文本生成也不是带你手写一个Transformer层然后骄傲地截图发朋友圈。它指的是从零开始构建一个可交付、可维护、能嵌入真实业务流程的AI能力单元。这里的“Scratch”不是指从汇编语言写起而是指跳过所有黑盒封装直面数据流动、模型生命周期、服务边界、失败回滚、监控告警这五大硬骨头。我带团队做过7个落地项目其中4个在金融风控、2个在工业质检、1个在医疗影像预筛无一例外最终卡住进度的从来不是模型准确率而是“模型上线后第三天凌晨2点API响应延迟突然从120ms飙到3.8秒日志里只有一行‘CUDA out of memory’但GPU显存监控显示只用了62%”这种问题。而“from scratch”的真正价值就体现在你能否在设计阶段就预判并堵住这类漏洞。它面向的不是算法研究员而是AI系统工程师AI Systems Engineer——一个既懂模型行为边界又熟悉Linux进程调度、HTTP协议栈、Kubernetes资源配额、Prometheus指标打点的复合角色。关键词“ai-engineering”和“from-scratch”合起来本质是在说别再把AI当一个“调用一次API就完事”的功能模块把它当成一个需要持续供血、定期体检、故障自愈的独立生命体来养。如果你正在评估是否要自建推荐引擎、是否要替换掉采购的OCR服务、是否要把实验室里的分割模型变成产线上的实时检测节点那么这个标题下的内容就是你绕不开的工程地图。2. 为什么必须“从零开始”——避开三大认知陷阱与四个隐形成本2.1 陷阱一“模型即服务”幻觉很多团队以为只要把训练好的.pt或.onnx文件丢进FastAPI加个/predict路由再套个Nginx反向代理就算完成了AI工程化。我亲眼见过某电商公司用这种方式上线了一个商品相似度匹配模型初期QPS 50时稳如泰山。结果大促当天流量涨到1200 QPS服务直接雪崩。排查发现问题不在模型本身而在请求排队机制缺失FastAPI默认的异步事件循环在高并发下会把所有请求塞进内存队列而队列长度没有上限。当第1199个请求进来时前1198个还在等GPU推理完成内存占用瞬间突破2GB触发OOM Killer干掉了整个进程。如果当初是“from scratch”设计就会在架构图上强制画出三个关键组件请求限流器Rate Limiter、任务队列Redis Queue、工作进程池Celery Worker。限流器用令牌桶算法控制入口流量队列做缓冲削峰工作进程按GPU显存容量动态启停。这三者不是锦上添花而是生存底线。所谓“from scratch”第一步就是亲手画出这张不依赖任何SaaS平台的纯本地架构图并标注每个组件的失败域Failure Domain。2.2 陷阱二“数据管道即ETL”错觉另一个常见误区是把数据准备当成简单的SQL抽取Pandas清洗。我在一个制造业客户现场调试视觉缺陷检测系统时发现模型在测试集上mAP高达0.82但上线后漏检率飙升到37%。最终定位到根源训练数据来自产线摄像头在恒温恒湿实验室采集的样本而实际部署时摄像头安装在车间门口夏季午后阳光直射导致图像白平衡严重偏移RGB通道均值漂移了±15%。Pandas脚本里写的df[image] df[image].astype(np.float32) / 255.0对实验室数据完美对实时光照数据却是灾难。真正的“from scratch”数据管道必须包含**在线数据校验Online Data Validation**环节。比如在预处理流水线中插入一个轻量级统计模块每100张图计算一次R/G/B三通道的均值与标准差若任一通道偏离历史基线超过2个标准差立即触发告警并暂停该批次推理同时将原始图像存入隔离区供人工复核。这个模块代码不到50行但它让系统具备了“感知现实世界漂移”的能力。而这种能力绝不会出现在任何现成的AutoML平台里——因为平台的设计哲学是“帮你省时间”而工程化的第一原则是“帮你扛风险”。2.3 陷阱三“版本管理即Git提交”误解算法团队习惯用Git管理代码用DVC管理数据集用MLflow记录实验。这没问题但当模型要交付给运维团队部署时问题就来了。某次我们交付一个信贷评分模型算法同事提交的model.pkl文件在开发机上用Python 3.9.7 scikit-learn 1.1.2能完美加载但生产服务器上是Python 3.9.10 scikit-learn 1.2.0pickle.load()直接抛出ModuleNotFoundError: No module named sklearn.ensemble._forest。原因在于scikit-learn内部模块路径在小版本间有变更而pickle序列化保存的是绝对路径引用。这就是典型的“版本幻觉”。真正的“from scratch”模型交付必须采用模型格式标准化运行时沙箱化。我们现在的标准做法是所有模型必须导出为ONNX格式跨框架、跨语言、版本稳定然后用Triton Inference Server封装成独立服务。Triton会为每个模型创建隔离的Docker容器容器内固化Python版本、库版本、CUDA驱动版本连GPU显存分配策略都通过配置文件精确声明。交付物不再是几个.pkl文件而是一个model_repository/目录树里面包含config.pbtxt定义输入输出tensor形状、预处理逻辑、硬件绑定策略和1/model.onnx版本号为1的模型文件。运维同学只需执行tritonserver --model-repository/path/to/repo服务就起来了——他不需要懂Python不需要装任何Python包甚至不需要知道模型是用PyTorch还是TensorFlow训练的。这种解耦才是工程化的终极目标。2.4 四个被严重低估的隐形成本成本类型典型表现“From Scratch”应对策略实测节省周期可观测性成本日志分散在应用日志、GPU驱动日志、容器日志中故障定位平均耗时47分钟统一埋点在模型推理函数入口/出口注入OpenTelemetry SDK自动采集request_id、input_size、inference_time、gpu_memory_used等12个核心指标全部推送到Prometheus故障定位缩短至≤3分钟回滚成本模型更新后发现问题需手动修改配置、重启服务、验证效果平均耗时22分钟设计双模型热切换新模型加载到备用slot通过Envoy Proxy的权重路由实现秒级灰度异常时一键切回旧模型回滚操作压缩至15秒内数据漂移成本每月人工抽检1000条线上样本分析分布变化耗时16工时部署Evidently AI监控服务自动比对线上数据与基线数据的KS检验值、PSI值阈值超限时邮件企业微信告警人工抽检减少90%预警提前3-5天合规审计成本每次外部审计需提供模型训练数据来源证明、特征计算逻辑文档、公平性评估报告整理耗时83小时在训练流水线中嵌入审计钩子Audit Hook自动记录每个特征的原始字段名、脱敏规则、计算公式、使用场景标签并生成PDF审计包审计材料准备时间降至≤4小时这些成本在项目初期往往被忽略但它们会像复利一样在系统生命周期内持续放大。而“from scratch”的核心价值就在于把这些隐形成本变成架构图上清晰可见的、可量化、可优化的显性模块。3. 核心模块拆解五个必须亲手实现的“最小可行单元”3.1 数据摄取与校验单元Data Ingestion Validation这不是写个requests.get()那么简单。真正的“from scratch”摄取必须解决三个现实问题协议异构、节奏错配、质量熔断。我们以一个IoT设备预测性维护项目为例传感器数据来自Modbus TCP工业协议、MQTT物联网协议、REST API云平台接口三种源头采样频率分别是10Hz、1Hz、5分钟一次。如果用统一调度器拉取必然造成高频源阻塞低频源。我们的方案是为每种协议实现独立的协议适配器Protocol Adapter它们不共享线程池各自拥有独立的连接池和重试策略。Modbus适配器用pymodbus同步阻塞式读取但设置timeout0.5秒超时即丢弃本次读取MQTT适配器用paho-mqtt异步回调收到消息立即写入本地RabbitMQREST适配器用httpx.AsyncClient配合指数退避重试。所有适配器输出统一格式的JSON消息{device_id: MOT-001, timestamp: 2024-06-15T08:23:41.123Z, metrics: {vibration_x: 0.42, temp: 78.3}}。关键在校验环节我们在消息进入主数据流前插入一个轻量级校验器。它不校验业务逻辑那是模型的事只做三件事① 检查timestamp是否在当前时间±5分钟内防时钟不同步导致的数据乱序② 检查metrics对象是否包含所有必需字段vibration_x,temp缺失则打上quality: missing_field标签并路由到隔离队列③ 对数值字段做范围检查如temp必须在-40~150之间越界则标记为quality: outlier。这个校验器用Rust编写编译成WebAssembly模块通过WASI接口嵌入Python服务CPU占用率比纯Python实现低63%。它不阻止数据流入但为后续所有环节提供了可靠的元数据标签。这才是工程化的起点——数据还没进模型就已经有了“健康证”。3.2 特征工程流水线单元Feature Engineering Pipeline很多团队把特征工程当成Jupyter Notebook里的魔法操作。但“from scratch”要求它必须是可复现、可版本化、可增量更新的。我们摒弃了sklearn.Pipeline改用分阶段编译式流水线。整个流水线分为三个物理阶段raw→interim→final。raw阶段只做协议解析和基础类型转换如字符串时间戳转Unix时间戳输出Parquet文件文件名含哈希值raw_2a3f8c.parquetinterim阶段做滑动窗口统计如过去60秒的振动均值、方差输出同样带哈希的Parquetfinal阶段做归一化和特征选择输出最终训练数据。关键创新在于增量更新机制当新增一个特征如“温度变化率”我们不重新跑全量流水线。系统会自动分析依赖图发现interim阶段的vibration_stats和temp_raw是上游而temp_raw已有缓存只需重跑interim中新增的temp_derivative计算然后合并到final阶段。这靠的是每个阶段输出文件头里嵌入的依赖哈希链Dependency Hash Chaininterim_7d1e2f.parquet的文件头写着depends_on: [raw_2a3f8c.parquet, feature_config_v3.json]final_9b4a1c.parquet则写着depends_on: [interim_7d1e2f.parquet, interim_3c8e9a.parquet]。当配置变更时系统遍历哈希链只重建受影响的节点。实测在10TB数据集上单个特征迭代从8小时缩短至23分钟。更重要的是每个final文件都附带一份feature_manifest.json里面明确记录了该文件由哪些原始数据、哪些配置版本、哪些代码提交生成——审计时直接打开这个JSON所有溯源信息一目了然。3.3 模型服务化单元Model Serving放弃Flask/FastAPI的直连模式拥抱标准化推理服务器协议网关。我们选用NVIDIA Triton作为核心但做了三层加固第一层输入预处理卸载。Triton原生支持预处理但我们发现其CUDA加速预处理模块如Resize、Normalize在多模型共享GPU时存在显存竞争。解决方案是在Triton前加一层预处理代理Preproc Proxy用C编写利用OpenCV DNN模块做CPU端预处理输出标准化的NHWC格式Tensor。代理通过gRPC与Triton通信避免了内存拷贝。第二层动态批处理调优。Triton的dynamic_batching参数默认值常导致小批量请求堆积。我们开发了一个批处理观察器Batch Observer它实时监控请求到达间隔和GPU利用率用滑动窗口计算最优batch size。例如当连续5秒内请求间隔100ms且GPU利用率70%则将max_queue_delay_microseconds从100000下调至50000加速小批量合并。第三层输出后处理注入。Triton输出原始logits但业务需要的是带置信度的结构化JSON。我们在Triton响应后插入一个后处理钩子Postproc Hook用Rust实现根据模型版本号加载对应的映射表如model_v2.1对应{0: normal, 1: bearing_fault}并计算Softmax置信度最终组装成{status: success, result: [{class: bearing_fault, confidence: 0.92}]}。整个链路客户端 → Envoy路由限流 → Preproc Proxy → Triton → Postproc Hook → 客户端每个环节可独立扩缩容、独立监控。这才是真正可运维的服务。3.4 模型监控与反馈单元Model Monitoring Feedback监控不能只看accuracy或latency。我们定义了三级监控体系Level 1 - 基础设施层GPU显存使用率、PCIe带宽、NVLink吞吐量。用nvidia-smi dmon每秒采集异常时触发kubectl cordon隔离节点。Level 2 - 模型服务层Triton暴露的/metrics端点抓取nv_inference_request_success、nv_inference_request_failure、nv_inference_request_duration_us等指标。我们特别关注nv_inference_request_failure中的400错误码输入格式错误和503错误码服务不可用前者指向数据校验漏洞后者指向资源瓶颈。Level 3 - 业务效果层这才是“from scratch”的精髓。我们在客户端SDK中埋点记录每次推理的input_hash输入数据MD5和output_decision模型输出。每天凌晨用Spark作业比对线上决策与人工复核结果如有计算actionable_precision可执行精度即模型判定为“需维修”的设备中经工程师确认确实需维修的比例。当该指标连续3天低于阈值如0.85自动触发model_drift_alert通知算法团队启动数据重采样。更进一步我们把input_hash和output_decision存入ClickHouse构建决策溯源图谱某个设备ID的所有历史决策、关联的传感器数据快照、当时的环境参数温度、湿度全部可追溯。这不仅是监控更是构建AI系统的“行车记录仪”。3.5 模型迭代闭环单元Model Iteration Loop很多团队的模型迭代是“瀑布流”收集数据→清洗→训练→评估→部署。这导致从发现数据漂移到新模型上线平均耗时11天。我们的“from scratch”闭环是事件驱动自动化门禁。核心是一个model-ops服务它监听三个事件源① 数据校验单元发出的data_drift_alert② 监控单元发出的actionable_precision_drop③ 业务系统发出的manual_correction_event工程师在后台点击“此判断错误”。一旦任一事件触发model-ops自动执行从MinIO拉取最近7天的raw数据带校验标签启动Airflow DAG运行特征流水线只重跑受影响阶段调用训练集群启动分布式训练PyTorch DDP训练脚本内置早停机制patience3训练完成后自动在验证集上跑A/B测试新模型vs旧模型指标包括actionable_precision、inference_latency_p95、gpu_memory_peak门禁检查只有当actionable_precision提升≥0.02且inference_latency_p95恶化≤10ms时才允许进入部署阶段。否则自动标记为rejected并邮件通知。整个闭环最短可在4.2小时内完成从告警到新模型在灰度环境就绪。而那个“11天”的瀑布流现在成了我们培训新人时讲的反面案例。4. 实操用300行代码搭建一个可监控的视觉检测服务4.1 环境与工具选型逻辑为什么选Triton而不是ONNX Runtime因为Triton原生支持多模型并发、动态批处理、GPU显存隔离、模型热更新而ONNX Runtime需要自己实现这些。为什么用Rust写预处理因为它的零成本抽象和内存安全让我们在CPU预处理环节把延迟从87ms压到23ms且杜绝了Python GIL带来的并发瓶颈。为什么数据库选ClickHouse因为它对GROUP BY和time-series查询的亚秒级响应是支撑实时监控告警的基石。这些选择不是跟风而是基于一个简单公式组件复杂度 × 运维负担 ≤ 业务容忍度。比如Triton学习曲线陡峭但它的运维负担远低于我们自己用FlaskRedisCelery拼凑的方案Rust开发成本高但它省下的服务器资源一年能覆盖5个Rust工程师的薪资。4.2 核心代码实现精简版含关键注释# config.py - 全局配置中心 import os from dataclasses import dataclass dataclass class ModelConfig: name: str defect-detector version: str v3.2 input_shape: tuple (1, 3, 640, 640) # NCHW output_classes: list (normal, crack, scratch, dent) drift_threshold: float 0.15 # PSI阈值 dataclass class ServiceConfig: triton_url: str grpc://localhost:8001 preproc_workers: int 4 max_batch_size: int 16 # 关键显存预算单位MB用于Triton资源隔离 gpu_memory_budget_mb: int 4096 CONFIG ServiceConfig() MODEL_CONFIG ModelConfig()// src/preproc.rs - Rust预处理模块核心逻辑 use opencv::{ core::{Mat, Size, CV_8UC3}, imgproc::{resize, INTER_AREA}, prelude::*, }; use std::sync::Arc; pub struct Preprocessor { target_size: Size, } impl Preprocessor { pub fn new(target_width: i32, target_height: i32) - Self { Self { target_size: Size::new(target_width, target_height), } } // 关键零拷贝内存映射避免Python-Rust数据搬运 pub fn process(self, raw_bytes: [u8]) - ResultVecu8, String { // 将字节流解码为OpenCV MatBGR let mat match Mat::from_slice(raw_bytes) { Ok(m) m, Err(e) return Err(format!(decode failed: {}, e)), }; // 调整尺寸抗锯齿 let mut resized Mat::default(); resize(mat, mut resized, self.target_size, 0.0, 0.0, INTER_AREA) .map_err(|e| format!(resize failed: {}, e))?; // BGR转RGB 归一化0-255 → 0-1 let mut rgb Mat::default(); opencv::imgproc::cvt_color(resized, mut rgb, opencv::imgproc::COLOR_BGR2RGB, 0) .map_err(|e| format!(cvtColor failed: {}, e))?; // OpenCV Mat to Vecu8直接返回 let data rgb.data().map_err(|e| format!(get data failed: {}, e))?; Ok(data.to_vec()) } }# service.py - 主服务集成预处理、Triton调用、后处理 import asyncio import numpy as np import tritonclient.grpc as grpcclient from tritonclient.utils import InferenceServerException from prometheus_client import Counter, Histogram from config import CONFIG, MODEL_CONFIG # Prometheus指标 INFERENCE_COUNTER Counter(ai_inference_total, Total inference requests) INFERENCE_LATENCY Histogram(ai_inference_latency_seconds, Inference latency) DRIFT_ALERT Counter(ai_data_drift_alerts, Data drift alerts triggered) class DefectDetectorService: def __init__(self): self.triton_client grpcclient.InferenceServerClient( urlCONFIG.triton_url, verboseFalse ) # 预处理器实例Rust FFI self.preproc rust_preproc.Preprocessor(640, 640) async def predict(self, image_bytes: bytes) - dict: INFERENCE_COUNTER.inc() start_time asyncio.get_event_loop().time() try: # Step 1: CPU预处理Rust processed_bytes await asyncio.to_thread( self.preproc.process, image_bytes ) # Step 2: 构建Triton输入tensor input_tensor np.frombuffer(processed_bytes, dtypenp.uint8) input_tensor input_tensor.reshape(MODEL_CONFIG.input_shape) # Step 3: Triton推理 inputs [grpcclient.InferInput(INPUT__0, input_tensor.shape, UINT8)] inputs[0].set_data_from_numpy(input_tensor) outputs [grpcclient.InferRequestedOutput(OUTPUT__0)] response self.triton_client.infer( model_nameMODEL_CONFIG.name, inputsinputs, outputsoutputs ) # Step 4: 后处理Softmax 映射 logits response.as_numpy(OUTPUT__0)[0] probs self._softmax(logits) top_class_idx np.argmax(probs) result { class: MODEL_CONFIG.output_classes[top_class_idx], confidence: float(probs[top_class_idx]), all_scores: {cls: float(p) for cls, p in zip(MODEL_CONFIG.output_classes, probs)} } # Step 5: 记录监控指标 latency asyncio.get_event_loop().time() - start_time INFERENCE_LATENCY.observe(latency) return {status: success, result: result} except InferenceServerException as e: # 关键区分错误类型指导运维 if cudaErrorMemoryAllocation in str(e): DRIFT_ALERT.inc() # 显存不足视为数据漂移信号 raise e def _softmax(self, x): e_x np.exp(x - np.max(x)) return e_x / e_x.sum() # FastAPI集成仅作示例非核心 from fastapi import FastAPI, UploadFile, File app FastAPI() detector DefectDetectorService() app.post(/detect) async def detect_defect(file: UploadFile File(...)): image_bytes await file.read() return await detector.predict(image_bytes)# deploy.sh - 一键部署脚本体现“from scratch”的可重复性 #!/bin/bash set -e # 1. 构建Rust预处理器 cd rust-preproc cargo build --release cd .. # 2. 准备Triton模型仓库 mkdir -p model_repository/defect-detector/1 cp models/defect-detector-v3.2.onnx model_repository/defect-detector/1/ cat model_repository/defect-detector/config.pbtxt EOF name: defect-detector platform: onnxruntime_onnx max_batch_size: 16 input [ { name: INPUT__0 data_type: TYPE_UINT8 dims: [3, 640, 640] } ] output [ { name: OUTPUT__0 data_type: TYPE_FP32 dims: [4] } ] instance_group [ [ { kind: KIND_GPU count: 1 gpus: [0] # 关键显存隔离 profile: [max_mem_4096] } ] ] EOF # 3. 启动Triton指定显存限制 docker run --gpus device0 \ --shm-size1g \ --ulimit memlock-1 \ --ulimit stack67108864 \ -p8000:8000 -p8001:8001 -p8002:8002 \ -v $(pwd)/model_repository:/models \ -e NVIDIA_VISIBLE_DEVICES0 \ -e CUDA_VISIBLE_DEVICES0 \ nvcr.io/nvidia/tritonserver:24.04-py3 \ tritonserver --model-repository/models --strict-model-configfalse # 4. 启动Python服务 uvicorn service:app --host 0.0.0.0 --port 8003这段代码的价值不在于它多精巧而在于它每一个环节都暴露了可干预点预处理器的target_size可调、Triton的max_batch_size可配、监控指标的drift_threshold可改。没有魔法全是开关。当你需要把服务部署到边缘设备时只需修改CONFIG.gpu_memory_budget_mb 1024Triton会自动降低批处理大小当你发现某类缺陷漏检率高只需在MODEL_CONFIG.output_classes里追加新类别后处理逻辑自动适配。这才是“from scratch”的底气——它不承诺“开箱即用”但保证“所见即所得所改即生效”。5. 踩过的坑与独家心得那些文档里永远不会写的真相5.1 关于GPU显存的残酷真相所有教程都说“Triton自动管理显存”这是个巨大误导。我们曾在一个8卡A100集群上部署12个模型每个模型配置gpus: [0]以为会均匀分布。结果6个模型挤在卡0上显存占用98%其余卡空闲。原因在于Triton的instance_group配置中count: 1表示“至少启动1个实例”但没说“最多启动几个”。解决方案是显式声明最大实例数在config.pbtxt中添加count: [1, 1]第一个1是min第二个1是max并配合--model-control-modeexplicit启动参数用model_repository/config.pbtxt全局控制。更狠的一招是在Docker启动时用nvidia-smi -L动态获取可用GPU列表生成config.pbtxt实现真正的负载感知部署。这招让我们在混合型号GPU集群V100T4A100上资源利用率从41%提升到89%。5.2 数据校验的“灰色地带”处理校验器发现数据越界是直接拒绝还是降级处理我们吃过亏。某次温度传感器故障连续输出temp999.9校验器按规则打标quality: outlier并路由到隔离区。结果业务方抱怨“系统不报警也不处理就扔在那里”。后来我们改成三级响应策略① 单点越界如单个温度值999.9→ 打标记录继续推理② 连续5次越界 → 触发sensor_health_warning告警但推理结果加warning: sensor_unreliable字段③ 连续60秒越界 → 切换到备用传感器数据源或启用历史均值填充。关键是所有策略都在data_validation_rules.yaml里配置算法团队可随时调整无需改代码。这体现了工程化的核心把业务规则从代码中解耦变成可配置的策略引擎。5.3 模型版本的“语义化”陷阱v2.1.0真的比v2.0.9好吗不一定。我们曾因一个微小的归一化系数调整从/255.0改为/255.5导致线上actionable_precision下降0.03但accuracy反而升了0.01。所以我们强制规定模型版本号必须绑定业务指标。v3.2的含义是“在2024年Q2基准数据集上actionable_precision≥ 0.88inference_latency_p95≤ 120ms”。每次发布新版本必须跑完全量指标测试生成version_manifest.json里面包含所有指标快照。运维同学部署时tritonserver会自动校验该版本是否满足当前环境SLA如生产环境要求latency_p95 ≤ 100ms不满足则拒绝加载。版本号不再是开发者的随意命名而是业务承诺的契约。5.4 监控告警的“静默期”设计刚上线时我们设了actionable_precision 0.85就告警。结果前两天每天收到23封邮件全是误报——因为新模型需要几天“热身”数据分布逐渐适应。后来我们加入动态静默期Dynamic Silence Period新模型上线后前48小时告警阈值放宽至0.80之后每24小时收紧0.01直到回归标准阈值。这个逻辑写在Prometheus的alert_rules.yml里用absent()函数检测新模型上线事件用time()函数计算经过时间。它让告警从“骚扰”变成“精准狙击”工程师的睡眠质量提升了不止一个数量级。5.5 最后一个忠告警惕“完美架构”的诱惑我见过最失败的“from scratch”项目是团队花了3个月设计出一个理论上能支持百万QPS、自动弹性伸缩、全链路加密的超级架构结果第一版只需求50 QPS。他们被自己的架构图绑架了连最简单的图片上传功能都迟迟无法交付。我的经验是用最小可行架构MVA启动每交付一个业务价值点就加固一个工程模块。第一周只做通“图片→预处理→Triton→返回JSON”这条主链监控只加latency第二周加上数据校验和quality标签第三周接入Prometheus和告警。架构是长出来的不是画出来的。当你在深夜接到告警电话发现是GPU显存泄漏而你的日志里只有CUDA error 2那一刻你会明白所有炫酷的架构图都不如一行能定位问题的日志来得实在。所以动手吧从写第一行pip install tritonclient[grpc]开始而不是从画第一张架构图开始。
返回列表