1. 从零搭建AI工程能力:为什么我劝你别一上来就调包
这两年AI应用开发的门槛肉眼可见地在降低,随便拉个框架、调个API就能跑出一个能对话的Demo。但我带过的新人里,十个有八个在遇到"模型输出不稳定""推理延迟飙高""上下文一长就崩"这类问题时,完全不知道从哪下手。根子就在于——大家跳过了"从零理解AI工程"这一步,直接站在了工具链的最顶层。
ai-engineering-from-scratch这个方向,说白了就是把AI应用从"能跑"到"跑得稳、跑得省、跑得可维护"这条路上,那些被框架封装掉的底层环节,自己动手重新走一遍。它适合三类人:一是刚转行做AI应用、只会调API的开发者;二是想搞清楚推理服务内部到底发生了什么的后端工程师;三是需要给团队搭建AI基础设施、但不想被某个框架绑死的技术负责人。
我自己走过这条路,也踩过不少坑。这篇文章不讲虚的,就把我从零搭一套AI工程能力时,真正需要理解的几个核心模块——推理引擎、上下文管理、缓存策略、评测体系——拆开揉碎讲一遍。每个环节我都会说清楚"为什么这么设计""参数怎么算""实际会遇到什么问题",你照着做能少走至少半年的弯路。
2. 整体设计思路:为什么"从零"比"调包"更值钱
2.1 先搞清楚AI工程到底在工程什么
很多人把AI工程等同于"写Prompt"或者"调模型API",这是最大的误解。一个能上生产的AI应用,本质上是一个带概率输出的分布式系统,它和传统后端系统最大的区别在于:输入输出都是不确定的,而且计算成本极高。
这就带来三个传统工程里不存在的核心问题:
- 不确定性管理:同样的输入,模型可能给出不同输出。你得设计重试、校验、降级策略。
- 成本控制:每次推理都是真金白银。Token消耗、GPU占用、并发排队,每一项都要精打细算。
- 质量评测:传统系统测的是"对不对",AI系统测的是"好不好",而"好"是个连续谱,需要一套评测体系来量化。
ai-engineering-from-scratch的核心思路,就是把这三个问题当成一等公民来对待,而不是等出了问题再打补丁。我见过太多项目,前期Demo跑得飞快,一上量就各种崩,就是因为这三个问题从来没被认真设计过。
2.2 分层架构:把"模型"当成一个可替换的零件
从零搭建时,我建议采用清晰的分层架构,而不是把所有逻辑塞在一个文件里。我的分层是这样的:
| 层级 | 职责 | 关键设计点 |
|---|---|---|
| 接入层 | 请求路由、鉴权、限流 | 与模型无关,可独立扩展 |
| 编排层 | Prompt组装、上下文管理、工具调用 | 业务逻辑核心,最需要测试 |
| 推理层 | 模型调用、批处理、重试 | 可替换,支持多模型路由 |
| 缓存层 | 语义缓存、结果缓存 | 成本控制的关键 |
| 评测层 | 离线评测、在线监控 | 质量保障的闭环 |
这么分层的最大好处是:模型是可以随时换的。今天用A模型,明天B模型降价了想换,你只需要改推理层的适配器,编排层和评测层完全不用动。我见过把模型调用散落在各处的项目,换个模型要改几十个文件,那才叫痛苦。
2.3 技术选型:别被"最新最强"绑架
选型这块我的原则很朴素:优先选你能读懂源码的、社区活跃的、能本地跑起来的。具体来说:
- 推理引擎:如果只是调云端API,那选型意义不大;但如果要自部署,我建议从支持连续批处理(continuous batching)的引擎入手,这是吞吐量的分水岭。
- 向量检索:小规模(百万级以下)用内存索引就够,别一上来就上分布式向量库,运维成本远大于收益。
- 编排框架:能用代码写清楚的就别用框架,框架的抽象泄漏在调试时是灾难。我个人的做法是先用纯代码实现,等模式稳定了再考虑抽象。
提示:选型时一定要问自己一个问题——"如果这个组件明天停止维护,我迁移的成本有多大?" 答案超过一周的,就要慎重。
3. 核心模块拆解:推理、上下文、缓存、评测
3.1 推理引擎:吞吐量和延迟的平衡术
推理层是整个系统的心脏。从零理解它,你需要搞明白两个核心指标:首Token延迟(TTFT)和每Token输出时间(TPOT)。用户感知到的"快",主要取决于TTFT;而整体吞吐量,取决于批处理效率。
连续批处理(continuous batching)是绕不开的概念。传统批处理要等一个批次里所有请求都生成完才能处理下一批,而连续批处理允许新请求随时插入、完成的请求随时退出。实测下来,在并发场景下吞吐量能提升3到8倍,具体取决于请求长度的分布。
如果你要自部署,关键参数是max_batch_size和max_num_seqs。这两个值不是越大越好:
max_batch_size过大,单次前向计算显存占用飙升,容易OOM;- 过小则GPU利用率上不去,成本浪费。
我的经验算法是:先用显存容量反推最大批大小,再根据P99延迟要求往下调。比如一张24G显存的卡,跑7B模型(FP16约14G权重),留给KV Cache的显存大概8G,按每个序列平均2K上下文估算,能同时容纳的序列数大概在30到50之间。这个数就是你的max_num_seqs上限,实际设置时留20%余量。
3.2 上下文管理:长对话不崩的关键
上下文一长就崩,是新手最常遇到的问题。根子在于KV Cache随上下文线性增长,而显存是有限的。从零做上下文管理,核心是三件事:
第一,滑动窗口加摘要。不要傻乎乎地把所有历史都塞进去。我的做法是保留最近N轮完整对话,更早的用模型压缩成一段摘要。N的取值看任务,客服场景5到8轮足够,代码助手可能要保留完整文件上下文。
第二,Token预算分配。给系统提示、历史对话、检索内容、用户输入各分配一个预算上限,超了就按优先级裁剪。这个预算要动态调整,比如检索内容相关度高时多给检索、少给历史。
第三,位置编码的处理。很多模型对超出训练长度的上下文外推能力很差,硬塞长文本会导致"中间遗忘"。如果必须处理超长文本,要么用支持长上下文的模型,要么做分段处理再聚合。
注意:上下文不是越长越好。实测中,把无关历史塞进去反而会降低输出质量,因为模型注意力被稀释了。宁可精准检索,不要暴力堆砌。
3.3 缓存策略:省钱的命脉
AI应用的成本大头在推理,而推理成本的大头在重复计算。缓存做得好,成本能降一半以上。缓存分三层:
精确缓存:输入完全一致时直接返回。实现简单,用哈希做key就行。命中率在客服、FAQ类场景能到30%以上。
语义缓存:输入语义相似时复用结果。需要向量检索加相似度阈值。这里有个坑——阈值设太高命中率低,设太低会返回不相关答案。我的经验是先用0.92起步,根据badcase调整。
前缀缓存:相同系统提示和前缀的请求,复用KV Cache。这个在自部署场景收益巨大,尤其是系统提示很长的时候。主流推理引擎都支持,开启后TTFT能降50%以上。
| 缓存类型 | 实现复杂度 | 典型命中率 | 主要收益 |
|---|---|---|---|
| 精确缓存 | 低 | 20%-40% | 成本、延迟 |
| 语义缓存 | 中 | 15%-35% | 成本、延迟 |
| 前缀缓存 | 低(引擎支持) | 60%+ | 首Token延迟 |
3.4 评测体系:没有评测就没有迭代
这是最容易被忽略、但最重要的一环。没有评测,你改一个Prompt、换一个模型,根本不知道是变好了还是变坏了。从零搭评测,我建议分两步走:
离线评测:准备一个覆盖核心场景的测试集,每个case标注期望输出或评分标准。每次改动跑一遍,看指标变化。指标不要只看准确率,还要看格式合规率、拒答率、平均长度等。
在线监控:生产环境采样,人工抽检加自动指标。重点关注用户反馈(点赞点踩)、重试率、异常输出率。这些信号比离线指标更真实。
评测集的建设是个持续过程。我的做法是:每次发现一个badcase,就把它加进评测集。半年下来,评测集就成了项目的护城河。
4. 实操落地:从零到一搭建的完整流程
4.1 环境准备与最小可运行版本
第一步不是搭架构,而是跑通一个最小闭环。我的建议是先写一个纯Python脚本,实现"读输入→组装Prompt→调模型→输出结果"这条链路,不引入任何框架。
# 最小推理闭环示例 import time def generate(prompt, model_client, max_retries=3): for attempt in range(max_retries): try: start = time.time() response = model_client.chat(prompt) latency = time.time() - start # 记录关键指标 log_metrics(ttft=response.ttft, latency=latency, tokens=response.usage) return response.text except RateLimitError: time.sleep(2 ** attempt) # 指数退避 except Exception as e: if attempt == max_retries - 1: raise return None这个脚本虽然简单,但包含了三个关键设计:重试机制、指标记录、异常分类处理。很多人的Demo里这三样都没有,一上生产就抓瞎。
环境准备上,我建议用虚拟环境隔离依赖,把模型客户端、向量库、评测工具分开管理。配置文件用YAML或环境变量,别硬编码密钥和模型名。
4.2 编排层的实现要点
编排层是业务逻辑的核心,也是最需要测试的地方。我的实现原则是:把Prompt组装做成纯函数,输入是结构化的上下文对象,输出是最终的Prompt字符串。这样测试起来非常方便,给定输入就能断言输出。
上下文对象我一般设计成这样的结构:
class Context: system_prompt: str history: list[Message] # 历史对话 retrieved: list[Document] # 检索到的文档 user_input: str budget: TokenBudget # 各部分的token预算组装时按预算裁剪,优先级是:系统提示 > 用户输入 > 检索内容 > 历史对话。裁剪历史时从最旧的开始丢,但保留第一轮(通常包含关键设定)。
工具调用(function calling)也在这层处理。我的经验是:工具描述要写得像给新人看的文档,参数说明清楚、给例子、说明什么时候用。工具数量控制在10个以内,太多模型会选错。
4.3 缓存层的落地细节
缓存层落地时,key的设计是关键。精确缓存的key不能只用用户输入,还要包含模型名、温度参数、系统提示版本。否则改了系统提示,缓存还返回旧结果,那就出大事了。
语义缓存的实现,我建议用两阶段:先用向量检索找候选,再用一个轻量模型或规则判断是否真的等价。纯靠向量相似度会有误判,尤其是"帮我查A"和"帮我查B"这种,向量可能很近但答案完全不同。
前缀缓存的开启,取决于推理引擎。自部署时记得把系统提示放在最前面且保持稳定,这样前缀命中率才高。如果系统提示里带了时间戳之类的动态内容,前缀缓存就废了。
提示:缓存一定要有失效机制。模型更新、知识库更新时,要能批量清理相关缓存。我一般给缓存加个版本号,版本变了自动失效。
4.4 评测流程的自动化
评测要自动化才有意义。我的做法是写一个评测脚本,输入是评测集文件,输出是各项指标的报表。每次代码合并前跑一遍,指标下降就阻断合并。
评测集格式我一般用JSONL,每行一个case:
{"id": "case_001", "input": "...", "expected": "...", "tags": ["faq", "chinese"]}评分方式分两种:有标准答案的用精确匹配或相似度;没有标准答案的用模型打分(LLM-as-judge),但要固定打分模型和Prompt,保证可比性。
评测指标我重点关注这几个:准确率、格式合规率、平均延迟、平均Token消耗。前两个看质量,后两个看成本。四个指标一起看,才能判断一次改动是"提质增效"还是"拆东墙补西墙"。
5. 常见问题与排查技巧实录
5.1 输出不稳定怎么办
这是最高频的问题。排查思路按顺序来:
- 先看温度参数。温度大于0.7时输出波动大是正常的,业务需要稳定就调到0.1到0.3。
- 再看Prompt是否有歧义。同一个问题换个说法答案就变,说明Prompt约束不够。加上明确的输出格式要求和边界条件。
- 最后看模型本身。有些模型在特定任务上就是不稳定,换模型或加few-shot示例。
我的经验是,80%的"不稳定"其实是Prompt问题,不是模型问题。与其频繁换模型,不如把Prompt打磨到位。
5.2 延迟突然飙高怎么排查
延迟问题要分段定位。我在代码里会给每个阶段打点:检索耗时、Prompt组装耗时、模型推理耗时、后处理耗时。哪段涨了一眼就能看出来。
常见原因和对策:
| 现象 | 可能原因 | 对策 |
|---|---|---|
| TTFT高但TPOT正常 | 排队严重或前缀缓存失效 | 扩容、检查前缀稳定性 |
| TPOT高 | 输出太长或批处理效率低 | 限制max_tokens、调批参数 |
| 检索耗时高 | 索引未加载或数据量涨了 | 检查索引、加缓存 |
| 整体都高 | 下游依赖慢 | 加超时、降级策略 |
5.3 成本失控怎么止血
成本失控通常有三个来源:重复推理、超长上下文、无效重试。止血顺序:
先上精确缓存,这是最快见效的。再检查上下文预算,把不必要的历史和检索内容砍掉。最后看重试逻辑,是不是失败后无脑重试导致成本翻倍。重试一定要有次数上限和退避策略。
我踩过的一个坑是:流式输出中途失败后重试,导致用户看到重复内容。后来改成流式失败不重试,直接返回错误让用户重发,反而体验更好。
5.4 评测指标好看但线上效果差
这是典型的评测集和真实分布不一致。评测集是你自己造的,真实用户的问题千奇百怪。解决办法是持续从线上采样,把真实case补充进评测集。另外,评测时要注意数据泄漏——如果评测集里的问题和训练数据或Prompt示例高度相似,指标会虚高。
我的做法是维护两个评测集:一个是"核心集",覆盖必须做对的场景,指标要求严格;一个是"探索集",收集线上真实case,指标作为参考。两个集一起看,才能既保底线又看趋势。
6. 我在这条路上踩过的几个坑
第一个坑是过早抽象。一开始就想搞一套通用框架,结果业务还没跑通,框架先把自己绕进去了。后来我改成"先写三遍重复代码,再抽象",反而清晰很多。
第二个坑是忽视可观测性。早期只记了个总延迟,出问题完全不知道哪慢。后来给每个环节都加了打点,排查效率提升十倍不止。AI系统的可观测性比传统系统更重要,因为不确定性更高。
第三个坑是评测集造假。为了让指标好看,专挑简单case放进评测集,结果线上被真实用户教做人。评测集必须诚实,宁可难看,不要虚假繁荣。
最后一个体会是:AI工程的核心竞争力不在模型,而在工程。模型大家都能用,但谁能把不确定性管好、把成本控住、把质量评准,谁就能做出真正能上生产的东西。这条路没有捷径,但每一步都算数。