从零搭建AI工程能力:数据、向量化与推理的完整链路实操指南
1. 从零搭建AI工程能力为什么我劝你别一上来就调包这两年AI应用开发的门槛肉眼可见地降低了随便拉个框架、调个API就能跑出一个能对话的Demo。但我自己带过几支团队、也帮朋友救过好几个“Demo很惊艳、上线就崩盘”的项目之后越来越确信一件事真正拉开差距的不是你会不会调包而是你懂不懂这套东西从底层到工程化是怎么串起来的。“ai-engineering-from-scratch”这个方向之所以值得认真做一遍核心就在于它逼着你把那些平时被框架封装掉的环节亲手走一遍——数据怎么进、模型怎么算、推理怎么加速、服务怎么扛住并发、成本怎么控住。我先把话说在前面这篇不是教你从零训练一个GPT那是另一个量级的事情。这里说的“from scratch”指的是从工程视角把AI应用的完整链路自己搭一遍包括数据处理、特征与向量化、模型调用与本地推理、服务封装、性能压测、监控与迭代。走完这一遍你再回头看那些框架会发现它们帮你省掉的是什么、又在哪些地方悄悄埋了坑。适合谁看如果你已经会用Python写点脚本、调过一两次大模型接口但一到“要上线、要稳定、要省钱”就心里没底那这篇就是写给你的。如果你是完全零基础也没关系我会把每个环节的“为什么”讲清楚你照着做也能跑通。整篇内容偏实操参数和步骤我都会给到能直接抄的程度中间穿插我自己踩过的坑。2. 整体架构设计先想清楚链路再动手写代码2.1 为什么我坚持先画链路图再写代码很多人做AI项目的第一反应是打开编辑器写import openai然后一路往下堆。我早期也这样结果就是代码写到三千行的时候发现数据格式在三个地方不一致、缓存层没地方插、想换个模型要改二十个文件。后来我强制自己养成一个习惯——任何AI项目先花半小时把数据流画出来再动手。一个完整的AI工程链路我一般拆成六段数据接入层、预处理与向量化层、推理层、服务层、可观测层、迭代层。这六段不是每个项目都要全上但你在设计阶段必须知道哪几段是当前需要的、哪几段是未来要预留的。比如一个内部知识库问答数据接入和向量化是重点推理层可能直接调云端API而一个要做实时语音转写的产品推理层的本地部署和延迟优化就是生死线。画链路图还有个隐性好处它逼你把“输入是什么、输出是什么、中间态存在哪”想明白。我见过太多项目挂在“中间态”上——embedding存哪、缓存key怎么设计、会话上下文怎么截断这些在Demo阶段无所谓一上量全是问题。2.2 技术选型的三个决策维度选型这件事没有标准答案但有几个维度你必须过一遍不然就是拍脑袋。第一个维度是延迟预算。用户能接受的响应时间是多少如果是聊天类首字延迟超过1.5秒体感就明显变差如果是批处理任务几秒到几分钟都无所谓。延迟预算直接决定你是走云端API还是本地推理是走小模型还是大模型。第二个维度是成本结构。云端API按token计费本地推理按GPU小时计费两者的成本曲线完全不同。调用量小的时候云端便宜且省心调用量一大本地部署的边际成本优势就出来了。我一般会算一个盈亏平衡点假设本地一张卡每小时成本X元云端每百万token Y元那么当你的吞吐超过某个阈值时本地就更划算。这个计算后面我会给具体例子。第三个维度是数据敏感度与可控性。有些数据不能出内网那就只能本地有些场景需要频繁微调那本地部署的迭代速度也更快。这三个维度交叉一下选型基本就定了。2.3 一个我常用的最小可行架构对于大多数中小规模的AI应用我推荐的最小可行架构是这样的FastAPI做服务层 Redis做缓存和会话 向量库Qdrant或pgvector做检索 云端API或本地vLLM做推理。这套组合的好处是每一层都可以独立替换和扩展不会牵一发动全身。为什么是FastAPI因为它异步支持好、生态成熟、上手快而且和Pydantic结合做请求校验非常舒服。为什么向量库推荐Qdrant或pgvectorQdrant是专门的向量库性能和过滤能力强pgvector的好处是你如果已经有Postgres不用额外维护一个组件。这两个我都用过小规模pgvector够用上了千万级向量还是Qdrant更稳。推理层我一般先用云端API把业务跑通等调用量和成本数据出来了再决定要不要迁到本地。这个顺序很重要不要一上来就折腾本地部署那会让你在业务还没验证的时候就陷进运维泥潭。3. 核心环节拆解数据、向量化与推理的实操要点3.1 数据预处理脏数据是AI项目最大的隐形杀手我做过一个统计在我接手过的出问题的AI项目里超过一半的根因在数据而不是模型。数据预处理这一步看起来枯燥但它决定了你后面所有环节的天花板。文本数据预处理我一般分四步走。第一步是清洗去掉HTML标签、多余空白、乱码字符。这一步用正则就能搞定但要注意别把有意义的格式也洗掉了比如代码块里的缩进。第二步是分块chunking这是RAG场景的重头戏。分块策略直接决定检索质量我试过固定长度、按段落、按语义三种方式实测下来按语义分块 适当重叠效果最好。具体参数上我一般把chunk size设在300到500个tokenoverlap设在50到100个token。为什么是这个范围太小了语义不完整检索出来答非所问太大了噪声多而且会挤占上下文窗口。overlap的作用是防止关键信息正好被切在边界上这个细节很多人忽略但实测能明显提升召回。第三步是元数据抽取给每个chunk打上来源、时间、类别等标签。这些标签在检索时可以做过滤比如“只搜最近三个月的数据”没有元数据你就只能全量搜。第四步是去重重复数据会让检索结果同质化浪费上下文窗口。注意分块参数没有万能值一定要拿你自己的数据做小规模测试。我一般会准备20个典型问题用不同参数跑一遍看召回率和答案质量再定最终参数。3.2 向量化模型选择与批量处理的性能陷阱向量化就是把文本变成一串数字embedding让语义相近的文本在向量空间里距离也相近。这一步的模型选择很关键我一般看三个指标维度、语言支持、推理速度。维度方面常见的有384、768、1024、1536。维度越高表达能力越强但存储和检索成本也越高。我一般用768或1024性价比比较平衡。语言支持上如果你的数据有中文一定要选多语言模型纯英文模型在中文上效果会断崖式下跌这个坑我踩过。推理速度是很多人忽略的点。向量化通常是一次性离线做的但如果你的数据在持续增长增量向量化的速度就很重要了。我一般会用ONNX Runtime或者直接上GPU来加速批量大小batch size设在32到128之间具体看显存。这里有个性能陷阱要特别说不要一条一条地调embedding接口。我见过有人写个for循环逐条请求一万条数据跑了一下午。正确做法是批量请求一次传几十上百条。如果是本地模型用DataLoader做批处理如果是云端API看它的批量接口限制一般一次能传几十条。# 批量向量化的典型写法 from sentence_transformers import SentenceTransformer model SentenceTransformer(BAAI/bge-base-zh-v1.5) texts [...] # 你的文本列表 # batch_size根据显存调整GPU上一般64或128 embeddings model.encode(texts, batch_size64, show_progress_barTrue, normalize_embeddingsTrue)注意最后那个normalize_embeddingsTrue做归一化之后余弦相似度计算会更快而且很多向量库默认用内积归一化后内积等价于余弦相似度这个细节能省不少事。3.3 推理层云端API与本地部署的取舍实操推理层是整个链路的心脏。我一般先用云端API把业务逻辑跑通因为这个阶段你要验证的是产品而不是基础设施。云端API的好处是零运维、模型强、上手快缺点是成本随量线性增长、数据要出网、延迟受网络影响。当你决定要迁到本地时vLLM是目前我最推荐的推理框架。它的PagedAttention机制对显存利用率的提升非常明显吞吐能比朴素实现高好几倍。部署一个本地推理服务大概是这样# 启动vLLM服务以Qwen2.5-7B为例 python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192 \ --port 8000几个参数解释一下。tensor-parallel-size是多卡并行数单卡就设1。gpu-memory-utilization是显存占用比例0.9意味着留10%给其他进程设太高容易OOM。max-model-len是最大上下文长度设得越大占的显存越多按实际需求来。迁到本地之前我强烈建议先算一笔账。假设一张消费级显卡整机成本约1.5万元按三年折旧、每天跑10小时算每小时成本约1.4元。云端API假设每百万token收费10元一个7B模型每秒大概能出50个token一小时就是1.8亿token云端要1800元本地只要1.4元。这个差距是数量级的但前提是你的利用率要够高如果一天只跑几分钟那还是云端划算。4. 服务化与性能优化让AI应用真正扛得住4.1 服务层设计异步、限流与优雅降级把模型跑起来只是第一步让它作为一个服务稳定对外提供能力是另一回事。我用FastAPI做服务层的时候有几个设计是必做的。首先是全异步。AI推理是IO密集和计算密集混合的场景同步阻塞的写法会让并发能力惨不忍睹。FastAPI的async def配合httpx的异步客户端能把并发能力提升一个数量级。但要注意如果你在async函数里调用了同步的阻塞代码比如某些本地模型的同步推理整个事件循环会被卡住这时候要用run_in_executor把它丢到线程池里。其次是限流。AI服务很容易被打爆一个用户疯狂发请求就能拖垮整个服务。我一般用令牌桶算法做限流按用户维度或IP维度限制QPS。Redis是实现限流的好帮手用INCR加过期时间就能做一个简单的计数器限流。第三是优雅降级。当后端推理服务超时或过载时不能直接给用户报错要有降级策略。常见的降级有返回缓存结果、切换到更小的模型、返回预设的兜底话术。我一般会设一个超时阈值比如3秒超过就触发降级。# 带超时和降级的异步调用示例 import asyncio import httpx async def call_llm(prompt: str, timeout: float 3.0): try: async with httpx.AsyncClient() as client: resp await client.post( http://localhost:8000/v1/chat/completions, json{model: qwen, messages: [{role: user, content: prompt}]}, timeouttimeout ) return resp.json() except (httpx.TimeoutException, httpx.ConnectError): # 降级返回缓存或兜底 return {fallback: True, content: 当前请求较多请稍后再试}4.2 缓存策略省下的都是真金白银缓存是AI工程里性价比最高的优化手段没有之一。我做过一个项目加了缓存之后API调用量直接降了40%成本同步下降。缓存分几层。第一层是精确缓存key就是请求的完整hash命中就直接返回。这一层适合那些重复率高的场景比如FAQ问答。第二层是语义缓存把请求向量化在缓存里找相似度超过阈值的已有结果。这一层能命中那些“问法不同但意思一样”的请求命中率更高但实现复杂一些阈值一般设在0.9到0.95之间。缓存的key设计有讲究。除了请求内容还要把影响输出的参数都放进去比如模型名、温度、最大长度。我见过有人只拿prompt做key结果换了模型还返回旧结果排查了半天。缓存的过期策略也要想清楚。知识库类的内容变化慢可以设长一点比如一天实时性要求高的场景可能几分钟就要过期。我一般用Redis的EXPIRE来做简单可靠。4.3 压测与容量规划别等上线了才发现扛不住压测这一步很多人跳过然后上线当天被打挂。我一般用Locust或wrk做压测重点看三个指标QPS、P99延迟、错误率。压测的时候要注意AI服务的瓶颈往往不在你的服务层而在推理层。所以压测要分层做先单独压推理服务看它的吞吐上限再压整个链路看服务层有没有额外瓶颈。我遇到过一次服务层本身能扛1000 QPS但推理层只能扛50结果整体就被卡在50。容量规划上我一般按峰值QPS的1.5倍来准备资源留出缓冲。同时要设计好扩容策略是加机器还是加卡是水平扩展还是垂直升级。如果是云端API要确认账号的配额够不够我见过有人压测把配额打满正式上线反而没额度了。5. 常见问题与排查技巧实录5.1 检索质量差从分块到重排的排查路径RAG场景最常见的问题就是“检索出来的东西不对”。排查这个我一般按顺序走先看分块再看embedding模型最后看重排。分块问题表现为检索出来的chunk语义不完整或者关键信息被切断了。解决办法是调整chunk size和overlap或者换成语义分块。embedding模型问题表现为语义相近的文本向量距离却很远。这时候要检查模型是不是适合你的语言和领域中文场景一定要用中文或多语言模型。重排是最后一道防线用一个交叉编码器cross-encoder对初步检索的结果重新排序能明显提升精度代价是增加一点延迟。我整理了一个排查速查表现象可能原因排查方法解决方向检索结果不相关embedding模型不匹配人工看几条向量的相似度换多语言或领域模型关键信息检索不到分块切断语义检查chunk边界调整size和overlap结果同质化数据重复统计重复率去重排序不合理缺少重排对比重排前后加cross-encoder5.2 推理延迟高从显存到批处理的逐层定位延迟高是另一个高频问题。我一般先看显存占用如果显存快满了推理会频繁触发换页延迟飙升。这时候要降低max-model-len或者gpu-memory-utilization。如果显存没问题就看批处理。vLLM默认会做连续批处理continuous batching但如果你的请求是串行发的批处理就发挥不出来。要确保并发请求能同时到达推理服务。如果延迟还是高可能是模型太大考虑换小模型或者量化。INT8量化一般能降一半显存、提升30%左右速度精度损失通常在可接受范围。还有一个容易被忽略的点是首token延迟和总延迟的区别。首token延迟主要受prefill阶段影响和输入长度强相关总延迟还受decode阶段影响和输出长度相关。优化的时候要分清你优化的是哪个。5.3 成本失控token消耗的监控与优化成本失控往往是因为没有监控。我一般会在服务层记录每次请求的输入token、输出token、模型名、耗时然后按天聚合。有了这些数据你才能知道钱花在哪了。优化token消耗有几个方向。一是压缩上下文把历史对话做摘要而不是全量塞进去。二是限制输出长度很多场景不需要长篇大论设个max_tokens能省不少。三是缓存前面说过了。四是路由简单问题用小模型复杂问题才用大模型这个能省一大笔。我做过一个对比同样一批请求全用大模型和用路由策略成本差了将近三倍而用户满意度几乎没差别。路由的实现可以用一个小的分类模型或者简单的规则判断。提示token计数不要自己估算用官方的tokenizer或者tiktoken估算误差能到20%以上做成本核算会失真。6. 迭代与可观测让系统越跑越好的闭环6.1 日志、指标与追踪三件套一个能持续迭代的AI系统可观测性是基础。我一般上三件套日志、指标、追踪。日志记录每次请求的输入输出、参数、耗时、错误。这里要注意脱敏用户隐私数据不能明文落盘。指标是聚合数据比如QPS、P99延迟、缓存命中率、token消耗用Prometheus加Grafana就能搭起来。追踪是分布式链路追踪能看到一个请求在各个环节的耗时分布排查性能问题特别有用OpenTelemetry是现在的标准方案。这三件套搭起来之后你对系统的掌控感会完全不一样。以前出问题靠猜现在看数据就知道瓶颈在哪。6.2 反馈闭环把用户反馈变成迭代燃料AI系统和其他软件最大的区别是它的输出质量是概率性的需要持续迭代。我一般会设计一个反馈机制让用户能对结果点赞或点踩这些反馈数据就是迭代的燃料。反馈数据怎么用一是用来评估定期抽样人工评估看整体质量趋势。二是用来构造测试集把典型的bad case收集起来作为回归测试。三是用来微调积累到一定量之后可以做微调让模型更贴合你的场景。我一般会维护一个“黄金测试集”包含几十到几百个典型问题和期望答案每次改动之后都跑一遍看有没有退化。这个习惯帮我避免了好几次“改了一个地方、坏了另一个地方”的事故。6.3 版本管理与灰度发布AI系统的版本管理比传统软件复杂因为你要同时管理代码版本、模型版本、数据版本。我一般用Git管代码用模型注册表比如MLflow管模型用数据版本工具管数据。三者要能对应上出问题才能回溯。发布的时候一定要灰度。先放1%的流量观察指标没问题再逐步放大。灰度期间要重点看延迟、错误率、以及业务指标比如用户满意度。我见过一次直接全量发布结果新模型在某个边缘场景下疯狂输出乱码等发现的时候已经影响了大批用户。灰度还有一个好处是能做A/B测试对比新旧版本的效果。这个在AI场景特别有价值因为模型效果很难离线评估准确线上数据才是真相。7. 我个人的一些实操体会走完这一整套“from scratch”的流程最大的收获不是学会了某个工具而是建立了一套判断力。你知道每个环节的瓶颈可能在哪知道一个方案的成本和收益大概是什么量级知道出了问题该往哪个方向排查。这种判断力是调包调不出来的。如果让我给刚入门的朋友一个建议我会说先跑通一个最小闭环再逐步加深。不要一上来就追求完美架构先用最简单的方案把数据、推理、服务串起来跑通之后再针对瓶颈优化。我见过太多人卡在“选型纠结”上半年过去了还没跑通第一个版本。另外工具是死的场景是活的。我在这篇里给的参数和方案都是基于我的经验但你的数据、你的用户、你的约束可能完全不同。所有参数都要拿你自己的场景验证这才是工程的本意。踩坑不可怕可怕的是踩了坑不知道为什么踩、下次还踩。把每次踩坑都变成一条经验积累下来你就有了别人拿不走的东西。