拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零到一构建RAG服务:AI工程师的工程化实战路线

从零到一构建RAG服务:AI工程师的工程化实战路线

这几年总有人问我同一个问题:AI工程师到底从哪入门?特别是看到Github上那些ai-engineering-from-scratch风格的项目之后,总觉得东西很多,却不知道先啃哪块。我自己的感受是,这个方向最大的门槛不在数学,也不在模型结构,而在“工程化”这三个字——你得把一个模型从能跑通,变成能用、能扛住访问、出问题能快速定位的服务。这篇文章就是我根据自己的实操整理出的从零到一的完整路径,适合刚转行的新人,也适合后端同学想接AI项目的场景。我不想讲那些花哨的Demo,也不会让你一上来就啃大部头,咱们按真实项目会遇到的环节,一步步拆开说清楚。

1. 先搞懂AI工程是什么,别被各种名词带偏

1.1 AI工程和算法岗的本质区别

不少人对AI工程师的理解还停留在“调模型、调参数”上,搞错了重点。算法岗和AI工程岗最大的区别在于:算法岗的交付物是“模型效果”,AI工程的交付物是“稳定运行的服务”。前者关心准确率,后者关心响应时间、可用性、并发能力、成本,以及能不能再迭代。

换句话说,算法岗像是做菜,研究这道菜怎么更好吃;AI工程岗是开餐馆,要把这道菜稳定地给很多客人做出来。两者有重叠,但工作重心完全不同。作为搞工程的人,你真正要解决的是环节问题:模型推理的性能瓶颈在哪,数据怎么流转,接口怎么暴露,请求并发大了会不会崩,用户反馈了坏结果怎么定位修复。

所以如果你是后端工程师背景,转AI工程其实有天然优势。你已经理解了服务化、缓存、权限、日志这些东西,缺的只是模型和AI周边工具链的认知。反过来,算法同学转AI工程反而要补不少工程课。这也是为什么ai-engineering-from-scratch这类项目对后端同学特别友好的原因——你是在已有的工程能力上叠加AI能力,而不是从零开始学一门全新手艺。

1.2 从零到一需要的技能地图

我按项目会碰到的真实环节,把AI工程涉及的技能拆成四块:

  • 数据工程能力。怎么收集、清洗、切分、构造数据,怎么把非结构化文档转成结构化检索库。这块经常被新手忽略,但一个AI项目的上限,往往取决于数据准备的精细程度。
  • 模型应用能力。包括怎么调用大模型API,怎么设计提示词,怎么搭检索增强生成(RAG)链路,怎么选择开源模型并做推理部署。不需要你会自己训练基础模型,但得会选模型、会调推理参数。
  • 服务化能力。用FastAPI或者Flask把AI能力封装成HTTP接口,能处理流式输出,能做权限和限流,能挂到容器里。
  • 运维与观测能力。会用Docker,知道GPU显存怎么监控,能看懂日志,能记录每次请求的输入输出用于排查。

这四块不是简单的加和,而是要串起来跑通一个完整链路。你有一个目标场景,比如做一个企业内部知识库问答机器人,你得知道文档怎么进来、向量怎么存、检索怎么召回、答案怎么生成,整个过程还要有人盯着,出问题知道看哪。

1.3 先做一次自我评估,对照查缺补漏

我建议新人拿到一个from scratch项目之后,第一步不是直接开始敲代码,而是先做一次摸底。把上面四块技能列一个表,按“完全不会、知道概念、写过Demo、有生产经验”四个等级给自己打打分。绝大多数人卡在“模型应用”和“服务化”之间的缝隙里,也就是模型跑起来了,但不知道怎么写接口供外面调用,也不知道怎么优化延迟。

这块评估还有个作用,就是帮你决定先投入精力补哪块。如果Python不太熟,不要硬着头皮去啃机器学习理论,优先补语言和基本的数据处理;如果你已经能用LangChain搭出来一个简单链路,那就应该把重心放到工程化上,哪怕效果一般,也要先把接口、日志、部署这些骨架搭起来。别追求一次到位,AI工程本来就是“先能用,再好用”的过程。

2. 从零学习的技术栈选型,少走冤枉路

2.1 编程和数据基础:Python确实够用

AI工程的语言选择基本不用纠结,Python是生态最全的选项。你可能听说过有人用Rust或Go做AI推理服务,那是优化到了后期才考虑的事,起步阶段用Python能把所有精力花在业务链路上。

