先聊一个很多人都踩过的坑:你看了不少深度学习的书,也跟着教程跑通了好几个开源模型,手写数字识别、猫狗分类、情感分析都能出结果,然后你觉得自己已经会“AI”了。但你试着把模型部署成一个别人能用的服务,或者想把它接进一个真实的业务系统里,就会发现一堆完全没有见过的问题——模型文件怎么管理、接口怎么设计、线上数据长什么样完全不可控、效果变差了怎么排查、模型怎么更新才不影响线上。到了这一步,你才真正碰到“AI工程”这个概念。
“ai-engineering-from-scratch”这个标题,本质上就是在说一件事:不靠零散的教程片段,而是从零开始,把AI从“能跑通的脚本”变成“能稳定运行的系统”需要的那一整套工程能力补齐。它不是再讲一遍反向传播公式,也不是介绍某个框架的API,而是站在“造一个真正能用的AI系统”的角度,把数据、模型、部署、监控、迭代这条链路从头走一遍。这篇文章就是给那些准备系统入行AI工程、或者已经在做算法但始终觉得工程能力是短板的人,梳理一条从零开始的完整路径,并且会用一个真实的端到端案例——文本分类服务——把每个环节拆开讲透。
1. 先想清楚:AI工程到底在解决什么问题
1.1 一个“能跑的模型”和一个“能用的系统”之间,隔着什么
很多初学者最大的认知偏差,是觉得训练出一个准确率不错的模型,工作就完成了。实际上在一个真实的AI项目里,模型训练可能只占整个工作量的一小部分。你随便打开一个招聘网站的AI工程岗位要求,会发现它要求的技能通常是:Python开发、数据处理、Docker容器化、模型服务化部署、CI/CD、监控告警、实验管理……这些才是AI工程的核心。
你可以把AI系统想象成一家餐厅。模型只是那个最擅长做某一道菜的厨师。但一家餐厅要正常运转,需要有人买菜(数据获取)、洗菜切菜(数据清洗)、设计菜单(特征与模型选型)、制定出餐流程(服务化部署)、有人盯着后厨别起火(监控告警)、定期更新菜品(模型迭代)。AI工程做的事情,就是把“一个厨师”变成“一家能稳定营业的餐厅”。
所以这个问题的答案很直接:隔着一整套工程基础设施。模型是AI系统的心脏,但AI工程是整个身体。你只训练模型,相当于只培养了一颗心脏,它没法独自存活。
1.2 from scratch 的真实含义:不是重造轮子,而是重建认知顺序
我见过不少人看到“from scratch”这个词,误以为是要从零手写神经网络、自己实现反向传播、不用任何深度学习框架。这个理解是把“教学手段”和“工程目标”搞混了。从零学的意思是:不要靠碎片化的知识拼凑,而是系统性地、按照合理的依赖顺序,把AI工程所需要的各项能力逐个建立起来。
打个比方,学开车的时候你会学交通规则、练直线行驶、倒车入库,最后上路,这个顺序是固定的。AI工程的顺序也很明确:工程底座(Python、Git、Linux)→ 数据能力 → 模型开发 → 部署运维 → 监控迭代。它的核心是建立一套完整的“问题拆解能力”——遇到任何AI项目,你知道第一步做什么、第二步做什么、每一步做到什么程度算完成。
之所以强调这一点,是因为大部分网上资料都是“场景驱动”的——教你用某个框架跑一个具体任务,但不会告诉你这些知识点在整个工程体系里的位置。等你真正进入项目,遇到问题时你会发现:每个点好像都见过,但串不起来。这就是缺乏系统性认知的典型症状。
1.3 这个内容适合谁,需要什么前置基础
最适合读这条路径的人,大致有三类。第一类是想转行做AI工程的学生或初级开发者,你缺少的是一张地图,告诉你该往哪走。第二类是算法工程师,你模型做得不错,但部署、运维、上线这些环节总依赖别人,补齐工程短板之后,你的方案才能真正独立落地。第三类是已经在做传统后端开发、想切入AI方向的工程师,你的工程基础很扎实,但对数据、模型训练这些环节比较陌生,需要补齐的是另外半边。
前置基础其实不需要多高。会Python基础语法、懂一点机器学习的入门概念(哪怕只是听说过损失函数和梯度下降)就足够了。如果连这些也没有,也不是不能学,只是你需要在学习过程中同步补Python基础知识,稍微慢一点,但路径是一样的。
2. 从零起步的知识地图:该学什么、为什么按这个顺序学
2.1 阶段一:工程底座,决定你后续所有环节的效率上限
很多人会忽略这个阶段,觉得Python语法会写循环、会用列表推导式就够了。但进入AI工程之后,你会发现真正消耗时间的不是模型本身,而是代码组织、环境管理、别人协作时搞坏你的代码、在服务器上装了一个包导致环境崩了等这类问题。所以工程底座这关,至少要过三样东西。
第一是Python开发能力的进阶。不是学语法,而是学会怎么写“可维护的代码”:函数怎么拆分、类型注解怎么用、异常怎么处理、日志怎么打。这些看起来很基础,但决定了你后续能不能debug、能不能跟别人合作。Python在这条链路里是粘合剂语言,数据清洗用Python、训练脚本用Python、部署服务还是用Python。
第二是Git版本控制。这是所有工程协作的地基。你需要熟练掌握的不是那几个炫酷命令,而是最日常的流程:创建分支、提交代码、合并分支、解决冲突。几乎所有AI项目的失败都是从代码混乱开始的——谁也不知道当前这份模型代码是哪一版的,跑出来的结果不可复现。
第三是Linux基础。因为几乎所有AI系统的生产环境都是Linux服务器。你得会看日志、查进程、管理文件、配环境变量,还要能在命令行里用vim或者nano改配置。如果你看到这里觉得有点虚,可以自己建一台云服务器,或者哪怕在Windows上用WSL也行,实际操练一遍,不然后面部署环节你会卡得很痛苦。
2.2 阶段二:数据工程能力,AI项目里最耗时也最决定上限的环节
一个经常被忽略的事实是:在真实的AI项目里,数据准备的时间通常会占到60%甚至更多。因为现实中的数据不会像Kaggle比赛那样整齐地躺在CSV文件里等你下载。数据可能散落在数据库里、日志文件里、第三方接口里,而且极其肮脏——字段缺失、格式混乱、值域离谱、标注错误。如果你没有数据工程能力,你会发现自己训练的根本不叫模型,而是“垃圾进、垃圾出”的豪华版。
这个阶段的核心技能有四个。第一是数据采集,会从各种来源拿数据:SQL查库、API拉取、爬虫采集、读日志文件。第二是数据清洗,处理缺失值、去重、格式转换、异常值过滤,这一块是pandas的主场,你得熟练到条件筛选、groupby、apply这些操作不需想就能写出来。第三是特征工程,把原始数据变成模型能用的特征,这里需要你理解“特征”的本质——它是你对数据规律的先验总结,好的特征工程师其实是在用自己对业务的理解指导模型学习。第四是数据版本管理,这是很多团队忽略的:你的模型是用哪一版数据训练的?不记录这个,后面模型出问题你连排查的入口都找不到。
这一块还有个容易被忽略的点:数据质量评估。不要以为数据清洗完了就没事了,你需要用统计方法检查数据的分布是否合理、训练集和线上数据是否同分布、有没有数据泄漏。上面提到的每一个环节,后面都会用案例代码演示。
2.3 阶段三:模型开发能力,知道“调”和“训”的区别
很多过来人都会说一句话:“训练模型不难,难的是训练出一个能在真实场景表现的模型。”这里有三个层次。
第一个层次是会用框架。比如PyTorch或TensorFlow,能搭一个网络、跑通训练循环,这其实是最基本的,几天就能学会。第二个层次是理解训练过程。知道损失函数怎么设计的、学习率怎么影响收敛、过拟合怎么判断和抑制、数据增强怎么用、Batch Size怎么影响训练速度和模型表现。到了这个层次,你才算是“在训练模型”而不是“在跑代码”。第三个层次是评估能力。不要只看准确率,还要看精确率、召回率、F1、AUC,更重要的是要会结合业务场景选指标——例如垃圾邮件识别里,漏掉一封垃圾邮件和误杀一封正常邮件,代价完全不同。
有个例子我记得特别清楚。有个朋友做了一个风险识别模型,线下测试的准确率做到了98%,他自己很满意。但上线之后,他发现大部分风险案例根本查不出来。后来我们排查发现,他用的训练数据里风险样本占比是15%,但真实业务里风险样本的可能占比只有千分之几。模型在训练集上学会了“只要像正常样本就输出正常”,因为它的损失函数被绝大多数正常样本主导了。这就是典型的“不考虑业务分布”的训练。所以在模型开发阶段,你不仅要学会训,还要学会“用业务视角审视训练任务”。
2.4 阶段四:部署与运维,把模型从笔记本搬到服务器上
训练结束不是终点。在真实的AI系统里,模型要变成可以被外部调用的服务。这一阶段有两个关键技能:模型服务化和容器化。
模型服务化最主流的方式是用FastAPI或者Flask包一个HTTP接口,让业务方可以发送请求、接收预测结果。听起来简单,但细节非常多——怎么处理并发请求、怎么控制请求超时、怎么做批量预测的优化、模型文件怎么加载到内存里、GPU怎么分配。这些细节决定了你的服务在高负载下会不会崩。
容器化是另一道坎。为什么需要Docker?因为模型训练和部署的环境通常不一致,你在本地装了一堆依赖包,到服务器上可能版本对不上、缺库、系统不兼容。Docker把整个环境打包成一个镜像,不管到哪里都能重现一模一样的运行环境,这就从根本上解决了“在我机器上明明是好的”这个经典问题。
再往后是CI/CD,也就是持续集成和持续部署。它的意义在于把代码提交、测试、构建镜像、发布服务这些操作自动化,每一次改动都走一条标准流水线,避免手动操作带来的不确定性。这一阶段是整个AI工程里最“硬核”的部分,也是很多算法工程师最头疼的部分,但它恰恰是AI系统能否长期稳定运行的关键。
2.5 阶段五:监控与迭代,让系统在变化中保持稳定
模型上线只是开始。AI系统比较特殊的地方在于:它运行在动态变化的环境里。用户的分布会变、数据模式会变、坏人的攻击方式也会变。一个上线时表现很好的模型,可能三个月后就在不知不觉中退化。你如果只看整体准确率,是发现不了问题的——因为准确率通常不会断崖式下跌,而是以缓慢的方式持续劣化,等你注意到时已经晚了。
所以监控的核心是:持续观察模型输入数据的分布、预测结果的分布、以及你关心的核心业务指标。具体来说,你需要记录模型每一次请求的输入特征分布,定期和训练时的分布做对比,发现明显偏移就要及时预警。同时还要监控服务的延迟、吞吐量、错误率,确保服务本身健康。
这就是为什么我在前面的知识地图里把监控与迭代放在最后——它不是独立的技能,而是综合考验你对数据、模型、系统三者的理解。数据漂移你要会检测,模型重训你要会设计训练流程,代码更新你要会走发布流程。到这里,一个AI系统才真正形成了闭环。
3. 实操闭环:从零构建一个文本分类服务
3.1 项目选型和整体设计思路
理论聊再多,不如动手做一个项目。我推荐从零构建一个“垃圾短信文本分类服务”,这个选题有三个原因。第一,数据非常容易获取,你可以用公开数据集,也可以自己收集标注几百条样本,门槛极低。第二,问题足够典型——从文本预处理到模型训练再到服务部署,每个环节都有代表性。第三,它是一个完整的闭环场景,可以清晰地展示AI工程怎么做端到端交付。
整个系统拆解下来一共四层:数据层负责收集和清洗短信文本;模型层负责训练一个文本分类模型,判断一条短信是正常短信还是垃圾短信;服务层用FastAPI把模型包装成一个HTTP接口,业务方POST一条短信文本进去,返回分类结果和置信度;运维层则负责容器化部署、日志记录和基础监控。
下面我按照这个设计,把每层的实现细节一步步讲清楚。这一段会非常实操,你可以直接照着做。我也建议你不要只抄代码,而是跑通之后自己改一改,比如换一个数据集、调整模型结构——这是把技能内化的唯一方式。
3.2 数据准备与清洗:用pandas把脏数据变成模型能吃的格式
首先准备一个训练数据集。假设我们手头有一批短信文本,每条短信带有标签,1代表垃圾短信,0代表正常短信。原始数据通常是CSV文件,但里面的文本往往非常脏:有超链接、HTML标签、特殊符号、各种Emoji、繁体简体混杂、大小写混杂。这些杂讯对模型训练没有帮助,反而会引入噪声,所以先写一个文本清洗函数。
import re import pandas as pd def clean_text(text: str) -> str: # 移除HTML标签 text = re.sub(r'<.*?>', '', text) # 移除URL text = re.sub(r'http\S+|https\S+|www\.\S+', '', text) # 移除连续数字(短信中常见的验证码、电话号码等) text = re.sub(r'\d{5,}', '', text) # 移除特殊符号,只保留中文、英文、基础标点 text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s,。!?.!?]', '', text) # 统一转为小写 text = text.lower() # 压缩连续空白 text = re.sub(r'\s+', ' ', text).strip() return text df = pd.read_csv('sms_data.csv') df['clean_text'] = df['text'].apply(clean_text) # 去除空文本和重复内容 df = df[df['clean_text'].str.len() > 0] df = df.drop_duplicates(subset='clean_text') print(df['label'].value_counts())这一步里有很多容易踩的坑。一个是清洗顺序很讲究,比如一定要先移除URL再移除链接文本里的http残留,否则光匹配http\S+可能把以“http://xxx”开头的整块文本去掉后又把残留的http当成正常词留下来。另一个是尽量不要把文本中所有数字都删掉,因为验证码短信里的验证码数字是有业务含义的,只删连续5位以上的长数字通常更合理。
清洗完之后,还有两个关键动作。第一个是检查类别分布——如果垃圾短信和正常短信的比例严重失衡,比如100:1,你需要考虑采样策略。第二个是划分训练集和验证集时,必须使用分层抽样,也就是保证训练集和验证集里的正负比例一致。
from sklearn.model_selection import train_test_split # stratify参数保证分层抽样,测试集占20% train_df, val_df = train_test_split( df, test_size=0.2, random_state=42, stratify=df['label'] ) print(f'训练集大小: {len(train_df)}, 验证集大小: {len(val_df)}')3.3 模型训练与评估:训练参数怎么定、怎么判断效果
文本分类的方法有很多:传统机器学习用TF-IDF加逻辑回归,深度学习可以用BERT等预训练模型。对于一个小型短信分类项目,我建议先跑一个TF-IDF加逻辑回归的基线模型,它的训练速度快、可解释性强、在中小规模数据上效果往往不差,而且不需要昂贵的GPU资源。
这一步的核心不是模型有多新,而是让你建立“训练闭环”的概念:特征提取 → 模型训练 → 效果评估 → 错误分析 → 改进迭代。下面给出基线模型的完整代码:
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.metrics import classification_report # TF-IDF参数说明: # max_features=5000 控制特征维度,文本词表过大容易过拟合 # ngram_range=(1,2) 保留单个词和相邻两个词的组合,能捕捉一些短语特征 vectorizer = TfidfVectorizer(max_features=5000, ngram_range=(1,2)) model = Pipeline([ ('tfidf', vectorizer), ('clf', LogisticRegression(max_iter=1000, C=1.0)) ]) model.fit(train_df['clean_text'], train_df['label']) # 在验证集上评估 val_pred = model.predict(val_df['clean_text']) print(classification_report(val_df['label'], val_pred, target_names=['正常短信', '垃圾短信']))看评估结果时,不要只看准确率,要看查准率和查全率的平衡。一个特别常见的坑是模型对所有文本都预测为“正常短信”,因为绝大多数样本本来就是正常的,这时准确率也会很高,但这个模型毫无价值。你要检查分类报告里的各类别查准率和查全率,特别是少数类(垃圾短信)的查全率。
如果基线模型效果不理想,可以依次尝试调整三个方向。第一个方向是文本预处理,比如保留数字、添加自定义分词词典。中文短信需要选用合适的分词工具,比如jieba,不同的分词策略对效果影响很大。第二个方向是模型参数,比如增大max_features,调整正则强度C,或者切换成朴素贝叶斯等其他模型。第三个方向是改变特征,比如加入文本长度特征、是否含URL特征等。每一轮调整后都在验证集上重新评估,记录效果变化。
3.4 部署成HTTP服务:这一步会暴露所有你没想过的问题
模型训练好之后,保存成文件,让它成为一个可对外提供预测的服务。我用FastAPI来实现这个环节,它有几个明显的优势:代码量小、自带交互式API文档、异步支持好、性能足够。
import joblib from fastapi import FastAPI from pydantic import BaseModel app = FastAPI(title="垃圾短信识别服务") model = joblib.load("sms_model.joblib") class SmsRequest(BaseModel): text: str @app.post("/predict") def predict(req: SmsRequest): # 注意:线上请求的文本必须走和训练时相同的清洗流程 cleaned = clean_text(req.text) prob = model.predict_proba([cleaned])[0] label = int(prob[1] > 0.5) return { "label": label, "confidence": float(prob[1] if label == 1 else prob[0]) }这里藏着一个非常关键、但很多人会忽略的问题:线上推理时的数据预处理必须和训练时完全一致。你在训练前做了一套清洗函数,在线服务里也必须调用同一个函数,一个字符都不能差。我曾经见过一个线上服务因为少做了一个strip()操作,导致所有请求的预测置信度都偏低,排查了很久才发现是清洗流程不一致。
验证服务可以这样测一下:
# 启动服务 uvicorn main:app --host 0.0.0.0 --port 8000 # 测试请求 curl -X POST "http://localhost:8000/predict" \ -H "Content-Type: application/json" \ -d '{"text": "恭喜您获得100万大奖,点击链接领取!"}'服务跑通之后,一定要问自己一个问题:如果同时来100个请求,服务能不能扛住?如果不能,就要考虑优化点:要不要加并发处理?要不要用批处理?要不要上GPU?这些优化方向,是AI工程里非常典型的性能调优场景。
3.5 容器化部署与基础监控:往生产环境迈出最后一步
为了在真实的服务器上稳定运行,我用Docker把服务和环境一起打包。
FROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple COPY main.py . COPY sms_model.joblib . EXPOSE 8000 CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]Dockerfile写好后,构建镜像并运行:
docker build -t sms-classifier . docker run -p 8000:8000 sms-classifier做完这步,你的服务已经被装进一个可移植的“集装箱”里了,在任何有Docker的机器上都能一键启动,不会再出现“代码在笔者的电脑能跑,在服务器上就报错”的情况。
基础监控这块,我建议起步阶段先做两件事就够。第一件,在服务里埋日志:记录每次请求的文本、预测结果、置信度、处理耗时。这不仅是审计需要,也是排查线上问题的最重要依据。第二件,定期统计线上调用中垃圾短信的占比,把它和训练集的分布做对比,一旦发现偏差超过阈值,就说明模型可能已经不能适应当前数据分布,需要重新训练了。
import logging import time logging.basicConfig( filename='service.log', level=logging.INFO, format='%(asctime)s %(message)s' ) @app.post("/predict") def predict(req: SmsRequest): start = time.time() cleaned = clean_text(req.text) prob = model.predict_proba([cleaned])[0] label = int(prob[1] > 0.5) latency = time.time() - start logging.info(f"label={label} confidence={float(max(prob)):.4f} latency={latency:.3f}s text={req.text[:50]}") return {"label": label, "confidence": float(max(prob))}4. 常见问题与排查技巧实录
4.1 本地跑得通,上线就报错:环境和依赖的隐形差异
这是我见过出现频次最高的一类问题。代码在本地好好的,环境变量一换就崩。典型症状包括:ModuleNotFoundError: No module named 'xxx'、系统库版本不兼容、Python版本不一致导致的语法错误。这个问题的根源就是环境不一致。
排查思路只有一个方向:把你的环境完整地交付给目标机器。在个人实践里,Docker是解决这个问题的最强手段,没有之一。如果你因为某些原因用不了Docker,也要用requirements.txt配合pip freeze精确锁定所有依赖的版本号。我的建议是:项目从一开始就容器化,不要等到部署的时候再临时抱佛脚。
还有一个小技巧:本地开发时使用虚拟环境(virtualenv或conda),不要直接装在系统Python里。不然你的环境会被不同项目的依赖搞成一团乱麻,最终你甚至不知道当前Python环境里装了哪些包,排查起来非常痛苦。
4.2 测试集表现好,线上效果差:数据分布不一致与特征穿越
这个问题的背后通常有三个原因。第一个是采样偏差,训练数据的分布和真实业务分布不一样,比如前面提到的风险识别例子,训练集正负比例与真实场景严重不一致。第二个是特征泄漏,你在训练时使用了未来信息或不可在线获取的信息,导致离线评估虚高。第三个是线上数据格式发生了漂移,线上来的文本里大量出现训练阶段没有见过的新词汇和格式。
排查方法很简单:拿出线上日志里的真实请求数据,重新组成一个“伪测试集”,让模型预测一遍,对比线上表现和测试表现。如果伪测试集上的效果远差于原始测试集,问题就出在分布不一致上。这个操作在工程上叫“线上样本回放”,是非常好用的一招。
至于解决手段,除了重新按真实分布采样训练数据,还有一个思路是给服务加一个输入校验层,把异常的输入请求拦截下来,而不是硬生生喂给模型。
4.3 训练时GPU显存溢出:搜索空间太大导致的资源管理问题
训练脚本很好写,但把训练塞进一台机器时,显存管理就成了一门手艺。最常见的报错是CUDA out of memory。这跟模型本身大小关系不大,更多是你在一个batch里放了太多数据,或者显存被上一次训练残留占着。
排查和调整的顺序是:先检查是否有残留进程占用显存,用nvidia-smi看进程列表,关掉不需要的进程。然后尝试减小batch_size,这是最简单直接的方案。接着检查输入数据的尺寸,比如文本长度有没有异常的长样本——一条超长文本会显著增加显存消耗。最后再考虑梯度累积、混合精度这些高级手段。
我自己的习惯是,把batch_size定成一个能整除训练数据总量的数字,并设置num_workers控制数据加载进程数。给一个参考:一个BERT-base模型,在普通24G显存的卡上,batch_size设置为16到32通常没什么问题。如果你用的是超大模型或长文本,就要在性能和显存之间做权衡,不断做减法。
4.4 模型更新上线后效果变差:回归测试与发布策略缺失
很多团队犯过一个错误:训练了一个效果更好的模型,直接替换线上的旧模型,结果用户反馈“变笨了”。为什么离线评估更好,线上反而更差?除了数据分布问题,还有一个非常重要的原因是缺乏回归测试。
回归测试的思路是:保留一份历史真实请求样本集,新模型上线前,先在这个样本集上跑一遍,和旧模型输出做对比。重点看两类差异:新模型把原本正确的预测改错了多少,把原本错误的预测改对了多少。只关注准确率提升是不够的,必须关注“预测回退”。比如一个垃圾短信识别服务,新模型可能提高了5%的准确率,但同时有0.2%的正常短信被新模型误判成了垃圾短信,对业务来说这可能是不可接受的损失。
发布策略上也有讲究。不要直接全量替换,可以先灰度发布——给5%的流量用新模型,其余95%还是旧模型,线上对比效果后再逐步放量。如果发现异常,一键切回旧模型。这是AI工程里很常见的“灰度+回滚”策略,它不增加多少工作量,但能避免很多线上事故。
这里额外分享一个经验:模型要管理好版本,不要只存一个“final_model.joblib”。推荐在文件名里带上训练时间和评估指标,例如sms_model_20250110_f1_0.942.joblib,同时保留一份训练配置的记录文件。这样你才能回答“这个模型是怎么来的、凭什么用这个”这种问题。
5. 我个人的一些体会
最后闲聊几句。做了这么多年AI工程,我最想对准备走这条路的朋友说的一句话是:不要急着去追新模型、新框架,先把端到端的流水线能力打扎实。一个能把模型按标准流程上线、监控、迭代的人,在团队里的价值远高于一个只会读论文调参数的人。因为算法可以学,但工程能力必须靠一个个项目磨出来。
我自己刚入行的时候也走过弯路,连续几个月醉心于改进模型结构,结果一上生产环境全傻眼,后来才开始回头补工程短板。所以你们的起点已经比我好很多——至少一开始就知道要从系统的角度看AI。拿一个实际项目走完整条链路,里面每个环节你都亲手做一遍,遇到问题把它记下来,下次你就不再是“踩坑”而是“避坑”了。
从零到一很难,但从一到一百,其实都是同一套方法论的重复应用。祝你们都能跑通属于自己的第一个AI工程闭环。