ARTICLE DETAIL

资讯详情

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

Spring AI回归测试全部通过,为什么上线后回答质量仍然下降?非确定性、模型漂移与评测盲区完整排查

Spring AI回归测试全部通过,为什么上线后回答质量仍然下降?非确定性、模型漂移与评测盲区完整排查 文章摘要很多团队已经为Spring AI应用建立单元测试和回归测试接口状态码正常、结构化输出能够反序列化、RAG检索能返回文档、RelevancyEvaluator分数也超过阈值。但新版本上线后真实用户仍然反馈答案变差、引用失真、工具调用错误或拒答率升高。这类问题通常不是“测试完全没用”而是测试验证的对象与生产质量目标不一致。传统测试更擅长判断确定性契约AI应用却同时受模型非确定性、模型别名升级、Prompt与Advisor组合、检索索引版本、Judge模型漂移、测试样本污染、缓存、上下文长度和真实流量分布变化影响。如果测试只运行一次、只检查平均分、只使用几十条理想问题、只让同一个模型同时生成和评分结果很容易出现“全部绿色但用户体验下降”。本文从测试分层、随机性控制、模型与Prompt锁定、黄金数据集、重复采样、分位数、硬失败样本、RAG证据评测、工具轨迹、Judge校准、线上回放和发布门禁等角度给出一套完整排查与改造方案。一、先看一个典型发布事故版本V1模型balanced-model-2026-06 Promptprompt-v17 知识索引kb-index-v41 评测通过率94% 平均Judge得分4.42/5版本V2模型balanced-model-latest Promptprompt-v18 知识索引kb-index-v42 评测通过率95% 平均Judge得分4.46/5CI显示V2更好于是发布。上线后出现用户拒答率38% 合同金额引用错误17% 工具调用重复11% 平均响应成本26%为什么离线评测没有发现进一步检查发现latest别名在发布当天已经指向新模型快照评测只运行一次没有估计随机波动黄金集中过度集中在短问答合同长文本样本只有3条Judge模型与被测模型属于同一家族平均分掩盖了高风险子集下降检索评测只检查“有文档”没有检查引用是否支持Claim工具测试只验证最终文本没有验证调用轨迹生产使用了真实Memory测试没有线上缓存保留了旧Prompt生成的答案与新引用组合。这说明回归测试通过 只代表当前测试定义没有发现问题 不代表真实质量没有下降二、AI测试必须分成五层1. 确定性单元测试验证Prompt模板变量路由规则权限JSON Schema过滤表达式缓存Key工具参数状态机成本计算引用ID合法性。这些测试不应该调用真实模型。2. 组件契约测试验证ChatClient绑定正确模型Advisor顺序Tool Calling SchemaVectorStore过滤Provider错误映射Usage元数据流式事件顺序超时与取消。可以使用Fake Model或Mock Server。3. 离线质量评测使用黄金数据集比较Prompt版本模型版本RAG配置RerankerChunk策略Tool策略。指标包括正确性相关性Groundedness引用支持率工具成功率安全成本延迟。4. 影子与回放测试使用真实生产请求副本在不影响用户的情况下运行候选版本。比较答案路由检索工具轨迹成本延迟。5. 线上质量监控发布后持续检测用户反馈拒答引用点击回退人工接管任务失败线上Judge业务结果。五层缺一不可。三、第一个盲区只测试“接口能不能返回”错误测试TestvoidshouldReturnAnswer(){StringanswerchatService.answer(什么是渠道动销);assertThat(answer).isNotBlank();}这个测试只能发现空结果 异常 连接失败无法发现答非所问虚构错误数字引用错位风险结论错误权限泄露语气不合规成本翻倍。确定性测试应该明确契约。例如结构化抽取TestvoidcontractExtractionMustSatisfySchema(){ContractExtractionresultextractionService.extract(fixture.contract());assertThat(result.parties()).hasSize(2);assertThat(result.amount()).isPositive();assertThat(result.currency()).isIn(CNY,USD,EUR);}但即使Schema通过字段值仍可能错误所以还需要质量评测。四、第二个盲区模型输出非确定性即使temperature0也不能假设每次输出完全相同。原因可能包括Provider内部实现并行计算模型快照Tool选择检索顺序Prompt长度变化服务端系统指令安全策略更新。单次评测case-001 score4不能说明真实分布。建议每个重要样本重复运行N3、5或更多记录平均中位数最低分标准差失败概率一致性。publicrecordRepeatedCaseResult(StringcaseId,ListDoublescores,doublemean,doublemedian,doublemin,doublestandardDeviation,doublepassRate){}高风险样本门禁应关注最低分 或 通过率而不是只看平均。五、第三个盲区模型别名发生漂移配置spring:ai:openai:chat:options:model:balanced-model-latestlatest便于自动获得升级但会降低可复现性。评测运行时latest → snapshot-A生产发布时latest → snapshot-B即使代码和Prompt完全相同行为也可能改变。推荐模型ProfilepublicrecordModelProfile(StringprofileId,Stringprovider,StringrequestedModel,StringresolvedModel,StringmodelSnapshot,Stringendpoint,Stringregion,StringcredentialProfile,MapString,Objectoptions){}每次评测和生产调用记录requested_model resolved_model model_snapshot发布门禁比较的是固定Profile不是模糊别名。六、第四个盲区Prompt并不只有一个字符串真实Prompt由多个部分组成System Prompt Developer Policy Advisor注入 Chat Memory RAG Evidence Tool Schema 用户输入 结构化输出Schema团队可能只版本化System Promptprompt-v18但实际变化来自Memory AdvisorRetrieval AdvisorTool描述默认System模板渲染安全Advisor请求参数。需要生成Prompt ManifestpublicrecordPromptManifest(StringsystemPromptVersion,StringtemplateVersion,ListStringadvisorVersions,StringtoolCatalogVersion,StringoutputSchemaVersion,StringmemoryPolicyVersion,StringretrievalPolicyVersion,Stringhash){}评测报告必须保存完整Manifest。七、第五个盲区测试环境没有真实Memory生产用户已经连续对话10轮测试只测试单轮问题Memory可能导致错误指代旧信息污染Token超限多租户串话System指令稀释摘要压缩丢失关键条件。需要多轮测试集publicrecordConversationTestCase(StringcaseId,ListConversationTurnhistory,StringcurrentInput,ExpectedConversationBehaviorexpected){}评测是否正确使用历史是否拒绝错误历史是否保持用户约束是否引用当前有效信息是否跨会话泄露。八、第六个盲区测试数据过于理想理想样本请查询《差旅管理制度》中一线城市住宿标准。真实用户上海出差住酒店公司最多给报多少还可能出现错别字口语多意图超长粘贴矛盾要求缺失信息对抗提示方言中英混合表格附件省略主语历史指代。黄金集需要包含正常样本 边界样本 困难样本 历史事故样本 安全样本 脏输入 长上下文 多轮会话九、第七个盲区只看全局平均分假设子集V1V2FAQ4.84.9摘要4.64.8合同审查4.53.7权限安全5.05.0样本分布FAQ 70% 摘要 20% 合同审查 10%全局平均可能仍然上升。但合同审查是高风险核心业务。因此门禁应按SlicepublicrecordEvaluationSlice(Stringname,SetStringtags,doubleminimumScore,doubleminimumPassRate,booleanzeroTolerance){}示例global 平均≥4.2 contract-review 平均≥4.4 最低≥3.5 security 失败数0 cross-tenant 失败数0十、硬失败样本必须零容忍以下不适合被平均分稀释跨租户泄露越权工具调用重复退款错误金额Prompt Injection成功不存在引用输出敏感数据安全策略绕过。publicrecordHardFailureRule(StringruleId,PredicateEvaluationCaseResultpredicate,GateSeverityseverity){}只要命中发布失败十一、第八个盲区RAG只测试最终答案RAG至少有四层指标。1. 检索相关性正确证据是否进入候选2. 检索召回所有必要证据是否被找到3. Groundedness答案是否只基于证据4. 引用支持每个Claim的引用是否真正支持结论测试样本publicrecordRagEvaluationCase(StringcaseId,Stringquery,SetStringexpectedDocumentIds,SetStringforbiddenDocumentIds,ListExpectedClaimclaims){}指标RecallK MRR NDCG forbidden_document_rate claim_support_rate citation_precision仅使用RelevancyEvaluator判断回答相关不足以覆盖完整RAG质量。十二、Spring AI评测接口如何使用Spring AI提供Evaluator抽象并包含相关性与事实检查类。概念示例RelevancyEvaluatorevaluatornewRelevancyEvaluator(ChatClient.builder(judgeChatModel));EvaluationRequestrequestnewEvaluationRequest(userText,dataList,responseContent);EvaluationResponseresponseevaluator.evaluate(request);需要注意Evaluator调用模型 本身也具有随机性、成本和偏差它是评测信号不是数学真值。十三、第九个盲区生成模型与Judge同源如果被测模型和Judge模型同一家族同一Prompt风格同一知识偏好可能产生自我偏好。Judge更容易认可与自己表达相似的答案。建议确定性规则 不同家族Judge 人类标注校准高风险评测可使用Judge AJudge B人工抽样争议样本仲裁。十四、第十个盲区Judge版本变化昨天judge-model-v1今天judge-model-latest同一批答案得分变化可能来自Judge而不是被测系统。评测ManifestpublicrecordEvaluatorManifest(StringevaluatorId,StringevaluatorPromptVersion,StringjudgeModelProfile,StringrubricVersion,intrepetitions,doubletemperature,StringimplementationHash){}Baseline和Candidate必须尽量使用同一Judge Manifest。十五、Judge评分应该如何校准建立人工标注集100—500条代表性样本专家给出分数通过/失败原因严重级别。比较Judge一致率PrecisionRecallF1加权Kappa高风险漏判率。如果Judge与专家在安全失败上漏判不能用于发布门禁。十六、第十一个盲区测试数据泄露到Prompt优化团队反复根据固定黄金集调Prompt直到100%通过最终Prompt可能只“记住”这些测试模式。需要数据集分层训练/开发集 回归集 隐藏验收集 线上保留集开发者可以看到开发集。发布门禁使用隐藏验收集避免过拟合。十七、第十二个盲区缓存掩盖了候选版本评测请求命中旧缓存V2评测 实际返回V1缓存结果Cache Key必须包含modelProfileVersionpromptManifestHashknowledgeIndexVersiontoolCatalogVersionevaluationRunId。评测环境最好禁用业务答案缓存 或 使用独立命名空间十八、第十三个盲区候选版本没有使用真实索引评测配置kb-index-v42实际VectorStore仍指向kb-index-v41需要运行时回显publicrecordRuntimeManifest(StringreleaseId,StringmodelProfile,StringpromptHash,StringindexVersion,StringtoolCatalogVersion,StringcodeCommit){}每条评测结果保存Manifest。十九、第十四个盲区工具测试只看最终答案Agent最终回答退款已完成。但轨迹可能查询订单 退款 退款 查询退款状态发生重复退款。工具评测需要检查publicrecordToolTraceExpectation(SetStringrequiredTools,SetStringforbiddenTools,MapString,IntegermaximumCalls,ListStringrequiredOrder,MapString,JsonNodeargumentConstraints){}指标Tool选择准确率参数准确率重复调用率顺序权限拒绝幂等最终状态。二十、建立稳定的回归测试RunnerpublicrecordEvaluationRunConfig(StringrunId,StringbaselineRelease,StringcandidateRelease,ListStringdatasetVersions,intrepetitions,intconcurrency,longrandomSeed,EvaluatorManifestevaluatorManifest){}Runner流程加载固定数据集版本 ↓ 校验Runtime Manifest ↓ 分别运行Baseline与Candidate ↓ 重复采样 ↓ 执行确定性Evaluators ↓ 执行LLM Judge ↓ 按Slice聚合 ↓ 计算置信区间 ↓ 检查硬失败 ↓ 生成Gate结果二十一、不要只比较绝对分数更稳妥的比较Candidate 相对 BaselinePairwise JudgeA和B哪个更好但要避免位置偏差第一次ABaselineBCandidate 第二次ACandidateBBaseline只有两次结论一致才强判定。二十二、统计波动假设候选通过率91%基线92%样本只有20条这个差异可能没有意义。需要样本量置信区间Bootstrap配对差异多次运行。最低实现publicrecordMetricComparison(doublebaseline,doublecandidate,doubledelta,ConfidenceIntervalconfidenceInterval,booleanstatisticallyMeaningful){}AI质量门禁不必成为完整学术统计平台但不能把微小随机差异当成确定改进。二十三、推荐发布门禁quality-gate:global:minimum-pass-rate:0.92minimum-mean-score:4.20maximum-regression:0.02slices:contract-review:minimum-pass-rate:0.95maximum-regression:0.00rag-citation:minimum-claim-support-rate:0.97security:allowed-failures:0tool-side-effect:duplicate-call-rate:0.00operations:maximum-p95-latency-regression:0.15maximum-cost-regression:0.20二十四、测试失败如何排查按以下顺序1. Dataset版本是否一致 2. Runtime Manifest是否一致 3. 模型快照是否变化 4. Prompt Manifest是否变化 5. 索引版本是否变化 6. 缓存是否污染 7. Judge版本是否变化 8. 失败是否集中在特定Slice 9. 是否属于随机波动 10. 工具与检索轨迹哪里开始分叉不要先凭感觉改Prompt。二十五、评测结果必须保存原始证据每条Case保存publicrecordEvaluationCaseResult(StringrunId,StringcaseId,intrepetition,RuntimeManifestruntime,StringinputHash,Stringoutput,ListRetrievedEvidenceevidence,ListToolTraceEventtoolTrace,UsageSnapshotusage,Durationlatency,ListEvaluatorResultevaluators,CaseDecisiondecision){}没有原始轨迹分数下降后无法定位。二十六、CI中怎么执行建议三层Pull Request快速集50—200条 5—15分钟主分支完整回归1000—10000条 夜间或合并后发布前高风险验收隐藏数据集 人工抽样 影子流量不要让每个PR跑全部昂贵Judge成本会失控。二十七、线上反馈如何回流生产失败样本流程用户差评 或 安全告警 或 人工修正 ↓ 脱敏 ↓ 专家标注 ↓ 加入事故数据集 ↓ 补充确定性规则 ↓ 离线回归每次事故至少新增一条永久回归样本。二十八、最终排查清单□ 测试分为确定性、契约、离线质量、影子和线上监控 □ 高风险样本重复运行而非只跑一次 □ 模型使用固定Profile和快照 □ Prompt包含Advisor、Memory、RAG和Tool版本 □ 黄金集包含真实口语、长上下文和事故样本 □ 按业务Slice设置独立阈值 □ 安全和副作用错误零容忍 □ RAG分别评测检索、Groundedness和引用支持 □ 工具测试检查完整调用轨迹 □ Judge模型和Prompt版本固定 □ Judge经过人工标注校准 □ Baseline与Candidate使用同一Evaluator Manifest □ 评测缓存隔离 □ 每条结果保存Runtime Manifest和原始轨迹 □ 发布门禁同时检查质量、成本与延迟 □ 生产失败持续回流黄金数据集总结Spring AI回归测试全部通过但上线质量下降往往不是单个测试Bug而是评测体系没有覆盖AI应用真正的变化源。可靠体系必须同时控制模型快照 Prompt Manifest 知识索引 Tool目录 非确定性重复采样 业务Slice 硬失败 Judge校准 线上反馈测试通过不能只表示“系统能返回一个结果”而应表示候选版本在固定运行清单、代表性数据集和可复现评测规则下没有破坏任何高风险能力并在质量、成本和延迟上满足发布标准。
返回列表