1. 从零搭建AI工程能力为什么我劝你别一上来就啃论文ai-engineering-from-scratch这个标题第一次看到的时候我以为是又一个教你怎么调API的速成教程。点进去翻了翻发现方向完全不是那么回事——它讲的是从最底层开始把AI工程化落地所需要的整套能力一点点搭起来。这个思路我太认同了。过去两年我带过几个团队做AI相关的项目见过太多人卡在同一个地方模型能跑通demo能演示但一上生产就各种问题。延迟高、成本失控、效果不稳定、数据管道三天两头断。根子在哪不是模型不够好是工程能力没跟上。很多人学AI的路径是看论文→跑开源代码→调参→部署每一步都在跳跳到最后发现自己只会复制粘贴遇到新问题就懵了。ai-engineering-from-scratch这个项目标题背后的核心价值就是帮你把这条断裂的路径补全。它适合谁我认为有三类人特别需要第一类是有一定编程基础但没系统做过AI项目的开发者第二类是从数据/后端转AI工程方向的工程师第三类是带团队的技术负责人需要理解AI工程的全貌才能做技术决策。这篇文章我会把这个项目涉及的核心思路、技术选型、实操步骤和我自己踩过的坑尽可能完整地拆开讲一遍。2. 整体设计思路为什么从零不等于从底层造轮子2.1 先搞清楚AI工程到底包含什么很多人把AI工程等同于训练模型这是最大的认知偏差。训练模型只是其中一环而且对大多数应用场景来说这一环甚至不是最耗时间的。一个完整的AI工程体系我把它拆成五层数据层数据采集、清洗、标注、版本管理、特征存储训练层实验管理、分布式训练、超参搜索、模型评估推理层模型服务、批处理、流式推理、GPU调度应用层Prompt管理、RAG管道、Agent编排、输出校验运维层监控告警、成本追踪、A/B测试、灰度发布从零搭建的意思是这五层你都要理解但不意味着每一层你都要自己写。比如特征存储可以用Feast实验管理可以用MLflow推理服务可以用Triton或vLLM。关键是你要知道每一层解决什么问题、什么时候该引入什么工具、工具之间的边界在哪里。我见过一个团队上来就自己写了一套模型服务框架花了三个月最后发现vLLM一行配置就能达到更好的吞吐。这就是典型的从零理解错了——从零是让你从零理解问题不是从零造所有轮子。2.2 技术选型的核心原则按阶段匹配复杂度从零搭建AI工程能力最容易犯的错是过度设计。我的建议是按阶段来阶段数据量级推荐方案核心目标验证期10万条单机脚本快速验证可行性成长期10万-1000万轻量管道托管服务稳定迭代规模化1000万分布式自建核心组件成本与效率平衡验证期就别想着上Kubernetes一个Jupyter Notebook加几个Python脚本就够了。成长期再引入Airflow或Prefect做编排用托管的向量数据库省运维精力。到了规模化阶段才需要考虑自建推理集群、做GPU利用率优化这些事。这个原则背后的逻辑很简单工程复杂度必须匹配业务复杂度。过早引入复杂架构只会让你在还没验证需求的时候就陷入运维泥潭。2.3 为什么我建议从推理侧入手而不是训练侧这是我自己踩坑之后的经验。大多数人学AI工程第一反应是从训练入手——搞数据集、调模型、跑实验。但训练侧的门槛其实很高需要GPU资源、需要理解模型架构、需要调参经验而且反馈周期长一个实验跑几个小时甚至几天。推理侧不一样。你拿一个开源模型部署起来马上就能看到效果。在这个过程中你会遇到真实的问题显存不够怎么办、并发上不去怎么办、输出格式不稳定怎么办。这些问题逼着你去理解tokenization、KV Cache、批处理策略这些核心概念。等你把推理侧摸透了再回头看训练侧很多概念是相通的。ai-engineering-from-scratch这个思路里我理解的核心路径就是先让模型跑起来→再让模型跑得稳→最后让模型跑得便宜。这个顺序不能反。3. 核心细节解析从零搭建必须吃透的四个技术点3.1 数据处理管道别小看清洗和分块数据是AI工程的地基但大多数人在这上面花的时间太少。我见过一个RAG项目效果怎么调都不行最后发现是文档分块策略有问题——把表格和正文混在一起切了检索出来的内容全是碎片。从零搭建数据管道你需要关注这几个环节采集与去重网页数据、PDF、数据库导出格式五花八门。去重不是简单的字符串匹配要用MinHash或SimHash做近似去重否则你的训练数据里会有大量重复内容导致模型过拟合。清洗与标准化HTML标签、乱码、特殊字符、多余空白这些都要处理。我一般会写一个清洗管道用正则加规则引擎把常见问题一次性解决。注意保留原始数据的备份清洗规则改了可以重新跑。分块策略这是RAG场景下最关键的环节。我的经验是技术文档按标题层级切每块500-1000 token对话记录按轮次切代码按函数或类切。切完之后要加overlap一般10%-20%防止边界信息丢失。版本管理数据也要做版本控制。DVC是个不错的选择它把大文件存在对象存储里Git里只存元数据。每次数据更新打一个tag模型效果出问题可以回溯到具体的数据版本。注意数据清洗规则一定要写成可配置的不要硬编码在脚本里。我吃过亏规则改了要改代码重新部署效率极低。3.2 模型推理服务从单机到高并发的演进路径推理服务是从零搭建AI工程能力最核心的实操环节。我按演进路径来讲第一步单机直接加载。用Transformers库的pipeline几行代码就能跑。这个阶段重点是理解模型的输入输出格式、tokenizer的行为、生成参数temperature、top_p、max_tokens的影响。第二步引入推理框架。vLLM是目前最主流的选择核心优势是PagedAttention和连续批处理。实测下来同样的硬件vLLM的吞吐能比原生Transformers高5-10倍。部署命令很简单python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192这里有几个参数需要根据实际情况调gpu-memory-utilization控制显存占用比例0.9是个比较安全的默认值max-model-len决定最大上下文长度设太大浪费显存设太小长文本会截断。第三步加一层网关。当你有多个模型、多个版本需要管理时需要一个统一的入口。这层网关负责路由、限流、鉴权、日志。可以用FastAPI自己写也可以用现成的方案。核心是要把模型版本和API路径解耦方便做灰度发布。第四步GPU调度与弹性伸缩。到了这个阶段你需要监控GPU利用率、请求队列长度根据负载动态调整实例数。Kubernetes加KEDA可以做基于指标的自动伸缩但配置起来比较复杂建议规模上来之后再考虑。3.3 评估体系没有评估就没有优化这是最容易被忽视但最重要的一环。很多人做AI项目效果好不好全靠感觉这是不行的。从零搭建评估体系我建议分三层第一层自动化指标。分类任务看准确率、召回率、F1生成任务看BLEU、ROUGE、BERTScoreRAG场景看检索命中率、答案忠实度。这些指标可以自动化跑每次模型更新都跑一遍防止回归。第二层LLM-as-Judge。用更强的模型来评估弱模型的输出。比如用GPT-4评估你的7B模型生成的答案质量。这个方法有争议但实测下来在大多数场景下和人工评估的相关性能到0.8以上。关键是评估Prompt要设计好给出明确的评分标准和示例。第三层人工抽检。自动化指标再全也替代不了人工。我一般会每周抽100条线上请求人工标注质量和自动化指标做对比。如果发现偏差大说明自动化指标需要调整。评估数据集要单独维护不能和训练数据混在一起。我建议至少准备200-500条覆盖主要场景的评估样本每次迭代都跑一遍。3.4 监控与可观测性上线只是开始AI系统的监控和传统后端系统不一样除了CPU、内存、延迟这些常规指标还要关注Token吞吐量每秒处理的输入/输出token数直接关系到成本首Token延迟用户感知最明显的指标流式输出场景下尤其重要输出质量指标拒答率、格式错误率、重复率成本指标每千次请求的GPU成本、API调用成本我一般用Prometheus收集指标Grafana做面板。日志方面每次请求的输入输出都要记录但要注意脱敏和存储成本。可以用采样策略正常请求采样10%异常请求全量记录。实操心得监控面板不要做太多一屏能看完最好。我见过一个团队做了十几个面板结果没人看。核心指标就那几个放在最显眼的位置。4. 实操过程从零搭建一个完整的AI工程Demo4.1 环境准备与依赖安装我以一个RAG问答系统为例把从零搭建的完整流程走一遍。硬件要求一张24G显存的GPU3090或4090都行32G内存100G硬盘。基础环境用conda管理conda create -n ai-eng python3.11 conda activate ai-eng pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121 pip install vllm transformers sentence-transformers pip install fastapi uvicorn pip install chromadb pip install ragas这里解释一下选型vLLM做推理sentence-transformers做embeddingchromadb做向量存储轻量适合验证期ragas做RAG评估。都是经过验证的组合踩坑少。4.2 数据准备与向量化假设我们有一批技术文档放在docs/目录下。第一步是加载和分块from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.document_loaders import DirectoryLoader loader DirectoryLoader(docs/, glob**/*.md) documents loader.load() splitter RecursiveCharacterTextSplitter( chunk_size800, chunk_overlap150, separators[\n## , \n### , \n\n, \n, 。, ] ) chunks splitter.split_documents(documents) print(f共切分 {len(chunks)} 个块)分块参数怎么定chunk_size800是因为大多数embedding模型的最佳输入长度在256-512 token之间800字符大约对应400-600 token留了一定余量。overlap150是为了防止边界信息丢失。separators的顺序很重要优先按标题切保证语义完整性。然后是向量化和入库from sentence_transformers import SentenceTransformer import chromadb model SentenceTransformer(BAAI/bge-large-zh-v1.5) client chromadb.PersistentClient(path./chroma_db) collection client.get_or_create_collection(tech_docs) for i, chunk in enumerate(chunks): embedding model.encode(chunk.page_content).tolist() collection.add( ids[fchunk_{i}], embeddings[embedding], documents[chunk.page_content], metadatas[{source: chunk.metadata.get(source, )}] )embedding模型选bge-large-zh-v1.5中文场景下效果稳定而且维度是1024不算太大。如果你做英文场景可以用bge-large-en-v1.5或者e5-large。4.3 推理服务搭建与RAG管道串联启动vLLM服务python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --served-model-name qwen \ --gpu-memory-utilization 0.85 \ --max-model-len 8192 \ --port 8000然后写RAG管道import requests from sentence_transformers import SentenceTransformer import chromadb embed_model SentenceTransformer(BAAI/bge-large-zh-v1.5) client chromadb.PersistentClient(path./chroma_db) collection client.get_collection(tech_docs) def retrieve(query, top_k5): q_emb embed_model.encode(query).tolist() results collection.query(query_embeddings[q_emb], n_resultstop_k) return results[documents][0] def generate(query, contexts): context_text \n\n.join(contexts) prompt f基于以下参考资料回答问题。如果资料中没有相关信息直接说不知道。 参考资料 {context_text} 问题{query} 回答 resp requests.post(http://localhost:8000/v1/completions, json{ model: qwen, prompt: prompt, max_tokens: 512, temperature: 0.1 }) return resp.json()[choices][0][text] query vLLM的PagedAttention是什么 contexts retrieve(query) answer generate(query, contexts) print(answer)这个管道虽然简单但包含了RAG的核心环节检索→拼接→生成。temperature设0.1是为了让输出更稳定RAG场景不需要太多创造性。4.4 评估与迭代用ragas做自动化评估from ragas import evaluate from ragas.metrics import faithfulness, answer_relevancy, context_precision from datasets import Dataset eval_data { question: [vLLM的PagedAttention是什么, 如何做数据分块], answer: [answer1, answer2], contexts: [contexts1, contexts2], ground_truth: [标准答案1, 标准答案2] } dataset Dataset.from_dict(eval_data) result evaluate(dataset, metrics[faithfulness, answer_relevancy, context_precision]) print(result)faithfulness衡量答案是否忠实于检索到的上下文answer_relevancy衡量答案和问题的相关度context_precision衡量检索的准确度。这三个指标能覆盖RAG系统的主要质量维度。跑完评估根据低分指标针对性优化。比如faithfulness低说明模型在编造内容需要调整Prompt或换更强的模型context_precision低说明检索不准需要优化embedding或分块策略。5. 常见问题与排查技巧实录5.1 显存不够用怎么办这是最高频的问题。排查思路按优先级来现象可能原因解决方案启动就OOM模型太大用量化版本GPTQ/AWQ或换小模型运行中OOM并发太高降低max_num_seqs或限制max_model_len显存碎片频繁变长请求开启vLLM的PagedAttention设gpu_memory_utilization0.9量化是最直接的方案。7B模型FP16需要约14G显存INT4量化后只要4G左右效果损失在可接受范围内。vLLM支持AWQ和GPTQ加载时加--quantization awq就行。5.2 输出格式不稳定怎么治LLM输出格式不稳定是通病。我的经验是三层防护第一层Prompt里给明确格式要求和示例。比如请以JSON格式输出包含answer和confidence两个字段示例{answer: ..., confidence: 0.95}。第二层用结构化输出工具。vLLM支持guided decoding可以强制模型按JSON Schema输出from vllm import SamplingParams from vllm.sampling_params import GuidedDecodingParams guided GuidedDecodingParams(jsonschema) params SamplingParams(temperature0.1, guided_decodingguided)第三层后处理校验。解析失败时重试或降级处理。我一般会重试2次还失败就返回兜底答案。5.3 检索效果差怎么排查RAG效果差80%的问题出在检索环节。排查步骤先看检索到的内容是否相关。打印top_k结果人工判断。如果检索内容不相关检查embedding模型是否适合你的领域。通用模型在专业领域可能表现不好需要微调或换领域模型。如果检索内容相关但答案不对检查Prompt模板。上下文太长可能导致模型忽略关键信息试试把最相关的内容放在最前面。如果都正常但效果还是差考虑加一个rerank模型。bge-reranker是个不错的选择对top_k结果做精排能显著提升准确率。避坑技巧分块的时候保留标题信息。我习惯在每个chunk前面加上所属的标题路径比如## 3.2 模型推理 ### 3.2.1 vLLM部署这样检索时能保留层级语义。5.4 成本失控怎么控制成本控制要从三个维度入手Token层面限制max_tokens别让模型无限生成。RAG场景一般512就够了。输入侧做上下文压缩只保留最相关的片段。请求层面加缓存。相同或相似的query直接返回缓存结果。可以用语义缓存相似度超过阈值的直接命中。硬件层面监控GPU利用率。如果利用率长期低于30%说明资源浪费考虑合并服务或换更小的实例。如果长期高于80%说明需要扩容。我一般会做一个成本看板按天统计token消耗和GPU时长设置预算告警。超过阈值就触发排查。5.5 模型更新后效果回退怎么办这是没有评估体系的典型症状。解决方案建立回归测试集每次模型更新前跑一遍对比关键指标。做灰度发布新模型先接10%流量观察一周再全量。保留模型版本回滚能力出问题能快速切回旧版本。我自己的做法是维护一个黄金测试集200条左右覆盖核心场景和边界情况。每次更新必跑指标下降超过5%就阻断发布。6. 我在这条路上踩过的几个坑第一个坑是过早优化。刚开始做的时候总想着一步到位搞了一套复杂的微服务架构结果光运维就耗掉大半精力核心功能反而没时间打磨。后来全部推倒重来用单体加脚本两周就跑通了。先跑通再优化这个顺序不能反。第二个坑是忽视数据质量。有段时间模型效果怎么调都不行最后发现训练数据里有30%是重复的还有大量格式错误。花了一周做数据清洗效果直接提升了一大截。数据上的投入回报率远高于调参。第三个坑是不做评估。早期全靠感觉判断效果结果每次更新都像开盲盒。后来建了评估体系才发现之前很多优化其实是负优化。没有度量就没有改进这句话在AI工程里尤其正确。第四个坑是忽略成本。有一次跑实验忘了设max_tokens模型生成了上万token一天烧掉了几百块。从那以后我所有请求都设上限并且加了成本告警。这条路走下来最大的体会是AI工程不是纯技术问题是工程思维和系统思维的结合。你得知道每个环节解决什么问题、边界在哪里、什么时候该引入什么工具。从零搭建的意义不在于什么都自己写而在于你理解了整个系统的运作逻辑遇到问题知道从哪里下手。这个能力比会调几个API值钱得多。