ARTICLE DETAIL

资讯详情

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

AI工程化实战:从零搭建文档问答系统的完整路径

AI工程化实战:从零搭建文档问答系统的完整路径 不开源的话光“怎么把模型塞进自己的服务里”就是一个大坑开了源部署、微调、数据管线的活又是一套完全不同的技能树。我身边不少人是在这一步被劝退的要么被“AI”两个字吓住要么被铺天盖地的教程带偏跑了个demo就以为懂工程了真上线才发现连日志都不知道怎么查。所以这次我干脆以自己最近从零搭建的一套文档问答系统为线索把AI工程化的完整路径拆开揉碎讲一遍——从数据准备、模型选型、微调决策到部署监控、成本优化、故障排查。这套流程不依赖某一家云厂商也不绑定某个特定的模型你在自己的电脑上、自己的服务器上都能照着走一遍。1. 决定做AI工程化之前先想清楚“工程化”和“写脚本”的分界线在哪里1.1 我接到的第一个AI项目的真实起点事情要从一个很普通的业务需求说起公司内部有大量产品文档、技术手册和客服问答记录散落在不同的wiki和共享盘里每次找答案都要翻半天。老板的想法很简单——“搞个AI问答机器人呗把文档喂进去让它自己回答。”听起来像是个周末就能搞定的小项目但真正开始动手我才发现这压根不是一个“训练模型”的问题而是一个系统性的工程问题。最直接的冲击来自数据几千篇文档格式五花八门有PDF、有Word、有Markdown还有不少扫描件。每份文档的排版、层级、术语都不同有的甚至带页眉页脚和表格。我原以为“喂给AI”是个简单动作实际上你要先解决“怎么把文档变成模型能理解的干净文本”这件事。等到数据清洗完又一个问题冒出来了模型到底该用哪个用商用API还是开源模型要不要微调向量检索该怎么做回答质量怎么评估上线后用户多了怎么办这些问题没有一个能在写脚本的阶段想到但它们恰恰是“工程化”的真正含义。1.2 工程化的核心元器件拆解数据层、训练层、部署层、监控层如果让我用一个类比来说明AI工程化和普通脚本的区别我会说写脚本像是做一个手工零件虽然能用但只能解决当下的一个问题工程化则是搭一条流水线要考虑原料供应、加工流程、质检标准、故障检修还要算生产效率。对AI系统来说这条流水线至少包含四个核心层缺一不可。第一层是数据层负责原料的采集、清洗、切分和版本管理。没有这一层后面的一切都是空中楼阁。第二层是训练层或者更广义地说模型层包含模型选型、微调、评测你要搞清楚“现成的模型够不够用”“要不要为特定领域再训练一步”。第三层是部署层涉及推理服务、接口封装、资源调度和并发控制它决定系统能不能在真实流量下稳定跑起来。第四层是监控层做的是日志收集、质量评估、效果回归和告警没有它你就像一个蒙着眼睛开车的人。我当时犯的一个典型错误就是一开始把注意力全放在第二层天天琢磨“该用哪个模型”结果数据没整理、评测没设计等到模型接进来才发现连个客观标准来判断好坏都没有。这是新手最容易踩的坑——你以为是模型不够强其实是工程链路上的其他环节在拖后腿。2. 从零搭建的第一关数据收集、清洗与验证远比模型选型更耗时2.1 数据从哪来冷启动阶段的三种来源和取舍对于一套从零开始的AI系统数据收集永远是最先要面临的现实问题。我当时面对的是三种典型来源第一是企业内部的wiki和知识库导出后是HTML或者Markdown格式结构相对清晰但噪声大夹带着导航栏、广告位和大量无关链接第二是历史客服对话记录以Excel和CSV为主口语化严重错别字多还有各种脱敏不彻底的风险第三是产品PDF手册最头疼因为它们里大量内容是表格和图片PDF解析器稍微不给力内容就乱成一团。面对这三种来源我的处理策略是分优先级先处理wiki和Markdown类数据因为它们质量相对高能快速构建一个可用的初始语料库其次处理PDF手册但OCR和版面解析要额外投入精力最后才处理客服对话记录因为它们需要大量的清洗和脱敏。如果你反过来一上来就跟最脏的数据较劲大概率第一周就被干趴下了。我建议冷启动阶段的原则是“先让系统跑起来再逐步喂更多数据”别指望一次就把所有数据完美入库。2.2 清洗脚本里的那些细节去重、去噪、格式归一化数据清洗是这个阶段最无聊但又最不能跳过的工作。我给你看几个我实际遇到的细节你就明白它有多琐碎文档去重同一个文档在不同目录下有多个版本内容相似但又不完全相同。我用的是MinHash近似去重先把文本分片成n-gram集合再算Jaccard相似度相似度超过0.85的文档标记为重复人工确认后丢弃旧版本。这个方案对长文档很有效但对短文本容易误伤所以阈值要调。格式归一化编码统一转成UTF-8换行符统一成\n全角半角符号统一标题层级统一成#式Markdown。这些看似无关紧要的差异到了文本切分和向量化阶段都会变成隐患。比如全角逗号和半角逗号混在一起切出来的文本块就可能带上莫名其妙的空字符。页眉页脚和导航噪声从wiki导出的HTML里要专门写规则去掉“相关文章”“目录导航”这些区块从PDF解析出来的内容要去掉页眉、页码和页脚的重复文本。我一开始偷懒没做这一步结果向量检索时经常命中的是“第3页共20页”这种垃圾片段气得我半夜爬起来加正则。这一步给我的最大体会是清洗脚本本身不复杂复杂的是你必须对数据内容有足够敏感度。别急着写自动化先抽样看50条原始数据摸清噪声规律再动手设计规则。2.3 验证集怎么构建标注一致性比想象中更关键清洗完数据下一步不是急着灌进向量库而是先构建一套可以用来评估后续模型效果的“金标准”数据。我当时的方法是从各个业务部门收集了200个真实问题每个问题由两个人分别标注标准答案和对应的参考文档片段然后比对标注一致性。标注不一致的地方去和业务方确认最终形成一份带标准答案的验证集。这个过程比想象中耗时但价值巨大。因为后面不管你是调prompt、切文本块还是换模型、做微调都要靠这套验证集来量化“变好了还是变坏了”。没有验证集你的所有优化都是凭感觉很容易被几个典型案例误导。我后来还做了一步扩展把验证集里的每个问题按类型打标事实型、操作型、对比型、主观型这样评测时能看清系统在哪类问题上表现差从而倒推优化方向。3. 模型选型与微调的真实决策过程跑通demo只是万里长征第一步3.1 选型背后要算的三笔账性能、成本、可控性数据备好了终于到了选模型环节。但请注意选模型绝不是一个单纯比“谁聪明”的过程而是要算三笔账性能、成本、可控性。性能包括理解能力、生成质量和检索能力成本包括训练成本、推理成本和人力成本可控性则包括数据隐私、本地部署可行性、以及你是否能对模型行为进行干预。我当时对比了商用API和开源模型两大路线。商用API的优势是开箱即用、效果优秀尤其在国内服务方对中文理解做得相当好劣势是按量计费高频调用时成本飙升而且数据要出内网过不了信息安全这关。开源模型如Qwen系列、ChatGLM系列等优势是部署在自己服务器上数据不出内网没有按量计费的压力劣势是对硬件资源和工程能力要求高。综合下来我最终选了开源模型作为底座因为合规和成本这两项权重在我们场景里远高于那点性能差距。如果你面临同样的选择我建议你把决策表拉出来按每个维度的权重打分而不是被单点性能吸引。表1是我当时用的简化版比较表决策维度商用API开源模型本地部署中文理解效果优良好取决于具体型号单次调用成本按量计费长期偏高主要是硬件折旧和电费数据隐私合规依赖服务商承诺数据完全本地可控二次开发自由度低只能调接口高可微调、可定制工程复杂度低高需自建服务上线速度快较慢3.2 微调的判断标准什么时候该微调什么时候不该很多教程一上来就教你微调模型但微调绝不是默认选项。我在这个项目里一度陷入“不微调等于不专业”的焦虑中后来做了几轮对比实验才冷静下来。判断要不要微调我总结出三条标准模型是否在目标领域频繁出错、领域知识是否需要内化到参数里、以及是否有足够的高质量标注数据。以我的文档问答场景为例我先用未微调的模型配合全文检索测试发现它应对“标准操作流程”“故障排查”这类问题效果不错但在“产品版本差异”“专有名词解释”上频繁出错。这说明通用能力够用缺的是特定领域知识。此时有两个选择一是基于检索增强RAG把知识塞进上下文二是微调把知识内化进参数。RAG的优点是灵活、无需训练缺点是回答质量依赖检索质量且上下文长度有限微调的优点是一劳永逸缺点是成本高、有灾难性遗忘风险。我最终的策略是“先RAG后微调”先用检索增强把绝大多数问题解决掉再针对检索也解决不了的极小部分疑难case做微调。这种分层策略的好处是省资源、见效快、风险可控。3.3 实测对比base模型、RAG方案、微调模型的效果差异这里把实测数据放出来供参考。我用前面说过的200道验证题做了三类方案的对比纯base模型直接问不检索。准确率只有63%而且很多回答“一本正经地胡说八道”因为它没有外部知识来源。base模型RAG先检索再生成。准确率提升到86%尤其在事实型问题上进步明显。微调模型RAG在检索基础上进一步微调准确率到了91%。提升主要集中在专有名词解释和复杂指令跟随上但V100级别的单卡训练花了两天数据标注花了三个星期。这个结果告诉我们对多数业务场景RAG带来的收益远超微调微调更像是锦上添花。我见过不少团队一上来就微调结果训练数据质量不行微调后的模型反而变笨了。所以我的建议是先用最轻量的方案把系统跑起来用数据说话再决定是否投入更大的训练成本。4. 部署上线的工程细节推理服务、评估回放与模型版本管理4.1 推理服务三个容易忽略的细节显存管理、动态批处理、超时重试模型选好、效果验证通过之后真正的工程挑战才刚开始。部署推理服务时有三个细节最容易被忽略我一个个说。第一个是显存管理。很多人以为只要模型能加载进显存就算完实际上还要考虑推理时的KV Cache和临时张量。以ChatGLM3-6B为例FP16全精度加载权重约12GB推理时KV Cache可能再占2-4GB所以你至少需要24GB显存的显卡如3090/4090才能舒服地跑并发。我一开始用T4的16GB显存硬扛结果一上并发就OOM才明白要预留buffer。实践上我建议把单卡并发数控制在8左右不要为了省成本硬压并发显存溢出导致的服务抖动代价更大。第二个是动态批处理。推理框架如vLLM支持把多个请求拼成一个batch并行计算吞吐量能提升好几倍。但动态批处理不是免费午餐它会牺牲单请求延迟。我在实际压测中发现batch size从1升到16总吞吐能提升6倍但单次响应时间会从800ms上升到2000ms。所以你要根据业务的延迟要求反推并发上限而不是一味追求吞吐。第三个是超时与重试。LLM推理天然存在不确定性同一个问题有时2秒返回有时要20秒。所以接口超时不能设太短我设为30秒同时要做客户端重试机制但要加退避策略不然模型卡住的时候所有请求都重试直接把服务打挂。这些细节不在生产环境被虐几次很难提前想到。4.2 没有评估回放机制你根本不知道新模型是变好还是变坏这是我认为整个AI工程化里最重要、但最容易被忽视的环节。所谓“评估回放”就是把历史线上请求存下来当模型更新或参数调整时用同一批历史请求重新跑一遍自动对比新旧版本的输出质量。没有这个机制你换模型只能靠肉眼抽查换了也不知道是变好了还是变坏了。我实现评估回放的方式很朴素线上每次请求的输入、输出、检索的文档片段、用户反馈都存日志每周抽一批代表性请求组成回归集模型版本更新时自动跑一遍回归集按规则评分。评分有两层一层是机器评分用规则检查和内置评判模型打分另一层是人工抽验挑出分数波动最大的case来看。这套机制上线后我们避开了一次严重的“升级事故”——新模型在基准测试上分数更高但实际在特定类型问题上全面退化靠回放及时发现并回滚了版本。从此我坚信没有评估回放的模型迭代就是耍流氓。4.3 模型版本管理和回滚比代码回滚更需要设计传统软件都有版本管理但模型版本管理更麻烦因为模型文件动辄几个GB而且一个线上服务可能在同时服务多个版本。我的做法是把模型服务分为“预发”和“生产”两套模型文件放在对象存储里通过带版本的路径引用。更新流程是新模型先在预发跑评估回放通过后再切生产切的时候用负载均衡按10%灰度引流观察几个小时后全量。一旦线上发现问题回滚操作就是改一个配置指针把流量切回到上一个版本整个操作一分钟之内完成。这个设计让我在后续几次模型迭代中都能大胆试错因为我知道最差也能快速回退。如果没有这套机制你一定会在“要不要升级”这件事上变得畏手畏脚最后反而让系统停滞在旧版本上。5. 成本、延迟与稳定性的三角博弈我的实测数据与调优记录5.1 成本拆解训练、推理、存储分别花在哪AI系统的成本和传统服务完全不同它不是均匀分布在每台服务器上而是集中在三个大头训练成本、推理成本、存储成本。训练成本是一次性的包括GPU租用或折旧、数据标注人力、实验试错消耗推理成本是长期的跟着线上流量走模型越大、并发越高这一项越惊人存储成本则来自向量库、日志和模型版本增长虽然慢但容易被忽视。我这套文档问答系统的成本结构大致是这样训练阶段用了一张V100租用加上标注人力约一周时间折算下来约2000元推理阶段部署了一台双卡服务器电费加折旧每月约1500元存储方面向量库和日志每月几百元。整体算下来比商用API在中等规模调用下每月约5000元略低但如果流量翻十倍本地推理的成本优势会更明显。这也解释了为什么很多AI应用在验证阶段用API规模化之后反而转向自部署。5.2 延迟优化三板斧量化、缓存、路由策略用户对问答系统的延迟感知是很敏感的超过3秒就会觉得“卡”。我把延迟优化归纳成三板斧亲测都有效。第一板斧是量化。FP16转INT8能让模型体积缩小一半推理速度提升30%-50%而效果损失在可接受范围内我实测准确率下降不到2%。如果你用的是支持AWQ或GPTQ的模型建议直接跑量化版本性价比极高。第二板斧是缓存。高频问题如“密码忘了怎么办”“如何申请权限”命中缓存后可以直接返回这一招能把有效QPS需求砍掉40%以上。但要注意缓存必须设置过期时间和基于语义的相似度匹配不然用户换了种问法就命中不了。第三板斧是路由策略。简单问题走小模型或规则引擎复杂问题才走大模型在大模型之前加一个意图分类器能明显降低平均响应时间。我实测三类请求全走大模型时平均延迟2600ms路由分流后降到1100ms体验提升非常明显。5.3 稳定性建设限流、熔断与降级方案稳定性的重要程度是在你第一次线上事故后才真正理解的。我遇到的是典型的“热点问题雪崩”一个内部公告引发大量员工同时提问同一个问题结果模型服务被打满请求排队时间飙升最后整个系统陷入死锁。事后我补了三道防线这也是我认为AI服务必备的稳定性组合拳。第一道是限流在网关层按用户维度设置单机QPS上限超出部分直接返回排队提示而不是让请求继续往后打。第二道是熔断当模型服务的错误率超过阈值我设的10%或平均延迟超过5秒时熔断器自动打开后续请求快速失败不再打到模型上给它时间恢复。第三道是降级熔断期间自动切换到规则引擎版本比如从向量库直接返回相关性最高的文档摘要保证用户至少能拿到部分信息而不是完全不可用。这三道防线让系统在最坏情况下也能“优雅失败”而不是全线崩溃。6. 一次pipeline偶发返回离谱答案的根因定位排查链路与方法6.1 现象特定类型问题开始频繁“答非所问”在某次模型版本升级后系统整体准确率没有明显变化但“版本对比类”问题开始频繁出现离谱答案——比如用户问“V2.1和V2.0有什么区别”系统却回答“V2.1没有这个功能”。这类case在验证集上也有但比例不大一开始我没太在意直到业务方专门来投诉才意识到问题严重了。我起初怀疑是模型变了因为刚好是模型升级后出现的。于是先做了评估回放把新旧模型在同样输入下的输出拉出来对比结果发现同一个问题的检索结果变了旧模型能检索到正确的文档片段新模型却检索到另一篇完全不相关的文档。这立刻把矛头指向了检索环节而不是生成环节。6.2 顺着数据链路排查检索源变更、切块策略、向量偏差定位到检索环节后我开始逐步排查。第一个怀疑点是索引数据源是不是索引更新任务出了问题导致部分新文档没被写入向量库查了任务日志发现索引更新正常但文档库里的确有部分文档的内容和源文件对不上——原来是清洗脚本在处理某种特定表格格式时把表格的行列顺序打乱了导致语义变化。第二个怀疑点是文本切块策略。版本对比类文档通常用表格描述差异而我的切块策略是按Markdown标题切分一个表格被硬切成多块单块语义不完整检索时就失去了关键信息。这是很多RAG系统的通病切块不是按“语义完整”来切而是按“结构整齐”来切。第三个怀疑点是向量检索的相似度阈值。我检查后发现由于切块不完整相关文档片段的向量相似度被拉低部分相关片段被阈值过滤掉了剩下的不相关片段反而被召回。6.3 根因确认与修复一个清洗规则引发的连锁反应顺藤摸瓜最终确认了根因链条清洗脚本里我没料到一个“表格识别”函数在特定目录下的PDF中会把两列内容的顺序颠倒导致生成的产品对比信息文本里“新版本”和“旧版本”的指代被互换。这种语义翻转在单看文本时几乎察觉不出来但一旦和检索、生成联动系统就会一本正经地给出完全相反的答案。修复分两步。第一步是修正清洗脚本针对这类表格增加行列校验逻辑同时加入一条“自检规则”凡是解析出的文本中包含“V2.0”“V2.1”这类版本号且同时出现在同一段自动标记为人工复核候选。第二步是调整切块逻辑改用滑动窗口加标题分割的混合策略让表格内容尽可能完整保留在同一个块内避免语义被切断。修复后版本对比类问题的检索准确率从71%回升到94%。6.4 这次故障给我留下的排查方法论先数据后模型先链路后节点回头复盘这次故障之所以难查是因为表象在“生成”根源却在“数据清洗”跨越了整条流水线的好几个环节。我事后总结出一套排查顺序之后遇到类似问题基本都按这个思路走先查数据层清洗、切块、索引有没有问题再查检索层召回质量、排序阈值最后才查模型层生成有没有跑偏。这个顺序和大多数人的直觉相反但在我接触的案例里绝大多数“模型变笨了”的问题根因都在数据链路。具体做法上我会在排查一开始就同时拉三份日志——输入日志、检索结果日志、模型输出日志——先看检索结果是否靠谱。如果检索结果是对的但输出错的那是生成问题。如果检索结果就是错的那就继续往前查索引和切块。用这种“从中间切开、向两边定位”的方式能极大缩小排查范围而不是一头扎进模型参数里瞎调。写在最后的一点经验一路从零走下来最大的感受是做AI工程化真正考验人的不是那点模型调参的手艺而是你能不能把数据、模型、部署、监控、成本这些环节捏合成一个整体系统。如果你现在正准备开一个类似的项目我建议你从第一天起就按工程化的思路来先搭好数据管道和评估机制再谈模型选型和效果优化——因为效果问题可以迭代解决但骨架没搭好后面每一步都会给你埋雷。最后分享一个小技巧每次对系统做任何改动无论改prompt、换模型、调切块还是改清洗规则都把修改前后的验证集分数记录在一个表格里附带修改说明。这个习惯帮我避免了很多“改着改着变回去了”的尴尬局面也让我在向团队解释决策时有了客观依据。AI工程化没有银弹但记录和回放就是最好的防护网。
返回列表