ai-engineering 这两年热度一直没降过,但真正能讲清楚“从零开始怎么学、怎么做”的路径,市面上其实不多。我这个项目标题起得很直白,ai-engineering-from-scratch,就是一套我亲手整理并在实际工作中反复验证过的 AI 工程入行路径,覆盖从数学基础、机器学习原理,到模型选型、检索增强、部署监控的完整闭环。这篇文章不是教材复述,而是把这条路上那些“文档不会写、但踩过一次就会疼”的经验摊开讲。无论你是想转行的后端工程师,还是刚毕业准备进场的学生,只要你已经有半年到一年的代码基础,这条从零开始的路都值得完整走一遍。
1. 为什么是 AI 工程,而不是单纯学模型
1.1 AI 工程与机器学习研究的边界到底在哪
很多人对“AI 工程”有误解,以为它等同于“会训练模型”。实际上,这个领域的核心矛盾早就不是“模型精度还能不能再涨几个点”,而是“一个模型怎么稳定地跑在真实业务里”。研究者关心的是新算法、新架构,工程师关心的是系统的确定性、可控性、成本和故障恢复能力。可以打一个比方:研究是造发动机,工程是把发动机装进一辆能跑街的车,还得保证它冬不熄火、夏不过热、每跑十万公里能被人维护。
所以我理解的“AI 工程”,是数据、模型、基础设施、评估体系四件事的交叉学科。它要求你既看得懂模型的输入输出,又搞得清一条请求从客户端发出到返回结果的完整链路。在这个定义下,模型扮演的角色只占整体工作量的三成左右,数据工程、评测、调优和部署占剩余七成。很多从零开始的人恰恰把比例搞反了,整天盯着模型的训练指标,忽略了真实项目里真正卡脖子的往往是数据格式不干净、接口不稳定、评测口径不统一这些问题。
从零开始走这条路线,第一步不是急着跑大模型,而是先建立“端到端系统”的直觉。你得清楚:一条用户请求进来,先经过路由、权限、预处理,再决定用哪个模型、带多少上下文,输出之后还要做格式校验、引用回溯、缓存和埋点。每一个环节都可能成为瓶颈,AI 工程的能力本质上是“把不确定的模型行为装进确定的产品框架”的能力。
1.2 从零到能独立做项目,最合理的路线图
这套路线图我前后迭代过很多次,最终固定成五步:
- 打地基:Python 编程、SQL、基础数据处理。目标是能熟练写脚本处理真实脏数据,不依赖 Notebook 也能独⽴完成数据清洗。
- 机器学习基础:用经典数据集跑通监督学习的完整流程,理解训练集、测试集、过拟合、正则化、评估指标这些概念。
- 深度学习进阶:吃透神经网络、反向传播、常见结构,最后手写一个简化版 Transformer,理解自注意力到底在算什么。
- 大模型工程:掌握 Token、Embedding、上下文窗口、RAG、指令微调、评估指标,能独立搭一个问答系统或 Agent。
- 系统化收口:模型服务化、推理优化、监控告警、评测回归,把单点 Demo 升级成可以上线的服务。
这个顺序不是随便排的。很多人一上来就追最新的大模型框架,结果遇到 prompt 不稳定、幻觉、上下文截断等问题时,连“为什么模型输出这么离谱”都定位不了,因为缺少对模型运行机制的底层直觉。反过来,我也见过理论功底不错的人,把数学推导得很熟,却不知道怎么把一个模型包装成 API 给前端调用。真正有效的路线,是理论和系统两条腿交替走:学一个概念,就立刻用代码实现两次以上,然后再进入下一个概念。
这里还想强调一个很多人忽视的点:学习节奏比学习内容更重要。我给新人推荐的节奏是每天固定两小时的连续投入,比周末一次性猛学八小时有效得多。AI 工程知识点之间的依赖关系太强,一旦断档两天,再捡起来的时间成本高得吓人,连续性本身就是这条路径最大的护城河。
2. 核心地基:数学、机器学习与深度学习的必修课
2.1 数学学到什么程度才算够用
纯算法岗位当然需要深厚的数学功底,但 AI 工程岗更在意的是“能用数学语言理解模型行为”。以我的经验,线性代数、概率统计、微积分三块,每块只需要掌握一小部分高频工具,就足够支撑绝大多数工程场景了。
线性代数里最关键的三个概念是向量、矩阵乘法、点积。自注意力机制本质上是“用点积算两个词之间的相关度”,而 Embedding 本质上是“把文本映射成向量空间里的坐标”。你不一定需要会证明矩阵分解的性质,但必须能在代码里把torch.matmul和 normalization 的操作对应到模型结构上。
概率统计里最常用的是条件概率、贝叶斯公式、期望和方差。交叉熵损失函数是语言模型的训练核心,它背后就是“模型给出的概率分布和真实分布之间差多少”这一个朴素思想。很多模型输出的错误,追根溯源都是概率层面的理解偏差:采样温度、Top-k、Top-p 这些生成参数,全部建立在对概率分布的直觉上。
微积分里需要实际用到的只有导数和链式法则。反向传播问的就是“某个参数变化一点点,对损失的影响是多少”,这个影响就是导数。我推荐一个自学路径:先用 NumPy 手写线性回归,再手写一个两层神经网络,务必自己写出反向传播的过程。这一步做完,后续看任何模型结构的代码都会豁然开朗。
2.2 机器学习基础:先把有监督这件事吃透
深度学习和传统机器学习共享同一套地基,所以直接跳进深度学习其实不划算。你需要先掌握几个绕不开的概念:
- 过拟合:模型在训练集上表现很好,在新鲜数据上一塌糊涂。这是整个机器学习领域最核心的敌人,后面所有正则化、数据增强、早停技巧都在围攻它。
- 训练集 / 验证集 / 测试集:三者的划分和用途必须形成肌肉记忆。我见过有人在 RAG 项目里直接把全部文档丢进向量库,然后用同一批文档问问题来测准确率,结果自我感觉良好,上线后被真实用户问得露馅——这其实就是典型的评估集设计错误。
- 评估指标:分类问题看准确率、精确率、召回率、F1;检索问题看 Recall@k、MRR。很多新手只看准确率,但在样本不平衡的场景里准确率会严重误导判断。
传统机器学习里的特征工程在深度学习时代看起来没那么重要了,但它的思想还在:你用什么信息喂给模型,决定了模型能学到什么边界。在 RAG 系统里,分块策略、元数据设计、检索逻辑本质上就是新一代的“特征工程”。搞懂这个类比,你就理解了为什么 AI 工程里数据处理永远比模型调参影响更大。
另一个必须早早就刻进脑子的概念是数据泄漏。所谓数据泄漏,就是测试信息在训练阶段就被模型看到了,导致评估指标虚高。工程上最常见的泄漏场景是:清理重复数据时没有排查相似样本,或者用了未来的数据做特征。从零开始的人不要觉得这是小事,很多正式团队的错误率评估翻车都源于此。
我一贯的建议是,用两周左右的时间把经典机器学习流程跑透,训练一个二分类模型、一个多分类模型,再做一个简单的回归任务。不要追求模型有多深,重点是走完“数据清洗—特征构造—训练—评估—调优—复盘”的完整循环。这个过程建立的系统感,是后面所有复杂项目的地基。
2.3 深度学习:必须亲手推一遍的东西
到了深度学习阶段,理论水平和代码实现必须同步前进。我强烈建议每个人至少从零实现一次以下内容:
先写感知机和单层神经网络,理解权重、偏置、激活函数。然后扩展成多层感知机,并手动实现反向传播。这个阶段你会真正理解“为什么深层网络比浅层强”和“为什么需要非线性激活函数”这两件事。
接下来进入卷积和循环结构时,不需要每个都手写,但至少要理解它们的核心直觉。值得花时间精读的是 Transformer 结构,因为它直接决定了我们今天怎么使用大模型。我建议的拆解方式是把它切成几块来理解:
- Embedding + 位置编码:词怎么变成向量,以及怎么保留顺序信息。顺序问题很关键,因为 Transformer 本身不天然具备顺序概念。
- 自注意力:每个 token 和句子里的其他 token 两两计算相关度。这里的点积缩放、多头机制,是后面理解长上下文表现的核心。
- 前馈网络 + 残差连接 + 层归一化:这几块虽然看起来是“工程细节”,但决定了模型能不能稳定地深下去。
等你能用 PyTorch 在 CPU 上跑起来一个小 Transformer 后,LLM 的生成逻辑就很好懂了:它本质是在做下一个 token 的预测,训练目标就是最大化正确 token 的概率,推理时根据概率分布采样生成文本。这个“概率采样”的视角特别重要,因为它解释了为什么同一个 prompt 每次都可能得到不同回答,也解释了为什么会有幻觉——模型生成本来就是在大概率路径里猜,没有看过正确答案的题目它也会硬答。
3. 工程化全貌:用 AI 解决问题,本质是在搭系统
3.1 一个完整 AI 服务到底由哪些部分组成
工程化的第一课,是学会把“模型”放回“系统”里看。一个真实可用的 AI 功能,至少包含这几个环节:
- 数据接入与预处理:从文件、数据库、消息队列里拿到原始数据,做清洗、格式统一。
- 数据处理与索引:把文本切块、向量化,存入向量库或倒排索引,这一步直接决定后续检索质量。
- 模型推理:调用基座模型或微调模型,可能还有前置的检索模块、外挂工具调用模块。
- 业务逻辑与接口:把模型的输出转换成产品能用的结构,例如引用来源、结构化字段、权限过滤。
- 观测与运维:日志、指标、告警、反馈收集,让系统行为可回溯、可评估、可优化。
大多数从零开始的实践者,大脑里只有“模型推理”这一环,项目一上线就出各种事故:答案不准、响应超时、用户骂了半天但团队完全没有日志可查。所以我在路线图里特别强调,第二个完整项目一定要做“带监控的 API 服务”,而不是停在 Notebook 里跑通为止。
数据环节的重要性值得单说。业界一直有句老话叫 garbage in, garbage out,模型再强也填不了数据的坑。我处理过一个看起来很简单的企业文档问答需求,文档是扫描件加表格混排 PDF,页面结构混乱、编码错乱,单是把这些文件转成干净的文本就花掉了整个项目 60% 的时间。如果你打算从零进入 AI 工程,提前做好心理准备:数据永远是最大的隐性工作量。
3.2 工具链选型:不要迷信大而全,够用才是真理
工具选型的原则可以浓缩成一句话:先跑通最小闭环,再按压力和需求逐步升级。很多新手一开始就搭 Kubernetes 集群、上分布式向量库,结果光维护基础设施就耗光了精力,业务功能却还没影子。
如果你是自己学习或做中低并发的原型,按这套组合最稳:
- 语言与科学栈:Python 3.10+,PyTorch,Transformers,HuggingFace Datasets
- 向量存储:起步用 FAISS(单机版就够),数据量到了百万级再考虑 Milvus,或者如果团队已经有 PostgreSQL,直接用 pgvector 还能少维护一套服务
- 服务框架:FastAPI + Uvicorn,生态成熟、文档丰富,做 AI API 很顺手
- 推理优化:先用 PyTorch 原生推理跑通,需要降延迟再做量化或 ONNX Runtime
- 实验与模型管理:MLflow 或 Weights & Biases 二选一,早期建议直接上,养成记录实验参数的习惯比什么都重要
- 监控:Prometheus + Grafana 这套组合虽然配置有点繁琐,但它能同时刻画出延迟、QPS、模型 token 消耗等关键口径
选型时有一个很容易犯的错:过度追求“主流大厂同款”。大厂那套分布式技术栈,背后有专门的平台团队支撑,个人项目照搬只会拖垮自己。性价比最高的走法永远是“低复杂度起步、按瓶颈横向升级”。
3.3 评估体系:没有评测,就没有真正意义上的工程
我在带新人的时候,最常唱的反调是:别急着调 prompt,先把评测集建起来。没有评测集,你所有的“我感觉效果好了一点”都是玄学。评测集怎么建?从真实业务场景里抽 30 到 100 条典型问题,每一条标注期望行为和判定标准。比如文档问答系统,你要标记哪些问题答案应该来自哪几份文档,哪些问题应判定为“文档里没有答案”,还要规定引用格式对不对。
有了评测集之后,每一次改动 prompt、换 embedding 模型、调 chunk 参数,都要在同一个评测集上重新跑一遍。这个动作和传统软件开发里的回归测试本质相同。没有这套机制,很可能出现的情况是:你今天为了提升某个问题的回答质量调整了 prompt,三天后发现另外一批问题的回答质量悄悄崩了,但完全没有感知到。
针对大模型系统,我额外推荐关注三类指标:一是答案正确性的硬指标,如准确率、F1、Bleu/Rouge 这类可量化指标;二是检索质量指标,如 Recall@k,它告诉你正确答案有没有进到给模型看的上下文里;三是更现代的 LLM 评估框架,例如 RAGAS,它对生成答案的事实一致性和答案相关性做自动化打分。真实项目中三者组合使用:检索阶段盯 Recall,生成阶段盯事实一致性,线上再通过用户点踩和人工抽检兜底。
4. 实操:第一次完整项目——搭建个人知识库问答系统
4.1 需求定义与方案设计
我建议零基础入门做的第一个完整项目,是个人知识库问答系统。选它是因为它覆盖了 AI 工程的主干链路:数据接入、文本切分、向量化、检索、生成、 API 服务、评估,而且不需要训练模型,单张消费级显卡或者纯 CPU 也能把原型跑起来。
先明确需求(把模糊目标翻译成可验收的指标):
- 能上传一批 PDF/TXT 文档,构建私有知识库
- 用户提出问题后,系统只依据知识库内容回答
- 每条回答必须附带引用来源,方便用户核对
- 单次回答延迟控制在 5 秒内(本地模型)或 3 秒内(云端 API)
- 系统能记录每次提问的日志,方便事后评测和复盘
有了需求,架构就自然出来了:读取文档 → 分块 → 向量化 → 存入向量库;查询时向量检索 TopK 相关片段 → 组装 prompt → 送给 LLM → 返回带引用答案。这套结构现在常被称为 RAG(检索增强生成),但它不是某种复杂的高深框架,本质就是“先查资料再作答”,和人类翻文献写综述是一个思路。
4.2 环境准备与依赖安装
环境搭建这件事看似简单,却是新手第一个劝退点。先说我的建议:用 Miniconda 管理环境,Python 版本选 3.10,不要一上来就追 3.12 甚至 3.13,很多深度学习库对新版本的支持是滞后的,装了之后 nightly 报错会瞬间浇灭学习热情。
创建环境后,按以下顺序安装依赖:
conda create -n ai-eng python=3.10 -y conda activate ai-eng pip install torch --index-url https://download.pytorch.org/whl/cpu # CPU 起步够用 pip install transformers sentence-transformers faiss-cpu pip install fastapi uvicorn pypdf openpyxl这里有两个细节提醒一下。第一,先把 CPU 版跑通,不要一上来就折腾 GPU 的 CUDA 版本,等后面确实需要加速再装 GPU 版,否则光是驱动和 cuDNN 的版本匹配就能耗掉你一个晚上。第二,sentence-transformers这个库封装了 Embedding 模型的加载和推理,后面做向量化非常省事,强烈建议直接用。
Embedding 模型我推荐从小的开始,比如BAAI/bge-small-zh-v1.5这类中文专用模型,向量维度低、速度快、单机运行无压力;生成模型可以二选一:本地跑 Qwen 系列的小尺寸模型,或者调用云端 API。两者不冲突,代码里做一个 providers 抽象层,以后想切换都很方便。
4.3 核心实现与关键参数选择
完整代码非常长,这里只贴骨架和最有价值的那部分。第一步是文档加载与分块:
from pypdf import PdfReader def extract_text(path: str) -> str: reader = PdfReader(path) return "\n".join(page.extract_text() for page in reader.pages) def chunk_text(text: str, chunk_size=512, overlap=32): chunks = [] for i in range(0, len(text), chunk_size - overlap): chunks.append(text[i:i + chunk_size]) return chunks分块参数怎么定?我用的chunk_size=512(大约两百多字的中文内容)是试出来的经验值,不是拍脑袋。块太长,一个块里塞满了无关内容,检索时相关片段会被稀释,语义匹配度下降;块太短,比如 128,单个片段给出的上下文不够,模型无法完整理解问题背景。overlap=32则是为了保住句子边界跨块的语义,避免一个完整的意思被硬切成两半。真实项目中,分块策略应该跟着文档类型走:段落结构清晰的文档优先按段落切,代码文档可以按函数切块,这点后面做精细优化时可以慢慢打磨。
向量化和索引构建的骨架如下:
from sentence_transformers import SentenceTransformer import faiss model = SentenceTransformer("BAAI/bge-small-zh-v1.5") def embed_chunks(chunks): return model.encode(chunks, normalize_embeddings=True) vectors = embed_chunks(all_chunks) index = faiss.IndexFlatIP(vectors.shape[1]) # 内积,配合归一化就是余弦相似度 index.add(vectors)构建索引时有一个容易踩的坑:加了normalize_embeddings=True的情况下,FAISS 的IndexFlatIP(内积距离)在数学上就等价于余弦相似度。如果你忘了归一化,又不小心选了内积索引,检索结果会明显偏差。这个问题排查起来非常隐蔽,我会在下一节“避坑实录”里再展开。
查询和生成环节是 RAG 的核心:
def search(query, top_k=5): qvec = model.encode([query], normalize_embeddings=True) scores, idxs = index.search(qvec, top_k) return [all_chunks[i] for i in idxs[0]] def build_prompt(question, contexts): context_text = "\n\n".join(f"[{i+1}]{c}" for i, c in enumerate(contexts)) return ( "你是一个严谨的知识库助手。请只基于以下资料回答问题。" "如果资料中没有答案,请明确说'资料中未找到相关信息'。\n\n" f"资料内容:\n{context_text}\n\n" f"用户问题:{question}\n" "你的回答需在末尾标注来源编号,格式如(来源:1、3)。" )这里 prompt 的设计有几个工程级细节值得讲透。首先,明确输出约束“资料中没有答案就直说”,这是抑制幻觉最简单也最有效的手段。其次,要求标注来源编号,这不仅让用户可以使用溯源能力,更关键的是让你在评估时能快速判断“模型是不是真的在看资料回答”。最后,上下文数量top_k我默认设 5,取值在 3 到 7 之间最合理:太少会导致信息不全,太多则容易让模型抓不住重点,还会让输入 token 成本上升。
生成阶段如果调用云端 API,代码很简洁:
import openai client = openai.OpenAI(base_url="...", api_key="...") def ask(prompt, temperature=0): resp = client.chat.completions.create( model="qwen-plus", messages=[{"role": "user", "content": prompt}], temperature=temperature ) return resp.choices[0].message.content注意 RAG 场景下temperature=0,理由很简单:检索增强生成的任务目标不是创造力,而是忠实回答,温度越低,输出确定性越强,越不容易虚构细节。如果你想让最终回答的覆盖面更广一些,最多调到 0.2 就够了,再高就是在给自己制造幻觉风险。
4.4 调试、评测与部署上线
项目跑通之后,工程量才算真正开始。第一步是建一个 30 条问题的评测集:从你导入的文档里挑出核心知识点,写成问题,记录标准答案和对应章节来源。然后写一个自动化评测脚本,跑一遍全量问答,逐条记录“是否检索到正确答案”和“回答是否包含关键事实”。
跑完评测你会发现几个规律:回答质量差的原因通常不是模型不行,而是检索结果根本没把正确答案传上来。这时先用最朴素的方式验证检索:单独打印出每个问题的 Top5 检索片段,肉眼看相关度。这一步能帮你快速判断问题出在哪个环节,也就能避免“对着错误的问题疯狂调 prompt”这种最典型的新手时间黑洞。
评测稳定后,开始封装服务。用 FastAPI 写一个/ask接口,增加基础限流和请求日志;日志至少记录问题、命中片段编号、模型输出、耗时、token 用量。上线后,这些日志就是你优化系统的唯一依据。有条件的话,在接口返回里加入一个feedback字段,让用户点“有用/无用”,这些显式反馈比任何离线指标都更贴近真实体验。
5. 常见问题与避坑实录
5.1 新手最容易踩的坑,我先替你踩一遍
第一坑:不建索引就开始调 prompt。很多人觉得回答效果不好,第一反应永远是改 prompt,但实际原因往往是索引里的文档没切好,或者 embedding 模型选错了语言域。把检索和生成两个环节拆开定位,是 AI 工程调试的第一直觉。
第二坑:把整个 PDF 一股脑塞进上下文。有次我见到有人把一份几十页合同直接丢给模型,让模型“总结要点”。上下文窗口看似装得下,但模型对长文本的真实注意力分布并不友好,中间部分的信息经常被忽略。正确做法永远是“先检索,再送入片段”,而不是盲信模型的超长上下文能力。
第三坑:Embedding 模型和文档语言不匹配。中文文档用了英文主训练的 embedding 模型,语义检索效果会大打折扣。选型时先确认模型的训练语料覆盖你的业务语言,中文场景优先选中文或者中英双语模型,别只看榜单分数。
第四坑:忽略 embedding 归一化。前面提过,normalize_embeddings=True配合IndexFlatIP才等价于余弦相似度。如果你忘了归一化,检索到的“相似片段”实际上可能只是长度更长的文本。这类 bug 的隐蔽性在于整个链路完全跑得通,但效果就是不对,非常消耗人。
第五坑:没有评测集就上线。全部改完、肉眼看着效果不错,这种感觉最危险。上线后只要出现一个翻车案例,用户对你的信任就会打折。评测集、日志、人工抽检,这三件套是 AI 工程在生产环境里的安全绳。
5.2 问题定位与排查技巧
下面这张速查表是我在实际项目和带人过程中沉淀下来的,绝大多数新手的疑难杂症都能在这里找到方向:
| 现象 | 优先排查方向 | 具体操作建议 |
|---|---|---|
| 回答内容与资料无关 | 检索模块 | 打印 TopK 检索片段的原文,确认正确答案是否出现在上下文里 |
| 检索结果相关但不精确 | 分块大小与重叠 | 调整chunk_size和overlap,按文档结构重新切分 |
| 模型说“资料里没有”但实际有 | 检索召回不足 | 增加 top_k 数值,或改用混合检索(向量+关键词) |
| 回答流畅但频繁幻觉 | 温度与 prompt 约束 | 温度降为 0,prompt 中强制“无依据时明确说明” |
| 回答质量忽高忽低 | 缺评测集 | 建 30+ 条黄金问题集,每次改动后全量回归对比 |
| 响应速度慢 | 链路瓶颈分析 | 分别计时分块、embedding、检索、LLM 推理四段,定位耗时大头 |
| 向量库检索结果异常相似 | embedding 漏归一化 | 检查normalize_embeddings与索引类型是否匹配 |
| 上下文超长被截断 | 分块与裁剪逻辑 | 限制单次送入的片段数量和总字符数,加截断保护 |
排查的时候,我的原则是“先把系统切成两段”。AI 服务本质上可以切成“输入到模型之前的检索质量”和“模型根据上下文生成的质量”两段。你先随便抽几个问题,肉眼检查检索出来的片段和问题是否相关:不相关,说明问题出在前半段;相关但回答还是不合理,问题才可能出在后半段的 prompt 或模型。这套二段论看起来简单,却自动过滤掉了大多数调参类的无用功。
另外一个值得养成的习惯是给每个实验写日志。改了一个参数、换了一个模型,就把改动和评测结果记下来。这样下次你发现效果变差,可以直接回溯是哪一次改动引入的回归。很多从零起步的人都会在这一步偷懒,直到调参调到怀疑人生,才意识到实验记录本身就是最硬核的工程能力。
我最后再说一个小技巧:在我自己带过的所有学员里,进步最快的那批人,共同特点不是基础多好,而是特别舍得“做减法”——用最小数据集、最小模型、最小服务把整条链路跑通,再一步步加复杂度。很多人失败,不是因为东西太难,而是因为一上来就把所有先进技术都堆进去,最后卡在系统复杂度里寸步难行。从最简单的闭环开始,让每个环节的行为都可观测、可解释,这是我这几年做 AI 工程最深的体会,也是ai-engineering-from-scratch这条路自始至终想传递的东西:AI 工程不是玄学,它每一步都可以被理解、被验证。