基础部分我建议分三步走。第一步,掌握Python的语法、虚拟环境、依赖管理,能写脚本处理数据;第二步,学会用Pandas或者纯Python对常见格式做预处理,很多AI项目的数据清洗就是这一步;第三步,了解异步编程的基本用法,因为后面写API服务时,异步是处理高并发的关键。

我已经见过不止一个新人在from scratch项目里栽在依赖管理上。本地环境能跑,到了服务器上就各种报错。所以从一开始就养成用虚拟环境、锁定依赖版本的习惯,哪怕只做一个小项目也要这样。另外,别急着学各种高级语法,AI工程里大量代码是平铺直叙的流程脚本,能写出清晰可读的结构比花哨的语法重要得多。

2.2 机器学习和深度学习核心:抓住一条主线

不建议普通工程师花几个月从头学机器学习经典课程,那样投入产出比太低。我的建议是抓住一条主线:先从神经网络和梯度下降的基本概念入手,然后重点理解Transformer,因为现在绝大多数模型都是基于这个结构。

学习这部分最好的方式,是找一个开源的模型推理代码跟着读一遍,看输入怎么从文字变成token,token怎么经过Transformer,最后一层怎么变成一个文字序列。不要求能背住attention公式,但你要能回答“为什么很多大模型有上下文长度限制”“为什么同样一个模型,换一段提示词效果差别那么大”。

如果你连“模型”和“服务”的关系都没理清,可以这样类比:模型就是一个极复杂的计算函数,你输入一段文本,它输出下一段文本。部署模型就是把这个函数变成可被其他程序调用的接口。至于训练过程,你暂时只需要知道它是怎么产生的,不需要重走一遍。

2.3 LLM应用开发:把模型变成产品能力

这里要单独说下大语言模型应用开发,这是ai-engineering-from-scratch里面最关键的一块。你不需要参与模型训练,但你要知道怎么跟模型交互、怎么把模型能力接进产品流程。

最基础的是一个API调用封装:把用户输入拼到一个提示词模板里,调模型接口,拿到输出,再解析结果返回给用户。你想做知识库问答,就在调用模型前加一个检索步骤:根据用户问题找出最相关的文档片段,再把片段和问题一起交给模型回答。这个模式就是RAG,也是当前落地最广泛的大模型应用形态。

在这一步你还会接触到提示词工程。不要把它想得太玄,本质上就是你怎么给模型布置任务。你需要学会把指令写清楚,把限制条件前置,把输出格式固定下来。好的提示词能显著减少模型乱跑偏的概率,而且它是成本最低的效果优化手段——改文本永远比重训模型便宜。

2.4 部署与运维基础:GPU、Docker、API这些绕不开

很多人学AI工程最后卡在部署上,因为本地跑得好好的模型,一到服务器就出状况。这块至少你要掌握三样东西:Docker的基本使用、GPU环境的概念、API服务的发布方式。

Docker的作用是把你整个运行环境打包带走。你本地可能装了正确的Python版本、CUDA版本、依赖库,但换台机器就全乱了。写一个Dockerfile,把你需要的环境固定下来,这样无论部署在哪,跑起来的逻辑都一样。

GPU你需要了解的有几个概念:显存是模型推理时的主要瓶颈,不同大小和精度的模型占用显存不同;CUDA是NVIDIA显卡的计算平台,装错版本会直接导致模型无法加载。先不需要深入了解底层原理,但要能看懂报错信息是缺依赖还是显存不足。API服务方面,FastAPI是目前最顺手的框架,自带接口文档、参数校验和异步能力,足够应对大多数场景。

3. 从零实操:跑通一个RAG问答助手的完整链路

3.1 为什么第一项目选RAG

我觉得最适合作为from scratch第一个完整项目的,不是什么ChatBot聊天窗口,而是一个基于自有文档的RAG问答系统。原因在于它覆盖了AI工程的大部分核心环节:数据加载、文本切分、向量化、存储检索、提示词构造、模型推理、接口封装。而且它的业务价值直观,无论是用工作文档、产品手册还是学习笔记,都能做出一个有用的东西,而不只是一个练手玩具。

另外RAG系统难度适中。不需要你训练模型,也不需要处理复杂的多轮会话逻辑,但你做完之后,对“AI应用是怎么串起来的”会有非常具体的体感。之后再做Agent、再搞微调,就是在已有的底子上加东西,不会觉得断档。

3.2 准备数据和向量库:先定切分策略

