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, dropout0.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: 用于执行四则运算输入是一个数学表达式字符串例如 12*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, temperature0) 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外部工程结构。它指的是围绕模型智能而构建的操作系统包括工具定义、权限控制、循环逻辑、记忆管理、错误恢复、安全护栏。通俗讲模型是CPUharness就是主板、内存、外设和电源管理。你在工程中真正花大力气的不是让模型更聪明而是设计一个稳定的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开源模型上跑了几小时。效果上来了而且请求时可以大幅压缩prompttoken消耗降到原来的十分之一左右速度和成本同时改善。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工作流把常见的琐事外包给AgentAI工程能力最直观的幸福感来自于搭一套自己的工作流以前需要两三个小时处理的琐事现在让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校验并在失败时重试一次降低temperatureAgent死循环反复调用同一工具且参数不变设置最大步骤数连续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源码和他的视频《Lets 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工程的门槛没有你想的那么高但它的深度也没有任何捷径可以绕开。这条路值得走一遍而且走完之后你会获得一种未来再出现什么新框架都不慌的从容感。
阅读完成 · 觉得有帮助?