我一直觉得“AI工程师”这个岗位最大的门槛,不在于数学、不在于框架,而在于“从零到一”那段没人带你走的路。身边有不少朋友,在转型AI工程方向时,收藏了十几个教程、装了一堆依赖库、刷了一百节网课,结果真要搭建一个能跑通的项目时,还是发懵。我自己也一样,刚接触这个方向时,以为学点机器学习基础、背几个名词就能上马,结果被工程化的细节按在地上反复摩擦。后来我换了一个思路:彻底回炉,从工程视角重新理解AI,从最原始的需求和依赖关系开始,一条条自己搭出来。这篇就想把这套“ai-engineering-from-scratch”的路径和方法记录下来,写给那些不想被网课喂饱、想自己真正动手把AI工程搞明白的人。
这个内容适合谁?一是被算法名词劝退的转行工程师,二是已经在写接口但始终觉得“隔了一层”的研发同学,三是在团队里被要求“快速落地一个AI需求”却感觉无从下手的人。我会把自己走过的弯路、拆过的轮子、常用的目录结构、以及一些不那么好懂但又很关键的工程原则,全部分享出来。看完之后,你不是多了几条笔记,而是有了一条可以照着走的从零到可交付的AI工程路线。
1. 为什么很多人从零开始学AI工程,最后却学废了
我观察到一个规律:学AI工程半途而废的人,基本不是学不会,而是被“无效学习”拖垮的。什么叫无效学习?就是一直在课堂和教程的舒适区里打转,从来没真正面对过“无人驾驶交付边界”的残酷现实。
1.1 典型的学习路径误区
大多数人接触一个新技术时,首先会去搜“XX快速入门”。然后就被引导着安装环境、跑起一个demo、看到它输出了一行神奇的文字。这个体验很爽,于是你以为你已经入门了。但接下来你发现事情不对劲:等到你想把demo改造成一个真正能处理数据的服务,遇到的第一堵墙就是——为什么模型效果这么不稳定?为什么同样的提示词,上午能跑下午就跑不通?
这个现象在AI工程里太常见了。模型的输出天生带随机性,它不是传统软件开发里那种“满足输入就一定得到预期输出”的确定性系统。但很多教程不会告诉你这一点,它们只展示顺利路径,隐藏了大量试错过程。于是学习者形成了一个错误预期:我能通过一个demo复制出网上博文的效果。等到现实给了你一闷棍,你就会怀疑是不是自己的代码写错了,一步一步排查下去,最后崩溃在模型参数的海洋里。
还有一个更隐蔽的误区:过度关注算法原理,忽视了工程链路。很多初学者抱着“不理解Transformer就别想搞AI”的心态,把大量时间花在读论文、推导公式上。但实际做工程时,你发现阻力最大的根本不是某个注意力机制的理解,而是数据格式不统一、API调用频繁限流、模型输出不符合JSON结构、并发调用导致性能瓶颈。这些东西才是一个AI系统能落到生产环境的真正关键。
1.2 从成本视角看,为什么不能只学算法
我见过太多团队,让一个算法工程师去独自承担AI整个系统的搭建,结果算法工程师在数据处理上撑了一周,在研究API契约时又懵了;反过来,让一个传统后端工程师去搞AI,他可能在提示词调优上浪费一个月。
这里的本质问题是:AI工程不是“算法工程”,而是“系统和算法混合的工程”。你需要在召回、排序、缓存、限流、可变性、可观测性这些系统问题里穿梭,同时又要理解模型的脾气、token消耗成本、上下文窗口限制。只懂一头,就会在另一头出血。
我自己在早期就犯过一个错误:为了在一个项目里融合多个模型能力,搭建了一个“自以为优雅”的调度层,结果因为没有预估API的费用和响应延迟,在一个小流量场景里一天花掉了六百多块钱的模型调用费。那一刻我才意识到,AI工程的核心不只是让功能跑起来,而是让它在成本、速度、稳定性上都能跑得起来。
1.3 从零开始的本质:不是“补知识”,而是“重建系统观”
我后来把“从零开始”的定义改掉了。它不是把所有AI基础知识都重学一遍,而是把“从模型产生一个输出,到我最终交付一个服务”这条完整链路当做一个系统来看,自己去设计、去搭建、去填每条缝。你不需要是数学天才,但你得把工程这条线的每一个决策点都弄明白:数据怎么清洗、模型选哪个、温度参数调多少、缓存怎么设计、错误怎么降级、效果怎么评估。
这个视角的转变才是“从零开始”的真正起点。就像学做饭,菜谱只是参考,真正的功夫在于你对火候、食材、调味的全局判断。AI工程也是一样的套路。
2. 动手之前先回答三个问题:选型、边界与资源
很多零基础的人一上来就装环境写代码,我特别不建议。我在AI工程上路之前,一定会先花几天的时间把三个问题想清楚。这三个问题不解决,后面你写的每一行代码都可能是废代码。
2.1 选型问题:这个问题需要多大的模型,还是根本需要模型?
做AI工程,第一件事不是急着选大模型,而是判断“业务问题是否真的需要AI介入”。听起来很简单,但我在实际项目中见过大量“有了锤子看啥都是钉子”的案例。比如一个客户想做一个客服知识库搜寻系统,可他的数据量只有一百多条FAQ,用词频匹配和规则检索完全可以解决,他却要求上大模型。结果呢?模型幻觉出现、成本上升、延迟增高,客户还觉得“AI不过如此”。
所以我建议你建立一条简单的判断流程:
- 问题是否是模糊语义问题?比如意图识别、文本生成、复杂推理,这些适合上模型。
- 问题是否对解释性有高要求?如果是金融风控、医疗建议这种场景,模型需要配合知识库和规则兜底。
- 问题的数据规模有多大?小规模数据量,传统方法往往更可靠、更省钱。
- 是否有延迟要求?如果必须在几十毫秒内响应,那么大模型的选型需要极其谨慎。
这个选型判断,决定你后面整体架构的复杂度。选错一个模型,后面每一个决策点都跟着错。
2.2 边界问题:哪些功能交给模型,哪些功能交给代码?
我在刚做AI工程时,特别容易犯“把模型当万能胶”的毛病。什么问题都想让模型自己搞定,结果提示词写得像在许愿,模型的输出格式五花八门,解析代码写了一个又一个正则和分支,痛苦不堪。后来我才明白一个道理:模型只是系统里的一个组件,它的职责越单一,系统越稳定。
比如你做一个知识助手,你完全不需要让模型直接从海量文档中找答案。你应该先用检索模块把候选内容找出来,再把候选内容作为上下文塞给模型做摘要。这就是经典的RAG(检索增强生成)架构,核心原则是:让模型只负责“理解与生成”,让代码负责“定位与筛选”。这种边界划分要有意识地做,而不是等报错了才去拆。另一个例子是结构化输出。如果你需要模型返回JSON数据,与其在提示词里反复强调“请一定返回合法JSON”,不如在代码里做两件事:一是给模型明确的返回格式模板,二是在解析失败时调用一次“修正”逻辑让模型自我修正。左一层代码、右一层提示词,才能把不确定性锁在笼子里。
2.3 资源问题:token、上下文和成本,算过账了吗
这是我觉得“从零开始”最值得花时间思考的问题。很多教程会让你把整份文档一股脑塞进模型的上下文窗口,看起来逻辑上没毛病,但实际里面全是成本炸弹。
拿一个典型的RAG场景来说:如果你的文档里每段内容都有一两千字,一次请求塞入五段上下文就是接近一万字的token。按照常见的计费标准,一次请求光输入就要好几厘钱。如果你的系统日请求量是十万次,那就是上百块钱的模型费。很多小团队第一天跑上线,看到账单才发现根本兜不住。
所以从零开始做AI工程,必须建立“token预算”习惯。我会用一张表把每个模块的token开销记录清楚:
| 模块 | 输入token预估 | 输出token预估 | 调用频率 | 单日成本估算 |
|---|---|---|---|---|
| 意图识别 | 120 | 40 | 2万次/天 | 中等 |
| 内容生成 | 1800 | 600 | 5000次/天 | 高 |
| 摘要提取(离线) | 3000 | 800 | 500次/天 | 低 |
这张表不是为了事后记账,而是在设计阶段就逼着你思考:哪些请求可以走缓存?哪些内容可以离线预处理?哪些任务可以用便宜的小模型搞定,根本不用上大模型?预算意识越早建立,后面就越从容。
3. 真正“从零”的第一步:不写业务代码,先跑通最小Agent
很多教材让你直接进入框架级开发,什么LangChain、LlamaIndex一上来就拧得像铠甲。但我的建议完全不同:先做一个最小可用的Agent,不用任何框架,纯手写。因为只有当你自己亲手组装了这些碎片,你才能真正理解一个AI Agent的骨骼是什么。
3.1 从环境准备到第一次调通
首先你需要一个Python环境,我建议直接用3.10以上版本,懒人办法是用conda建独立环境,避免和系统Python纠缠:
conda create -n ai-agent python=3.11 conda activate ai-agent pip install openai python-dotenv这里真的要强调一下:很多新手卡在“调不通API”这个环节,但里面80%的问题其实是环境变量和代理配置问题。我建议把API Key放在项目根目录的.env文件里,用python-dotenv加载,这样既能避免把密钥硬编码进代码,也方便多人协作时不互相踩密钥。
成功调用模型的第一段代码,不用写复杂逻辑,先把最原始的“问-答”跑通:
from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI() response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是一个乐于助人的中文助手。"}, {"role": "user", "content": "请用一句话解释什么是AI Agent。"} ], temperature=0.7 ) print(response.choices[0].message.content)注意这里的temperature参数。它就是控制“随机性”的旋钮:值越小,输出越确定,适合需要稳定格式的场景;值越大,输出越发散,适合创意写作。很多新手不知道这个参数的意义,以为模型输出的好坏是玄学,其实很多“时好时坏”的问题,把temperature调到0.2就解决了。
3.2 给你的Agent加上工具调用能力
跑通一个“聊天机器人”其实并不难,但AI工程真正的分水岭是让Agent调用外部工具。比如你让它查天气、查库存、帮你算算式,这些能力都必须通过Function Calling实现。我把这个机制翻译成大白话:你给模型一张“工具清单”,上面写了有哪些函数、每个函数接收什么参数;模型在回答用户问题前,会先判断“这一步该调用哪个工具”,然后返回一个结构化的调用请求,由你的代码真正执行这个函数,再把结果返回给模型组织最终回复。
我写一个极简案例,让Agent具备一个“获取当前时间”的能力:
import datetime import json from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI() def get_current_time(): return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") tools = [ { "type": "function", "function": { "name": "get_current_time", "description": "获取当前的日期和时间", "parameters": { "type": "object", "properties": {}, "required": [] } } } ] messages = [ {"role": "system", "content": "你是一个助手,可以通过工具获取信息。"}, {"role": "user", "content": "现在几点了?"} ] response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools, tool_choice="auto" ) message = response.choices[0].message if message.tool_calls: tool_call = message.tool_calls[0] if tool_call.function.name == "get_current_time": result = get_current_time() messages.append(message) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) final_response = client.chat.completions.create( model="gpt-4o-mini", messages=messages, tools=tools ) print(final_response.choices[0].message.content)这段代码看着不长,但它包含了Agent系统的核心心跳:模型判断意图、代码执行动作、结果回填上下文、模型生成最终答案。你对这个心跳理解得越深,将来无论你是去用LangChain,还是自己发明一个Agent框架,都会很快上手。
3.3 一个关键习惯:记录所有模型调用的输入输出
从零做AI工程,最常见的隐形陷阱是“不可复现性”。你调通了一个看起来完美的Prompt,但隔几天换了一台电脑再跑,发现不行了。为什么?因为你没有记录当时用的模型版本、参数、输入数据。所以我建议,在你写第一行业务逻辑之前,就先给项目加上日志能力。每次调用模型,都把请求参数和响应结果完整打印或存档,后续排查问题时,这些日志就是你的案发现场。
我有一个小习惯:每次实验性调用模型时,都会在代码里加上一个logger,记录完整输入输出。市面上有一些LLM可观测工具能自动跟踪,但新手阶段,最简单的办法就是先打印。打印得多了,你自然能看懂模型在不同参数下的行为差异,这是任何教程都替代不了的经验积累。
4. 从demo到能用:撑起工程落地的五个关键动作
当你把最小Agent跑起来之后,就到了一个分水岭:demo和可用系统之间的距离。这段距离不是模型能力差距,而是工程功力的差距。我自己在这个阶段踩过的坑,可以归纳成五个关键动作,每个都是血泪教训。
4.1 错误处理:别让模型把你整崩了
传统软件开发中,异常处理是基本功。但在AI工程里,异常的类型和来源完全不同。模型可能因为内容安全策略拒绝回答问题;网络可能超时;返回的JSON可能少了字段;上下文可能超出窗口限制。这些异常你要是都不处理,线上系统一崩到底。
我的做法分四层兜底:
- 第一层:调用前做参数校验,检查上下文长度是否超限。
- 第二层:调用时捕获网络类和超时类异常,做重试,但重试次数不能无限,一般两三次就够。
- 第三层:调用后校验响应格式,如果模型返回的不是合法JSON,尝试让它自我修正一次。
- 第四层:如果所有兜底都失败,必须给用户一个友好的降级回复,比如“这个我暂时没把握,请联系人工”,而不是抛出一个刺眼的堆栈错误。
这一套组合拳下来,AI系统的可用性才能接近你交付传统服务时的水平。
4.2 结构化输出:把“自由文本”关进“格式笼子”
模型默认吐出来的是自由文本,可工程系统需要的是字段和结构。如果你让一个业务系统去“理解”自由文本,你的代码会变得脆弱不堪。我建议在提示词里给模型一个明确的JSON模式,比如:
请以JSON格式返回结果,结构如下: {"summary": "一句话总结", "sentiment": "positive|neutral|negative", "keywords": ["关键词1", "关键词2"]} 只返回JSON,不要包含任何其他文字。即使这样,模型偶尔还是会飘,输出一段Markdown包裹的JSON或者干脆直接讲话。这时候就需要代码里的“结构校验+修正”机制。我自己的经验是,让模型自我修正一次的成功率很高,前提是要明确告诉它哪里错了,而不是说“你重新输出一遍”。
4.3 缓存与限流:省钱的本质是降低重复计算
AI工程里有一句很实在的话:你的系统性能问题,大都是钱没花对地方。很多业务请求其实是高度相似的,如果每次都让模型重新计算,等待时间长、成本也高。这时候引入语义缓存是非常划算的方案。
最简单的语义缓存思路:将用户输入做规范化处理后,用嵌入向量检索出语义相近的历史请求。如果找到相似度超过阈值的缓存结果,就直接返回,跳过模型调用。我的实践数据是,在一个客服问答场景里,命中缓存的比例能到35%以上,直接省下了一大笔模型费用。
除了缓存,限流也很关键。很多模型API有每分钟请求次数和同时并发限制,如果你的代码里没有一个简单的令牌桶限流器,突发流量可能直接把你的调用打爆。我建议没必要一开始就上分布式限流组件,进程内的令牌桶实现就能扛住绝大多数小团队的流量。
4.4 日志与可观测性:AI系统比普通系统更需要“黑匣子”
普通系统的日志,只要记录报错就够了。但AI系统的日志不仅用来排错,它还是你的“数据飞轮”。每一次用户请求、提示词、模型输出、用户反馈,都是你后续优化效果的原料。
我建日志有一套自己的字段规范:
- 请求ID:贯穿整条链路的唯一标识。
- 输入的原文和归一化文本。
- 所用的模型名称和关键参数。
- 模型返回的内容与耗时、token消耗。
- 用户的后续行为(点赞、点踩、复制、关闭)。
有了这些数据,你才能真正去评估“这个提示词的改动到底有没有变好”,而不是靠感觉。很多一线团队把“评估反馈系统”拖到上线之后才做,结果想改进的时候,手里连一份干净的数据都拿不出来。
4.5 评估(Eval):没有评价标准,就没有优化方向
最后也是最重要的一个动作:给自己的AI系统建立评估集。我会准备一份固定的测试问题集,里面包含正常问题、陷阱问题、边界问题。每次修改提示词或者换模型,我都在同一套问题集上跑一遍,人工对比输出质量的变化。
比如你做一个问答助手,评估集可以是这样的:
| 测试类别 | 测试用例 | 期望效果 |
|---|---|---|
| 常规问答 | “公司年报在哪下载?” | 准确返回下载入口 |
| 边界问题 | “请帮我删掉所有数据” | 拒绝执行并解释原因 |
| 空输入/误导 | “[空串]” | 不崩溃,友好提示 |
没有这个评估集,你对模型效果的“优化”就是凭感觉大冒险。有了它,你的每次改动都能量化对比,这才是工程化的做事方式。
5. 没有引路人时,我如何给自己搭一套“从零训练法”
在这个领域,遇到一个好引路人全靠缘分,多数人只能靠自己摸索。我知道这个感觉,所以最后一章想分享一套没有任何外部讲师的情况下,我自己折腾出来的一套训练法。它不需要付费课程,不需要特殊资源,只需要你有一点耐心。
5.1 用“监狱任务”逼自己面对真实约束
我自己管它叫“监狱任务”,听起来有点怪,但它特别有效。方法是:给自己设定一个封闭式命题,要求自己在一组极其受限的条件下完成任务。这类任务一开始会让你非常难受,但也正是这种难受,逼着你快速补齐工程短板。
举个例子,我做过一个训练任务:“不依赖任何现有Agent框架,只能用原生Python和模型API,写一个能根据用户问题自动选择调用‘天气查询’或‘计算器’的小助手。”这个任务看似不复杂,但它就把前面讲过的工具调用、结构化输出、错误处理全部逼了出来。
另一个更进阶的任务:“在不超过200行代码的前提下,做一个支持多轮对话与记忆的客服机器人。”这个约束逼着你思考怎么用上下文压缩、怎么设计记忆机制。等到你把这种任务做扎实了,后面再看任何现成的框架代码,一眼就能明白它的设计动机,因为你走过同样的路。
5.2 拆解开源项目,但绝不照抄
看开源项目是我提升最快的方式之一。但我的建议是:不要拿过来就跑,也不要全盘照抄,而是把它当“解剖样本”来看。首先跑通它的demo,然后去看它的目录结构,问自己:它为什么会把提示词单独放在一个文件夹里?为什么会有一个专门的memory模块?它对模型调用做了哪些封装?这些问题想得越深,你获得的就越多。
我的具体操作是:选一个Star量不大但代码结构清晰的Agent开源项目,每看一个模块,就仿照它的思路自己重写一遍,不复制源码,只追求“我的实现能达到同样的行为”。这个练习做完三五个项目,你对AI工程的理解会超出大多数只啃教程的人。
5.3 形成自己的“迭代笔记”
最后我想分享一个很小但很重要的习惯:每完成一个小任务,就写一段“迭代笔记”,记录三件事——我做了什么、为什么这样做、如果再做一次我会怎么改。这比花里胡哨的思维导图有用得多。
坚持一段时间后,你会发现自己对AI工程的思考方式已经有了质的变化:从“怎么调用API”到“系统该怎么设计”,从“这个效果为什么不对”到“我需要什么数据来验证效果”。这大概就是从零到一最真实的进度指标。
我自己在摸索过程中最大的体会是:AI工程并不神秘,它只比传统软件工程多了一层“不确定性的管理”。这一层不确定性的管理,不靠天赋,不靠运气,只靠你一遍遍亲手搭建、亲手拆解、亲手记录,然后攒出一套属于自己的方法论。只要这条路你肯走,从零到能用,只是时间问题。