
我经常在私信里看到一类问题想搞AI但完全不知道从哪儿下手。不是那种“先学Python还是先学数学”的纠结而是打开招聘网站发现岗位要么写“算法工程师要求顶会论文”要么写“AI应用开发熟悉LangChain”中间好像隔着一条河。也有不少朋友自学了几个月模型能跑通但一说到上线就发怵更别提把整个AI工程串起来了。今天想聊的“ai-engineering-from-scratch”就是这条河的渡口。它不是什么高级理论而是一种把AI从论文变成可用产品的完整工程实践覆盖数据处理、模型训练/微调、应用封装、部署运维这一整条链路。如果你是开发背景想切入AI或者已经在用现成API但想进一步掌控模型底层逻辑这个方向会非常适合你。这篇文章我会用自己从零搭项目踩过来的经验把核心路径、关键技术选型、实操细节和最容易翻车的坑一次说清楚。1. AI工程到底在做什么先把这个概念说透。很多人以为AI工程就是调包、调用大模型API或者写写Prompt其实这只是冰山一角。真正的AI工程是从一个模糊的业务需求出发经过数据、模型、系统三层递进最终交付一个稳定、可评估、可维护的AI系统。1.1 AI工程与传统算法岗的差异我见过不少算法背景的朋友代码写得很好模型指标刷得很高但一到生产环境就水土不服。因为算法岗的核心是“离线指标”精度、召回率、F1这些。而AI工程的核心是“在线可用性”延迟要低、并发要扛得住、输入要能容错、失败了要能自愈。举个例子你用BERT微调一个文本分类模型离线F1做到0.95觉得很稳。但上线后用户输入一堆表情包、错别字、中英混排你的预处理逻辑根本没覆盖模型输出直接乱套。工程化要解决的就是这类“真实世界的脏乱差”包括数据漂移检测、模型回退机制、异常输入兜底。所以AI工程的核心其实是围绕两条主线一条是如何高质量地获取和准备数据让模型学得动另一条是如何把模型塞进完整的软件系统里让它稳定地服务业务。两条线缺一不可。1.2 现代AI工程的核心技术栈从目前主流技术栈来看AI工程已经分成明显的三层数据层数据采集、清洗、标注、增强以及特征工程的自动化。这层用到的工具很杂从Pandas到Spark都可能出现核心目的是让下人模型前的数据是可解释、可追踪、可复用的。模型层训练或微调模型。传统路线是PyTorch、TensorFlow配合HuggingFace Transformers全家桶。现在大模型路线则多围绕LoRA、QLoRA做低成本微调以及Embedding模型选型、RAG检索增强生成架构设计。系统层这一层最容易被忽视却是AI工程和“写脚本跑模型”的本质区别。它包含API服务搭建FastAPI/Flask、并发与异步处理、容器化部署Docker/K8s、模型监控与CI/CD流水线。这层的目标就是让你训练出来的模型变成可以被业务方随时调用的服务。这三层不是一步到位的我的建议是先以一条极小的端到端路径打通全流程再逐层加深。1.3 为什么“从零开始”反而有优势这里说句可能得罪人的话如果抱着“我这里调不动、那里配不好干脆先背熟概念再动手”的心态你永远走不出新手村。AI工程这个领域最大的壁垒其实不是知识而是经验。经验从哪儿来只能从一次次失败的实操里攒。从零开始反而有优势因为你没有被“以前都是这么做的”束缚住。你会遇到许多让资深工程师头疼的问题但也会用最新、最合适的工具解决它们。比如现在搭建RAG系统你不会去翻Lucene的老黄历来推倒索引设计而是直接用Chroma、FAISS这类向量数据库成本低很多。这就是“没有历史包袱”的红利。2. 从零开始的学习路径设计我不太同意那些“三个月从零到AI大牛”的路线图太贩卖焦虑了。合理的路径应该像盖房子先打地基再砌墙最后才是装修。2.1 基础层Python、Linux、GitPython是AI的世界语言这不是说你得成为语言专家但有几个点必须非常熟练列表推导、生成器、装饰器、异常处理、虚拟环境管理venv/conda。你不会写业务代码没关系但你必须能读懂开源项目的代码能直接把别人的工具拿来用。Linux也是一样你不一定要精通运维但至少熟练使用Linux基础命令、文件权限、进程管理、端口占用排查。我接手过太多同事的项目光是在Windows上花了一天解决环境依赖问题而同样的流程在Linux服务器上两条命令就完事了。Git则是最容易被忽略但最实用的技能。AI项目的实验是高度迭代的模型版本、数据版本、代码版本任何一个没管好复现实验的时候都会把人逼疯。建议至少掌握git init、branch、merge、revert这些日常操作别光会commit。2.2 核心层机器学习、深度学习与LLM的交叉走到这一层很多人会焦虑是不是要把高数、线代、统计全都补一遍。我的经验是不用贪多先把最关键的几个概念吃到透包括梯度下降、反向传播、过拟合/欠拟合、损失函数、评估指标。这些概念不一定要你用笔推公式但你必须清楚它们影响模型训练的哪一环出了问题才能定位。深度学习层面PyTorch的Tensor操作和自动求导机制是必修课至少自己写过一个简单的两层神经网络。之后上手Transformer会轻松很多因为无论是BERT、GPT还是现在满大街的LLM本质上都是Attention机制的变体。把Transformer的结构理解透很多东西就通了。到这里我特别提醒一句别急着怼大模型API。先用小模型把全流程跑通再逐步替换成大模型。否则你会在“封装的接口报错”和“模型效果不好”之间无从下手。2.3 工程化层从模型到服务这一层是整个“AI工程”区别于“AI实验”的分水岭。核心技能是API设计推荐FastAPI写起来轻快、自带Swagger文档、异步支持好非常适合AI服务。容器化Docker是标配把模型服务打镜像连依赖一起打包走到哪儿跑到哪儿。进阶再学K8s但最初用Docker Compose编排两三个服务已经足够。自动化评估不要用肉眼评估模型效果。设计一批固定测试集用离线指标和在线日志持续追踪模型改没改好让数据和指标说话而不是“我感觉效果不错”。工程化层是大多数自学者容易停滞的地方。因为模型训练能带来即时反馈但工程化需要一点一点磨成就感不强。可一旦你把API、Docker、监控这一套跑顺你的AI能力就真正从实验性质升华到了产品性质。3. 实操搭建一个本地知识库问答系统光说理论是空中楼阁。我从自己做到项目里挑一个最适合上手的例子本地知识库问答系统。这个项目麻雀虽小五脏俱全用到了数据准备、向量化、检索、模型文本生成、API封装正好覆盖AI工程的完整链路还不需要花一分钱的API费用。3.1 环境与依赖准备我的建议环境是Linux或macOSWindows可用WSL替代。用conda创建独立环境避免版本冲突。conda create -n rag-demo python3.10 conda activate rag-demo接下来安装核心依赖。这里我选择轻量方案不强制装全套框架pip install fastapi uvicorn sentence-transformers chromadb对整个项目需要的大模型推理我用Ollama来管理本地模型日常测试完全够。在安装好Ollama后拉取一个中等体量的模型比如Qwen2.5系列的7B版本ollama pull qwen2.5:7b为什么选这个模型一是中文效果好二是7B对单卡消费者级GPU或纯CPU推理都比较友好三是Ollama作为推理服务的抽象后续替换别的模型只需要一条命令不用改代码。这比直接让应用依赖某个厂商的API方案灵活得多。3.2 数据准备与索引构建这一步的核心工作把你的知识库文档Markdown、TXT、PDF等切成小段用Embedding模型转成向量灌进向量数据库。切分是第一个坑文本切太短会丢失上下文切太长检索不精确。我实测下来中英文混合文档用300到500字的滑动窗口重叠50字左右效果最均衡。如果文档结构性强也可以按Markdown的标题层级切分效果会更好。这里我写一个简单的build_index.py脚本from sentence_transformers import SentenceTransformer from chromadb import PersistentClient import os CHUNK_SIZE 400 OVERLAP_SIZE 50 model SentenceTransformer(BAAI/bge-m3) client PersistentClient(path./kb_index) collection client.get_or_create_collection(docs) def chunk_text(text): chunks [] start 0 while start len(text): end start CHUNK_SIZE chunks.append(text[start:end]) start CHUNK_SIZE - OVERLAP_SIZE return chunks def load_docs(folder): docs [] for fname in os.listdir(folder): if fname.endswith(.md): with open(os.path.join(folder, fname), encodingutf-8) as f: docs.append({id: fname, text: f.read()}) return docs if __name__ __main__: for doc in load_docs(./kb): chunks chunk_text(doc[text]) embeddings model.encode(chunks) ids [f{doc[id]}_{i} for i in range(len(chunks))] collection.add(idsids, embeddingsembeddings.tolist(), documentschunks) print(findexed {collection.count()} chunks)运行一次本地就会出现kb_index目录里面就是索引好的向量库。3.3 本地模型接入与问答测试接下来写一个query_demo脚本实现“检索增强生成”的完整闭环from sentence_transformers import SentenceTransformer from chromadb import PersistentClient import requests import json embedder SentenceTransformer(BAAI/bge-m3) client PersistentClient(path./kb_index) collection client.get_or_create_collection(docs) def retrieve(query, top_k5): q_emb embedder.encode(query) res collection.query(query_embeddings[q_emb.tolist()], n_resultstop_k) return res[documents][0] def ask_llm(prompt): resp requests.post( http://localhost:11434/api/generate, json{model: qwen2.5:7b, prompt: prompt, stream: False} ) return json.loads(resp.text)[response] query 什么是AI工程 context \n.join(retrieve(query)) prompt f你是知识库助手请基于以下材料回答问题\n\n{context}\n\n问题{query} print(ask_llm(prompt))先启动Ollama服务ollama serve然后运行python query_demo.py就能看到模型基于本地文档回答问题了。这个流程里模型本身不包含知识答案全部来自你的检索材料所以不会胡编乱造。这也是RAG的价值所在把外部知识低成本地接入大模型不用重新训练。3.4 用FastAPI封装成服务脚本能跑通只是第一步。要成为“工程”必须封装成可并发的API服务。我用FastAPI改写from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uvicorn app FastAPI(titleLocal RAG QA) class QueryRequest(BaseModel): query: str app.post(/qa) def qa(req: QueryRequest): try: context \n.join(retrieve(req.query)) prompt f知识库内容如下\n\n{context}\n\n问题{req.query} answer ask_llm(prompt) return {query: req.query, answer: answer} except Exception as e: raise HTTPException(status_code500, detailstr(e)) if __name__ __main__: uvicorn.run(app, host0.0.0.0, port8000)这样一来业务方只需要通过HTTP请求就能管理自己的知识问答curl -X POST http://localhost:8000/qa -H Content-Type: application/json -d {query:如何对A/B切分文档}到了这个阶段你就有了一个完整的AI工程最小闭环数据→索引→检索→生成→API服务。在这个骨架上往任意一个环节深挖都可以对应到一套成熟的生产级方案。4. 真实项目中的常见坑与排查手册这部分全是实操现场得来的教训。我做过多个类似项目这里把它们整理成速查表比你看十篇教程都有用。4.1 环境与依赖问题坑版本不一致导致模型推理报错。PyTorch的CUDA版本不对、Transformers和Tokenizers版本不匹配这类问题最常见。排查时先看完整报错栈别直接重装环境。我习惯用pipdeptree看依赖树定位到具体冲突包再决定往上还是往下调版本。坑Ollama端口被占用。默认跑在11434端口有时会被别的服务占用。排查方式很简单lsof -i :11434如果是自己机器上的残留进程直接kill掉如果是端口冲突设置OLLAMA_HOST环境变量换端口即可。坑中文乱码。在Linux上跑Python脚本文件路径或日志输出经常遇到编码问题。建议环境里统一设置export PYTHONIOENCODINGutf-8同时在脚本里打开文件时写encodingutf-8别相信默认值。4.2 数据与检索问题坑切分太碎导致检索结果上下文断裂。我见过不少新人把文档按固定200字切块结果答案明明存在但模型就是答不出来因为切碎的片段缺少上下文线索。定位方法很直观打印出检索到的chunks人工读一读如果自己也看不懂切分方案肯定要改。坑Embedding模型与查询场景不匹配。通用Embedding在中英文混排、专业术语多的内容上效果会很差。目前我测下来BAAI的bge-m3对中文效果不错多语言支持的覆盖性也好。但换领域时一定要自己建一个小规模测试集把检索命中的Top3精度跑出来别只看宣传指标。坑检索Top K值毫无依据地调大调小。K值太小会漏信息太大会把噪声带进Prompt。经验法则是单论问题用K3~5多跳推理或者文档长、结构松散时可以到K10但每多一段都会增加模型阅读负担和延迟。测试时扫K从1到10的效果曲线选拐点附近值。4.3 模型效果问题坑Prompt写得太宽松模型发挥不稳定。我调试RAG时一个常见死法是LangChain默认的Prompt不指定“只基于材料回答不要用材料外知识”模型经常自由发挥。一定要把边界在Prompt里写死对不相关的问题让模型直接说“材料中未找到相关信息”这样可空、可控、不误导人。坑本地小参数量模型输出质量不足。Qwen2.5:7B在一般问答上可用但涉及复杂推理时容易露馅。建议升级到14B或32B或者尝试在本地部署量化版蒸馏模型。没有GPU时可以退而求其次用API但要注意成本和数据隐私边界。坑认为RAG一定能解决所有问题。RAG适合的是“知识获取成本高但文档质量高、范围可控”的场景。如果你文档本身都是噪声或者用户问题需要多步逻辑推理RAG救不了。遇到这种情况要重新回看信息架构而不是反复调检索参数。4.4 部署与运维问题坑内存不足导致服务被杀。加载Embedding模型加LLM很容易吃掉十几个G内存。如果部署在2C4G的小机器上纯CPU推理会很痛苦。建议用内存占用更少的量化模型加swap并且用gunicorn配合worker数量限制并发。坑docker run命令起镜像后服务不可达。十有八九是端口映射忘了写-p参数或者容器内服务绑定了127.0.0.1而不是0.0.0.0。FastAPI默认监听127.0.0.1这是新手常踩坑点解决方法是启动时明确host0.0.0.0。坑没有做接口参数校验和超时控制。生产环境一定要在API层加入请求体大小限制、字段校验比如query非空、推理超时中断。否则用户一个巨大请求就能让模型服务卡死几分钟后面所有请求排队超时。必要时引入Redis做简单的请求限流。5. 心得与下一步扩展做了这么多个AI项目我最深的感受是AI工程门槛不在模型本身而在你能不能把一个混沌的业务需求拆成可执行的系统工程。今天你看到很多花哨的Agent、复杂的调度、自动化流水线本质上都是“数据→模型→系统”这个基本框架的不断强化和细化。继续扩展的方向我建议按自己的业务场景来选如果数据量大去钻研数据Pipeline和数据版本化管理如果模型效果不足往模型微调和RAG算法优化深挖如果服务要支撑高并发去补齐分布式推理、模型缓存、负载均衡这些后端基本功。每一条路都能走得很深但地基都是本文讲的基本框架。最后分享两个我在实操中积累的小技巧。第一别怕把每个环节都打印日志AI项目在本地跑通不等于在服务器上跑通每一步的耗时与结果都记录下来排查问题时至少能判断是哪一层的锅。第二一定要养成“版本固化”的习惯记录依赖的具体版本、数据文件哈希、参数配置这比任何训练报告的复盘都更有说服力。这套从零开始的路我已经走过一遍过程中踩的坑远不止文章里的这些。但只要你动手把第一个RAG服务搭起来你就已经超越了绝大多数“只看不动”的人。接下来就是不停地迭代和踩新坑的过程这正是AI工程最有魅力的地方。