☰
AI工程从零开始:大模型原理到落地全复盘
2026/11/4 10:21:42 网站建设 项目流程

ai-engineering-from-scratch,这个项目标题我看着特别亲切,因为它基本就是我过去这一年半折腾AI的真实路线图。字面意思是"从零开始的AI工程",但真正动手走过一遍以后你会发现,它不只是一个技术话题,更是一套完整的方法论:怎么拆问题、怎么搭工具链、怎么把模型从原理到落地跑通、怎么在踩坑之后形成自己的判断力。

这篇文章就是基于我这次项目经验做的一次完整复盘。我会把整条路线拆开讲清楚:为什么我建议你从最底层的原理和最小实现入手,而不是直接套框架;提示工程(Prompt Engineering)、AI Agent、数据与微调、AI辅助编程与工作流这些能力分别在解决什么问题;每一步实操里面有哪些关键参数和决策点;以及那些我真正踩过、你大概率也会遇到的坑。

内容会比较长,但你可以把它当成一份地图来用。不管是刚转行想入门的同学,还是已经在做AI产品但觉得根基不够扎实的工程师,这条"from scratch"的路线应该能给你一个相对完整的坐标系。

1. 重新理解"从零开始":AI工程到底在学什么

1.1 拆解标题:ai、engineering、scratch这三个词各是什么

先把这个标题的三个关键词拆开看,因为它们合在一起的时候,容易被误读成一句口号,但实际含义非常具体。

ai在这里不是什么泛泛的"人工智能",而是当前这轮AI能力里最核心的一组技术栈:大语言模型(LLM)、提示工程、检索增强生成(RAG)、智能体(Agent)、微调和部署。这些能力不是彼此孤立的,它们之间是有依赖关系的。我的理解里,大模型是底座,提示工程是与底座交互的接口,Agent是任务调度的外壳,RAG和微调是为特定场景补充知识的手段,而部署评测让前面这些真正进入产品闭环。

engineering是整条路线里最容易被人忽略、但价值最大的部分。工程意味着什么?意味着你可控、可复现、可度量、可维护。写一个demo让GPT输出一段漂亮的文案很容易,但你要让它稳定地输出JSON、你不给示例它也能按格式返回、你改了prompt之后不会影响其他场景,这就需要工程能力。工程还意味着你要会做版本管理、写单元测试、搭评测集、观察token消耗、排查上下文溢出。很多从算法转过来的人都把精力放在调loss上,但真正到了产品环境,数据清洗、错误处理、接口稳定性、成本控制,这些"脏活累活"才是决定项目成败的部分。

scratch是这条路线最鲜明的态度:不依赖黑盒API、不直接套现成框架、不跳过推导过程。我个人的经验是,如果你只用OpenAI的API调个聊天接口,或者直接拿LangChain串个流程,你很难建立起真正的判断力。因为出了问题你根本不知道是模型的问题、是上下文的问题、还是框架封装的问题。从scratch开始,意味着你要亲手实现一个微型GPT、写一个没有LangChain的Agent循环、自己用requests调模型接口而不是用封装好的Client。这个过程看似绕远路,但它是建立"模型直觉"最快的途径。

1.2 为什么"from scratch"路线能避开90%的学习误区

现在市面上绝大多数AI入门教程都在教你怎么更快地"用"AI,而我更关心的是你怎么"驾驭"AI。这两者的差别非常大。

用类比来说,学开车只需要学会踩油门、打方向盘就够了,但学修车你必须打开引擎盖、看懂每个部件怎么配合。AI工程更像是修车,而不是开车。

如果你第一次接触Agent就直接用LangChain,你看到的是一堆抽象概念:Chain、Tool、Memory、Callback。你按照文档把代码粘过来跑通了,但你根本不知道当LLM调用一个工具失败时是为什么,你不知道Agent内部为什么偶尔会陷入死循环,你也不知道上下文窗口是怎么被一点点塞满的。这些问题,只有当你从底层自己写过一遍Agent循环之后,才能真正理解。

我自己就有一个特别直观的体会:我一开始也是从LangChain入门的,当时觉得这东西真好用,几行代码就能串起一个Agent。但后来我遇到一个诡异的问题——Agent在特定场景下反复调用同一个工具,导致token消耗爆炸,表现像死循环一样。我排查了半天,文档翻了个遍,最后还是自己写了打印日志的中间件才定位到原因。说白了,框架帮你封装了99%的复杂性,但剩下的1%问题出现时,你要面对的却是100%的复杂度。

而当你自己从零写过一遍Agent之后,再回头看LangChain,你会清晰知道它帮你做了什么、哪些抽象是好设计、哪些抽象是在给你制造黑盒。这就是from scratch路线的核心价值:你花了两三倍的初期学习成本,换来的却是长期的高倍判断力和排错效率。

1.3 这条路适合谁,不适合谁

必须诚实地说,不是所有人都适合从scratch开始。我身边有朋友只是想用AI工具提升工作效率,那完全没有必要走这条路,直接用好现成产品就完了。但如果你是想做AI应用开发、算法工程、或者想深入理解智能体产品背后的技术逻辑的人,那这条路线基本是绕不开的。

