AI工程这个词最近被反复提,但很多人理解成了“用AI写代码”或者“写几个Prompt调通接口”。我见过太多团队,Demo做得风生水起,一上生产环境就各种翻车:模型输出不稳定、数据质量一团糟、成本直线上升、出了问题不知道怎么排查。真正意义上的AI工程,是把模型、数据、评估、部署、运维串成一条完整链路的能力,而不是训练一个模型或者调一个接口。
如果你正在从零开始学AI工程,或者你已经会一些Python但不知道下一步该学什么,这篇文章就是为你准备的。我会从工程视角拆解AI工程的技术栈,给出一条可复现的学习路径,再用一个RAG问答系统的实战案例讲清楚核心环节,最后把我在实际项目中踩过的坑和排查思路整理成一张速查表。整体内容偏落地,不绕理论,也不讲玄学。
1. AI工程不是“写模型”,而是“造系统”
1.1 从“调API”到“工程化”的距离
很多人觉得,AI工程就是调API。拿到一个OpenAI Key,写一段Prompt,返回结果,完事。但如果只是这样,AI应用就跑不远。简单调用只考虑输入输出,而工程化必须考虑以下这些维度:
- 数据:数据从哪来,怎么清洗、怎么保证质量,是不是有标注,要不要定期更新。
- 模型:怎么在效果、成本、延迟之间做选择,怎么评估多个候选模型。
- 输入输出约束:怎么让模型稳定输出结构化内容,怎么兜底错误输出。
- 链路的可观测性:线上出问题,是数据问题、模型问题还是代码逻辑问题。
- 成本控制:调用频次、Token消耗、缓存策略、模型规模。
- 安全合规:用户输入要不要过滤,内容要不要做审核,日志要保留多久。
我自己在这个问题上栽过跟头。当时我用LangChain搭了一个看起来很漂亮的流程,Demo阶段给老板演示,效果惊艳,一连问了七八个问题都答得不错。结果放到真实用户流量下面,RAG检索返回的前几个片段全是无关内容,模型生成的摘要杂乱无章。排查了大半天,发现根因是Embedding模型选择不当,文档切分策略也没有适配原文档结构,与Prompt本身关系不大。如果不懂整条链路,这种问题根本无从下手。
这种体验让我把“工程化”的定义改成:让模型在不可控的真实环境里持续稳定地完成价值输出。写一个Demo是艺术,跑起一个产品是工程。
1.2 AI工程的技术栈全景
如果给AI工程画一个技术栈,大概长这样:
- 基础概念层:Token、Embedding、Transformer、注意力机制、温度参数、函数调用、RAG、Agent。不需要从零手写Transformer,但至少要理解输入输出、能力边界,知道什么任务适合什么技术。
- 数据处理层:导入、清洗、切分、向量化、缓存。SQL和Pandas至少要熟练一个。
- 模型应用层:Prompt Engineering、上下文组装、结构化输出、工具调用、长短记忆管理。
- 检索与记忆层:向量数据库、混合检索、重排序、索引管理、会话记忆。
- 服务与部署层:FastAPI服务、容器化、限流、重试、缓存、日志。
- 评估与监控层:测试集、自动化评估、线上指标收集、反馈循环。
这六个板块不是割裂的。工程能力恰恰体现在如何把它们连接在一起:从用户输入到最终返回,链路中的每一环都有可能出现瓶颈。比如检索质量差会表现为回答事实性错误;日志不完整会表现为线上问题无法复盘;缓存策略不对会表现为成本飙升。
1.3 为什么“从零开始”反而比“半路出家”更快
这句话可能会引发争议。毕竟行业里已经有很多懂机器学习的人。但AI工程和传统机器学习的思维模型真的不太一样。传统ML的核心是特征工程、模型训练、超参调优;而今天应用型AI工程的核心变成了上下文组织、工具调用、评估循环和产品集成。
很多算法工程师会被旧思维拖住。他们习惯“先训练、再上线”,遇到问题第一反应是换模型、加数据。但现在的很多应用问题,其实是系统问题:Prompt写得不清晰、检索召回不对、输出Schema不稳定。解决这些问题不一定要重新训练模型。
从零开始的人没有这些包袱,直接上手“数据进 → 上下文组装 → 模型推理 → 结果校验 → 反馈优化”这个闭环,反而更快建立工程直觉。当然,懂机器学习是加分项,但不需要把它当成起跑线。我见过不少从非科班转过来的朋友,因为会写脚本、会用工具、懂一点数据,再加上项目实践,三个月左右就能独立做AI原型。
2. 从零开始的分阶段学习路线
2.1 第一阶段:建立“最小必要认知”
很多新手容易陷入理论恐惧,觉得不把机器学习啃完就不能动手。真没必要。第一阶段只需要建立几个核心概念,就能正确理解后续的各种坑。
- Token:模型不是按字母读文本,而是按子词切分。比如“输入成本”可能被切成多个Token。所以上下文长度、计费方式、生成延迟都跟Token数量挂钩。
- Embedding:把文本变成一串浮点数,语义相近的向量在空间中距离更近。它是向量检索的基础。
- 自回归生成:模型逐个预测下一个Token。理解这点就知道为什么输出有随机性,为什么Temperature参数会影响结果。
- Prompt Engineering:通过输入文本组织来引导模型输出的技术。它不是几句“魔法话术”,而是清晰、结构化、带约束、带示例的工程行为。
这个阶段一到两周足够。如果你已经会Python,甚至可以压缩到三四天。核心不是记住所有概念,而是能用它们解释你看到的现象。比如模型突然答非所问,你要能想到是不是上下文里混进了无关片段;Token消耗比预期高,你要能想到是不是历史记录没有截断。这种解释能力比背概念有用得多。
关键是要跑通一个最小API调用。我的建议是写一个终端聊天脚本:读取环境变量里的API Key,在终端输入问题,打印回答,并输出Token统计。别小看这个脚本,它之后会成为你调试各种Prompt和代码的主战场。你所有的实验、迭代、日志分析,都可以从这个小脚本延伸出来。
2.2 第二阶段:构建数据与评估闭环
很多教程会让你直接写Agent、搭RAG,但如果你一开始就忽略数据和评估,后面会很难受。因为没有评估,你根本不知道改动是变好还是变坏。我看过太多人消耗大量时间调Prompt,结果只是“感觉变好了”,用测试集一跑,指标反而下降了。
这一阶段准备三类数据:
- 场景样例(100-200条):模拟真实用户输入,覆盖正常输入、边界情况(比如超长文本)、错误输入(比如空白、含义不清)。
- 期望输出或评分维度:不需要每条都有标准答案,但要有可判断好坏的指标。比如“是否包含关键信息”“是否忠实于资料”。
- 运行日志:每次模型调用的输入、输出、耗时、Token消耗、元数据。
然后,把评估做成一个脚本。最简单的形式:测试集存成JSONL文件,逐条运行AI流程,生成结果,再调用一个规则打分函数或人工打分。这个脚本会成为你迭代的核心工具。我以前常说:“没有评估脚本的项目,都是在凭感觉优化。”凭感觉优化不是不行,但效率太低,尤其当你要对比两个Prompt方案时,没有数据你根本说不清哪个更好。
2.3 第三阶段:掌握大模型应用开发的核心模式
这个阶段重点练四种模式,每一个都值得写一个最小Demo:
- 结构化输出:让模型返回JSON而不是自由文本。最简单的方式是在Prompt里写“请返回JSON格式:{...}”,但更可靠的方式是使用函数调用(Function Calling)或输出Schema校验。
- 上下文组装:把系统提示词、用户输入、检索片段、历史记录拼接成一个完整请求。核心是Token预算,要确保不会超窗口,也不会浪费。
- 工具调用:模型在推理时决定是否需要调用外部函数,比如查天气、查数据库、做计算。这个机制是Agent的基础。
- 多步Agent工作流:模型决策 → 调用工具 → 观察工具结果 → 继续决策,直到完成任务。
以“查天气”为例:写一个get_weather(city)函数,让模型根据用户问题决定调用哪个函数,拿到函数返回结果后再组织回答。这个Demo只有几十行代码,却能让你彻底理解Function Calling的交互过程。很多人一上来就搭复杂的Agent,结果遇到一个报错都不知道是模型没触发调用,还是函数返回格式不对。先把这类小模式吃透,复杂Agent只是这些模式的排列组合。
2.4 第四阶段:完成一个“完整”的项目
到这个阶段,挑一个你熟悉领域的小项目。我最推荐的是“个人知识库问答助手”,因为它可以串联所有知识点,而且结果容易评估。把项目目标写清楚,不要做无穷无尽的新功能,要关注闭环。
需求清单:
- 支持上传PDF或Markdown文档
- 自动切分、向量化、写入向量库
- 用户提问时检索相关片段
- 模型基于检索片段回答,并给出引用来源
- 增加“点赞/点踩”反馈按钮
- 记录每次查询的日志
这个项目看着简单,但当你真正动手做完,就会理解“端到端”是什么概念:从文件上传,到文本解析,到向量检索,到Prompt组装,到模型生成,再到结果展示和数据回流。中间每一步都有可能有坑。做完之后不要停,继续收集几个测试集,看看当前系统的检索命中率是多少、回答忠实度如何、单次成本多少。把这些数字都记录下来,才叫完整项目。
3. 实操要点:从零搭一个RAG问答系统
3.1 需求定义与方案选型
我建议第一版RAG不要一上来就上LangChain,先用Python手写最简版本,理解每个环节后再用框架减少重复代码。先定义需求:
- 输入:用户一个问题
- 输出:一个带引用的答案
- 知识来源:本地一批文档
- 约束:回答只基于文档内容,不确定时明确说“不知道”
方案选型上,Embedding模型我试过开源的BGE-M3,也用过OpenAI的text-embedding-3-small。如果你追求低成本和离线能力,BGE系列很不错;如果追求简单,OpenAI API更方便。关键是先跑通,再谈优化。很多人在选型上纠结很久,其实第一版用哪个都行,重要的是把整条链路走通,拿到真实数据后再替换也不迟。
3.2 数据准备与向量化
这一步决定系统上限。数据预处理基本就是三件事:
第一,清洗。把原始文本里的多余换行、空字符、乱码清掉。PDF转出来的文本尤其需要小心,经常夹杂各种异常字符。我踩过一个大坑:PDF转出的文本里,原本的标题和正文被打散成无数个短行,向量化之后检索质量惨不忍睹。后来加上一行简单的换行合并逻辑,效果立刻改善。清洗规则不需要很复杂,先做“合并连续换行、去除孤立空行、统一编码”这三步,就能解决大部分问题。
第二,切分。不要固定字数切块。虽然很多人用200字、300字一刀切,但这样会把语义割裂。更好的做法是按语义单元切:优先以标题、段落为边界。比如Markdown文档,可以写一个简单的解析函数,按#、##、空行切分。如果文档结构复杂,还可以用递归切分策略:先尝试按段落切,段落太长再按句子切。切分粒度需要根据文档类型调整,没有万能参数。
第三,向量化并保存元数据。每个片段用Embedding模型转成向量,同时保存来源文件、章节标题、页码等元数据。这一步的好处是你可以在检索时按元数据过滤(比如只看某个章节),也能在引用时给出准确来源。不要只存向量的裸文本,元数据会帮助你做后续的权限控制、统计分析和引用回显。
3.3 检索与生成的串联
RAG流程本身很简单:用户问题向量化 → 在向量库中做相似度检索,取Top-K(K一般取3-5)→ 把问题和检索片段组装成Prompt → 调生成模型 → 得到回答。再加上重排序,就是更完整的链路。
下面是一个极简Python实现,适合跑通链路:
import numpy as np from openai import OpenAI client = OpenAI() def embed(text): resp = client.embeddings.create( model="text-embedding-3-small", input=[text] ) return resp.data[0].embedding def search(query, corpus, k=4): q_vec = np.array(embed(query)) scores = [] for item in corpus: doc_vec = np.array(item["embedding"]) # 如果embedding已经归一化,内积等价于余弦相似度 scores.append(np.dot(q_vec, doc_vec)) top_idx = np.argsort(scores)[::-1][:k] return [corpus[i] for i in top_idx] def generate_answer(query, context_items): context = "\n\n".join( [f"[{i+1}] {item['text']}" for i, item in enumerate(context_items)] ) prompt = f"""仅根据以下资料回答问题。 资料: {context} 问题:{query} 如果资料中没有相关信息,请回答“资料中没有提到”。回答时逐个列出你使用的片段编号。""" resp = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": prompt}] ) return resp.choices[0].message.content这段代码虽然简单,但已经把核心链路完整覆盖了:Embedding召回、相似度排序、上下文组装和生成。实际工程中,你需要在这个骨架上加日志、缓存、重试、并发、失败兜底。我强烈建议你自己动手把这段代码敲一遍,再试着加一个“输出JSON引用来源”的后处理步骤。只有手写过一遍,你才能真正理解RAG编排的含义,后面再引入LangChain或LlamaIndex时也不会被抽象层绕晕。
3.4 评估与迭代:最容易被忽略的一环
RAG系统不是搭完就结束,后续迭代才是真正的工程。你需要建立三个层面的评测:
- 检索质量:检索到的Top-K片段是否真的包含答案。用命中率评估。
- 生成质量:答案是否忠实于检索片段、是否完整、是否有事实性错误。
- 端到端体验:用户点不点赞、问题能否解决。
具体做法:准备100个“问题-预期片段-预期回答要点”三元组。跑完整链路后,先看检索命中的片段是否包含预期片段,再看生成答案是否覆盖要点。可以用一个小的LLM做初筛,再人工抽样复核。一开始不用追求复杂指标,先看几个数字:命中率、事实错误率、无答案率。
这里有个反直觉经验:很多生成质量问题,根因其实是检索质量问题。你改Prompt改到嘴秃,不如回头调整切分策略和Embedding模型。所以每次迭代前,先看检索结果,再看生成结果。如果检索命中率只有60%,你无论怎么优化Prompt,生成质量都会受限。把检索命中率提到85%以上,生成质量往往会有肉眼可见的提升。
4. 工具选型解析:如何搭出趁手的工作台
4.1 模型层:API优先还是开源优先
我的建议是第一版项目用API优先。省时间、免运维、效果有保障。你只需要关心应用逻辑。等要控制成本或数据合规时,再换开源模型。开源模型这几年进步很快,但部署、调优、运维成本并不低,对一个从零开始的项目不是最优选择。
常用的模型选型表:
| 场景 | 推荐模型 |
|---|---|
| 快速原型 | GPT-4o-mini / Claude Haiku |
| 高质量摘要 | GPT-4o / Claude Sonnet |
| 离线/私有化 | Qwen / Llama 3 等开源模型 |
| Embedding | text-embedding-3-small / BGE-M3 |
选模型不要只看基准分数,最好让自己的一小批测试集同时跑两三个模型,用评估脚本对比输出。模型A在公开榜单上很强,但可能在你的具体任务上并不比模型B好,这事很常见。把测试集固定下来,谁的输出在你的指标上得分最高,就用谁,这才是工程决策。
4.2 编排层:框架 vs 手写代码
很多人纠结LangChain好还是LlamaIndex好。我的看法是:
- LangChain:组件多、生态大,但抽象层多、版本变动快,适合复杂Agent和工具调用。
- LlamaIndex:对索引和检索更专注,适合知识库问答。
- 手写代码:适合理解原理、定制化强的小项目,也适合排查问题时不至于一头雾水。
坦白讲,我见过太多人陷入“LangChain Release Notes”的泥潭。如果你已经理解了完整链路,手写几百行代码一点都不难。框架的价值在后期,比如处理工具循环、并发、重试等。新手前期最好少依赖框架,免得报了错却不知道黑盒里发生了什么。我自己的习惯是:先裸写跑通,再根据重复度和维护成本决定要不要引入框架。框架是提升效率的,不是掩盖理解缺口的。
4.3 部署与观测:不翻车的关键
部署阶段至少要解决五个问题:
- 服务化:用FastAPI把AI流程包成一个接口。用Pydantic做请求和响应的Schema验证。
- 限流与重试:模型调用要加指数退避和最大重试次数;对外API要加限流,防止被刷爆。
- 日志:每次请求记录输入、检索到的上下文片段、输出、耗时、Token数和成本。这是排查问题的命根子。
- 缓存:相同问题在短期窗口内命中缓存,能省大量成本。我习惯用Redis存最近几天的问答结果。
- 监控:设置错误率和延迟告警。关键告警走即时通知,避免半夜才发现系统挂了。
我推荐一套朴素的起步方案:FastAPI + Pydantic + SQLite日志表。不需要一上来就上Kubernetes、分布式追踪,先把“跑起来、能观测”做到,后面再演进。先动手,再谈扩展。
5. 常见问题与排查技巧实录
5.1 上下文窗口不够怎么办
出现“上下文窗口不够”时,先看日志,确认是哪一步把窗口撑爆了。常见的三个来源:
- 检索片段太多或太长。解法:减少Top-K、限制每个片段的最大长度,或者做重排序后只取最相关的片段。
- 多轮历史记录无限膨胀。解法:只保留最近N轮,或对历史做摘要压缩。
- 系统提示词太长。解法:精简提示词,把不常用的说明移到单独文档。
如果长文档确实必须塞进去,可以使用“分治”思路:一次性问不出答案,就先把文档拆成目录,每章单独提问,再汇总。或者对文档做摘要,把摘要作为上下文。
经验是:与其想办法扩大上下文窗口,不如从源头减少不必要的内容。保存Token就是省钱,减少噪声就是提高质量。你可以在检索阶段就过滤掉不相关片段,而不是把整个文档都塞进Prompt;也可以在历史管理里只保留用户问题和最终答案,丢弃中间过程。这既能控制成本,还能降低模型被无关信息干扰的概率。
5.2 生成结果不稳定怎么管
模型输出天然有随机性,Temperature设为0并不等于每次结果都一致。工程上要做的是把随机性控制在可接受范围,并用校验和重试兜底。
推荐做法:
- 设置较低Temperature,比如0-0.3。
- 用结构化输出或函数调用,把模型回答限定在固定字段。
- 在后处理中做Schema校验,不满足要求就重试一次或返回默认值。
- 在Prompt里给出输出格式的示例,能显著减少格式混乱。
我见过一个很实际的例子:让模型生成JSON,偶尔会在JSON外面夹一段解释文字,导致解析失败。后来用函数调用强制返回结构化字段,问题基本消失。不要相信“模型一定会遵守格式”,要把它当成“大多数时候会遵守,所以你需要兜底”。每次调用都做好异常捕获和默认分支,比追求模型绝对稳定更现实。
5.3 检索质量差怎么调
检索质量差通常有五个原因,按优先级排查:
- 切分不当,导致一个片段里包含多个主题,检索向量代表“平均值”。
- Embedding模型与领域不匹配,比如通用模型对专业术语理解不够。
- 查询与文档表达差异大,比如用户说“离职”,文档里写“离开公司”。
- Top-K不合适,太小漏掉相关信息,太大混入噪声。
- 缺少重排序,向量检索的第一候选不一定是语义上最相关的。
解决方案也很直接:先看具体失败案例,再动手。比如查询“小张的离职时间”,文档里写“2023年5月31日离开公司”,向量检索可能排在后面。此时可以考虑混合检索,让关键词命中参与排序,或者用重排序模型精排。不要盲目换模型,先找出“差”的具体表现。把失败案例聚成几类,比如“问题含专有名词”“文档表达口语化”“片段边界切错了”,再针对每一类做策略调整。
5.4 成本失控与性能优化
AI应用的钱主要花在API调用和向量化上。成本失控的常见原因:全程都用最强模型、做了大量不必要调用、没有缓存、Agent级联反复调用同一个模型。
优化手段:
- 简单任务用小型模型,复杂任务用大型模型。
- 增加短期缓存,相同问题当天不再重复调用。
- 批量处理Embedding请求,减少请求次数。
- 对Prompt做瘦身,减少系统提示词和历史占用的Token。
- 使用流式输出可以改善首字延迟,但总Token不会减少。
性能优化则优先看延迟瓶颈。如果是检索慢,换HNSW索引;如果是生成慢,换小模型或减少max_tokens。不要上来就优化和横向扩展,先找到最慢的1秒在哪。用日志里的耗时分布数据说话,而不是靠猜。
6. 最后分享几点经验
从零开始到能独立搭建AI应用,我最大的体会是:工程化是一种“让模型可被预测地工作”的能力,而不是追求模型本身的神奇效果。你不需要把每个模型论文都读懂,但你需要有能力控制模型在业务中的行为边界。
第一,务必先搭评估闭环,哪怕很粗糙。我有段时间觉得“差不多就行”,导致每次优化都在原地打转。后来强迫自己每改一次,就用同一批测试集跑一遍,记录数字。这样我才能清晰地说:“切分改小之后,检索命中率上升了8%,但回答完整度下降了一点。”这种数据化反馈特别重要,能帮你避开很多主观感受的陷阱。
第二,日志从一开始就埋好。结构化的日志模板在前期看起来很“浪费”,但后面做监控、排查、复盘会特别省力。加日志这十分钟,能帮你省下后期几天。我甚至会在每个AI调用前后加耗时和Token统计,成本核算全靠它。
第三,别迷信最强模型。我一开始总想用最强的模型,结果成本高、延迟大。后来把简单任务切给小型模型,效果差别不大,成本却降了七成。模型选型是持续迭代的过程,不是发布会后一次决定。你手上的测试集和日志,才是选型最可靠的依据。
希望这些经验能帮你少走弯路。AI工程现在还很新,没有一套标准答案,但真正动手做过完整项目的人,一定比看教程多的人走得更远。祝你早日把第一个端到端的项目跑起来。