RAG链路里,文档切分和向量化经常决定了最终效果的一半。我建议第一版直接做一个最小闭环,不要设计太复杂:读取文档,按固定长度切块,每块之间留一些重叠,然后生成向量存入向量数据库。

切块长度该怎么定?主要看两个因素:模型支持的上下文长度,以及你的业务问题需要多长的上下文才能覆盖答案。一般在200到800字之间比较常见,可以结合具体文档调整。我把常见参数和适用场景整理了一下:

参数常见值适用场景注意点
切分大小(chunk_size)256~1024文档结构清晰、问题偏专题太短信息不完整,太长容易混入噪声
重叠(overlap)50~200句子和段落语义有连续性的场景防止关键信息被拦腰截断
Embedding模型bge-m3、m3e、text2vec等中文场景优先考虑按你的语言和领域选择,向量维度影响检索速度

这里要提醒一个新手常犯的错误:切分是纯文本层面的操作,但语义是段落甚至全文级别的。如果你机械地按字数切分,可能一个句子的前半段和后半段被分到了不同的向量块里,检索时反而召不回正确内容。所以切分策略的重点不是选一个神级参数,而是你在处理具体文档时,要尽量保持段落完整性。我的做法是,先按文档原有结构拆出段落,再对大段落做进一步切分。这比一上来就套固定切块参数有效得多。

向量库的选择上,第一版用轻量的就行,比如Chroma或者FAISS。它们支持本地运行,不需要额外起独立数据库服务。当你数据量到达百万级别,再考虑换成专门的向量数据库也来得及。用什么Embedding模型,我建议优先选社区主流、中文效果好的方案,比如bge系列,维度适中,部署成本低,检索效果在大多数业务场景足够用。不要把时间花在反复比较不同Embedding模型上,那属于后期调优项。

3.3 用FastAPI封装服务和异步处理

数据准备完之后,下一步是写一个服务把整个链路暴露出去。这里我强烈推荐FastAPI,它自带异步支持,接口文档是自动生成的,调试起来非常方便。

核心接口至少要有两个:一个是文档上传接口,接收文件后做切分、向量化、入库;另一个是问答接口,接收问题,从向量库检索相关片段,拼接到提示词里,调用模型得到答案。这两个接口拆开设计有个好处:数据入库是低频操作,问答是高频操作,两者可以独立扩缩容,互不干扰。

问答接口的伪代码大致是这样:

from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Question(BaseModel): text: str @app.post("/answer") async def answer(q: Question): # 1. 向量化用户问题 question_vector = embed_query(q.text) # 2. 在向量库中检索相关文档片段 candidates = vector_store.search(question_vector, top_k=5) # 3. 构造提示词,把所有候选片段作为上下文 context = "\n---\n".join([c.text for c in candidates]) prompt = build_prompt(context, q.text) # 4. 调用大模型生成回答 result = call_llm(prompt) return {"answer": result}

异步处理是这里比较容易忽视的点。如果你在接口里用了同步方式调用外部模型API,遇到并发请求时,进程会被阻塞,一个请求没返回,后面的请求全堵住了。更合理的做法是使用异步HTTP客户端,在等待模型API返回时让出控制权,这样同一个进程就能同时处理多个请求。

我在最开始写自己的RAG项目时就是忽略了这一点,压测的时候发现并发数一上来,响应时间跟着翻倍。最后排查到根因就是同步调用阻塞了事件循环。改动其实不大,但效果差异非常明显。还有一个容易被忽略的点:检索阶段的耗时虽然比模型调用短,但在高并发下同样会放大压力。给检索结果加上缓存是性价比很高的优化,比如短时间内遇到相同或高度相似的问题,直接从缓存返回结果,不再重新走一遍向量检索和模型调用。

3.4 部署到GPU机器上:从本地到容器

本地跑通之后,下一步是部署到服务器上。这里需要一台有NVIDIA显卡的机器,哪怕只有一块入门级GPU,也能让开源模型跑起来。你会用到的两个关键工具是Docker和容器运行时。

一个典型的部署流程:写一个Dockerfile,把Python环境、模型文件、代码都打包进去;启动容器时把GPU参数传给Docker;服务起来后,外部通过端口访问FastAPI。

很多人在这个环节遇到的问题,不是代码逻辑问题,而是版本兼容问题。比如CUDA版本不对,模型加载报错;Python版本不对,某个依赖库编译失败。这些问题很难一次搞定,我的建议是,尽量使用官方镜像和带版本号的依赖声明,不要用latest。记录下当时能跑通的完整环境组合,下次复制这套组合,成功率会高很多。