我的判断标准是这样。你最好已经有基础的Python能力,至少看过源码,不是只会写脚本调库;你愿意花时间去读论文和源码,而不是满足于stuck在"能跑通就行"的水平;你有耐心做实验并记录结果,因为AI工程里很多东西是经验性的,你得积累自己的"手感"。

反过来,如果你想一周之内就做出一个能见人的产品demo,那这条路确实不适合你。从scratch意味着前期大量时间花在看不出成果的地方,没有任何正反馈,很容易劝退。我的建议是:你可以主线走from scratch,但副产品开发同步进行——比如一边学习一边用学到的知识搭个小工具,这样既有长期积累,又有短期激励。

我自己的背景算是从推荐系统工程师转过来的,数学基础还行,Python熟练,但对深度学习的理解停留在调包层面。我走完这条路大概花了四个月时间,平均每天两三个小时,算是一个正常速度。如果你基础比我好或者时间投入更多,可以压缩到两个月左右。

2. 设计自己的学习路径:从大模型原理到第一条提示词

2.1 先搞懂大模型的工作机理:从Tokenizer到注意力机制

任何"from scratch"的路线都逃不开一个核心问题:你手里这个模型到底是怎么工作的?说实话,不需要你推导完整的Transformer反向传播公式,但你得知道数据流是怎么走的。

大语言模型的工作流程可以拆成四步。第一步是Tokenizer,把文本切成token——可以理解成把一句话拆成一个个语块,中文里通常一个token不是一个字而是一个词或者一个常见词组。第二步是Embedding,每个token变成一个向量,也就是在高维空间里的一个坐标。第三步是Transformer层,这是最核心的部分,每一层都用自注意力机制(Self-Attention)让每个token"看到"它前面所有token的信息,不断更新自己的向量表示。第四步是输出层,根据最后一个位置的表示,预测下一个token的概率分布。

如果你要我给一个生活化类比,我会说:大模型是一个在"高维语义空间"里玩的接龙游戏玩家。它读到你给它的一堆上文,然后根据它所见的上下文,在所有可能的下一块词里按概率挑选一个。注意,是概率,不是确定的唯一答案。这就是为什么"温度"(temperature)这个参数会影响输出的多样性——它本质上是在调整这个概率分布的"锐利程度"。

理解这个机理对后续一切工作都有用。比如你做提示工程的时候就知道:模型的推理能力受限于它提前训练好的知识,但它对上下文的利用是动态的,你给的上下文越清晰,它的"接龙"方向就越准确。再比如你之后遇到"AI在长对话中变笨了"的问题,根源其实就是注意力机制对长上下文的"稀释",这不是玄学,而是数学上的必然。

2.2 搭建最小工具链:Python环境与常用库清单

明白了原理之后,下一步就是搭建一个真正能干活的开发环境。我不主张第一个星期就上重型工具,一个干净的Python环境加几个库就够了。

Python版本我建议直接上3.10以上。编辑器的话,VS Code加上Python插件就够用,当然如果你做AI编程提效,也可以试试JetBrains家的PyCharm加AI插件,后面我会专门说这个。

核心库清单我列一下,你自己对照着装就行。torch是深度学习基础库,后面训练微型GPT要用;transformers是Hugging Face的模型库,统一了众多开源模型的加载和推理接口;datasets用于数据集的加载和预处理;peft是做LoRA等参数高效微调的工具库;vLLM是高性能推理服务框架,本地部署大模型时效率极高;ollama则是一个极其友好的本地大模型运行工具,一行命令就能跑起Llama或Qwen的量化版,适合日常试验。还有一个轻量级的HTTP库,requests或httpx,用来直接调用模型服务。

很多教程会推荐你一开始就装LangChain,我的建议恰恰相反:先不装。或者装了也先别用。等你把Agent的底层循环自己写一遍之后再回来用它,你会发现LangChain只是一个"脚手架",而不是"承重墙"。

硬件方面说一下我的经验。如果你手头有NVIDIA显卡,哪怕只有8G显存,也足够跑7B量级的量化模型做实验了。如果没有显卡,就用云GPU或者直接调用API,先把流程跑通,等做微调实验时再去找卡。不必因为硬件焦虑而耽误学习进度。

2.3 第一个动手项目:用300行代码训练一个微型GPT

讲完原理和工具,第一件正事来了。我强烈建议你的第一个项目是:从零实现并用一个很小的数据集训练一个字符级GPT。这一步是整个route中性价比最高、最值得投入的部分。

参考的经典实现是Andrej Karpathy的minGPT,以及他更简化的nanoGPT。我自己动手时做了个简化版,核心结构大概这么几块:一个字符级的Tokenizer、一个Embedding层、一个Transformer层(我用了两层而不是六层)、一个输出层,用交叉熵损失函数训练。关键在于,300行左右代码就能完成一个可训练、可生成文本的完整模型。

