去年做了一个挺有意思的 AI 落地项目把两个看似不搭边的需求揉在一起一个是用 RAG 做汽车故障检测一个是基于机器学习给二手车估值。做之前我也犹豫过这两个场景一个偏自然语言问答、一个偏结构化数据回归强行放一起会不会搞成四不像。但实际跑下来发现底层的数据基建、服务化框架、甚至排查思路都能复用整体收益比单独做两个独立小项目高不少。这篇复盘会把整个项目的设计思路、核心实现、踩过的坑完整过一遍给正在做类似 AI 应用落地的朋友一个参考。1. 项目整体拆解业务痛点与方案选型先说背景。项目来源是一个做汽车交易服务的平台他们手里有两批比较头疼的数据一批是维修厂积累的故障描述文本和维修工单另一批是过往成交车辆的参数和成交价。业务方想同时解决两个问题用户报一个故障码或描述一段异响系统能直接给出可能的原因和维修建议用户想卖车或买车系统能根据车况给一个参考价格区间。1.1 两个需求为什么不能分开做一开始业务方提的是两个独立需求我也打算分两个小项目各做各的。但技术评审时发现几个共性点第一数据源是同一套基础设施。故障工单里包含了车辆品牌、车型、里程、年限、维保记录这些字段和二手车估值需要的字段高度重合。分开做意味着同一份数据要清洗两遍、存储两遍后续更新维护也要改两处。第二用户画像一致。故障检测和估值功能最终要嵌入同一个App服务的是同一批车主或买家。一个车主查完故障码顺手就会想看看这车现在还值多少钱。两个功能在交互逻辑上前缀相同。第三工程成本可以摊薄。鉴权、限流、模型服务化、日志链路、监控告警这些基础设施做一次就行。如果拆成两个项目这些成本会翻倍。所以最后决定做成一个“双引擎”系统RAG管道负责故障检测问答机器学习模型负责估值共享一套数据管线和API网关。架构上解耦数据上统一。1.2 RAG为什么比微调更适合故障检测故障检测这个场景当时有两套可选方案微调一个开源大模型或者直接用 RAG。我最终选了 RAG核心原因是知识更新和出错成本的问题。微调模型的一个硬伤是知识固化在参数里每次维修手册、故障码表更新都要重新训练和评估。汽车故障知识不是静态的新车型、新故障码、新的维修案例每个月都在增长微调跟不上的话模型就开始一本正经胡说。RAG 的路线是“检索 生成”。把维修手册、故障码含义、历史维修工单切片后存入向量数据库用户提问时先检索出相关片段再把片段连同问题一起交给大模型生成答案。这样知识更新只需更新知识库不需要重新训练模型。另外很重要的一点是故障检测这种场景需要对回答负责。RAG 天然可以给出来源引用——是哪份文档、哪个工单支撑了这个结论。微调模型给不出这种依据出了责任事故很难追溯。当然 RAG 也有自己的问题比如检索不到、检索到但没答对、上下文片段冲突等。这些在后面踩坑部分详细说。1.3 模型选型与技术栈一览RAG 这部分我用的是自建全套没有直接用现成的 RAG 应用框架因为要对接内部数据权限和后期定制排序策略。技术栈大概是基座模型一个开源的中大规模对话模型部署在内网 GPU 机器上量化后单卡可以跑。选择开源模型主要是数据不出域和成本考量。Embedding 模型选用通用中文向量模型支持把故障文本和维修手册内容映射到同一向量空间。向量数据库基于容器的开源向量库支持过滤查询和混合检索。数据量在百万级以内完全够用没必要上分布式。重排模型一个轻量级 Cross-Encoder用于对召回结果做精排。机器学习估值模型先跑线性回归和树模型做基线后来上了一个开源自动机器学习框架对比效果。整条链路用Python实现服务层用异步框架提供 HTTP 接口向量检索和重排都有缓存。2. RAG汽车故障检测从知识库到诊断报告RAG的成败六成在知识库三成在检索一成才在生成。很多项目翻车都是因为知识库没建对后面怎么调都没用。2.1 故障知识库怎么建我们手里的原始数据有三类品牌维修手册PDF、故障码表结构化CSV、历史维修工单非结构化文本。这三类数据不能直接塞进向量库必须先统一成“问题-答案-来源”的结构。维修手册是 PDF需要先做版面解析把段落和表格抽取出来。抽取后按章节切片比如“发动机电控系统”“变速箱控制模块”“ABS系统”等每个切片标注来源手册、页码、适用车型、年份范围。故障码表是结构化的格式大概像code,description,possible_cause,suggest_action P0301,Cylinder 1 Misfire Detected,Spark plug/ignition coil faulty; vacuum leak; injector issue,Check spark plug and ignition coil; test compression P0171,System Too Lean (Bank 1),Vacuum leak; MAF sensor dirty; fuel pressure low,Inspect vacuum hoses; clean MAF sensor; check fuel pressure故障码表不需要做向量化切片直接作为结构化元数据存进检索系统。系统先精确匹配故障码匹配不到再走向量检索。历史维修工单是最有价值但也最脏的数据。维修工单通常长这样“客户反映冷车启动困难怠速抖动。读取故障码P0301检查发现火花塞电极烧蚀更换火花塞和点火线圈后故障排除。”这类数据清洗要点三个去掉客户姓名、电话等隐私信息把口语化描述和维修结论拆开给每条工单打标签——车型、故障码、系统类别、问题现象、维修措施。清洗后的工单作为补充语料向量化。建知识库时我犯过一个错刚开始把所有PDF整篇塞进去结果一个切片几百行检索召回时命中一个没头没尾的大段落模型读完根本提取不到关键信息。后来改成按“故障现象-可能原因-维修建议”的结构切片每片控制在500-800字左右召回质量立刻上来了。2.2 文本切分与向量化切片大小这个参数值得细说。我当时的经验是切片太小比如128个token检索能精确命中但上下文信息不全生成时模型缺乏全局判断切片太大比如2048个token上下文够了但向量空间里整篇文档被压缩成一个点细分内容互相干扰。我最终用的策略是分块大小512字符、重叠50字符。这个配置跑下来在测试集上的召回率比256/1024两个配置都高几个点。可以用维度拆解的思路理解这个选择512字符大概能覆盖一段完整的故障描述加对应维修建议而50字符的重叠保证段落断裂时不丢失边界信息。Embedding模型选择也踩过小坑。一开始用一个轻量的中文Embedding模型检索效果马马虎虎换上更大参数量的中文开源Embedding模型后同一个query的召回精度肉眼可见地提升。当然代价是单次向量化耗时从几毫秒变成几十毫秒但知识库是一次性离线构建在线查询只影响向量检索阶段可接受。向量化时要在metadata里存好结构化字段包括来源、车型、年份、系统类别、故障码等。这样检索时可以先按metadata粗过滤再算向量相似度效率和精度都能提升。2.3 检索策略与生成链路设计早期我用了最简单的top-K向量检索默认召回3个片段。结果问题集中在两类一类是召回片段里有相关内容但答案被淹没在无关上下文里另一类是多个片段互相矛盾比如一个说“P0301优先检查火花塞”另一个工单写“P0301更换喷油嘴解决”。后来我把检索策略改成了三段式第一阶段结构化元数据过滤。用户如果直接输入了故障码比如“P0301”先按故障码表精确匹配把该故障码对应的标准描述、可能原因、维修建议作为高优先级上下文。第二阶段向量召回。把用户描述文本向量化从知识库里召回top 20候选片段。这个阶段宁可多召回不要漏召回。第三阶段重排序。用一个Cross-Encoder对20个候选片段逐条计算与query的相关性分数取分数最高的3-5条作为最终上下文喂给大模型。加了重排后答案准确率提升比较明显。纯向量召回的问题在于Embedding模型对语义相近但字面不同的表达比如“车子抖”和“怠速不稳”能识别但对故障现象和维修结论之间的隐含逻辑关系判断不了。Cross-Encoder建模的是query和document的交互关系能更好地判断“这条工单对回答当前问题有没有用”。生成链路的设计上提示词模板也需要反复调。我最终用的模板核心结构大致是你是一名有经验的汽车维修技师。请基于以下参考资料回答用户问题。 要求 1. 如果参考资料能回答请给出可能原因排序和对应维修建议。 2. 如果参考资料无法回答请明确说“当前知识库中没有相关案例”不要编造。 3. 引用资料时标注来源编号格式为[來源1][來源2]。 参考资料 {context} 用户问题{question}特别注意第2条。没有这条限制时模型在面对知识库覆盖不到的新车型故障时会一本正经编一个看似合理的维修方案这在汽车维修场景里是致命的。最终生成的诊断报告长这样故障码解析说明该故障码定义可能原因列表按概率排序每个原因附来源引用维修建议从低成本高概率项开始排查参考案例如果匹配到了相似历史工单则展示这套输出结构业务方接受度很高因为他们要的不是一个总结而是可以直接转给维修技师的可执行方案。3. 机器学习二手车估值数据、特征与模型二手车估值这个场景和故障检测完全不同这是典型的结构化数据回归问题不涉及文本生成。但难点不在模型算法而在特征工程和价格语义的理解上。3.1 数据的获取与标签构建做估值模型的第一步是把“成交价”这个标签定义清楚。同样的车在不同场景下价格可以差很多个人卖给车商是一个价车商卖给个人是另一个价4S店置换又是一个价。我们拿到的原始数据里混着多种成交类型的记录直接混在一起训练会让模型无所适从。处理方案是按交易类型分组训练。最终业务要的是个人买家对个人卖家的参考价所以模型以C2C成交记录为训练主集B2C和C2B成交记录只作为辅助特征或校验集。数据清洗的另一个重点是异常值处理。二手车价格分布以5-15万为主但数据里混着一些严重失真的样本比如用了特殊牌照的车辆、事故全损车、调表车。我按两个标准过滤成交价超出同车型正常区间上下5倍里程或年限与成交价明显不匹配。这两个规则过滤掉约3%的样本但对模型稳定性的提升远超3%。最终训练数据集大概包含十几万条真实成交记录字段包括字段说明brand_model品牌车型组合reg_year上牌年份mileage表显里程万公里displacement排量Lgearbox变速箱类型AT/MT/DCT/CVTfuel_type燃油类型汽油/柴油/混动/纯电color车身颜色seats座位数city交易城市condition车况评级优/良/一般/差transfer_cnt过户次数accident_history是否有重大事故记录trade_price成交价万元标签3.2 特征工程的重点与陷阱这一part直接决定模型天花板也是我和数仓同学来回沟通最多的地方。第一个重点是“不要用裸的品牌车型字符串”。二手车市场里同一品牌下不同车系的价格逻辑完全不同比如某品牌的运动型车和家用型车年限折旧曲线差别巨大。简单对brand_model做因子化会产生上千个稀疏列树模型能扛但线性模型会崩。我的做法是构建多层衍生特征品牌、车系、车型三代层级分别编码同时计算每个车系的历史平均成交价和价格波动系数。这样既保留了细分信息又给了模型一个可比较的基准。第二个重点是折旧曲线建模。二手车估值本质是折旧预测而折旧不是线性的新车前两年掉价最快五年后趋于平缓到了十年以上再次加速。与其让模型自己拟合这种非线性不如显式构造特征车龄平方项、车龄对数项、车龄乘以品牌档次。这几个特征对提升精度帮助很大。第三个重点是里程的处理。直接线性使用里程数也不是不对但二手车市场有一个“正常磨损带”的概念一年跑1万公里和一年跑1.5万公里车况差别没有想象中大但一年跑5万公里的车很可能被重度使用。我构造了里程偏离度特征即实际里程除以同车龄平均里程这个比值比绝对里程更能反映真实损耗。第四个坑是城市因素。同款车在不同城市价差可以达到15%以上原因包括限迁政策、新车优惠力度、消费水平等。直接用city做因子会有几百个类别而且模型学不到城市间的相似性。我改用城市等级一线/新一线/二线/三线以下、城市平均成交价、城市每千人汽车保有量等特征效果更好。3.3 模型选型与评估模型层面我用四个方案做了横向对比线性回归Ridge作为基线主要用于观察特征线性关系的解释性。 LightGBM作为主力模型树模型对表格数据的拟合能力很强训练快、调参相对少。 XGBoost作为对照和LightGBM做对比看是否有稳定性差异。 AutoGluon作为“开挂”方案自动特征工程和集成看上限能到哪里。评估指标用MAE平均绝对误差、RMSE均方根误差、MAPE平均绝对百分比误差。其中业务最关心MAPE因为老板看的是“估得准不准”的百分比口径。四组方案的最终评估结果大概是这样模型MAE万元MAPERidge线性回归1.8516.2%LightGBM1.129.8%XGBoost1.1810.3%AutoGluon1.029.1%LightGBM和AutoGluon差距不到一个百分点但LightGBM的推理速度快一个量级而且可以方便地导出特征重要性。最终线上选择LightGBMAutoGluon留作每周离线重训时的效果对照。价格区间输出这点也值得说。业务方一开始要的是一个点预测比如“这辆车值9.5万”。但用户真正需要的是一个可信区间。我最终输出的是预测价格和基于模型误差分布计算的80%置信区间。比如系统显示“预计价格9.0-10.2万中位估值9.5万”用户比看到一个干巴巴的数字放心得多后续业务转化率也更高。模型解释性方面我用SHAP做特征归因。这对业务运营尤其有用他们能拿“里程偏离度是当前价格的主要压制因素”这种结论去说服用户接受一个更低的估值比直接给个价签有说服力得多。4. 一套数据管线打通两个模块两个模块看似独立实际上可以共享一套数据底座和服务设施这是项目成本能控制住的关键。4.1 数据清洗与统一存储原始数据先落到统一数据仓库按来源分三张核心表车辆基础信息表、维修工单表、成交记录表。三张表通过VIN码车辆识别码关联。清洗规则集中管理比如VIN码校验、里程合理性校验、成交价异常值修正、隐私字段脱敏。清洗逻辑只写一遍两个模块共用从源头避免了两套数据口径不一致的问题。知识库构建和模型训练都从统一存储层读取不直接访问上游库。这样故障知识库每周更新、估价模型每月重训时数据版本可追溯出问题可以快速回滚。4.2 模型服务化与接口封装两个模型的推理服务封装为独立服务对外只暴露HTTP接口。故障检测接口和估值接口走同一个API网关复用鉴权和限流逻辑。故障检测接口设计为两个输入模式纯故障码输入如P0301自然语言描述如“冷车启动怠速抖动”。服务内部先尝试结构化匹配故障码表匹配不到再走RAG检索流程。估值接口输入车辆参数包括品牌车型、上牌年份、里程、排量、变速箱、车况等级、城市等。输出估值中位值、置信区间和主要影响因素。两个接口都要求幂等支持通过RequestID做日志串联方便追踪问题。4.3 降级、缓存与兜底策略线上系统一定要考虑模型挂了怎么办。RAG链路依赖向量库和大模型推理任何一个组件出问题接口必须快速失败而不是卡死在那等超时。我做了三级降级第一级向量检索结果为空或异常时直接降级为故障码表精确匹配如果精确匹配也没有就返回“知识库暂无此故障码信息”第二级大模型推理服务超时直接返回检索摘要不做生成第三级估值模型推理失败返回基于同车系历史均价计算的兜底估值并标记为“参考均值”。缓存策略上高频Query缓存24小时因为同一故障码和同一车型的答案短期内不会变。但注意故障知识库更新时要同步清理缓存不然新知识上线用户还在看旧答案。这里我加了一个版本号机制知识库version存在缓存key里版本更新自动失效。5. 踩坑实录与排查思路做这类项目几乎不可能一帆风顺。下面是几个典型问题和排查方法写出来供参考。5.1 RAG高频问题查得到但答不对项目上线第一周被业务方投诉最多的就是“知识库里明明有这条维修记录但模型答得不对”。排查下来有三个原因第一个是切片上下文干扰。召回片段里既有目标车型的故障记录又有相近车型的记录。模型混在一起后给出一个适用于大多数情况的通用答案。解决方法是在提示词中显式强调“优先参考与输入车型、故障码完全匹配的资料”同时在metadata里标注车型并让重排模型对精确匹配加权重。第二个是Top-K过大导致信息过载。K从3调到8后上下文塞进太多低相关片段模型主次不分。重新把K调回5同时在上下文展示时按重排分数排序低分片段放在末尾并注明“以下内容仅供参考”。第三个是知识库召回的是标准故障码表却召回不到同故障不同表现的维修案例。这是因为向量检索期间只embedding了文本没有充分运用metadata过滤。解决在向量检索前先按故障码、车型、系统类型过滤再用向量相似度排序。排查这类问题的通用思路是每次回答都记录下“实际召回了哪些片段、重排分数是多少、模型给了什么答案”。做一个结构化的评测集每次改动后跑一遍对比准确率变化。没有评测集的RAG是瞎调。5.2 估值模型误差收敛不动了模型开发中期MAE卡在1.2万左右再也降不下去。当时已经做了很多特征工程直觉上不该止步于此。后来排查发现一个严重的数据泄漏问题训练集和测试集来自同一批次成交记录但有一些接近尾盘的重复车辆同一辆车在同一时期多次成交同时出现在训练和测试中。模型相当于开卷考试看起来效果很好但上线后面对全新数据表现会明显变差。修复方案是按时间切分训练集用前80%时间的成交记录测试集用后20%时间的记录同时按VIN码去重保证测试集中不出现训练集里出现过的车辆。重新评估后测试集MAE反而“变差”到1.35万但这才接近真实线上水平。另一个问题是车龄超过10年的老车样本太少。二手车市场里老车交易频繁但样本分散车龄最大的车型往往样本量只有几十条模型对它们基本在瞎猜。解决方法是做分价位段建模把样本按价格区间分成高、中、低三档分别训练子模型投票集成。这个方案让老车和中低价车的预测误差明显下降。5.3 性能优化与成本控制RAG链路各环节都还挺吃资源的。上线初期的接口延迟在4-6秒业务方直接说不可接受。排查后发现瓶颈不在大模型推理而在向量检索和重排。向量检索本来只有几十毫秒但因为metadata过滤条件没走索引变成了全量扫描重排模型虽然效果好但单条查询要跑20次推理这20次推理是延迟大头。优化措施向量库建立结构化字段索引过滤查询耗时从300ms降到10ms。重排候选数量从20降到8延迟降低一半准确率只下降不到1个百分点。大模型推理采用流式输出首token延迟从4秒降到1.5秒。对高频故障码Query做结果缓存命中缓存时接口延迟直接降到50ms。优化后线上P95延迟控制在2.5秒以内高峰期上了一个小规模的GPU推理节点成本整体可控。6. 复盘总结与实用建议6.1 如果重做一次我会改什么第一一开始就搭建评测集而不是等项目成型后才补。RAG项目的优化循环是“改动-评测-再改动”没有评测集的每一次改动都像摸黑走路。前期花三天搭一个覆盖100条问题的人工标注评测集后面省下来的调试时间远超三天。第二会建议业务方初期就明确“价格区间”而不是“点价格”这个口径。我们做到一半才切换输出格式连带影响了前端展示和文案。如果一开始就以区间估值为目标整体开发节奏会顺很多。第三会在项目早期就把两个模块的监控面板做成统一视图。RAG的召回率和估值模型的误差漂移要放在同一个看板上排障时可以快速判断是知识库问题还是模型数据问题。6.2 给类似项目同学的几点建议RAG不是万能的选型时先确认知识更新频率和业务责任归属。如果知识半年不变且答案错了也无妨用微调模型更简单如果知识月度更新且答案需要可追溯RAG基本是唯一选择。二手车估值这类结构化数据任务别沉迷复杂的深度模型。树模型扎实特征工程就能达到业务可用的精度。深度模型在表格数据上没有大半级优势反而带来训练和维护成本。数据质量永远值得花最多时间。我在数据清洗和特征工程上投入的时间占整个项目六成以上但这也是最接近业务本身的部分。模型只能学到数据里有的规律数据不给到位算法再新颖也白搭。最后一点双模块项目的好处在于一套数据基建、一套服务框架、一套监控体系可以摊薄两个场景的研发成本。如果你们也面临多个AI需求不妨先看看底层设施能不能复用而不是一上来就开两个独立项目各做一套。
阅读完成 · 觉得有帮助?