如果你的模型选的是开源权重模型,比如Qwen系列或者Llama系列,在GPU上跑推理时建议用专门的推理框架,比如vLLM。它能提供更高的吞吐量和更低的延迟,还兼容OpenAI风格的接口。这样你在应用层写的调用代码,和调用线上大模型API的代码几乎是同一套,后面切换起来也方便。

4. 性能优化和踩坑实录:从“能跑”到“扛得住”

4.1 显存不足、推理太慢:模型量化与效率调优

等你把服务搭起来,立刻会碰到两个现实问题:显存不够用、推理速度慢。尤其是单卡小显存的环境,这两个问题几乎是必然出现的。需要从模型和应用两个层面同时想办法。

模型层面的优化,核心是量化和降低精度。最常用的方案是4bit量化,能把模型在显存中的占用缩小到原来的四分之一左右,很多原来跑不动的模型,量化后就能在消费级显卡上推理了。要注意的是,量化后的输出质量通常会有轻微下降,但对于大多数问答场景影响不大。如果对质量敏感,可以先做效果对比,再决定要不要量化。

在应用层面,效率优化主要靠批处理(batch)。单个请求逐个推理,GPU利用率很低,但如果把多个请求攒起来一起推理,吞吐量能提升好几倍。不过批处理会引入等待时间,你需要根据实际情况设置一个最大等待时间,比如攒够了8个请求就立即推理,或者最多等200毫秒,避免第一个请求等到超时。

我自己的压测经验是,先监控GPU利用率,如果利用率低于30%,说明瓶颈不在显存而在吞吐设计上,优先考虑批处理和并发优化;如果GPU利用率已经很高但响应时间依然较长,再考虑换更快的模型或者减少上下文长度。先看瓶颈在哪,再动手优化,不要想着一上来就换模型。

4.2 回答质量差:检索策略和提示词要一起调

很多人做完RAG第一版的感受是:“模型回答得不太对”。这里面有三个常见原因:检索召回了错误内容、提示词没有明确限定基于给定上下文回答、检索没有返回足够的相关信息。

这三个原因里,检索召回错误是最常见的。解决思路是调整top_k、改变切分策略、对检索结果做重排。top_k太小,可能漏掉关键片段;top_k太大,模型容易被无关片段干扰。我习惯在可接受的延迟范围内适当调大top_k,然后再加一个重排步骤,把最相关的几个片段排在前面,让模型优先看到。

提示词也需要一起配合调整。在RAG场景里,提示词至少要明确三件事:你是干什么的、只能基于哪些内容作答、遇到回答不了的情况怎么处理。实战里我常看到的问题是,开发者在提示词里写了“请根据上下文回答”,但忘记加“如果答案不在上下文中,请明确说不知道”。缺少这句话,模型就会自由发挥,把不存在的答案编出来。

效果调优是一件需要耐心的事。我的建议是准备一个固定的测试问题集,每次改动之后用同一组问题重新评估,不要靠感觉判断“好像好了一点”。所谓好和不好,要有对比意识。长期来看,维护一个业务相关的问题集,是整个项目持续迭代的重要基础。

4.3 接口偶发超时:并发、日志和错误处理

服务刚上线时都会遇到一类问题:偶尔有请求超时,但不是每次都超时。这种问题最让人头疼,因为它不可稳定复现。查下来,原因往往集中在几个地方:数据库连接池不够、外部模型API偶发变慢、跨区域网络延迟波动。

排查到根本原因之前,建议先把超时边界设计好。API调外部模型时一定要设置超时时间,默认值往往太长,用户等不起。同时要做好重试,但是要注意重试策略:模型API返回错误,可能是临时故障,间隔一两秒重试一次是可以的;如果是请求本身有问题,比如触发了内容审核,那就不该重试。

日志是排查这类问题的关键。很多项目日志只记一行“请求成功”或“请求失败”,这远远不够。至少应该记录下来:请求ID、输入问题、每一步的耗时、模型输出、当时的错误信息。有了这些信息,你才能判断到底是检索慢、模型调用慢、还是网络问题。别小看这一步,我之前调试过不少“偶发超时”,最后都是靠详细日志锁定了问题点,而不是靠猜。

4.4 上线后怎么监控:别再用print调试线上服务

