
1. 从零搭建AI工程能力为什么我劝你别一上来就调包这两年AI应用开发的门槛被各种框架拉得极低三行代码就能调用一个大模型接口于是很多人产生了一种错觉AI工程不过如此。但真到了要把一个AI功能塞进生产环境、扛住真实流量、控制住成本、还得让输出稳定可控的时候绝大多数人会发现自己在裸泳。ai-engineering-from-scratch这个标题戳中的正是这个痛点——它强调的不是“会用某个库”而是从底层把AI工程这条链路自己走一遍理解每一层在干什么。我自己带过几个从零起步的AI项目也见过太多团队在“调包能跑”和“上线能用”之间反复横跳。这篇文章想做的事情很明确把AI工程从零搭建的完整思路、关键决策点、实操步骤和踩坑经验摊开讲清楚。它适合两类人——一类是刚入行、想搞明白AI应用背后到底发生了什么的开发者另一类是已经会调API、但想把系统做得更稳更省更有掌控感的工程师。核心关键词就是从零构建、AI工程链路、可控与可观测这三个词会贯穿全文。我不打算写成教科书而是按一个真实项目从想法到落地的顺序来讲先想清楚架构再抠细节然后动手实现最后处理那些只有跑起来才会暴露的问题。每一段我都会解释“为什么这么做”因为AI工程里最贵的不是代码是决策。2. 整体架构设计与技术选型思路2.1 先明确一件事AI工程到底包含哪些层很多人把AI工程等同于“调模型”这是最大的认知偏差。一个完整的AI工程系统从下到上至少包含这几层模型接入层怎么拿到推理结果、编排层怎么组织多步逻辑、数据与上下文层喂给模型什么、评估与观测层怎么知道它好不好、服务与成本层怎么稳定且便宜地对外提供。ai-engineering-from-scratch的价值就在于它逼你把每一层都自己实现一遍而不是被某个框架的黑盒吞掉。为什么强调“自己实现一遍”因为框架帮你省掉的那些代码恰恰是你出问题时最需要理解的部分。比如检索增强生成里向量检索召回不准你如果只会调框架的retriever根本无从下手但如果你自己写过相似度计算和分块逻辑一眼就能看出是分块粒度还是距离度量的问题。这就是从零构建的意义——掌控感。2.2 技术选型的三个核心权衡从零搭建不代表什么都自己造轮子而是要在“自研”和“复用”之间做清醒的取舍。我一般用三个维度来判断可控性、开发速度、长期维护成本。模型接入层我建议直接用官方SDK或标准HTTP接口不要过早引入重型编排框架。原因很简单这一层逻辑最稳定抽象收益低一旦框架升级或行为变化你反而被绑架。编排层则相反如果业务涉及多步推理、条件分支、工具调用自己手写状态机很快就会失控这时候引入一个轻量编排库是划算的。数据层几乎必须自研因为分块策略、元数据设计、召回逻辑高度依赖你的业务语料没有通用方案。下面这张表是我在多个项目里总结的选型参考可以直接抄层级推荐做法不建议过早引入核心理由模型接入官方SDK/HTTP封装重型LLM框架逻辑稳定抽象收益低编排轻量状态机或编排库全功能Agent框架复杂分支手写易失控数据与检索自研分块向量库端到端RAG平台强依赖业务语料评估观测自建指标日志纯人工抽查需要可回归、可量化服务成本缓存限流降级无脑全量调用大模型成本是长期生死线2.3 一个最小可用架构长什么样我通常从一个“最小闭环”开始而不是一上来就设计大而全的系统。最小闭环包含一个入口服务、一个模型调用封装、一个上下文组装模块、一个结果后处理、一份基础日志。这个闭环能跑通一次完整请求就说明主干通了后面所有优化都是在这个主干上做加法。这个思路的好处是快速验证。很多团队花两周搭了个漂亮的分层架构结果发现模型输出根本达不到业务要求架构再美也没用。先用最小闭环验证“这件事到底能不能成”再决定要不要投入做工程化这是我从零做AI项目最看重的一条原则。3. 核心模块拆解与实操要点3.1 模型接入层别小看这一层封装模型接入看起来最简单但坑不少。我封装这一层时固定会做四件事统一请求格式、超时与重试、错误分类、用量记录。统一格式是为了上层不关心具体是哪家模型超时重试是因为网络抖动和模型侧限流太常见错误分类是为了区分“可重试”和“不可重试”避免无脑重试把配额烧光用量记录则是成本控制的基础。一个容易被忽略的点是流式输出的处理。如果你的产品需要打字机效果接入层必须支持流式而且要在这一层就把流式数据块拼装成完整结果上层逻辑不应该感知流式的复杂性。我见过把流式处理散落在业务代码各处的项目后期想加个“中途取消”功能都无从下手。注意重试一定要加指数退避并且对“内容审核拒绝”这类错误不要重试重试只会浪费配额且永远不会成功。3.2 上下文组装决定输出质量的关键环节模型本身的能力是固定的但你能给它什么上下文直接决定输出质量。上下文组装模块要解决三个问题放什么、放多少、怎么排。放什么取决于任务类型。问答类任务需要相关文档片段创作类任务需要风格样例工具调用类任务需要工具描述和历史调用记录。放多少受限于模型的上下文窗口但更重要的是信噪比——塞太多无关内容反而会稀释关键信息导致模型“抓不住重点”。怎么排我一般把最关键的指令放最前面和最后面中间放参考资料这是利用了模型对首尾位置更敏感的特性。这里有个实操技巧给上下文加显式分隔和标签。比如用【参考资料开始】...【参考资料结束】把检索内容包起来并明确告诉模型“只依据参考资料回答资料中没有的信息不要编造”。实测下来这一招能显著降低幻觉率比单纯调温度参数管用得多。3.3 检索与分块RAG系统里最容易被低估的部分如果你的AI工程涉及知识库检索质量就是天花板。而检索质量的第一决定因素是分块策略。我踩过的坑是一开始按固定字数切分结果把一句话从中间切断检索出来的片段语义不完整模型理解起来很吃力。后来我改成按语义边界分块优先在段落、标题、句子结束处切分同时保留一定的重叠窗口。重叠的作用是防止关键信息正好落在切分点上被割裂。分块大小没有万能值我的经验是中文场景下300到500字一段比较稳技术文档可以更小叙述性内容可以更大。向量化之后检索环节我建议至少做两路召回向量相似度召回加关键词召回然后做融合排序。纯向量召回对精确术语、专有名词不敏感关键词召回能补上这个短板。这个组合在真实业务里比单一召回稳得多。3.4 评估与观测没有它你就是在盲开这是从零做AI工程最容易被跳过、但最重要的一环。模型输出是非确定性的你今天调好了明天换个输入可能就崩了。没有评估体系你根本不知道改动是变好还是变坏。我的做法是维护一个黄金测试集收集几十到上百条真实场景的输入和期望输出每次改动后跑一遍用规则匹配加模型打分结合的方式评估。规则匹配处理格式类要求比如必须是JSON、必须包含某字段模型打分处理质量类要求比如相关性、完整性。这套东西搭起来不复杂但能帮你挡住绝大多数回归问题。观测方面每次请求都要记录输入、检索到的上下文、模型原始输出、后处理结果、耗时、token用量。这些日志在排查问题时是救命稻草。我遇到过用户投诉“回答不对”翻日志发现是检索阶段召回了一篇过期文档如果没有上下文日志这个问题能查一整天。4. 完整实操流程与关键环节实现4.1 环境准备与项目骨架从零开始我建议先把项目骨架搭清楚目录结构直接反映架构分层。一个我常用的结构是这样的ai-engineering/ ├── config/ # 配置与密钥管理 ├── gateway/ # 模型接入层 ├── context/ # 上下文组装 ├── retrieval/ # 检索与分块 ├── pipeline/ # 编排逻辑 ├── evaluation/ # 评估脚本与测试集 ├── observability/ # 日志与指标 └── service/ # 对外服务入口配置管理我要特别强调密钥绝不进代码库用环境变量或配置中心。模型名称、超时时间、重试次数、分块大小这些参数全部外置方便不改代码就调参。这一步做扎实后面迭代效率会高很多。4.2 模型调用封装的具体实现接入层我会写一个统一的调用函数输入是标准化的消息列表和参数输出是标准化结果。核心逻辑包括参数校验、超时控制、重试、错误分类和用量统计。下面是一个简化版的Python示例展示关键结构import time import logging def call_model(messages, model, max_retries3, timeout30): for attempt in range(max_retries): try: response client.chat( modelmodel, messagesmessages, timeouttimeout ) log_usage(response.usage) return response.content except RateLimitError: wait 2 ** attempt logging.warning(f限流{wait}秒后重试) time.sleep(wait) except ContentFilterError: logging.error(内容被拦截不重试) raise except TimeoutError: if attempt max_retries - 1: raise raise RuntimeError(模型调用失败)这段代码的关键在于错误分类处理限流用指数退避重试内容拦截直接抛出超时按次数重试。这个逻辑看着简单但能避免很多生产事故。4.3 上下文组装的参数计算上下文窗口是有限资源怎么分配需要算账。假设模型窗口是8000 token我的分配策略通常是系统指令预留500历史对话预留1500检索资料预留4000输出预留2000。这个比例不是固定的但思路是给输出留足空间否则模型回答到一半被截断体验极差。检索资料那4000 token怎么用我会按相关性排序从高到低填充填满为止。如果单条资料太长就截断并加省略标记。这里有个细节不要为了塞满窗口而塞入低相关内容宁可少放保证信噪比。4.4 检索模块的落地步骤检索模块我按这个顺序实现文档加载、清洗、分块、向量化、入库、查询、召回、重排。每一步都有讲究。清洗要去掉页眉页脚、乱码、重复段落分块按语义边界并保留重叠向量化选一个中文表现稳定的模型入库时把原文、向量、元数据来源、时间、标题一起存。查询时先做查询改写把用户口语化的问题改写成更适合检索的形式这一步能明显提升召回率。然后向量召回和关键词召回各取一批用倒数排名融合做重排最后取前几条作为上下文。这套流程我用了很多次稳定性和效果都比单路召回好。4.5 编排逻辑把各模块串起来编排层负责把上面这些模块按业务逻辑串起来。一个典型的问答流程是接收用户输入、查询改写、检索、组装上下文、调用模型、后处理、返回结果。如果涉及多轮对话还要维护会话历史。我建议编排逻辑用显式的步骤函数写而不是藏在一堆回调里。每一步的输入输出都清晰可见出问题时能快速定位是哪一步的锅。如果业务复杂到需要条件分支和循环再考虑引入状态机或编排库但前提是主干逻辑已经跑通。5. 常见问题与排查技巧实录5.1 输出不稳定、时好时坏怎么办这是最高频的问题。排查顺序我一般是先看是不是检索召回不稳定再看是不是上下文组装有随机性最后才怀疑模型本身。很多时候问题出在检索——同一类问题有时召回到好资料有时召回差的输出自然飘。解决办法是固定检索策略、加缓存、对高频问题做结果固化。如果确认是模型侧波动可以适当降低温度参数但别指望降到0就完全确定模型推理本身仍有随机性。更稳的做法是加输出校验和后处理比如要求模型输出结构化格式解析失败就重试或走兜底逻辑。5.2 成本失控怎么破成本问题通常来自三个地方无脑全量调用大模型、上下文塞太满、没有缓存。我的对策是分层简单任务用小模型复杂任务才上大模型上下文按需组装不无脑塞满高频相同请求加缓存命中直接返回。还有一个容易被忽略的点是输出长度控制。让模型输出简洁答案比让它长篇大论省得多。在指令里明确“回答控制在X字以内”效果立竿见影。5.3 常见问题速查表现象可能原因排查方向解决思路回答答非所问检索召回不准看上下文日志优化分块与召回输出格式错乱指令不明确检查系统提示加格式约束与校验响应特别慢上下文过长统计token用量精简上下文成本突然飙升重试风暴看错误日志修错误分类逻辑偶发内容被拦截输入含敏感词看拦截记录加输入预处理5.4 几个只有踩过才知道的坑第一个坑别在系统提示里写太长太复杂的规则。模型对超长指令的遵循度会下降规则要精简、分点、加粗关键约束。第二个坑检索的元数据一定要存时间否则无法处理“最新信息”类需求也无法清理过期内容。第三个坑评估测试集要持续更新业务在变老测试集很快就不代表真实场景了。提示每次线上出问题修完之后一定要把那个case加进测试集这是让系统越来越稳的最有效手段。6. 从能跑到好用还差哪些工程化细节6.1 缓存策略的设计缓存是AI工程里性价比最高的优化。我一般做两层精确缓存和语义缓存。精确缓存对完全相同的请求直接返回实现简单语义缓存对意思相近的请求复用结果需要算相似度但命中率更高。语义缓存要设相似度阈值太高命中少太低会返回不相关结果我通常从0.9开始调。缓存还要考虑失效。知识库更新后相关缓存必须清掉否则用户会拿到过期答案。我的做法是给缓存打上知识库版本标签版本一变就整体失效。6.2 降级与兜底生产系统必须假设模型会挂。降级策略我通常准备三档模型超时或报错时先重试重试仍失败走缓存或规则兜底再不行返回友好提示而不是报错页面。这套机制平时看不出价值但流量高峰或模型侧故障时能保住用户体验。6.3 安全与合规的边界AI应用要处理用户输入必须做输入输出过滤。输入侧过滤明显违规内容输出侧检查是否包含不该出现的信息。这一层不要省它既是合规要求也是保护系统不被滥用的屏障。过滤规则要可配置、可更新因为风险内容的形式一直在变。6.4 持续迭代的节奏从零搭起来的系统迭代节奏很重要。我的建议是小步快跑每次只改一个变量改完跑评估数据说话。不要一次改一堆东西否则效果变好变坏都说不清原因。评估数据积累起来后你会发现优化方向越来越清晰而不是靠感觉拍脑袋。这套从零构建的思路我在不同规模的项目里反复用过。最开始会觉得什么都自己写很慢但跑通一两个项目后就会发现你对系统的理解深度和排障速度是那些只会调框架的人完全比不了的。真正值钱的从来不是会用一个工具而是知道这个工具背后发生了什么以及当它不工作时你该怎么办。