ARTICLE DETAIL

资讯详情

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

从零构建AI工程:RAG链路、评估体系与部署优化实战

从零构建AI工程:RAG链路、评估体系与部署优化实战 这条“从零构建AI工程”的路很多人问过我怎么走。市面上关于AI的内容大部分是两类一类是教你调API的“速成教程”另一类是讲模型原理的“学术论文”。两者之间其实横亘着一条很宽的、真正决定项目能否落地的鸿沟——工程化。我自己的实践路径就是围绕“ai-engineering-from-scratch”这个项目展开的说白了就是不着调参训练也不止于调接口而是把提示工程、检索增强、评估体系和部署优化这条链路自己从地基开始一层层亲手搭起来。这篇文章不是复刻某节课的大纲就是把我趟过的路、踩过的坑、最后留下来的代码骨架按实操顺序给你捋一遍。如果你正在做AI应用开发或者准备把LLM能力接入自己的业务系统但总觉得“按教程做出来是能跑换个场景就不知道怎么办”那这篇文章应该能帮你补上中间那段最关键的工程思维。我会把为什么这么设计、每个环节的取舍逻辑、以及实测下来真正能用的配置都给你而不是只丢一堆概念让你自己猜。1. 项目整体设计与“from scratch”的边界界定先明确一下“from scratch”这个词在我这个项目里的定义。它不是让你从反向传播开始手写一个Transformer也不是让你用pip装几个包然后调通一个聊天接口就完事。我理解的“AI Engineering from Scratch”指的是不依赖任何封装好的、开箱即用的“AI应用框架”把所有LLM应用的核心环节——数据处理、检索链路、提示构造、评估验证、服务封装——都用最基础的组件自己搭一遍。1.1 核心需求解析这个项目到底要解决什么问题我给自己定的目标是不用LangChain这类重量级框架的“高级抽象”只用各模块最核心的底层库比如用OpenAI SDK直接调模型、用SentenceTransformers自己做向量化、用向量数据库原生API自己写检索逻辑从零拼出一个可以应对“私有知识库问答”场景的完整系统。为什么非要自己造轮子我用LangChain搭过好几个Demo很快真的很快但后面遇到问题就傻眼了。有人可能会问那你搭完这个“from scratch”项目最后跟你直接用LangChain做出来的东西功能上有什么区别功能上区别不大但你对系统的掌控力是完全不同的。我自己面临这些问题检索结果不对你不确定是Embedding模型选错了还是Chunk切分的策略有问题还是向量索引的相似度算法选得不对——如果全用封装好的框架你只能看到最终结果中间每一层都是一个黑盒。自己从零搭一遍等于给每个黑盒开了天窗。这个项目最适合的受众是那种“已经会用Prompt调模型也跑通过几行SDK但面对真实业务数据时总觉得心里没底”的开发者。它不适合压根没写过代码的人也不适合只想最快速度出Demo交差的人——后者直接用现成框架就好效率最高。1.2 技术选型与方案设计考量整个项目的技术栈我是在综合了国内网络环境的可得性、成本、以及可替换性之后才定下来的。核心选型如下基座模型用兼容OpenAI接口格式的国产模型服务比如智谱GLM、通义千问等这样代码里写的是统一的SDK调用方式万一哪家服务出问题换另一家只需要改一个base_url和api_key变量。Embedding模型第一版用OpenAI的text-embedding-3-small做基准对比后面切换到开源的BGE-M3或者M3E这类中英双语Embedding模型方便在本地私有化部署。这一步的思考是Embedding选择直接决定后续检索的上限后面我会展开讲。向量数据库先用FAISS做本地原型验证因为它轻量、不需要起一个独立服务适合开发调试。等到数据量超过几十万条、需要并发读写时再迁移到Milvus或者Qdrant这样的独立向量数据库服务。工程语言Python这个不用纠结理由AI工程领域Python的资源密度是其他语言没法比的。这个选型背后有个核心逻辑每一项都有“Plan B”。我对开源社区的项目有个原则——绝不把自己绑死在任何单一厂商的私有协议上。ChatGPT全家桶好用但万一业务方要求数据不出内网你怎么办所以从项目一开始我就在接口层和数据层做了抽象真正做到了“随时可以换”。2. 数据处理与知识库构建全流程从零构建AI应用第一步不是写Prompt也不是调模型而是搞定数据。这句话我反复跟人说因为绝大多数人把顺序搞反了。LLM应用的本质是在模型先验知识和你的私有数据之间搭一座桥——桥搭不好模型再聪明也没用。2.1 数据清洗与格式标准化处理我拿到的第一批测试数据是从公司内部Wiki导出的几百篇技术文档格式那叫一个乱有Markdown有PDF扫描件有Word文档里嵌的表格甚至还有几个PPT。格式化是整个流程里最脏最累的活我把它拆成三步来完成。第一步是统一文本抽取。PDF用PyMuPDFfitz把每一页的内容抽出来表格结构用camelot处理Word文档用python-docx直接读段落和表格Markdown相对简单但要小心代码块和特殊字符。第二步是清理“噪声内容”——页眉页脚、重复的导航栏文本、文档里夹带的Base64图片编码这些都是检索时最容易干扰语义的垃圾信息必须用正则批量干干净。第三步是我自己踩坑才加的统一编码格式。有几个老文档是GBK编码的不转成UTF-8的话后面向量化的结果会出一堆“乱码向量”检索效果被拖下去一大截确实不划算。2.2 Chunk切分策略与参数调优数据清理完接下来是分块Chunking。这一步的决策会直接影响后续检索的精度我自己的第一版做得比较粗暴——直接按每512个字一段切死结果检索出来的片段经常前言不搭后语。我后来整理出的组合策略是“结构感知切分 滑动窗口重叠”。具体来说优先按Markdown的标题层级#、##、###作为天然切分边界把每个标题下的内容视为一个语义块如果某个标题下的内容过长超过800字再按段落边界二次切分每个Chunk之间保留3050个字符的重叠区保证跨Chunk的上下文不丢。这里我提一个关键参数——chunk_size和overlap的比例大概控制在15:1到20:1之间。我刚开始把重叠设成100字符结果一个知识点被重复塞进好几个Chunk检索去重工作直接翻倍后来调成30-50字符效果好很多。这个数值没有绝对的黄金标准跟你数据的平均段落长度强相关用我上面给的初始值跑一遍再结合检索测试结果微调就行。做完分块之后我给每个Chunk做了一套“元数据标记”来源文档编号、所属章节路径、文档更新时间。这套元数据在后面做过滤检索和结果展示的时候帮了大忙——用户问“去年上线的那个功能怎么配置”我可以直接用时间元数据过滤掉过时文档这是纯向量检索做不到的。3. 检索增强生成RAG链路实现数据准备好了接下来就是RAG的核心链路。我对RAG的理解通俗点讲就是用户问一个问题大模型没见过你的内部文档所以你需要先在一堆企业文档里找出跟问题最相关的几句话然后把这几句话连同问题一起喂给大模型让它“看着材料回答”。整个RAG链路的核心竞争力就藏在这“找出相关几句话”的环节里。3.1 Embedding选型与向量化实现Embedding是整个检索链路的地基它的作用就是把一句话或一段话转换成一串高维向量——语义越接近的内容在向量空间里的距离越近。这段话解释起来很学术但实际操作里就是一件事选对模型才能让“让检索到的东西真正相关”这件事成为可能。我自己做了几轮对比实验发现不同Embedding模型在中文技术文档上的差异大得吓人。我量化成一个直观的对比结果如下模型维度中文语义理解检索Top-5命中率备注text-embedding-3-small1536良78%需要联网API调用text-embedding-3-large3072优84%成本高、维度大、延迟高BGE-M31024优87%开源、支持本地部署、多语言M3E-base768良75%轻量、速度快最终我选了BGE-M3做主力原因很简单检索效果最好且能本地部署。不过Embedding模型选完不是一劳永逸不同领域文档有不同表达习惯选好模型后一定要用你自己的文档跑一批检索验证让测试结果来拍板用模型跑一批检索看效果再定。向量化流程本身不复杂核心代码如下基于FlagEmbedding库from FlagEmbedding import BGEM3FlagModel model BGEM3FlagModel(BAAI/bge-m3, use_fp16True) chunks load_all_chunks() # 加载前面处理好的文档分片 embeddings model.encode( chunks, batch_size32, max_length1024, return_denseTrue, return_sparseTrue ) # 保存向量供后续构建索引使用 save_vectors(embeddings[dense_vecs])这里有个细节要注意BGE-M3同时支持稠密向量和稀疏向量这个概念下节会展开讲。另外跑向量化的时候我习惯用use_fp16True开半精度能用更少显存跑更大批量而且对结果精度几乎没有可感知的影响。3.2 混合检索与重排序机制做AI工程跟做家常菜有点像最开始用单一检索方式出来的结果往往不够“有滋有味”纯粹靠向量相似度做召回经常出现结果“沾边但不精准”的问题。比如用户问“数据库连接超时怎么办”向量检索只找语义相近的句子结果召回了“数据库连接被拒”——方向对但没命中要害如果想精确匹配某个报错码或接口名向量又不如关键词靠谱。这个问题我在第一版就撞上了逼着我上了第二套检索方案混合检索Hybrid Search就是同时用“语义相似度”和“关键词匹配”两条腿走路。具体实现是向量检索通道稠密检索用前面算好的Dense Vector做余弦相似度召回负责理解语义。关键词检索通道稀疏检索用BGE-M3算出的稀疏向量Sparse Vector作为补充它本质上暗含了BGE论文里结合BM25的思路能精准命中包含特定术语和编号的Chunk。两路结果合并用Reciprocal Rank FusionRRF做分数融合把两边的排名综合出一个最终分数。再做一层“重排序”Rerank用一个Cross-Encoder模型对候选人逐对打分把最相关的几个排到最前面。写到这里你可能觉得链路好长每个环节都挺费时间——但你真把每一步做扎实了检索效果是从60分到90分的跨越这层功夫省不了。Rerank这一步我用的是bge-reranker-base模型体积不大但效果立竿见影。处理流程就是一次query跟10个候选Chunk拼成对让模型输出一个相关度分数然后按分数重新排序。Top-5命中率从混合检索的87%又能往上涨不少。3.3 提示词动态构造与注入策略检索把材料捞上来以后第二个核心环节是“怎么把材料和大模型结合起来”。很多人的第一反应是直接把所有检索结果一股脑拼进Prompt——这其实是个大坑检索出来的10个Chunk里往往有两三个是不太相关的模型会被这些干扰项带偏顺着不相关的上下文信口开河。我的策略是重排序后只取Top-3到Top-5个Chunk并在Prompt里明确告诉模型“只用以下资料回答不要掺入你的先验知识”。动态构造的Prompt模板长这样你是一个企业知识库助手。请严格按照【参考资料】中的内容回答用户问题。 规则 1. 如果参考资料中找不到明确答案直接回复资料库中未找到相关信息禁止编造。 2. 回答时需要引用对应参考资料编号格式如[1][2]。 3. 不要回答与参考资料无关的内容。 【参考资料】 [1]第1个Chunk内容 [2]第2个Chunk内容 ... 【用户问题】 这里的用户问题原样插入这套Prompt在一次测试里把一个最容易触发幻觉的问题“你们平台支持导出哪些格式”的回答准确率从62%拉到了95%以上。核心在于规则设计给了模型一个“不知道就直说”的出口模型沿着“只能说知道的东西”这个安全线作答幻觉比例自然就掉下来了。4. 评估体系与生产化部署链路搭通了、演示也能跑了别高兴太早第二个深水区是“你怎么证明这个东西效果是好的”。凭感觉看几条回答就拍板这是Demo思维真正要做AI工程你需要一套能源源不断给出反馈的评估体系。4.1 离在线评估矩阵与指标解读我参照RAGAS的评估思想设计了一套自己的评估体系用三大核心指标来衡量链路质量忠实度Faithfulness检查回答是否严格基于检索到的上下文有没有“自由发挥”。这个指标我通过在Prompt里要求模型给自己“引用打分”来近似计算简单说就是看回答里的关键论断能不能在检索到的原文里找到出处。答案相关性Answer Relevance回答是否真正针对用户的问题而不是自我发挥写了一堆废话。用一个较小的模型给“问题回答”的组合打分我实测下来跟人工判断的相关性在80%以上。上下文召回率Context Recall给定标准答案看看检索到的Chunk里包含多少能支撑答案的内容。这个指标是检索环节的核心体检表。评估数据集的建设我采用的思路是“先人工后增强”人工挑出50个有代表性的用户问题逐条手工写好标准答案和参考依据再让大模型基于文档库仿写200个同风格问题跑批量评估。每一次链路调整之后就用这套数据集回归一遍拿分数对比来确认到底哪次优化是正向的。4.2 服务部署、并发优化与成本控制评估没问题之后才进入部署环节。我采用的是FastAPI做服务封装拆成/retrieve只检索和/chat完整RAG问答两个接口。为什么要拆两个因为实际业务里有些场景只需要检索不需要生成比如搜索引擎式问答拆开之后两个接口各自的负载特征更清晰后续按需做资源扩缩容也更方便。并发层和成本层有几个关键优化实测下来效果很明显Embedding结果缓存问答型场景里用户问题经常是相似的比如“怎么导出Excel”和“如何导出Excel表格”如果每次都重新向量化纯属浪费。我用Redis做了一层Embedding缓存把文本hash和向量存起来命中率实测在25%左右直接省了四分之一的向量化成本和时间。语义缓存再进一步如果新问题跟某条历史问题在语义上足够接近余弦相似度0.95那连检索和LLM调用都省了直接把缓存里的历史回答返回。这个策略能用Redis加一个简单的向量索引实现。Prompt缓存对完全相同的Prompt模型服务商侧一般有API层面的自动缓存计费优惠。即便用的第三方服务不支持也可以自己按Prompt哈希做结果缓存。部署时的详细参数我也总结了拿来就能参考项目推荐配置说明并发上限与模型API限流对齐预留20% buffer避免大量请求排队超时超时设置检索3s、生成30s、整体45s超时直接失败快速返回别让用户干等记忆/上下文窗口4轮对话摘要 当前问题历史太长会冲淡检索结果占比连续对话只携带“问题改写后的检索结果”不携带原文历史省Token4.3 上线后的监控告警与异常发现部署只是开始上线后你必须盯着它。我搭的监控体系分三层——最底层是基础设施监控CPU/内存/GC这些指标写个脚本定时检测就行用现成PrometheusGrafana最省心。中间层是AI链路专属监控比如“检索耗时p95”“向量化耗时”“Rerank耗时”这些跟生成模型强相关的指标。最高层是业务质量监控这里有一个我强烈推荐必须加的指标“检索失败率”也就是Top-3检索结果的平均相似度低于某个阈值的比例。这个指标一旦显著上升几乎可以断定知识库里的数据或者Embedding链路出了问题——它经常比用户投诉早上好几个小时发出警报。我上线后第一次真实事故就是靠这个指标救回来的。数据更新进库时格式出了兼容性问题新增的几百个文档向量化出来全是不合理的向量检索质量直线下降。用户那边感知还不明显的时候监控面板上的“检索失败率”已经跳红了——我赶在投诉产生之前就把数据回滚修复了。5. 踩坑实录与排查方法速查最后这部分我整理一下整个项目从零搭建过程中遇到过的典型问题。这些问题我基本都在网上翻了大量资料、踩过坑之后才总结出来的直接给你按“症状—原因—解法”列个速查表希望能帮你少走几小时弯路。症状根因分析解决措施检索结果看似相关但答非所问Chunk切分把完整语义切断导致上下文不完整换用结构感知切分增加重叠区或者调整Rerank阈值相似问题结果有显著差异没开随机种子或温度参数过高生成时固定temperature0.2以下top_p设为0.9回答总说“资料中没有”但文档里有检索召回率不够正确答案没进入Top-K增大检索候选数混合检索中给关键词通道更高权重某个领域的文档几乎不被检索到Embedding模型对该领域术语不敏感换垂直领域Embedding模型或微调专用Embedding系统响应极慢吞吐上不去向量化或Embedding调用占了大量时间加缓存检索和生成异步化用FP16推理加速数据更新之后效果反而变差新旧数据向量语义冲突或脏数据污染新增数据先跑一轮质量校验相似度分布检查再手动触发向量索引重建有了这套速查表排查问题基本能按图索骥不用每次从头疑神疑鬼。再单独说一个我在“AI工程from scratch”过程中最有价值的体会这条路线真正的收获不是省了哪个框架的License费而是你把AI应用的每一个环节都“亲手摸过一遍”之后对系统的直觉灵敏度完全不一样了。你不再需要靠猜来定位问题——看到现象就能大概率判断是哪一层出的问题这个能力上的跃迁远比你照着教程搭出的那一堆“会跑的Demo”值钱得多。最后再分享一个小技巧把整个“从零实现”的工程过程写成技术笔记每个环节的对比实验数据、踩坑过程、最终参数决策都记录下来。这份笔记既是你的个人知识库也能成为面试和对外分享时最有说服力的“实战资产”。如果你也打算从零构建一次AI工程我强烈建议你也记下属于自己的那份“踩坑录”。
返回列表