
1. 项目概述一个仓库背后的AI工程路线图1.1 为什么会有 ai-engineering-from-scratch 这个项目我接触 AI Engineering 已经三年多了。回想刚入门那会儿最痛苦的其实不是模型不会调参而是信息太碎。今天看到一段 Prompt 技巧明天刷到一篇 RAG 优化文章后天又被 Agent 概念刷屏好像什么都会一点真要动手搭一个能跑的应用时大脑一片空白。这个项目的初衷就是把自己踩过的坑、走过的弯路、验证过可行的方案整理成一条从零开始的路径存进一个叫ai-engineering-from-scratch的仓库里。这个仓库不是教科书更像一份“工程逃生手册”。里面记录的是从明确业务需求开始到选择模型、设计 Prompt、搭建检索链路、处理上下文、评估效果这一整套流程。核心思路很简单AI Engineering 不是算法岗不是你必须手写 Transformer、推导注意力公式才有资格入场。真正的工作重心是组合、编排、评估和调优。把大模型当作一个能力很强的实习生你负责给它明确任务、提供靠谱资料、检查交付质量。谁适合参考这份路线图如果你是后端开发者想给现有系统加 AI 能力或者是测试、运维、产品经理想搞清楚 AI 应用到底怎么落地再或者你已经调过几个 API 但总觉得项目停留在 demo 阶段那这份从实战中沉淀的思路会非常对路。它不要求你有深厚的机器学习背景但要求你愿意动手、愿意反复调试。1.2 AI Engineering 到底在做什么这几年“AI 工程师”和“算法工程师”的边界越来越清晰。算法工程师的重点是训练模型AI 工程师的重点是使用模型。用大白话讲算法岗解决“有没有模型能用”的问题AI 工程岗解决“模型怎么用出价值”的问题。同样是调大模型AI 工程师关心的是怎么让输出格式稳定、怎么控制成本、怎么把私有知识灌进去、怎么在用户访问量上来之后还能撑得住。从核心内容看AI Engineering 至少包含五大块Prompt 工程设计提示词让模型的输出在格式、内容、风格上可控。RAG 检索增强把外部知识库接入模型解决“模型不知道、容易编造”的问题。Agent 与工具调用让模型具备调用外部工具、拆解任务、多步执行的能力。应用工程化涉及流式输出、并发控制、缓存、可观测性、安全过滤。评估与调优建立评测集用数据判断每一次改动是变好还是变坏。现在回到ai-engineering-from-scratch项目本身。它的价值在于用一条主线把这些零散知识点串起来了。每个章节都对应一个真实会遇到的工程问题而不是孤立的教程。下面我就按这个项目的进阶逻辑逐层拆解每个阶段的要点和实操细节。2. 从零搭建AI工程技能树知识体系拆解2.1 第一层大模型基础与API调用习惯很多新手上来就纠结“该学 LangChain 还是 LlamaIndex”我个人建议先把裸调用 API 练熟。裸调用的意思是不用任何框架直接通过 HTTP 或 SDK 请求模型接口。为什么这一步如此重要因为框架每天都在变API 返回的原始结构、Token 计费规则、超时重试机制这些是不变的底座。你只有亲手处理过返回的 JSON、处理过截断的流式响应才可能在后面用框架时真正理解它的封装在做什么。一个最基础的调用包含三个要素模型名、消息列表、参数配置。很多初学者只关注前两个忽略了参数。以常见的 Chat 接口为例temperature控制随机性max_tokens限制生成长度top_p影响采样范围。不同场景对参数的要求完全不同做分类抽取任务时希望温度接近 0保证输出稳定做创意文案时希望温度调高让输出更发散。起步阶段建议把参数变化对输出的影响亲自测一遍记录几个典型值这是形成工程直觉的第一步。另一个需要养成的习惯是让模型输出结构化内容。早期我总让模型直接回答一段话结果下游系统根本没法解析。后来习惯在 Prompt 里强约束“只输出 JSON 格式字段固定为 xxx、yyy”再配合 API 的响应格式参数把模型的输出从“语文题”变成“填空题”。这个习惯越早养越好后面无论是接业务系统还是做评估结构化的输出都省大量麻烦。2.2 第二层Prompt 工程与上下文窗口管理Prompt 工程是 AI 工程里看起来门槛最低、天花板却很高的一块。说它门槛低是因为任何人都能写几句指令说它天花板高是因为同样一个模型高手写出的 Prompt 和新人写出的效果可能相差十倍。我的经验是一个稳定的 Prompt 至少要包含四个部分角色定义、任务描述、输入数据、输出要求。拿“从客服对话中提取客户诉求”这个场景举例。差的 Prompt 可能就一句“请提取客户诉求”模型输出五花八门。好的 Prompt 会这么写你是一名客户服务质检分析助手。下面是一段客服与客户的对话记录。 请从中提取客户的最终诉求以 JSON 格式输出字段 reason客户遇到的问题类型用简短短语概括。 emotion客户情绪只能填写 平静、不满、愤怒、焦虑 之一。 demand客户希望得到的具体解决方案不超过50字。 对话内容 ...角色定义让模型调用正确的知识面任务描述明确目标输入数据划定处理范围输出要求强制格式。四者缺一不可。上下文窗口管理是这一层另一个绕不开的话题。现在主流模型动辄支持几十万 Token 的上下文但从工程视角看应该持保留态度。上下文越长推理成本越高首字返回时间越长关键信息被稀释的风险也越大。更合理的做法是“够用就好”把必要的背景压缩成几百字的提示词把动态内容按相关性过滤后塞入而不是一股脑把整份文档丢进去。我自己的一个习惯是给 Prompt 里的历史消息设置滑动窗口。比如只保留最近 6 轮对话和数据库里提取出的用户画像摘要更早的内容就丢弃掉。这种做法既控制了成本又减少了模型被无关信息干扰的可能。2.3 第三层RAG 与向量检索的落地细节RAG 是绝大多数 AI 应用绕不开的一环。大模型的知识截止时间是固定的企业内部文档、最新的产品资料、个人笔记这些信息模型一概不知道。RAG 的核心思路是“先查后答”用户提问后先从知识库里找出最相关的片段拼进 Prompt再让模型结合上下文作答。这套机制有点像开卷考试允许模型翻书但翻书动作要在回答前完成。在ai-engineering-from-scratch的实践记录里我把 RAG 链路拆成了五个环节文档解析、切片、向量化、检索、重排。其中最容易翻车的是文档解析。很多 PDF 里其实是扫描图片直接切出来全是乱码必须先用 OCR 转成文本。表格信息在转为纯文本时会丢失结构遇到这种情况要把表格转为 Markdown 格式保留关系。这一步做不好后面检索质量再高也是空中楼阁。切片策略也是经验密集区。有人喜欢把文档固定切成 512 字的小块觉得这样精准。但切片过小会破坏语义完整性一个完整段落被切成两半检索时很可能只取到一半。我的验证结果是用“标题层级 段落语义”切分更可靠先按文档结构分块如果块太大再继续往下切同时给每块附带它所属的上层标题作为上下文前缀。这样既保持了语义完整又让向量能捕捉到段落主题。切片之后是向量化和检索。向量化是把文本映射成高维向量语义相近的内容在向量空间里距离更近。这里有个细节不是所有模型都擅长向量化也不是向量化之后就不能用关键词检索了。实践中把“向量检索的语义泛化能力”和“BM25 关键词检索的精确匹配能力”结合起来做混合检索通常能比单一检索高出十个点的召回率。检索出的 Top-K 片段不要直接丢给模型接一个轻量级的 rerank 模型把相关度重新排一遍效果提升非常明显。2.4 第四层Agent 与工作流编排Agent 是 AI 工程里最迷人的一部分也是翻车率最高的地方。模型本身并不“会”做事它只会生成文本。所谓的 Agent本质上是写了一段循环模型推理下一步该做什么 → 生成一个结构化的工具调用指令 → 代码执行工具并把结果返回给模型 → 模型基于新信息继续推理。这套机制看起来聪明实际上如果没有边界很容易陷入死循环。我在项目里总结的控制原则是“凡事有据可查”。第一步是给 Agent 定义严格的能力清单它能调哪些工具、每个工具的入参是什么、返回结果长什么样。第二步是限制迭代次数一般 5 到 10 步之内必须给出最终答复防止模型无限循环下去。第三步是要求每一步的关键决策都记录到日志里方便问题复盘。给 Agent 接入工具时有个细节值得单独说工具的入参描述一定要写清楚。模型不知道你的系统里有多少张表、每个字段什么含义它在决策时完全依赖你对工具的描述。描述越模糊它就越可能填错参数。比如一个“查询订单状态”的工具参数描述写成“订单号字符串”就远不如“从用户消息中提取的订单号通常是 10 位纯数字”效果好。这个现象业内叫“工具描述就像是给模型写说明书”说明书的质量直接影响协作质量。3. 实操过程从0到1搭建一个AI问答系统3.1 环境准备与依赖安装讲完理论进入实操环节。这里我以ai-engineering-from-scratch仓库里的一个入门案例为例搭建一个基于本地文档知识库的问答系统。这个案例篇幅适中既能覆盖 RAG 全链路又不会上来就挑战 Agent 的高复杂度。开始前需要准备以下环境Python 3.10 以上版本建议用虚拟环境隔离依赖。一个可调用的模型 API并准备好密钥。一个向量数据库实例本地项目优先选择轻量级的 Chroma 或 SQLite 支持的向量库避免一上来就部署集群。一份测试文档随便找几篇 Markdown 或 PDF 格式的说明书即可。依赖安装只需要少数几个库调用模型用的 SDK、文本切片用的langchain-text-splitters、向量计算用的框架、向量库客户端。我的建议是中途不要贪多装一堆框架先把链路上的每个环节用最薄的封装跑通再考虑上框架。在requirements.txt里核心依赖看起来像这样openai1.0.0 chromadb0.4.0 langchain-text-splitters0.2.0 pypdf3.17.0安装完成后先做一个最小连通性测试写一个几行的脚本调用模型 API 让它输出“hello”确认密钥有效、网络通畅、计费正常。这一步虽然简单却能把后面排错的变量隔离掉一大半。我见过太多同学在集成框架时报错最后发现是密钥填错了白白浪费一晚上。3.2 完整链路实现解析、切片、检索与应答搭建 RAG 系统的代码链路适合一步一步来。先实现文档加载from pypdf import PdfReader def load_pdf(path): reader PdfReader(path) return \n.join(page.extract_text() for page in reader.pages)看到extract_text()这里要警觉扫描版 PDF 提取出来大概率是空字符串。如果是这种情况需要先接入 OCR 服务不能直接往后走。文本加载完后进入切片环节。我用的是 MarkdownHeaderTextSplitter它会按标题层级切分并把标题信息保留为上下文from langchain_text_splitters import MarkdownHeaderTextSplitter splitter MarkdownHeaderTextSplitter( headers_to_split_on[(#, chapter), (##, section)] ) chunks splitter.split_text(document_text)每个切出来的 chunk 都带了chapter和section的元数据。向量化时把这两项也一起编码检索效果比纯正文好不少。切片做完之后写入向量库import chromadb client chromadb.PersistentClient(path./kb_store) collection client.get_or_create_collection( namenotes, metadata{hnsw:space: cosine} ) collection.add( ids[str(i) for i in range(len(chunks))], documents[c.page_content for c in chunks], metadatas[c.metadata for c in chunks] )写入向量库之后检索逻辑就简单了。调用collection.query时把用户问题转成向量按相似度返回 Top-K 结果。这里有一个经验Top-K 不要硬编码成一个值而要根据切片大小灵活调整。切片短、信息密度低时取 6 到 8 个片段比取 3 个可靠得多切片长、信息密度高时取 3 到 4 个就够用。把 Top-K 设计成可通过环境变量调整的参数方便后面做对比实验。检索完成之后把片段按相关度从高到低拼接到系统提示词里。注意控制总长度一个通用模板是“你的任务基于以下参考资料回答问题参考资料用 [1]、[2] 标注回答中须引用对应编号”。这样模型在给出答案时能返回出处下游用户可以回溯原文。最后把用户问题作为用户消息发送整个链路就闭环了。3.3 小型评估集如何快速判断系统好坏不少人在系统跑通之后就停下来了这恰恰是错的。能回答问题不代表回答得好。严格来说RAG 应用上线前至少要做一轮评估。评估不一定要高大上我建议先准备 30 到 50 条测试问题覆盖三种类型显式查询、推理查询、反向查询。显式查询指的是知识库里有现成答案的问题主要检验检索召回能力。推理查询需要模型综合多个片段得出结论主要检验指令遵循能力。反向查询是故意问知识库里没有的内容检查模型会不会老老实实说不知道而不是强行编造。三种类型比例可按 4:4:2 配比。评估指标上工程团队最容易上手的两个指标是“答案相关度”和“引用正确率”。相关度可以用另一个强模型来打分也可以人工看。引用正确率更好统计检查模型给出的答案里引用编号对应的片段是否真的支撑了结论。如果引用正确率低于六成说明切片或检索环节存在系统性问题光调 Prompt 治标不治本。我通常会把评估做成一个独立脚本输入测试集、调用问答接口、记录输出结果、生成评估报告。每次改动无论是更新 Prompt 还是调整切片策略都跑一遍同样的测试集用数据对比再决定是否上线。这个习惯帮我避开过很多“感觉变好了、实际变差了”的坑。4. 常见问题与排查技巧实录4.1 向量召回质量差怎么办我在项目运行中遇到过最频繁的问题就是召回质量差。典型表现是模型答非所问或者明明库里有答案模型却答不出来。排查这类问题第一步要区分是“没查出来”还是“查出来没用上”。方法很简单在日志里打印检索结果人工看一眼 Top-K 片段。如果检索结果本身就不相关往下看三个环节。第一检查文档解析质量文本是否乱码、表格是否丢失结构。第二检查切片粒度是不是切得太碎或切错了语义边界。第三检查向量化模型与检索需求是否匹配。很多中文场景使用通用向量模型效果不理想换成针对中文语料优化的向量模型就能显著改善。如果检索结果相关但模型没有利用好问题多半出在 Prompt 编排上。我踩过的一个典型坑是多个片段拼接时没有区分优先级模型被排在后面的低相关片段带偏了。解决办法是在模板中明确“优先依据编号靠前的内容作答”或者在检索结果拼接时把高置信片段固定放在最前面。4.2 Token 成本暴涨如何控制AI 应用的成本大头是 Token尤其是 RAG 和 Agent 场景。一次请求把几十个片段全塞进去单轮成本看起来不贵并发一上去账单就难看了。控制成本的手段有两个层面一是减少输入二是建缓存。减少输入方面除了前面说的滑动窗口压缩历史消息还要学会“摘要换全文”。对较长的历史会话定期让模型生成一个几百字的摘要后续请求只携带摘要而不是完整对话。对检索片段可以按相关度只保留 Top-K 中的前几段低分片段宁可不给也不要滥竽充数。缓存层面最简单的方案是“完全相同的问题直接命中缓存”。稍微复杂一点的是“语义缓存”把新问题向量化如果在过去几小时内已有相似问题出现过直接把当时的回答返回。这种方案对高频重复提问的场景能省下 80% 的调用量。不过缓存层要注意设置合理的过期时间因为知识库内容更新后旧缓存可能已经错了。还有一个省钱技巧容易被忽略为不同任务选择不同规格的模型。对简单分类任务用便宜的小模型对复杂推理任务才用旗舰模型。做好这个分级策略之后综合成本下降明显而用户体验几乎无感。4.3 上下文截断与输出异常长对话场景经常遇到上下文超限的问题。模型输入长度有上限一旦消息总长度超过限制就会报错。为了应对这个问题我养成了给 Prompt 做“预算”的习惯把模型输入上限想象成一块固定大小的硬盘系统提示词、历史消息、检索片段、用户问题这几部分各占多少配额心里有数。实际操作中我给系统提示词预留 20%检索片段预留 30%历史消息预留 30%其余 20% 留给用户问题和模型输出空间。一旦某一部分超预算优先压缩历史消息其次裁剪低相关检索片段。这种配额式的管理方式在任何模型升级时都能快速适配。输出异常主要指模型返回格式不合法比如要求 JSON 却返回了多余解释、输出在中间截断、生成了非预期的空字符串。这类问题排查时先看原始响应日志确认是模型行为问题还是客户端解析问题。如果是模型行为一般在 Prompt 里加强输出格式描述并开启“强制 JSON 输出”模式就能解决。如果是客户端解析问题要给 JSON 解析做好容错能处理模型中偶尔补全的尾逗号和非标准引号。项目里我用了一个容错逻辑解析失败时先尝试修复常见 JSON 语法错误再尝试用模型二次修复最后才报错给用户。这几层退路下来线上出错的概率大幅下降。5. AI工程的工具链选型我的最终取舍5.1 框架选择LangChain、LlamaIndex 还是原生代码ai-engineering-from-scratch的仓库里有一章专门记录工具选型的思考过程。很多人问到底是选 LangChain 还是 LlamaIndex我的答案是分阶段看。纯学习和低复杂度原型阶段建议直接写原生代码不引入框架。原生代码的好处是每个环节都是透明的出了问题一眼就能看到。随着项目复杂度上升比如要做多数据源的插件机制、需要丰富的文档加载器支持再引入框架会明显提速。框架能让你 10 分钟搭出一个 demo但也能让你在一个小版本升级后焦头烂额。LangChain 这类框架的抽象层次高API 变动频繁社区里戏称“跟着文档更新就已经耗尽精力”。如果你已经有了一段原生代码跑通的基线引入框架时就可以按需引入只用来处理文档加载和解析链路核心部分继续用原生代码控制。这样兼顾了开发效率和可控性。LlamaIndex 在数据索引和检索方面底子更扎实尤其适合以知识库为核心的 RAG 项目。租用工具越多越要理解每个组件的独立性。我最后的取舍是工程化程度高的业务系统里通信层和编排层自己写检索和解析部分适度引入成熟库避免被单一框架绑架。5.2 模型选择API 还是本地部署模型的选型决定应用效果的下限。很多团队困惑于“到底用闭源 API 还是本地部署开源模型”我的经验是看三个要素数据敏感性、成本结构、效果要求。数据敏感的场景没得选必须本地部署。虽然部署开源模型需要 GPU 资源团队初期可能在基建上投入较大但数据不出域的合规压力会小很多。效果敏感而数据不敏感的场景闭源大模型 API 是更省力的选择。它的综合能力通常优于同参数量的开源模型配套的工具链也更成熟接入成本低。成本结构上要注意别只盯着单价。本地部署看似单次调用便宜但 GPU 硬件折旧、运维人力、机房电费都是隐形成本。当并发量稳定且较高时本地部署才有规模优势当并发波动大或业务早期验证时按量计费的 API 更划算。我建议做一次成本模型测算把硬件、人力、调用量预估放进去用数据说话。另外还有一个折中方案值得考虑把两者结合。企业内部的敏感核心知识用本地模型处理非敏感的大规模开放域对话走云端 API甚至可以按请求路由实现“敏感数据隔离 顶级模型兜底”。这种混合架构在实践中越来越常见。5.3 向量数据库从原型到上线的选型建议向量数据库的选型也是个热点。个人项目或原型验证阶段直接用轻量级嵌入式向量库即可比如 Chroma 或 LanceDB。它们随应用一起启动不需要额外运维服务几十万条向量规模内表现良好。我很多实验性项目都跑在嵌入式向量库上省去了连接管理和部署的麻烦。到了百亿级向量规模或多实例并发访问阶段才需要考虑独立的向量数据库服务。目前主流的方案有三类专用向量库、自带向量检索的全文数据库、以及云厂商托管服务。专用向量库单查性能强但在复杂查询和多字段过滤上不如全文数据库灵活。在实际业务里仅仅按向量相似度检索往往不够还需要对文档来源、更新时间、用户权限做过滤这时全文数据库的 SQL 能力就显得很重要。所以在选型上我给出的建议是别迷信“向量”两个字先列出应用的过滤条件、数据量级、写入频率、延迟要求再倒推选型。原型阶段用嵌入式方案线上根据过滤复杂度选择。换库成本比想象中高先把需求定义清楚能省很多迁移的眼泪。6. 项目复盘与持续迭代思路6.1 仓库内容如何持续更新ai-engineering-from-scratch不是一份写完就固定的文档。AI Engineering 这个领域变化太快模型产品几乎每个月都在出新能力新框架也在不断洗牌。把项目当作“活文档”的关键在于每次实践都要留一手记录。我在处理新需求时会把“踩坑背景、实验参数、线上表现”三件事记录下来。这些记录比任何转载的技术文章都更有参考价值因为它们建立在真实业务数据上。具体操作上我给仓库每个关键模块都配了一个 README里面包含典型的失败案例和对应的决策过程。比如某个切片策略为什么被放弃、某次模型升级后哪些 Prompt 需要重写。这些“做决策时的上下文”是文档里最值钱的部分也是from scratch这条学习路径的灵魂你看到的不是结论而是结论是怎么来的。另外还要留意能力边界的迁移。以前觉得做不了的方案比如用大模型做结构化数据抽取、做 OCR 纠错、做代码评审会因为模型能力升级而变得可行。保持定期刷新的习惯用最小 demo 验证新能力把验证结果更新进文档这个项目才能真正陪伴你从入门到熟练。6.2 从问答系统走向复杂应用项目做到一定阶段单纯做问答已经不够了。后续迭代可以考虑三个方向第一个方向是把单个问答节点嵌入到业务流程里比如从“回答人力政策问题”升级为“根据问题自动提交工单并跟踪处理进展”。第二个方向是给问答系统加上多轮自主规划能力让它根据用户目标拆解任务、调用多个工具、逐步完成。第三个方向是建立反馈闭环收集用户对回答的点赞点踩定期微调 Prompt 和检索策略让系统越用越顺手。这三个方向越往后越考验工程能力尤其是可观测性和回滚机制。我不推荐一上来就把系统设计成纯 Agent 形态那容易陷入不可控局面。稳妥的思路是保持“一个主流程 多个确定性子流程”让 AI 只在需要判断和归纳的地方介入其余环节继续用传统代码控制。这个思路在稳定性和灵活性之间能取得很好的平衡。根据个人经验的体会从零开始做 AI Engineering 最忌讳的就是“感觉懂了就停手”。你花一天跑通的 demo 只证明模型能工作不证明方案能落地。真正拉开差距的地方在于你有没有想过回答质量怎么度量、成本失控怎么办、失败的时候能不能快速定位。把这些问题在每个项目里都主动过一遍你的工程判断力自然会成形。希望这份从 scratch 整理出的路径能让你少走一些我走过的弯路。