import torch import torch.nn as nn from torch.nn import functional as F class MultiHeadAttention(nn.Module): def __init__(self, n_embd, n_head, block_size, dropout=0.1): super().__init__() self.n_head = n_head self.n_embd = n_embd self.key = nn.Linear(n_embd, n_embd) self.query = nn.Linear(n_embd, n_embd) self.value = nn.Linear(n_embd, n_embd) self.proj = nn.Linear(n_embd, n_embd) self.register_buffer("mask", torch.tril(torch.ones(block_size, block_size)) .view(1, 1, block_size, block_size)) self.dropout = nn.Dropout(dropout) def forward(self, x): B, T, C = x.shape k = self.key(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) q = self.query(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) v = self.value(x).view(B, T, self.n_head, C // self.n_head).transpose(1, 2) att = (q @ k.transpose(-2, -1)) * (C // self.n_head) ** -0.5 att = att.masked_fill(self.mask[:, :, :T, :T] == 0, float("-inf")) att = F.softmax(att, dim=-1) att = self.dropout(att) y = att @ v y = y.transpose(1, 2).contiguous().view(B, T, C) return self.dropout(self.proj(y))

这段是我当时的简化实现里多头注意力的一部分。注意那个mask,它用的是下三角掩码,确保每个位置只能看到它前面的token,不能看到后面的——这是自回归语言模型的核心。

训练这个模型时可以用莎士比亚文集或者任意一本中文小说,字符级别建模。我把数据集切成固定长度序列,比如每个训练样本128个token。训练到损失值不再下降后,你就可以采样生成文本了。那种看着模型从初始的随机乱码慢慢输出像句子一样片段的时刻,是整个学习过程中最震撼的瞬间之一。

为什么说这个项目价值极高?因为你在训练过程中体验到的所有现象——损失下降的曲线、过拟合的出现、采样时温度对输出风格的影响、上下文长度对生成质量的影响——全部都是你在调试任何大模型时会遇到的问题的缩小版。有了这个微观直觉之后,你再面对动不动几百B参数的大模型时,就不会觉得它是个神秘黑盒了。

3. 核心技能一:提示工程实战,从风格控制到结构输出

3.1 提示词的底层逻辑:模型是推理引擎,不是数据库

学会了基础原理之后,你就要开始和大模型频繁打交道了。很多人把提示工程理解成"怎么说话模型才听",这当然没错,但太浅了。我的理解是:提示词是你对大模型进行"运行时配置"的唯一接口。

什么意思呢?大模型的参数在训练完成后就冻结了,它内部的知识和推理模式已经固定。但同一个模型,你可以通过不同的提示词,让它的行为模式产生巨大差异。你给它的角色、任务描述、约束条件、示例、上下文资料,组合起来决定了模型在推理时"激活"的是哪部分能力。

这里要澄清一个巨大的误区:很多人把大模型当成数据库,以为它"记住"了互联网上的所有资料,所以会问它"XX知识是什么",当它答错时就觉得模型不行。但实际上,大模型更像是一个"推理引擎"——它擅长的是根据你提供的上下文进行模式匹配和逻辑推导,而不是精确检索知识。所以你在写提示词时,要把模型当成一个能力很强但容易走偏的新人实习生来对待:它知道大概怎么做,但你得把任务边界、输出格式、约束条件交代清楚,它才能交出一份稳定可用的结果。

这个认知改变了我之后所有的prompt设计方式。我不再寄希望于让模型"回忆起"某个细节,而是尽量把必要的信息直接放进提示词里,然后让它基于这些信息做推理和整合。

3.2 一套完整可复用的提示词模板:角色、任务、约束、示例

我经过大量实验后,总结出一个四要素提示词模板,基本覆盖了80%的需求场景。这个模板不是固定格式,而是一种思考框架:角色(Role)—任务(Task)—约束(Constraint)—示例(Example)。

角色定义给模型一个"身份",让它在行为方式上更贴近你想要的调性。任务描述要具体,包含输入是什么、要做什么处理、输出什么。约束条件限定输出格式、长度、语气、禁止事项。示例是最重要也最容易被忽略的,用一两个示例告诉模型"我期望的输出长这样",比任何文字描述都有效。

我给你一个我实际用过的例子,用来从非结构化文本中抽取结构化信息:

角色:你是一个严谨的数据抽取引擎,从不臆造字段值。 任务:从用户输入的对话文本中抽取以下实体:地址、日期、问题类型、紧急程度。 输入文本用<text>标签包裹。 约束: 1. 输出必须是JSON格式,且只输出JSON,不要输出任何解释性文字。 2. 如果某个字段在文本中没有出现,输出null。 3. "紧急程度"只能是low、medium、high三选一。 4. 所有值必须是原文中的字符串原样,不能改写。 示例: 输入:<text>我们今天下午两点在A栋会议室碰一下吧,顺便讨论一下登录报错的问题。</text> 输出:{"address": "A栋会议室", "date": "今天下午两点", "problem_type": "登录报错", "urgency": "medium"} 输入:<text>{user_input}</text> 输出:

注意到关键点了吗?我用了<text>标签包裹输入,防止输入内容干扰指令解析;给出了具体的格式约束和值域;示例部分演示了"I want it look like this"的输出形态。这样一套提示词,我用在开源模型上也能获得相当稳定的输出。

这里还有一个核心技巧:如果模型偶尔输出不稳定的JSON,不要急着改prompt,先试两个参数——降低temperature到0.1或更低,同时把示例数量增加到3个以上。很多所谓的"模型不会用JSON",其实是因为采样温度太高导致输出发散。

3.3 提示词调试的四个层:格式、示例、温度、上下文

把提示工程做成"工程",你就要有系统的调试方法,而不是出了问题就瞎改prompt。我把调试过程分成四个层次,从上到下依次排查:

第一层是格式问题。模型输出的格式不符合预期,比如漏了字段、多了换行、JSON解析失败。这种情况优先检查约束条件是否写得足够死。我在约束里会写"只输出JSON,不要输出任何解释性文字",但有时候还是不行,那就得加上更外部的保障,比如用函数调用(function calling)机制,这比纯提示词更可靠。

第二层是示例问题。模型理解了格式但产出内容老跑偏,比如抽取任务中歪曲了原文含义。这通常是示例不够或者示例边界不清晰。我的经验是至少给三个示例,覆盖正常情况、边界情况、需要输出null的情况,模型才能学会完整规则。

第三层是温度问题。温度决定了输出的随机性。这是我在工程里变化最频繁的参数,不同任务的推荐值差别极大。我自己常用的参考值是这样的:

任务类型推荐温度原因
代码生成、SQL生成0 - 0.2需要确定性,不能容忍随机性
JSON结构抽取0 - 0.3严格格式要求
客服/意图分类0.3 - 0.5需要稳定但可以有一点灵活性
创意文案、头脑风暴0.7 - 0.9鼓励多样性,减少重复
故事创作、角色扮演0.8 - 1.2追求创造性表达

第四层是上下文问题。如果以上都调好了模型还是不行,那很可能问题不在提示词本身,而在于你的上下文里缺少关键信息,或者信息量太大导致模型被干扰。这时候你要考察的是:是不是该用RAG补充知识库?上下文是不是太长了需要摘要压缩?用户的输入是不是有歧义需要澄清。

3.4 实战案例:从对话流中稳定抽取结构化信息

我拿一个真实项目来说。当时我在做一个客服工单自动分类系统,输入是用户和客服的聊天记录,输出是要生成工单的字段:用户ID、问题描述、紧急程度、涉及产品、是否已经解决。

最开始我写了一个特别简单的prompt:"请从对话中抽取以下信息并以JSON格式返回",结果模型经常漏字段、甚至自己编造内容。被逼到没法子之后,我按照上述方法开始系统调。

第一步改格式约束,把每个字段的类型明确写死,并加了"只输出JSON"的限制。第二步加示例,先是两个正常对话示例,又加了一个"用户ID在对话中未出现,必须输出null"的边界示例。第三步把temperature降到0.1。然后再跑了100条对话样本,字段抽取准确率从最初的67%直接升到了94%。

最后一步的理解对整个方案影响最大:那剩下的6%错误,几乎都发生在对话特别长的case里。模型不是抽错了,而是信息湮没在大量冗余文本里,注意力被稀释了。解决办法是先用一个摘要Agent把长对话压缩成500字以内的脉络概括,再做信息抽取。这个"先压缩再抽取"的两阶段方案,最终把准确率推到了97%以上。

这个案例非常好地说明了提示工程不是"写好一句话"那么简单,它是一套关于格式、示例、参数、上下文的系统工程。而这套方法论,正是AI工程里最基础也最实用的部分。

4. 核心技能二:AI Agent与工具调用,从单轮到多步任务

4.1 Agent是什么:从单轮对话到多步任务调度

当你能稳定控制单次模型输出之后,下一个跃迁就是让模型自主完成任务——这就是AI Agent(智能体)干的事。

通俗理解,Agent就是"大模型 + 工具 + 循环"。大模型负责理解任务、做出决策,工具(比如搜索引擎、计算器、代码执行器、数据库查询函数)负责让模型具备模型本身不具备的外部能力,而循环让整个过程可以持续迭代:模型根据当前的观察(Observation)产生下一步行动(Action),拿到工具结果后再做新的判断,直到完成最终目标。

为什么Agent是AI工程里最激动人心但也最容易翻车的地方?因为从单轮对话到多步任务,引入的不只是流程复杂度,还有不确定性。你要面对模型做出错误计划、在循环里打转、模型幻觉式地声称"工具已经执行成功"等一系列问题。工程化Agent的核心,就是给这些不确定性设计约束和护栏。

当时我做了一个很出圈的功能:"让AI替我读完一份50页的行业报告并生成PPT大纲"。单轮对话是做不到的,因为你得先拆页、再摘要、再串联观点、再结构化输出。这个过程有清晰的任务拆分,非常适合Agent来做——一个规划节点负责拆任务,一个检索工具负责读取指定页内容,一个摘要节点负责提炼,最后一个生成节点负责组装。

4.2 用代码实现一个最简单的ReAct Agent

我在学习Agent时,强烈建议先不要用LangChain,而是用几十行代码自己实现一个ReAct风格的Agent。ReAct的核心框架是"思考—行动—观察"循环:大模型先输出它会怎么思考(Thought),然后决定调用哪个工具(Action),拿到工具返回的结果(Observation),再继续思考。整个过程就是一个while循环。

我这个实现的核心逻辑大致是这样的:

import json import requests TOOLS = { "calculator": { "description": "用于执行四则运算,输入是一个数学表达式字符串,例如 '1+2*3'", "execute": lambda expr: str(eval(expr, {"__builtins__": {}}, {})) }, "search_web": { "description": "用于搜索百度百科或维基百科的简要内容,输入是搜索关键词", "execute": lambda query: fetch_wiki_summary(query) }, } def run_agent(user_input: str, max_steps: int = 8): messages = [{"role": "system", "content": "你是一个能使用工具的智能体。你可以在思考后输出Action JSON调用工具。" "每次收到Observation后继续思考,直到给出Final。" '输出格式:{"thought": "...", "action": "tool_name", "input": "..."} ' '或最后输出 {"final": "..."}'}, {"role": "user", "content": user_input}] step = 0 while step < max_steps: step += 1 response = call_llm(messages, temperature=0) content = response.strip() # 如果模型给出最终答案,结束循环 if '"final"' in content: return extract_final(content) # 否则解析出工具调用 try: parsed = json.loads(content) tool_name = parsed["action"] tool_input = parsed["input"] except Exception: messages.append({"role": "assistant", "content": content}) messages.append({"role": "user", "content": "输出格式无法解析,请严格按照JSON格式输出。"}) continue tool = TOOLS.get(tool_name) if tool is None: result = f"错误:未找到工具 {tool_name},可用工具有:{list(TOOLS.keys())}" else: try: result = tool["execute"](tool_input) result = str(result)[:300] except Exception as e: result = "工具执行失败:" + str(e) messages.append({"role": "assistant", "content": content}) messages.append({"role": "user", "content": "Observation: " + result}) return "达到最大步骤,任务未完成。"

这个代码非常粗糙,有很多可以优化的点,但它就是Agent的本质。你发现没有,整个流程里,模型只是负责"思考下一步做什么"和"生成工具输入参数",真正的执行力来自你写的工具函数。这个过程你自己跑一遍之后,再去看LangChain中Agent相关源码,会瞬间有种"原来如此"的通透感。

关于Agent工程化有一个概念值得提一下:Harness(外部工程结构)。它指的是围绕模型智能而构建的操作系统:包括工具定义、权限控制、循环逻辑、记忆管理、错误恢复、安全护栏。通俗讲,模型是CPU,harness就是主板、内存、外设和电源管理。你在工程中真正花大力气的不是"让模型更聪明",而是设计一个稳定的harness让模型的聪明能够被安全地释放。

4.3 Agent踩坑记录:死循环、工具幻觉、上下文爆炸

我做了近一年的Agent应用,如果用一句话总结踩过的坑:Agent的失败模式比成功模式多得多,而且都很有规律。

第一个最典型的坑是死循环。模型在某个环节反复调用同一个工具,中间没有任何改变。我见过一次实际的例子:Agent要汇总几个网页内容,结果它反复调用search_web搜索同一个关键词,看起来像是在"确认",但实际上已经无意义消费了几万token。解决办法有三个,一是设置max_steps硬性上限,二是连续N次调用同一个工具且输入参数相同就强制中断并让模型"换个策略或直接停止",三是在prompt里明确"如果已经获得足够信息,直接输出最终答案"。

第二个坑是工具幻觉。模型会声称自己调用了某个工具并且获得了结果,但实际根本没有。这在早期不设函数调用机制、纯文本填充的Agent里非常常见。模型为了符合"行动-观察"的格式,编造了一个不存在的Observation。解决办法是:不要让模型的输出直接作为最终结果,工具观测记录必须来自真实工具执行的结果,然后由代码返回给模型,不能由模型自行声明。

第三个坑是上下文爆炸。Agent一旦跑多轮,每一步的thought和observation都会留在上下文中,任务稍长就会塞满上下文窗口。表现为模型到后期越来越"笨",甚至出现答非所问。我的处理方法分两步:一是把中间过程做摘要,每N轮就把之前的对话压缩成一段Summary;二是只把"关键结果"保留在上下文中,中间的计算过程直接丢弃,原始记录写到日志文件里做审计。

这些坑不是我一个人踩的,几乎所有做Agent的同行都会经历。它们的共性是:Agent的能力上限由模型决定,但体验下限由工程决定。你设计再多优雅的推理框架,都不如那几行硬编码的循环中断条件来得有用。

5. 工程化的完整闭环:数据、微调与评测

5.1 高质量数据:比模型大小更重要的因素

如果说提示和Agent是"外挂"能力,那数据和微调就是在"重塑"模型能力。而我从一开始就有一个很确定的态度:数据质量 >> 模型大小。这几乎是当前行业公认的经验结论。

在数据层面,我不是去做几十亿条的大规模预训练,而是聚焦在"让模型学会某个特定行为模式"的任务上。比如我想让模型从冗长的客服对话中自动判断退款原因分类,或者让模型学会特定风格的报告写法。这些任务,几千到几万条精心构造的数据就能带来明显变化。

数据准备的关键动作其实是清洗和构造。清洗是去掉重复样本、错误标注、格式混乱的样本;构造是根据任务定义,把输入输出对写好,保证每类case覆盖均匀。我特别想强调的是边界case的覆盖——模型在训练中见过"应该输出null"的样本,它部署时遇到未知信息就知道输出null,而不是强行编造一个值出来。很多微调项目数据量很大效果却差,问题就出在数据偏向"正常样本",边界样本极少。

有一个数据技巧很实用:不要只收集"模型原本就能做对"的数据,也就是从模型产出的结果里抽高质量样本,你还要人工构造"模型原本会做错"的样本放进去。这样模型才能学会纠正自己的错误,而不是躺在舒适区里。

5.2 什么时候需要微调,什么时候不需要

不少初学者动不动就想着微调,但实际上微调不是万能的,而且成本不低。我在项目里一般按一个标准来判断:优先用提示词和RAG解决,解决不了再用微调。

我把常见场景整理成了一个决策表,你遇到类似需求可以直接套:

场景推荐方案原因
需要模型回答私有知识库内容RAG(检索增强生成)知识变化频繁,需要实时更新
需要模型学会特定的输出格式和风格提示词 + 少量示例大模型本身具备格式能力
需要模型掌握私有领域术语提示词 + RAG 补充术语表术语可以动态维护
需要通过大量特定语料学会一套行为规律微调提示词难以承载海量规则
需要降低token成本和延迟,缩短提示词长度微调把知识行为固化到权重中
推理性很强的通用任务换更强基座模型微调无法提升原生推理能力

当时我做一个"合同关键条款抽取系统",第一版用了很长的提示词,上下文塞了各种示例,效果不错,但每次请求都要消耗4000多token,成本实在扛不住。后来我用微调方案,构造了大约8000条"合同原文—抽取结果"的训练样本,用LoRA在7B开源模型上跑了几小时。效果上来了,而且请求时可以大幅压缩prompt,token消耗降到原来的十分之一左右,速度和成本同时改善。

5.3 评测体系:无法量化就无法优化

微调和提示工程都有一个共同的大难题:怎么判断自己做得更好了?我见过太多人的AI项目,改了一版prompt或者微调了一轮,怎么评估效果完全靠"感觉变好了",这在一开始还能糊弄,但到了生产环境必然翻车。

所以从第一个项目开始,我就在搭评测集。做法其实不复杂:每次迭代都固定一个包含几百条样本的验证集,跑一遍任务,计算指标。对于结构化抽取任务,我用字段级的精确匹配率;对于生成类任务,除了人工抽检,我还可以用LLM-as-a-judge让一个更强的模型给输出打分;对于分类任务,用准确率和召回率。

具体到工具体系,我常用的开源方案是OpenAI的evals框架,但它对开源模型场景没那么友好。我更推荐你自己维护一个评测脚本:读取验证集,调用模型,把结果存在csv文件里,然后按定义好的口径做统计。重点是口径要一致,否则每次改版都换评测方式,那你的所有AB对比都是无效的。

我踩过一个很有意思的坑:我在微调模型后跑评测集,准确率从90%升到了93%,很高兴。结果上到线上测试之后,发现实际场景的准确率反而下降了一个多点。后来一查,原来我的评测集里训练时见过的样本占比过高,这属于"评测集泄漏"。我补救的方法是:评测集严格和训练集隔离,并且定期从线上真实数据里抽一批重新标记后加入评测集。从那以后我就养成了一个习惯,每次做完数据清洗和模型迭代,第一步先检查评测集是否与训练数据有交集。

6. 工程落地与提效:AI编程、工作流与生产力

6.1 用AI辅助编程:从IDE插件到日常开发流

当你在主线路上积累了足够的模型直觉后,你会发现你的日常工作效率也会被显著抬升——特别是编程环节。AI编程从一个玩具变成了我每天离不开的脚手架。

我常用的方式是IDE里的AI插件。如果你用VS Code的话,GitHub Copilot和Continue这两个插件都不错;如果你用PyCharm的话,它自带的AI Assistant插件也很好使。但我的核心体会是:AI编程的价值不取决于工具多智能,而取决于你怎么拆分任务、怎么检查验收。

我自己写的代码里,AI辅助生成的比例大概在50%到70%之间。具体怎么做呢?像这种需求:"写一个函数从JSON里读取配置并校验必填字段",这种边界清晰、模式固定的代码,让AI直接生成,我再快速检查就完事了。但像"设计一个多Agent协作的编排逻辑"这种偏架构的任务,我会自己去想清楚数据流和接口,然后让AI去补实现细节。AI做得好的永远是"带约束的实现",它做得差的永远是"需要创造性决策的设计"。

一定要有一个代码审查的习惯。AI生成代码最大的隐患就是表象正确但深处有坑——比如异常处理缺失、边界条件没覆盖、异步逻辑里锁的问题。你把AI代码接进业务主线之前,至少扫一遍关键路径。有一次我让AI生成一段批量调用的并发代码,它对每个批次做了try-except,但把错误吞掉后没有做重试也没有记录日志,导致生产环境出问题时根本排查不到原因。这种坑不踩一次真的不会长记性。

6.2 搭一套个人AI工作流:把常见的琐事外包给Agent

AI工程能力最直观的幸福感来自于搭一套自己的工作流,以前需要两三个小时处理的琐事,现在让Agent代劳。

举我自己常用的一个例子。我每周都要处理几十个咨询邮件,需要总结出待办事项、分类、生成回复草稿。以前我是开着两个窗口一件件手动处理,现在我用一个简单的Python脚本连接大模型,自动执行一个固定的流水线:读取邮箱新邮件→对每封邮件做分类和要点抽取→把重复类型的邮件合并→生成回复草稿→输出一个待办清单。脚本IO的活全是Python干的,理解分类和拟草稿的活是大模型干的,Agent比人靠谱在不会漏邮件、不会烦躁、格式永远一致。

更复杂的工作流则涉及多Agent协作。比如我用AI做过一个自动生成短视频脚本的流程:一个Agent负责搜索话题素材,把零散线索组织成大纲;第二个Agent不看大纲直接写分镜文案,用口语语体,控制字数在500字内;第三个Agent根据文案配画面提示词和背景音乐建议。这里每个人类同事有各自的职责边界,Agent之间通过文件格式传递中间结果,这样每步单测起来都方便。

顺着这个思路扩展,其实AI短剧、AI漫剧这类产品的生产链路也是一样的逻辑:脚本生成、分镜拆解、素材检索、渲染合成。最底层的工作流编排就是一套多Agent协同系统,哪一环用模型、哪一环用工具、哪一环需要人工审核,都是工程决策。我当时玩过几个短剧脚本生成的实验,核心其实就是把一个复杂的创作任务拆成多个有明确输入输出格式的节点,每个节点靠一个Agent或一个固定规则完成,串成一条流水线。

6.3 把项目放进生产环境:推理速度、成本优化与安全护栏

最后一个环节,也是最容易被忽视的:怎么把AI能力安全稳定地跑在生产环境里。

推理服务选型,如果项目基于开源模型,那本地推理服务优选vLLM,它靠PagedAttention、连续批处理这些机制可以把吞吐做到很高的水平。如果模型太大显卡带不动,就用API服务加缓存,至少把常用且结果确定的问题从模型请求改成缓存命中。

成本优化是工程里躲不开的KPI。token费用一涨起来,产品利润瞬间消失。除了我刚才提到的用微调压缩prompt长度之外,还有几个经验:一是可以先把输入做摘要再发给模型,长文档全文灌给模型是成本黑洞;二是尽量让模型输出短答案,把"回答不超过50字"直接写进约束;三是在Agent的循环里随时检查累计token消耗,一旦超过阈值强制收尾。成本优化永远是"需求变体":产品上让用户少一点无意义消耗,工程上让每token更值钱。

安全护栏方面,至少要做的有三件事:输入侧脱敏——用户数据和私人信息不能原样发给外部模型服务;输出侧过滤——检查模型输出中是否包含非法内容、恶意链接、个人隐私泄露;行为侧约束——Agent能调用的工具要做白名单控制,不能让Agent的权限超出业务所需。这个跟DevOps里面的最小权限原则完全一致。

我当时用自己的AI工作流时,一直坚持让代码里所有涉及模型调用的模块都走同一个封装层,方便随时加监控、日志、限流。没有这个封装层,等出问题再补救就非常被动了。

7. 常见问题与排错实战手册

7.1 高频问题速查表

我把这一年多来在实际项目里遇到的高频问题整理成一张速查表,排查时可以直接对着看,省掉很多重走弯路的时间。

问题典型现象排查思路与解法
输出总是带多余文字要求JSON却输出markdown代码块降低temperature;在prompt里显式声明"只输出JSON,不要markdown标记";或用function calling作硬约束
JSON偶尔解析失败同一批样本80%成功20%失败检查示例边界是否覆盖了null和缺失字段;用JSON Schema校验并在失败时重试一次;降低temperature
Agent死循环反复调用同一工具且参数不变设置最大步骤数;连续N次同工具同参数直接强制终止并提示模型换策略
长对话后模型变笨前几轮正常,后面答非所问上下文窗口溢出;做中间摘要替换历史;丢弃不关键的mid-process记录
检索结果对回答没帮助RAG召回了一堆无关内容用重排序模型过滤召回;提高top-k召回数量再重排;也可能query本身太模糊,先做query改写
微调后评测集中效果蛮好一到线上效果反而下降检查评测集是否有训练泄漏;引入线上真实样本做回测;增加边界样本在评测中的占比
模型总是偏离事实生成了看起来合理但错误的内容加强输入侧事实材料的完整性;在prompt里要求"只根据提供的资料回答,不自行补充";对高风险场景增加人工抽检环节

7.2 我踩过的几个特别的坑,以及我学到的

挑几个我觉得非常值得展开的坑,回忆一次踩一次都有收获。

第一个坑是评测集自己污染了自己。我在做文本分类微调的时候,把数据拆成了训练集和评测集,但粗心地在移除重复样本之前就开始拆分,导致同一句话的两个近似表述一个在训练集一个在评测集。评测指标虚高得很离谱。后来我专门写了一个基于文本向量相似度的去重脚本,保证交叉集相似度低于阈值后才进入评测。从那以后任何评测代码,第一步永远是先检测数据泄漏,不敢再信手搭评测集。

第二个坑是Agent的"假成功"。我当时做一个信息收集Agent,它负责去多个API拉数据然后汇总。某天日志显示Agent运行得非常顺利,每个工具调用日志都显示成功。后来我认真看了一遍数据,发现一些工具的返回其实是[],也就是空数据,但模型在汇总时很自然地说"查询到0条结果,符合预期"。这听起来很合理,但实际情况是API参数传错了,整个查询就是一个零查询。从那之后我就定了一条规矩:Agent里凡是工具返回空结果的情况,代码一律使用醒目的异常标注传给模型,并且agent若基于空结果输出"符合预期"的结论,需要额外人工确认。

第三个坑和上下文压缩有关。我在做长文档总结时,一开始直接把中间层摘要替换掉完整上下文,结果发现模型丢失了很多细节。后来我换了一种做法,摘要里强制保留"关键数据点"清单,同时保留每段结论的来源页码。这样摘要虽然长了点,但信息保真度高很多,最终总结的幻觉率断崖式下降。压缩上下文省token是好事,但省过头会造成信息灾难,关键是要在压缩过程中保留可追溯性。

7.3 学习资源清单与我的推荐顺序

最后列一下我走完这条路之后,真正觉得有价值的资源。这个列表不是要把所有论文都列出来,而是提供一个尽量不踩坑的阅读顺序。

第一梯队,打基础。Andrej Karpathy的nanoGPT源码和他的视频《Let's build GPT from scratch》,这个是目前公认最好的动手入门材料。配合李沐的《动手学深度学习》了解基本概念。语言模型方面,《The Annotated Transformer》值得精读,注意力机制的每个细节都要看懂。

第二梯队,深入大模型。强烈推荐读Transformer那篇原始论文《Attention Is All You Need》,但不用整篇精读,重点看结构图和数据流。接着读关于RLHF的经典论文《Training Language Models to Follow Instructions with Human Feedback》(InstructGPT论文),以及GPT-4的技术报告,虽然公开细节不多,但对理解能力边界很有帮助。

第三梯队,Agent与工具调用。必读ReAct论文《Synergizing Reasoning and Acting in Language Models》。我读完后最大的认知变化是:推理过程(Thought)不只是给模型自己看的,写出来反而对模型自身有正反馈。然后是Toolformer论文,以及OpenAI关于function calling的文档。如果你的Agent要处理复杂长程任务,强烈推荐看一下AutoGPT、BabyAGI的源码,但不是让你抄,而是看早期Agent的问题在哪。

第四梯队,工程实践与评测。读一下Prompt Engineering Guide项目,以及OpenAI的cookbook内容。我特别建议养成刷Hacker News上AI工程实践帖的习惯,很多踩坑经验只看论文学不到。评测相关可以看看Hugging Face的评估库文档,然后自己动手做一个评测脚本。

资源只是地图,真正的能力来自你亲手写代码、跑实验、踩坑修复的过程。尤其这第四梯队,我回顾下来,对我实际工作帮助最大的反而不是最深的论文,而是各种真实的工程案例教训——那些"原来这样也能翻车"的经验,恰恰弥补了学术文献和真实世界的缝隙。

8. 写在最后:我走完这条路线后的真实体会

回顾这段从零开始的AI工程之旅,我最大的收获其实不是会写多少代码、会调多少参数,而是建立了一套属于自己的判断体系。

以前我看到一个AI产品火了,第一反应是"这用了什么黑科技"。现在我的第一反应是"它的工程链路大概怎么拆,数据从哪里来,Agent的harness怎么设计,评测集怎么搭"。这种思维翻转,是我认为整个from scratch路线最有价值的部分。因为AI这个领域变化太快,今天好用的框架三个月后可能就没人维护了,但只要你掌握了最底层的原理和最本质的工程方法,工具怎么换都不是问题。

如果让我给后来者一条最实用的建议,我会说:别怕从最笨的地方开始。自己训练一个微型模型,自己写一个没有框架的Agent,自己搭一个最朴素的评测脚本。这些看起来"浪费时间"的事,恰恰是你未来应对复杂问题时最坚实的底气。

当你真正从零开始亲手构建了一个AI系统之后,你会发现,AI工程的门槛没有你想的那么高,但它的深度也没有任何捷径可以绕开。这条路值得走一遍,而且走完之后,你会获得一种"未来再出现什么新框架都不慌"的从容感。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询