资讯动态

从零手搓AI工程栈:推理、检索、编排与观测的完整实践

发布时间:2026/9/28 16:24:16 来源:尧图企业网站定制
1. 为什么我要从零手搓一套AI工程栈第一次看到ai-engineering-from-scratch这个标题我脑子里蹦出来的不是某个具体框架而是一连串踩过的坑模型在 notebook 里跑得飞起一上生产就各种超时本地推理好好的换个环境就报 CUDA 版本不匹配明明只是加了个缓存层结果整个链路延迟翻了三倍。这些问题没有一个是调个 API能解决的它们全都指向同一个东西——AI 工程能力。所谓 AI 工程说白了就是把一个能跑的模型变成一套能稳定、能观测、能扩展、能省钱的系统。它跟算法研究是两码事算法关心的是准确率能不能再涨 0.5 个点工程关心的是这套东西在 QPS 翻十倍的时候会不会崩、显存够不够、冷启动要几秒、一次推理成本是几分钱。ai-engineering-from-scratch这个项目标题核心价值就在于它不依赖任何开箱即用的托管平台而是从最底层的推理服务、向量检索、缓存、编排、观测这些模块一块块搭起来让你真正理解每一层在干什么。这套东西适合谁如果你已经会用 PyTorch 或 HuggingFace 跑通一个 demo但一到部署就抓瞎那这套从零搭建的思路就是给你准备的。如果你是大厂里只负责调参、从没碰过服务端的研究同学它也能帮你补齐工程这一环。哪怕你是后端出身、想转 AI 方向这套内容同样友好因为它本质上就是用工程思维重新组织 AI 能力。我下面会按我实际搭建的顺序把整体设计、核心模块、实操细节和踩坑经验全部摊开讲你能直接照着复现。2. 整体架构设计与技术选型思路2.1 从能跑到能扛的四个层次我先把整套系统拆成四层来看这样后面每一块放哪里就清楚了。最底层是推理层负责把模型加载起来、接收请求、吐出结果核心指标是延迟和吞吐。往上是检索与记忆层处理向量化、相似度搜索、上下文拼装这是 RAG 类应用的命脉。再往上是编排层把推理、检索、工具调用、缓存串成一条可控的流水线。最顶层是观测与运维层负责日志、指标、追踪、成本核算。很多人一上来就堆编排框架结果底层推理是个黑盒出了问题根本没法定位。我的建议永远是自底向上先把推理层做扎实再往上叠。这也是ai-engineering-from-scratch这个思路最值钱的地方——它逼你面对每一层的真实复杂度而不是被框架的抽象层糊弄过去。2.2 为什么不用现成的托管方案这里必须解释清楚选型逻辑不然容易被人说重复造轮子。托管方案的优势是快但代价是第一你无法控制推理的批处理策略延迟抖动大第二成本不透明token 一多账单就失控第三出问题时你只能看平台给的有限指标没法深入到 kernel 级别。自建的核心动机不是炫技而是可控性。具体到技术选型推理层我选的是vLLM或TGI这类支持 PagedAttention 和连续批处理的引擎原因是它们能把显存利用率拉高吞吐比朴素 HuggingFace pipeline 高一个数量级。向量库我倾向Qdrant或pgvector前者性能强、后者运维简单看你的团队栈决定。编排层我不用重型框架而是用轻量的 FastAPI 自己写的调度逻辑因为框架的抽象在调试时反而是负担。观测层用Prometheus Grafana OpenTelemetry这套标准组合成熟且生态好。2.3 关键指标先定下来再动手动手前一定要把目标指标写死否则你永远不知道做到什么程度算完成。我一般会定这几个P99 延迟比如首 token 小于 800ms、吞吐每秒处理请求数、显存占用峰值、单次推理成本、缓存命中率。这些指标会反过来决定你的批处理大小、量化策略、缓存粒度。比如你要求 P99 首 token 800ms 以内那批处理就不能无限堆积得设一个超时窗口强制 flush。提示指标一定要在真实流量分布下测别用固定长度的假数据。真实请求的输入长度是长尾分布短请求和超长请求混在一起对批处理策略的考验完全不同。3. 推理层从模型加载到连续批处理3.1 模型加载与显存规划推理层第一件事是把模型塞进显存。这里有个容易忽略的点权重占用只是显存的一部分KV Cache 才是大头。以 7B 模型 FP16 为例权重约 14GB但如果并发 32 路、上下文 4096KV Cache 可能再吃掉十几 GB。所以规划显存时公式大致是总显存 权重 KV Cache 激活值 框架开销 KV Cache ≈ 2 * 层数 * 头数 * 头维度 * 序列长度 * 并发数 * 精度字节我实测下来一张 24GB 的卡跑 7B FP16并发开到 16 路、上下文 2048 是比较稳的。想再往上要么上量化INT8/INT4要么用张量并行拆到多卡。量化这里要提醒一句INT4 对生成质量的影响在长文本任务上比短问答明显得多如果你的场景是长文摘要或代码生成慎用激进的量化。3.2 连续批处理为什么是吞吐的关键朴素推理是一个请求处理完再处理下一个GPU 利用率极低。连续批处理continuous batching的核心思想是不等整批做完只要有请求完成就立刻把新请求塞进去让 GPU 始终有活干。这背后的原理是自回归生成的每一步只依赖前面的 token不同请求之间互不干扰所以可以动态拼批。实现上vLLM 的 PagedAttention 把 KV Cache 切成固定大小的 block像操作系统管理内存页一样管理避免了显存碎片。我踩过的一个坑是block size 设太小会导致调度开销上升设太大又浪费显存一般 16 是甜点值。你可以通过压测不同 block size 下的吞吐曲线来确认。3.3 流式输出与首 token 延迟用户体验上首 token 延迟比总延迟更重要。流式输出SSE 或 WebSocket能让用户立刻看到字往外蹦感知延迟大幅下降。但流式会带来一个新问题你没法在生成完成前知道总 token 数所以计费和限流要按增量处理。我的做法是在流式响应里带上 usage 的增量字段客户端和服务端各自累计。注意流式场景下如果客户端断开连接服务端必须能感知并取消对应的生成任务否则 GPU 会白白算完整个序列。这个取消逻辑一定要在编排层显式实现别指望框架自动帮你回收。4. 检索与记忆层让模型用上外部知识4.1 向量化的分块策略RAG 的效果七成取决于分块。分块太大检索出来的内容噪声多分块太小语义不完整。我的经验是按语义边界切而不是按固定字数切。比如按段落、按标题层级切再对超长段落做二次切分保留一定的重叠overlap 10%~15%来避免边界信息丢失。Embedding 模型的选择也要看场景中文为主就选中文优化过的模型多语言就选多语言模型。这里有个反直觉的点更大的 embedding 模型不一定检索效果更好因为维度高带来的检索成本上升而且在小数据集上容易过拟合。我一般先用中等规模的模型打底效果不够再换大的。4.2 混合检索向量 关键词纯向量检索有个硬伤对精确匹配比如产品型号、专有名词不敏感。所以生产环境我基本都用混合检索——向量召回一批BM25 关键词召回一批再用 RRFReciprocal Rank Fusion融合排序。RRF 的好处是不需要调权重直接按排名倒数相加鲁棒性很好。RRF_score(d) Σ 1 / (k rank_i(d))k 一般取 60。实测下来混合检索在专有名词密集的场景下召回率比纯向量高 15% 以上。4.3 重排序与上下文压缩召回之后别急着塞给模型先过一遍重排序rerank。Cross-encoder 类的重排模型精度高但慢所以只对 top 20~50 做重排选出 top 3~5 给模型。再进一步可以做上下文压缩把检索到的段落里跟问题无关的句子删掉既省 token 又降噪。提示重排序模型和 embedding 模型最好来自同一家族否则语义空间不一致效果会打折。这是我调了好几轮才总结出来的。5. 编排层把各模块串成可控流水线5.1 请求生命周期管理一个请求进来编排层要做的事鉴权、限流、缓存查询、检索、拼 prompt、调推理、后处理、计费、写日志。这条链路里任何一环超时都要有降级策略。我的做法是给每一环设独立的超时预算比如检索 200ms、推理首 token 800ms超了就返回兜底结果或提示重试。缓存是省钱利器。精确缓存key 是完整请求命中率低但零风险语义缓存key 是 embedding 相似度命中率高但有误命中风险。我一般两层都上先查精确缓存再查语义缓存语义缓存的相似度阈值设 0.95 以上宁可漏命中也不误命中。5.2 工具调用与函数编排当模型需要调用外部工具查数据库、调 API时编排层要负责解析模型的工具调用意图、执行、把结果回填。这里的关键是幂等性和超时工具调用可能失败或超时必须有重试和兜底。我踩过的坑是模型偶尔会生成格式错误的调用参数所以解析层一定要做严格的 schema 校验校验失败就返回错误让模型重试。5.3 降级与熔断生产系统一定要有降级路径。推理服务挂了怎么办降级到小模型或缓存结果。检索挂了怎么办降级到纯模型回答。熔断器用标准的滑动窗口统计错误率超过阈值就断开一段时间。这套机制平时看不出价值但流量高峰或依赖抖动时能救命。6. 观测与运维看不见的才是要命的6.1 三类核心指标观测分三类指标metrics、日志logs、追踪traces。指标看趋势日志查细节追踪定位跨服务瓶颈。AI 系统特有的指标包括token 吞吐、KV Cache 命中率、批处理平均大小、检索召回率、缓存命中率。这些指标要跟业务指标成功率、延迟放在同一个看板上才能快速定位问题。6.2 成本核算怎么做成本核算是很多团队忽略的。我的做法是给每个请求打上 token 消耗标签按模型单价折算再按用户/租户聚合。这样你能清楚知道哪个功能最烧钱、哪个用户是成本大户。没有成本可视化的 AI 系统迟早会在账单上给你惊喜。6.3 常见故障排查表现象可能原因排查方向首 token 延迟飙升批处理堆积、显存不足触发换页看批处理队列长度、显存占用吞吐突然下降长请求占比升高、KV Cache 碎片看请求长度分布、block 利用率检索结果变差embedding 模型更新、索引未重建对比新旧索引召回率缓存命中率骤降请求分布变化、缓存过期策略看缓存 key 分布、TTL 设置显存 OOM并发过高、上下文超长限制最大并发和最大长度7. 实操心得与避坑清单7.1 我踩过的五个真实坑第一个坑用默认的 max_length 导致显存爆炸。模型默认可能允许 8K 甚至更长上下文但你的显存根本扛不住。一定要显式设置 max_model_len并在入口做长度校验。第二个坑批处理没有超时窗口。低峰期请求少如果一直等凑批首 token 延迟会很难看。必须设一个最大等待时间比如 10ms到点就 flush。第三个坑向量索引没做增量更新。数据是持续增长的如果每次全量重建索引成本和停机时间都受不了。要用支持增量写入的向量库。第四个坑日志里打了完整 prompt。这既占存储又可能泄露敏感信息。日志只打摘要和 hash完整内容按需采样。第五个坑没做压测就上线。我见过太多本地测着好好的一上线就崩。压测要用真实分布的数据逐步加压找到拐点。7.2 性能调优的优先级调优要有顺序别乱试。我的优先级是先调批处理参数收益最大再调量化省显存再调缓存省成本最后调网络和序列化收益最小。很多人一上来就优化 JSON 序列化其实批处理参数调一调吞吐就能翻倍。7.3 给不同基础读者的上手建议如果你是新手别一上来就搞全套。先跑通单模型 FastAPI 流式输出这条最小链路再逐步加检索、缓存、观测。如果你有后端基础重点补推理层的显存和批处理知识。如果你是算法出身重点补服务治理和观测。这套ai-engineering-from-scratch的价值就在于它让你按自己的节奏一块块把能力补齐而不是被某个框架绑死。最后分享一个我一直在用的小技巧给每个模块写一个最小可复现脚本比如单独测推理、单独测检索、单独测缓存。当系统出问题时你能快速定位是哪一层的问题而不是在整条链路里大海捞针。这个习惯帮我省下的排查时间比我优化任何参数都多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