本地开发可以用print随便打印,线上不行。线上服务一定需要结构化日志和基本的监控面板。所谓结构化日志,就是把日志输出成JSON格式,包含时间戳、请求ID、模块名、耗时、状态这些字段,方便检索和过滤。

监控面板方面,不用一上来就上全套系统。我的建议是先做两件事:第一,接口层面的实时统计,如每分钟请求数、平均响应时间、错误率;第二,当错误率或者响应时间超过阈值时能报警。这两个能力可以用现成的可观测性工具实现。数据指标不需要很多,多了反而不知道该看哪个。核心是你要能在服务出问题时,尽快知道“服务是否还正常”,以及“问题大概率出在哪个环节”。

5. 从个人项目到工程化:那些文档没写但必须懂的事

5.1 评测优先:建立自己的测试集

个人项目做到一定程度,一定会面临一个问题:改动提示词后,A场景变好了,B场景却变差了,到底改还是不改?如果你没有一套固定的评估集,这个问题根本无法回答。

所以我的建议是,项目一开始就要积累测试集。每当你发现一个模型回答不好的问题,就把问题记下来,同时把正确答案或者预期行为写清楚。积少成多,几十条之后,你就有了一份基础评测集。后续无论改动哪个环节,都可以拿这份测试集跑一遍,自己打分,对比前后差异。

有了评测集之后,你甚至可以做一些更精细的事情,比如按来源文档、按业务类型、按问题难度分组,分别看效果。这样可以知道下一步优化重点放到哪:是某些类型的文档检索效果差,还是模型对特定表述理解不够。没有评测的优化都是碰运气,这不是空话,是我在真实项目里反复体会到的教训。

5.2 提示词版本化和灰度:像管代码一样管配置

很多团队重视代码版本管理,却不重视提示词管理。提示词散布在代码里、数据库里、甚至同事的聊天记录里,一旦需要回滚,完全找不到上一版是什么。解决办法是让提示词“代码化”:把提示词模板写成独立的文件,纳入版本管理,连同模型名、温度参数、输出长度一起记录。

有条件的,可以在后端做简单的配置中心,让提示词可以在不重新部署服务的情况下更新。再做细一点,可以支持按用户或按流量比例做灰度,比如先让10%的流量用新提示词,观察效果没问题,再放量到全部流量。

有一次我在调提示词时发现,新提示词在测试集上分数更高,但上线后用户反馈却变差了。原因是测试集覆盖的偏正式问题,真实用户基本都是口语化提问。后来我把测试集扩充了口语化内容才对齐。这件事让我意识到,测试和灰度不是大公司才需要的流程,哪怕个人项目,也值得把迭代过程搞稳妥一点。

5.3 成本意识:API调用和GPU租赁怎么省钱

做AI工程绕不开成本问题。如果你是调用商业API,成本主要看输入输出token数量;如果你是部署开源模型,成本主要体现在GPU租赁和电费上。这两种模式各有省钱思路。

调用商业API时,核心思路有三点:缓存重复请求、压缩上下文、控制输出长度。明明用户问过一模一样的问题,第二次还走完整流程,就是纯浪费。压缩上下文,指检索时不要把无关内容都塞进提示词,每多一个token成本就多一分。输出长度经常被忽略,很多项目让模型自由输出,结果它写一大段废话,既费钱又拖慢响应。设置一个合理的max_tokens上限,能省不少。

如果用的是GPU机器,成本控制的核心是“不要空转”。很多人租了机器跑一个模型,平时没有请求也让服务一直占着显存。可以通过定时扩缩容、按请求量自动启停,或者把多个小模型合并到一个GPU上,充分利用显存资源。部署细节没有统一答案,要根据你的流量曲线去规划。

5.4 给新人的时间和精力分配建议

如果你准备认真走一遍from scratch这条路,我建议把时间比重安排成:20%用来补语言和基础,30%用来做RAG完整项目,30%用来做部署和压测,20%用来打磨和记录。别一开始就埋进深度学习理论里,那是越长越深的坑。先做出一个能用的服务,你才会真正理解那些理论是在解决什么问题。

在遇到“不知道下一步做什么”的时候,回到你自己的场景里去:找一个你真正关心的文档集合,做一个能实际回答问题的工具。这比跟着教程做一百个和自己业务无关的小项目都管用。做完了,还可以把它分享出来,接受别人的反馈,逼自己修正问题,这是工程能力成长最快的方式。技术这东西,看十遍不如自己跑通一遍,尤其AI工程,跑通一次完整链路,后面再遇到新组件,你也会有底。

返回列表