最近在带一个AI协作项目组里凑齐了产品、算法、后端、还有专门写提示词的同事。每天最热闹的不是需求评审而是“为什么模型这次又答错了”的追责大会。你会发现没人说得清问题到底出在提示词、RAG检索、数据标注还是模型本身因为一切都运行在一个“看起来合理但没人能拍胸脯保证”的模糊地带。后来我硬着头皮把“测试先行”这套思路搬进项目里不是让大家先写单元测试那种老套路而是先把这个项目里“什么叫答对”“什么叫符合预期”给用可执行的方式定下来。没想到效果出奇地好团队的扯皮少了迭代快了上线后的线上事故也基本绝迹。这篇就聊聊我在AI协作项目里实践“测试先行”的全过程包括为什么需要、具体测什么、怎么落地、用的什么工具以及踩过哪些坑。1. AI协作项目里为什么总在“返工”问题出在没有契约1.1 模型输出是概率的不是确定返回的传统软件里一个函数输入1输出就该是1。但大模型的输出不是查表而是概率采样。同一个提示词同一个人同一段上下文第二次调用可能给你完全不同的表述。把temperature调到0还会好一些但模型版本一更新、中间层处理一变结果照样漂。这个本质差异导致了一个直接后果团队里每个人对“这次改动有没有效果”的判断完全依赖自己偶然看到的几次输出。你觉得“好像变好了”另一个同事却觉得“明显变差了”。因为大家看的样本都不一样结论当然对不上。没有一把固定的尺子讨论就永远停留在“我感觉”。测试先行的第一个价值就是逼团队把这把尺子做出来。尺子不一定是精确到字节的断言但至少应该是可重复执行、结果可量化的校验逻辑。有了它你才不用靠感觉管理项目。1.2 多人协作里谁能说清楚“什么叫答对了”AI协作项目和传统项目有个非常大的不同角色的边界是模糊的。产品经理觉得“用户问退款流程模型给了正确步骤”就算答对算法工程师觉得“检索结果命中知识库文档”就算答对后端觉得“调用流程没抛异常”就算答对但最终用户觉得“我没问退款你扯了一堆发票的事”就是答错了。这种认知差异在代码开发时代不太突出因为接口和数据结构是明确的但到了语言模型这里就麻烦了。“回答质量”这种抽象词每个人理解都不一样。我记得项目初期大家经常为一个“是否达到上线标准”争半天。产品拿他想的50个问题去试算法拿他关注的指标看每个人得出的结论南辕北辙。直到我们引入了测试先行开了两次“用例评审会”把所有角色拉到一起逐条定义“哪些输入算正常问题、哪些是边界问题、模型输出达到什么标准算通过”项目才第一次有了公认的完成标准。1.3 传统“先写代码再联调”的习惯在AI场景里会死得很惨以前开发一个接口后端先按文档写前端也按文档写最后联调发现字段对不上改一下就完事。AI项目完全不是这样提示词、检索逻辑、模型调用散落在各个模块里你改了一个Prompt里的措辞可能让某个边缘case表现变好同时让另外一个核心case崩掉。这个回归效应是隐形的微小的甚至在下一次模型版本更新前都不会暴露。按旧习惯“把功能做出来再去测试”在AI项目里基本等于灾难。因为问题发现得越晚定位越难。你面对的不是一个明确报错的堆栈而是一堆看似正常的回答里藏着用户不满意的结果。事后去查“哪一次改动导致了这次回归”只能靠git历史和运气。从项目中期开始我们把所有提示词修改、检索策略调整、Agent流程变更全部绑定到事先定义好的测试集上。改动上线前必须先把测试跑绿。这是“测试先行”最核心的收益让问题在改动发生的当下暴露而不是等上线后用户替你发现。2. 测试先行到底测什么从“代码正确”到“行为符合预期”2.1 接口契约先把输入输出结构定死大模型的输出如果直接丢给下游代码解析你会踩到各种格式坑。模型可能多输出一句“好的我来查一下您的订单”也可能返回的JSON里嵌套层级和你预期的不一样。所以测试先行的第一层一定是接口契约测试。我们用的方案很简单定义输出Schema用pytest加上jsonschema做结构校验。比如在客服机器人项目里要求模型输出一个固定结构{ intent: refund, confidence: 0.95, arguments: { order_id: 20250112 }, reply: 您好已经帮您查询到订单20250112的退款进度... }这看起来很简单但真做起来会发现模型经常不老实。比如intent字段多写了“用户想退款”而不是“refund”或者arguments里塞了一堆废话或者reply里夹带了JSON字段名。没有契约测试这些轻微偏差会导致下游逻辑悄悄出错而且是“时好时坏”那种。我用jsonschema配合一个夹具来跑import jsonschema import pytest OUTPUT_SCHEMA { type: object, properties: { intent: {type: string, enum: [refund, track, chat, complaint]}, confidence: {type: number, minimum: 0, maximum: 1}, arguments: {type: object}, reply: {type: string, minLength: 1} }, required: [intent, confidence, arguments, reply] } pytest.mark.parametrize(sample_input, SAMPLE_INPUTS) def test_output_schema(sample_input): result call_llm(sample_input) jsonschema.validate(result, OUTPUT_SCHEMA)别小看这层测试它把“模型输出可以被安全消费”这个底线给守住了。所有下游代码都按这个契约来写只要测试绿后端就不会突然被奇怪的输出搞挂。这算是AI项目的“类型安全层”虽然不是类型系统但至少是个护栏。2.2 数据与评估集准备好“标答”是测试先行的地基接口契约只是骨架真正体现业务价值的是内容评估。这需要提前准备一份叫“评估集”的样本库也就是黄金数据集。我比较推荐按这几类来组织典型问题用户高频查询的场景比如“怎么申请退款”边界问题模糊表述、多轮上下文追问、语气变化闲聊问题和业务无关的“你好”“你是谁”测试模型会不会乱跑拒绝服务问题涉及敏感、越权、辱骂测试模型是否安全拒答每个样本要标注“期望行为”。注意是行为不一定是标准答案。比如对于“你们怎么退款”期望行为可以是“回复中包含退款入口说明且不会引导用户去线下门店”。这样产品、测试、开发都能看懂。评估集的构建过程最好是团队一起做。我们当时让产品出业务场景让算法出边界case让后端把历史线上用户反馈的问题全部捞出来然后一起过了两轮评审。这比一个人闭门造车要全面得多也顺便解决了“不同角色对答对标准理解不一致”的问题。有了这个评估集“测试先行”就有了靶子。后续写提示词也好调参数也好本质上是让整个系统在这个集上越来越绿而不是拍脑袋觉得“看起来不错”。2.3 模型行为口径相似度阈值、关键词断言、规则断言怎么选先别急着用一堆复杂框架评估集建好后第一件事是确定断言口径。我实践下来有三种常见方式各有各的适用场景。第一种是规则断言适用于结构化或者半结构化的输出。比如检查回复里是否包含某个业务关键词意图字段是否是允许列表中的值JSON结构是否正确。优点是执行快、稳定、不费钱缺点是太死板模型稍微换个说法就误杀了。第二种是语义相似度断言用embedding向量算余弦相似度命中的阈值比如0.75以上就认为通过。这种方法比较符合大模型的特点适合回答内容必须覆盖核心要点但没有唯一标准答案的场景。但要注意相似度阈值需要反复调调得太严每次红灯调得太松测试就失去意义。第三种是用另一个大模型当评判员也就是常说的LLM-as-a-Judge。适合复杂任务比如多轮对话的连贯性、回答是否站在用户立场、是否有幻觉。缺点也明显成本高、不稳定、评判模型本身也会受提示词影响。我的经验是能用规则断言解决的绝不用语义相似度能用相似度解决的绝不上LLM判官。测试要追求稳定可解释而不是什么都扔给AI再让AI去判AI。分层使用才能既控成本又保质量。我给你列个对比表参考断言方式适合场景优点缺点规则断言意图分类、字段格式、关键词要求稳定、便宜、快对变通表达不友好语义相似度开放回答、摘要、答案要点容忍表述差异阈值需要调有误判LLM判官对话质量、上下文连贯、整体体验评估维度丰富费钱慢本身不稳定3. 实操落地AI协作项目里的5个测试先行步骤3.1 第一步需求文档里必须带“测试用例清单”传统项目需求文档写功能描述AI项目里这远远不够。你得在需求阶段就要求产品把“验收case清单”整理出来。我们的做法是给需求模板增加一个固定章节叫“验收测试用例”至少包含10条。每条写清输入、期望行为、判断方法。别小看这一步它逼着产品去思考“这个功能到底要解决哪些具体而微的问题”而不是写一堆漂亮的抽象描述。早期的需求文档大多写着“提升用户满意度”“优化问答体验”这种话根本没法执行。后来要求带case清单很多需求在评审阶段就直接被干掉了因为它们连基本的验收场景都列不出来。这反而帮忙砍掉了一批伪需求团队效率明显提升。3.2 第二步把“核心提示词”和“评估用例”一起提交PR很多AI项目团队对代码有严格的Code Review但对提示词的管理非常随意经常就在群里甩一段话“我更新了提示词大家看看”。这就导致改动没有任何追踪谁改了、为什么改、对哪些case有影响全凭记忆。我们把提示词和代码一样纳入Git管理走Pull Request流程。PR描述里必须写明关联的测试用例编号、这次改动的预期影响、本地跑测试的结果快照。代码评审的人不必玄学式地看Prompt用词只需要看“你新增了哪些覆盖场景”和“哪些用例变绿了”。这个实践真正把测试先行落到了协作制度上。以前提示词是“艺术”改起来全凭感觉现在它变成了“工程”每一次改动都有迹可循有据可查。我们甚至还用过一个简单的脚本把PR里的提示词改动和测试结果差异自动贴到评论里省了很多沟通成本。3.3 第三步搭建“最小验收测试”先红灯后绿灯这是最核心的实操环节。整个流程我刻成这么个硬规矩没写测试前不许优化提示词测试没跑出红色前不许说“开始调”。什么叫先红灯后绿灯就是把评估集的用例接上测试代码刚开始跑的时候一定是有失败的这些失败是合理的它们代表当前系统的不足。如果一上来就全绿要么你的评估集太水要么断言太松。我们拿一个RAG场景举例测试代码大概长这样import pytest from semantic_score import compute_similarity GOLDEN_CASES [ {question: 订单超过7天还能退货吗, min_similarity: 0.75, keywords: [退货, 7天]}, {question: 怎么联系人工客服, min_similarity: 0.70, keywords: [电话, 工单]}, ] pytest.mark.parametrize(case, GOLDEN_CASES) def test_answer_quality(case): output run_pipeline(case[question]) for keyword in case[keywords]: assert keyword in output[reply], f缺少关键信息: {keyword} score compute_similarity(output[reply], case.get(reference_answer, )) assert score case[min_similarity], f语义相似度不足: {score:.2f}流程上新功能或者新的Prompt修改一律先跑这套测试。如果红灯就针对失败的case去分析是哪一层的问题知识库没检索到上下文被截断了提示词里约束不够定位清楚后再改改完必须把之前的case全部跑一遍确认没有回归。3.4 第四步Agent和工具调用场景断言“行为”而不是“字符串”现在越来越多AI项目不是简单一问一答而是带着工具调用的Agent。比如用户说“帮我查快递到哪了”Agent可能经历判断意图、选择工具、生成参数、调用物流接口、整理结果、生成回复。这种情况下测试重点要从“回答内容”转移到“行为轨迹”。我建议至少断言这些点是否调用了正确的工具传入的参数是否从用户表述里正确抽取工具返回异常时Agent是否有兜底策略多轮上下文里是否混淆了当前需求是否正确地拒绝了没有权限的操作写测试时可以把工具调用记录拦下来直接断言调用序列。下面是个简化的伪代码def test_refund_tool_called(): with patch(agent.tools.call_refund) as mock_refund: result agent.run(我要退掉昨天订单) assert mock_refund.called, 应当调用退款工具 args mock_refund.call_args[0][0] assert args[order_id] ! , 必须抽取订单号这种测试对协作项目尤其重要因为Agent的流程通常是算法和工程各自负责一部分你可能没法保证每个环节都正确但至少能保证整个链路的核心行为是符合契约的。我把这套叫做“行为断言”它比单纯看最后的回答文本要可靠得多。3.5 第五步把回归测试接进CI但要用“缓存抽样”控制成本AI项目的测试和传统测试有个很尴尬的差别贵。每次断言要调模型API一个case跑一次可能花几毛钱上千条case跑一次就几百块时间还久。直接像传统CI那样每次PR全量跑团队先被账单干崩溃。我试过几种策略比较稳妥的是分层跑PR级只跑规则断言和接口契约速度快、便宜作为硬性门槛每日任务全量评估集包括语义和LLM判官在夜间或低峰期运行关键路径核心case在每次合并前必须跑通但只截取在线上的高流量场景另外缓存也很有用。同一个输入、同一个模型版本、同样的温度参数输出结果基本稳定所以可以在本地把API结果缓存成文件模拟测试的时候直接用缓存。CI里也按“输入模型版本提示词版本”做结果缓存只有这三者变了才重新调模型。成本一下子就降下来了。4. 测试先行的工具链与评估指标4.1 从pytest到评估框架别一上来就上重型方案很多人一听“AI项目测试”就去搜各种评估平台我觉得没必要。先把pytest这类基础单元测试框架用熟它已经能覆盖大部分规则断言和流程断言。我们项目有八成测试都是pytest写的稳定、可读性强、团队同学都会。等做到语义评估或者复杂Agent评估再考虑引入更专用的框架。市面上有不少开源的评估工具支持跑一批case、计算指标、结果可视化。选型时别只看它“全能”要看和现有代码库的集成成本。直接用pytest加一些自定义的断言函数在很多情况下反而是最平滑的过渡方案。我个人的建议是小团队、快速验证阶段pytest足够当你的评估集超过几百条且需要多维度指标报表、历史趋势对比再引入专用评估框架。迁移成本也不高因为底层数据还是那些case和结果。4.2 量化指标别只看“准确率”覆盖率、幻觉率、拒答率、命中率评估集跑完你得有一组能指导决策的指标。千万不要只盯着一个“准确率”在AI项目里我比较关注这几个指标说明我的经验阈值参考用例通过率评估集里绿灯比例新功能不低于80%线上建议95%关键场景命中率核心业务case必须通过100%幻觉率回答包含编造信息小于2%才允许上线拒答率应当回答却拒绝控制在2%以内但不为0边界case覆盖率评估集里有多少是用户不会问的怪问题建议占30%定期把这些指标贴到项目公告栏里比任何KPI都直观。每次提示词改动后大家看图说话这个版本覆盖率上去了但幻觉率也涨了那说明提示词是“变大胆了”需要权衡。4.3 测试数据管理样本集别和生产数据混在一起评估集是项目的重要资产一定要像管代码一样管好它。我们的评估集是独立仓库维护的不与生产数据混放。主要做三件事第一是隔离。评估集只用于测试绝不进入模型的训练数据或者少样本示例里。一旦模型“见过”评估集的答案测试就彻底失效绿灯变成自欺欺人。第二是去重和差分。每个case必须有唯一编号内容改动走diff评审。我们吃过亏有人“优化”了某个case的表述结果相当于把测试要求降低了让一批差Prompt蒙混过关。第三是脱敏。评估集里的问题可能包含真实用户信息直接用会带来合规风险。所有个人信息、订单号、手机号要么模糊化要么用合成数据替换。这条不能省省了迟早出问题。5. 常见问题与避坑指南5.1 测试一跑就红灯但“感觉模型其实答对了”这是引入测试先行以后最先遇到的坎。明明人工看回复觉得挺好但测试就是红。原因多半是断言条件太苛刻比如对长文本要求关键词命中结果模型换了个同义词表达自然就挂了。我的排查顺序是先打印原始输出人和模型人工对照再检查断言逻辑是不是把老格式写死然后用语义相似度替代严格关键词判断或者把关键词改成一组同义词集合。别第一反应去放宽阈值先确认是测试不对还是模型不对。这里分享一个技巧把失败case单独存成一个JSON文件按失败原因分类比如格式错误、信息缺失、语义偏差、模棱两可。分类能帮助你快速定位是共性问题还是偶发问题而不是每次看到红灯就头皮发麻。5.2 “测试泄漏”评估集被模型记住了怎么办这个坑非常隐蔽。你可能会发现某段时间测试全绿但线上效果平平线下却跑得很漂亮。一个很可能的原因是评估集“泄漏”到了模型上下文里。举个例子你在写提示词的时候把评估集里某个标准答案当成了示例放进去。模型照葫芦画瓢自然对这类case表现极佳但对线上真实分布却没什么帮助。这属于测试数据污染。对策是每季度更新一轮评估集从线上日志里抽一批新的真实用户问题替换掉老样本同时任何示例进了提示词这个case就不允许再出现在评估集里。记住评估集是用来衡量“没见过的题”的而不是用来背题。5.3 协作中有人觉得“测试先行耽误时间”怎么办推行初期一定会有同事抱怨“写测试比写功能还久”。这个我有亲身体会。硬推是不行的越推大家越抵触最后测试就变成走过场。我更推荐用小实验去说服挑一个最容易出回归的模块比如某个复杂的对话流程先用传统方式改一次记下事后返工的时间再按测试先行的流程改一次对比一下就服气了。我们做过对比同样一个“语气优化”的需求传统方式改完上线后发现冷了用户几类case返工花了两天测试先行当天就把所有历史case跑了一遍发现2处回归半小时确认并修正。另一个诀窍是不要一步到位。开始只需要求核心链路带测试允许其他模块慢慢补。等大家尝到甜头自然就愿意推广开了。制度是死的工作是活的。5.4 非确定性问题同样的测试上次绿这次红大模型测试最烦人的就是“非确定性”。同样的case上次跑绿这次跑红你甚至怀疑是不是测试写错了。这在地球上任何AI项目里都会遇到。解决办法从这么几个方向入手固定temperature为0能显著降低随机波动固定模型版本模型升级要显式操作而不是悄悄踩线对无状态的LLM调用增加确定性重试比如第一次失败后按相同参数重跑一次再判断对语义断言除了输出“是否通过”之外附上相似度分数这样偶尔的分数波动不会导致完全不可追踪另外把CI配置里的超时时间放宽一点因为模型接口偶尔慢。很多红灯不是逻辑问题而是请求超时了。这两个原因要区分开不然排查效率极低。按测试的心态把随机性当成输入的一部分来管理而不是与之对抗。你没法控制模型每次吐字完全一样但你可以控制“可接受区间”和“重试策略”这就够用了。我个人在实际操作中体会最深的一点是测试先行在AI协作项目里带来的最大收益并不是“提前发现bug”这么简单而是它逼着整个团队把“什么叫做好”这件事给说清楚了。之前大家凭感觉协作现在凭用例协作。无论是产品、算法、后端还是提示词工程师讨论问题终于有了共同语言。最后再分享一个小技巧如果你不知道从哪开始就挑一条团队吵得最凶的核心链路让产品经理先写出5条黄金验收用例再让工程师照这个标准去实现。你会发现很多争论在写用例的过程中就已经消失了大半。这套方法门槛不高收益却立竿见影值得每个AI协作项目里试一试。