Agent语义化测试实战:LLM-as-a-Judge与Promptfoo方案解析
发布时间:2026/9/26 21:30:41来源:尧图网络
1. 语义化测试在Agent开发里到底卡在哪做Agent开发的人大概率都经历过这样一个阶段功能跑通了Demo演示效果惊艳然后准备上线。上线之前要干嘛测试。于是你打开测试框架开始写断言——问题就出在这里。传统软件测试的套路是给定输入断言输出等于某个确定值。这套逻辑对CRUD接口、对纯函数、对确定性系统都很好使。但Agent不是确定性系统。你给它同一个问题它可能今天回答北京今天晴气温25度明天回答今天北京天气晴朗温度大约25摄氏度左右。语义完全一致字符串完全不同。你写assert output 北京今天晴气温25度第二天CI就红了但功能其实没坏。这就是Agent开发中测试环节最核心的痛点输出是非确定性的自然语言而传统断言只能做精确匹配。你当然可以退而求其次用assert 晴 in output这种包含判断但这又太松了——Agent胡说八道的时候也可能包含晴字。松了漏报紧了误报两头不讨好。我在实际项目里踩过最典型的一个坑一个客服Agent测试用例断言回复中必须包含订单号。结果有一次模型把订单号格式从ORD-20240115-001改成了订单编号ORD20240115001断言直接挂了。功能完全正常用户也能看懂但测试红了。当时团队里有人提议把断言写宽松点有人提议让模型固定输出格式讨论了半天本质问题没解决——我们需要的不是字符串匹配而是语义匹配。语义化测试要解决的就是这件事判断两段文本在含义层面是否等价而不是在字符层面是否相同。这个需求在Agent开发里无处不在——回复质量评估、工具调用参数校验、多轮对话状态追踪、RAG检索结果相关性判断全都绕不开。关键词里提到的LLM-as-a-Judge、Promptfoo就是目前业界应对这个问题的主流思路。前者是用大模型当裁判来打分后者是一个专门做LLM评测的开源工具。这两个东西加上JSON这个胶水层基本构成了当前Agent语义化测试的完整技术栈。这篇文章我会从实际项目出发把语义化测试的几种替代方案掰开揉碎讲清楚每种方案解决什么问题、怎么落地、坑在哪里、什么场景该选哪个。不管你是刚接触Agent开发的新手还是已经在做Agent Evals的老手应该都能找到能直接抄作业的东西。2. 为什么精确断言在Agent场景下必然失效2.1 非确定性输出的三种典型形态要理解语义化测试的必要性先得看清楚Agent输出的非确定性到底长什么样。我把它归纳为三种形态每种对测试的破坏方式不一样。第一种同义改写。模型把请稍等说成稍等一下、请您耐心等待、马上为您处理。语义完全一致字符完全不同。这种最常见也最好处理——语义相似度算法基本能覆盖。第二种结构漂移。模型输出的JSON字段顺序变了或者把嵌套对象拍平了或者数字从25变成25。这种在工具调用场景特别常见。你期望{city: 北京, temp: 25}模型给你{temp: 25, city: 北京}JSON解析出来是等价的但字符串比对就是不等。第三种信息增减。模型多说了几句无关紧要的客套话或者省略了某个它认为不重要的细节。这种最麻烦——多说的部分可能无害也可能夹带了幻觉省略的部分可能无关紧要也可能是关键信息丢失。纯靠相似度判断容易翻车。我做过一个统计在一个中等复杂度的Agent项目里因为输出非确定性导致的测试失败占比超过60%。其中同义改写占一半结构漂移占三成信息增减占两成。这意味着如果你还在用精确断言你的CI有一大半时间在报假警。2.2 传统测试手段的三种失效模式面对非确定性输出大家通常会尝试三种补救手段但这三种都有各自的失效边界。手段一包含断言。assert keyword in output。问题是关键词选得松了漏报选得紧了误报。而且它只能判断有没有判断不了对不对。Agent说北京今天下雨也包含北京和今天但答案是错的。手段二正则匹配。用正则去匹配结构化部分。这对格式固定的输出有效但Agent的输出格式往往不固定。你写了个正则匹配订单号模型换个格式就挂了。维护正则的成本随着Agent能力增强反而越来越高。手段三固定输出格式。在Prompt里强制要求模型按模板输出然后用精确断言。这是很多团队的标准答案但它有个致命问题它把测试的负担转移到了Prompt上。为了让测试好写你得把Prompt约束得死死的结果Agent的灵活性和自然度大打折扣。而且模型并不总是听话尤其是长对话、复杂推理场景格式约束经常被遗忘。这里有个反直觉的结论测试越严格Agent能力越受限。如果你发现为了让测试通过不得不把Prompt写得像填表一样死板那说明测试方案选错了不是Agent写错了。2.3 语义化测试的边界它也不是万能的说了这么多精确断言的坏话也得给语义化测试泼盆冷水。它不是银弹有自己的失效边界。语义化测试判断的是含义是否等价但含义等价本身是个模糊概念。两句话语义相似度0.85算等价还是不等价这个阈值怎么定定高了误报定低了漏报和包含断言面临的是同一个问题只是从字符层面挪到了语义层面。更麻烦的是语义相似不等于事实正确。Agent说北京今天晴25度和北京今天晴26度语义相似度极高但温度差了一度。如果你的业务对温度敏感语义化测试就漏掉了这个错误。所以正确的姿势是语义化测试负责判断表达是否等价事实校验、格式校验、业务规则校验该用精确手段还得用精确手段。两者是互补关系不是替代关系。这一点想不清楚很容易从一个极端走到另一个极端。3. LLM-as-a-Judge用模型当裁判的实操细节3.1 核心思路与适用场景LLM-as-a-Judge的思路很直接既然判断语义等价这件事需要理解那就找个能理解的东西来判——大模型自己。你给它一个标准答案或者评分标准再给它Agent的实际输出让它判断两者是否等价或者打个分。这个方案最大的优势是灵活。你可以让它判断回复是否礼貌、是否包含幻觉、是否解决了用户问题这种传统手段根本没法量化的东西。在Agent Evals场景里这几乎是唯一可行的方案。适用场景我列一下方便你对号入座开放式问答的质量评估有没有答到点上多轮对话的连贯性判断有没有答非所问工具调用参数的合理性判断参数值对不对不是格式对不对RAG检索结果的相关性打分幻觉检测回复内容是否超出给定上下文不适用场景也说清楚需要精确数值、精确格式、精确逻辑判断的场景别用LLM-as-a-Judge。比如订单金额是否等于100元这种直接断言别绕弯子。3.2 一个能直接用的Judge Prompt模板网上关于LLM-as-a-Judge的文章很多但大部分只讲概念不给能用的Prompt。我把自己项目里打磨过的模板放出来你可以直接改改用。JUDGE_PROMPT 你是一个严格的评测裁判。你的任务是判断【模型输出】是否在语义上等价于【参考答案】。 【用户问题】 {question} 【参考答案】 {reference} 【模型输出】 {candidate} 判断标准 1. 如果模型输出与参考答案表达的核心含义一致即使措辞不同也判为等价。 2. 如果模型输出遗漏了参考答案中的关键信息判为不等价。 3. 如果模型输出包含参考答案中没有的事实性内容可能是幻觉判为不等价。 4. 如果模型输出与参考答案含义相反判为不等价。 请只输出一个JSON对象不要有任何其他内容 {{verdict: 等价 或 不等价, reason: 简要说明判断理由, confidence: 0到1之间的小数}} 这个模板有几个设计细节值得说。第一判断标准要具体。不要只说判断是否等价要把什么情况算不等价列清楚。模型对模糊指令的理解很不稳定标准越具体判断越一致。第二强制JSON输出。这是为了后续程序化处理。关键词里JSON出现频率极高不是没道理的——LLM-as-a-Judge的落地JSON是绕不开的胶水层。第三要求输出confidence。这个字段在后续做阈值过滤时非常有用。低置信度的判断可以人工复核或者直接标记为待定避免误判污染测试结果。第四要求输出reason。这个主要是给调试用的。测试挂了的时候你得知道模型为什么判不等价不然没法定位问题。3.3 位置偏差、冗长偏差与自洽性校验LLM-as-a-Judge有几个众所周知的偏差不处理的话判断结果会系统性偏移。位置偏差如果你让模型对比A和B两个答案哪个好它倾向于选先出现的那个。解决办法很简单——交换顺序跑两次如果两次结论一致才采纳不一致就标记为不确定。这个技巧叫位置交换校验成本翻倍但准确率提升明显。冗长偏差模型倾向于给更长的答案打高分哪怕长答案里废话更多。解决办法是在Prompt里明确说长度不影响评分或者干脆在评分标准里加入简洁性维度做对冲。自洽性校验同一个判断跑三次如果三次结论不一致说明这个case本身处于模糊地带应该标记出来人工处理。这个技巧在关键测试用例上很值得用成本是三次调用但能过滤掉大部分不稳定判断。我实测下来的经验是位置交换校验必做自洽性校验选做关键case做冗长偏差靠Prompt对冲。三者叠加能把LLM-as-a-Judge的准确率从70%左右拉到90%以上。3.4 成本控制不是每个case都值得上JudgeLLM-as-a-Judge最大的问题是成本和延迟。每次判断都是一次模型调用如果你的测试集有几百个case每次CI跑一遍就是几百次调用费用和时间都吃不消。我的做法是分层测试层级测试手段覆盖范围执行频率L1精确断言格式、必填字段、数值范围每次提交L2语义相似度同义改写类case每次提交L3LLM-as-a-Judge开放式质量评估每日/发版前L4人工抽检关键业务case每周L1和L2跑得快、成本低适合高频执行。L3成本高但覆盖质量维度降频执行。L4兜底防止自动化测试的盲区。这套分层下来既保证了覆盖率又把成本控制在了可接受范围。一个实操技巧把LLM-as-a-Judge的结果缓存起来。如果Agent的Prompt和模型版本没变同一个case的判断结果可以复用不用重复调用。这个缓存命中率在迭代期能到70%以上省下的成本很可观。4. Promptfoo把语义化测试工程化的工具选择4.1 Promptfoo解决了什么手工脚本解决不了的问题在Promptfoo出现之前大部分团队的语义化测试是手搓脚本——写个Python脚本循环调用模型收集结果自己算指标。能跑但问题一堆测试用例散落在代码里、结果没有可视化、不同人写的脚本风格各异、CI集成要自己搭。Promptfoo把这些东西标准化了。它的核心价值是把评测这件事从写脚本变成了写配置。你用一个YAML文件描述测试用例、断言方式、模型配置它帮你跑、帮你算指标、帮你出报告。具体来说它解决了这几个问题测试用例与代码解耦用例写在YAML里非技术人员也能改断言方式可插拔内置了contains、equals、similar、llm-rubric等多种断言覆盖从精确到语义的全谱系多模型对比同一个测试集可以同时跑多个模型直接看哪个效果好结果可视化Web界面看结果哪个case挂了、为什么挂一目了然CI友好命令行执行退出码规范直接接CI关键词里promptfoo教程是热搜词说明很多人正在找入门资料。我下面给一个能直接跑的配置。4.2 一份覆盖三种断言的Promptfoo配置# promptfooconfig.yaml prompts: - 你是一个客服助手请回答用户问题{{question}} providers: - id: openai:gpt-4o-mini config: temperature: 0 tests: - vars: question: 你们的退货政策是什么 assert: # 精确断言必须包含关键词 - type: contains value: 退货 # 语义断言与参考答案语义相似 - type: similar value: 我们支持7天无理由退货商品需保持完好 threshold: 0.8 # LLM裁判判断是否解决了用户问题 - type: llm-rubric value: 回复是否清晰说明了退货政策的核心要点包括退货期限和商品状态要求这份配置里三种断言各司其职contains保证基本要素不丢similar保证语义方向对llm-rubric保证质量达标。三层叠加比单一断言可靠得多。similar断言的threshold参数是关键。0.8是我实测下来比较平衡的值——低于0.7误报太多高于0.9漏报太多。但这个值跟你的embedding模型和业务场景强相关建议先用一批已知等价的case跑一遍看相似度分布再定阈值。llm-rubric是Promptfoo里最强大的断言类型。你给它一段评分标准它调用模型来判断。这本质上就是LLM-as-a-Judge但Promptfoo帮你把调用、解析、结果汇总都封装好了不用自己写胶水代码。4.3 断言选型的决策树Promptfoo内置断言很多新手容易挑花眼。我整理了一个决策树按这个顺序判断就行输出是确定性的吗是→用equals或contains别绕弯子。输出是JSON吗是→用is-json加javascript断言校验字段比字符串匹配可靠。需要判断语义相似吗是→用similar先定阈值。需要判断质量、礼貌、幻觉这种主观维度吗是→用llm-rubric。以上都不满足用javascript断言写自定义逻辑这是万能兜底。这个决策树的核心逻辑是能用精确手段就别用语义手段能用轻量手段就别用重量手段。语义化测试是补充不是替代。4.4 把Promptfoo接进CI的注意事项Promptfoo接CI本身不难promptfoo eval命令跑完会返回退出码非零就是有case挂了。但有几个坑要注意。坑一模型调用的网络抖动。CI环境网络不稳定模型调用偶尔超时会导致假失败。解决办法是配置重试Promptfoo支持在provider层面配maxRetries。坑二成本失控。CI每次提交都跑全量测试集如果测试集大、用了LLM裁判费用会飙升。解决办法是分层——快速测试集每次跑全量测试集定时跑。坑三结果不稳定。LLM裁判本身有随机性同一个case可能这次过下次挂。解决办法是把temperature设为0并且对关键case做多次判断取多数。坑四测试集腐化。随着Agent迭代老的测试用例可能不再适用但没人清理导致测试集越来越臃肿。解决办法是定期review测试集把长期稳定通过的case归档把频繁误报的case修正或删除。5. JSON在语义化测试链路里的胶水作用5.1 为什么语义化测试离不开JSON关键词里JSON相关词占了将近三分之一这不是偶然。语义化测试的整条链路——测试用例定义、模型输出解析、裁判结果结构化、指标汇总——每一环都需要一个结构化的中间格式而JSON是当前事实上的标准。原因很简单自然语言是给人和模型看的JSON是给程序看的。语义化测试的本质是用程序处理自然语言判断结果中间必须有个转换层JSON就是这个转换层。举个具体例子。LLM-as-a-Judge返回的是自然语言判断你要把它变成可统计的指标就得让模型输出JSON{ verdict: 等价, reason: 模型输出与参考答案都说明了7天退货期限和商品完好要求, confidence: 0.92 }有了这个JSON你才能程序化地统计通过率、按confidence过滤、把reason汇总成报告。如果模型返回的是我觉得这两个答案差不多你就得再写个解析器去提取信息麻烦且不可靠。5.2 让模型稳定输出JSON的三种手段让模型输出JSON这件事说简单也简单说坑也坑。我总结了三种手段按可靠性排序。手段一用模型的原生JSON模式。现在主流模型API都支持response_format: {type: json_object}这种参数开启后模型保证输出合法JSON。这是最可靠的方式能用就用。手段二Prompt里给JSON Schema。如果模型不支持原生JSON模式就在Prompt里明确给出期望的JSON结构包括字段名、类型、取值范围。给Schema比只给示例更可靠因为Schema约束了字段的合法性。手段三Few-shot示例。给一两个输入输出的完整示例让模型模仿。这个手段对格式复杂的情况有效但会占用较多token。三种手段可以叠加使用。我的实践是原生JSON模式 Prompt给Schema双保险基本不会出问题。5.3 JSON解析失败的兜底策略即使做了上面这些JSON解析失败还是会发生。模型偶尔会抽风输出带markdown代码块包裹的JSON或者在JSON前后加解释文字。这时候不能直接崩得有兜底。import json import re def parse_judge_output(raw: str) - dict: 解析裁判模型的输出带多层兜底 # 第一层直接解析 try: return json.loads(raw) except json.JSONDecodeError: pass # 第二层提取markdown代码块中的JSON match re.search(r(?:json)?\s*(\{.*?\})\s*, raw, re.DOTALL) if match: try: return json.loads(match.group(1)) except json.JSONDecodeError: pass # 第三层提取第一个完整的JSON对象 match re.search(r\{.*\}, raw, re.DOTALL) if match: try: return json.loads(match.group(0)) except json.JSONDecodeError: pass # 兜底返回标记为解析失败的结果 return { verdict: 解析失败, reason: f原始输出{raw[:200]}, confidence: 0.0 }这个函数的三层兜底覆盖了绝大多数情况。第一层处理标准输出第二层处理markdown包裹第三层处理前后有杂文字的情况。都失败就返回一个标记结果让上层逻辑决定怎么处理——通常是标记为待人工复核而不是直接判失败。一个经验解析失败率是衡量Judge Prompt质量的重要指标。如果解析失败率超过5%说明Prompt写得不够明确应该优化Prompt而不是加更多兜底。兜底是保险不是解决方案。5.4 用JSON Schema校验裁判结果解析成功不代表结果合法。模型可能输出了JSON但字段名拼错了或者verdict的值不在预期范围内。这时候需要JSON Schema校验。from jsonschema import validate, ValidationError JUDGE_SCHEMA { type: object, properties: { verdict: {type: string, enum: [等价, 不等价, 解析失败]}, reason: {type: string, minLength: 1}, confidence: {type: number, minimum: 0, maximum: 1} }, required: [verdict, reason, confidence], additionalProperties: False } def validate_judge_result(result: dict) - bool: try: validate(instanceresult, schemaJUDGE_SCHEMA) return True except ValidationError as e: print(f校验失败{e.message}) return FalseSchema校验的价值在于把模型输出不合法这件事显式化。如果不校验一个字段名拼错的结果可能悄悄流入统计导致指标失真。校验之后不合法的结果会被拦截要么重试要么标记不会污染数据。additionalProperties: False这个设置值得特别说。它禁止模型输出Schema之外的字段。这看起来严格但能防止模型自作主张加字段导致后续处理逻辑出错。我踩过这个坑——模型加了个score字段结果和confidence混淆了统计出来的指标完全不对。6. 从零搭一套语义化测试流水线的完整路径6.1 第一步把测试用例从代码里抽出来搭流水线的第一步不是选工具是整理测试用例。大部分团队的测试用例散落在各种测试文件里格式各异没法复用。先把它们抽出来统一成结构化格式。我推荐用JSON或YAML存测试用例每个用例包含这几个字段{ id: case_001, category: 退货政策咨询, question: 你们的退货政策是什么, reference: 我们支持7天无理由退货商品需保持完好, assertions: [contains:退货, similar:0.8, llm-rubric:是否说明退货期限和商品状态], priority: high }category字段用于分组统计priority字段用于分层执行。这两个字段在测试集变大之后特别有用——你可以只看高优先级case的通过率或者按category看哪类问题最容易挂。整理用例的过程本身就有价值。很多团队整理完才发现测试用例覆盖的场景和实际用户问的问题对不上——测试集里全是退货政策这种标准问题实际用户问的是我上周买的鞋能退吗这种具体问题。这个gap不整理是发现不了的。6.2 第二步确定断言策略与阈值用例整理完给每个用例配断言。这一步的核心是阈值标定。以similar断言的阈值0.8为例这个值不能拍脑袋定。正确做法是找一批已知等价的case人工确认过语义一致跑一遍相似度看分布。如果大部分在0.85以上那0.8是安全的如果有一半在0.75左右那0.8就会误报得降到0.7。我一般会画个直方图看等价case和不等价case的相似度分布有没有重叠区。重叠区越小阈值越好定重叠区越大说明相似度这个指标本身区分度不够得换方案比如上LLM裁判。阈值标定这件事一次标定不够要定期重标。因为Agent迭代、模型升级、embedding模型更换都会让相似度分布漂移。我一般每个大版本重标一次。6.3 第三步搭建执行与报告链路执行链路的核心是分层执行 结果汇总。分层执行前面说过L1/L2高频跑L3降频跑。实现上用不同的配置文件CI里根据触发条件选择跑哪个。结果汇总这块Promptfoo自带Web界面但生产环境我建议自己存一份结构化结果方便做趋势分析。每次执行把结果存成JSON包含执行时间、通过率、失败case列表、失败原因。积累一段时间后就能看出通过率的变化趋势——是稳步上升还是某次改动后突然下降。def save_eval_result(result: dict, output_dir: str): 保存评测结果用于趋势分析 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) filename f{output_dir}/eval_{timestamp}.json summary { timestamp: timestamp, total: len(result[results]), passed: sum(1 for r in result[results] if r[passed]), failed_cases: [ {id: r[id], reason: r[failure_reason]} for r in result[results] if not r[passed] ] } summary[pass_rate] summary[passed] / summary[total] with open(filename, w, encodingutf-8) as f: json.dump(summary, f, ensure_asciiFalse, indent2)这个汇总结果配合简单的可视化就能做出通过率趋势图。趋势图的价值在于发现渐进式退化——单次测试可能都过但通过率从95%慢慢滑到85%说明Agent在悄悄变差这种问题单次测试发现不了。6.4 第四步处理测试挂了但功能没坏的假阳性假阳性是语义化测试最烦人的问题。测试红了你去看发现功能其实正常是测试太严了。处理假阳性有一套流程。第一步确认是不是真假阳性。有时候你以为是假阳性其实是真bug只是不明显。先人工看一眼Agent的实际输出确认功能确实正常。第二步定位是哪个断言挂了。是contains挂了还是similar挂了还是llm-rubric挂了不同断言挂了处理方式不一样。第三步针对性调整。contains挂了可能是关键词选得太死换成更通用的词。similar挂了可能是阈值太高或者参考答案写得不好。llm-rubric挂了可能是评分标准太模糊模型理解不一致。第四步记录并复盘。每次假阳性都是一次测试集优化的机会。记录下来定期复盘看是不是有系统性的问题——比如某一类case特别容易假阳性那可能是这类case的断言策略需要整体调整。一个反直觉的经验假阳性不全是坏事。它说明你的测试在认真工作只是标准需要微调。真正危险的是假阴性——功能坏了但测试没发现。所以宁可假阳性多一点也不要为了减少假阳性把测试放松到漏报的程度。7. 几个真实项目里的踩坑记录7.1 坑一LLM裁判的和稀泥倾向我遇到过一个很典型的问题LLM裁判对明显不等价的case也判等价。比如参考答案是退货需要商品完好模型输出是退货需要商品未使用这两个在业务上是有区别的完好≠未使用但裁判判了等价。排查下来发现是Prompt里的判断标准写得太宽松。核心含义一致这个表述太模糊模型倾向于往宽松了判。改成如果模型输出遗漏了参考答案中的任何关键限定条件判为不等价之后判断严格多了。这个坑的教训是LLM裁判的严格程度完全取决于Prompt的严格程度。你写得松它就判得松你写得严它就判得严。不要指望模型自己把握尺度尺度必须你来定。7.2 坑二相似度阈值在不同语言间漂移一个多语言Agent项目中文case的相似度阈值定在0.8跑得好好的。加了英文case之后发现英文的相似度普遍偏低——同样语义的两句话英文的相似度只有0.7左右。原因是embedding模型对中文和英文的语义空间分布不一样。中文的语义相似度普遍偏高英文偏低。解决办法是分语言标定阈值中文0.8英文0.7各用各的。这个坑提醒我们任何阈值都不是全局通用的。不同语言、不同领域、不同模型阈值都可能不一样。标定阈值这件事得按维度分开做。7.3 坑三测试集里的僵尸用例项目跑了一年多测试集从最初的50个case涨到了300多个。有一天发现通过率一直卡在92%上不去排查发现有一批case长期失败但没人管——因为它们是早期版本的用例对应的功能早就改了用例本身已经过时了。这就是僵尸用例。它们不反映当前需求但还在测试集里占着位置拉低通过率干扰判断。解决办法是定期清理测试集把长期失败且确认过时的case归档把长期稳定通过的case降频。我现在养成的习惯是每个季度review一次测试集问三个问题这个case还反映当前需求吗这个case最近三个月挂过吗这个case的断言还合理吗三个问题有一个答否就处理掉。7.4 坑四CI里的模型调用超时CI环境跑语义化测试最怕的就是模型调用超时。本地跑得好好的CI里就超时排查半天发现是CI环境的网络出口不稳定。解决办法有几个配置重试Promptfoo支持maxRetries、设置合理的超时时间别用默认的默认往往太短、把模型调用结果缓存起来相同输入直接读缓存。缓存这个手段特别值得说。CI里跑的测试集大部分case的输入是固定的如果Agent的Prompt和模型版本没变输出也应该固定。把输入→输出缓存起来第二次跑直接读缓存既快又稳。缓存失效的条件是Prompt变了或模型版本变了这时候清缓存重跑。8. 语义化测试方案选型的个人建议聊了这么多方案最后说说选型。我的建议是按团队规模和项目阶段来。个人项目/早期原型别上重型工具直接用LLM-as-a-Judge写个简单脚本就够了。重点是快速验证想法不是搭测试体系。这个阶段测试用例有十几个就够手工维护。小团队/产品化初期上Promptfoo把测试用例结构化接进CI。这个阶段的核心是建立测试习惯让每次改动都有回归验证。断言以contains和similar为主LLM裁判用在关键case上。中型团队/规模化阶段分层测试体系L1到L4全上。这个阶段的核心是控制成本和保证覆盖率。需要专人维护测试集定期标定阈值清理僵尸用例。大型团队/平台化阶段自建评测平台Promptfoo作为其中一环。这个阶段的核心是标准化和可观测性。需要把评测结果和业务指标打通看测试通过率和用户满意度的相关性。不管哪个阶段有一条原则是不变的语义化测试是补充不是替代。能用精确断言的地方就用精确断言语义化测试只用在精确断言搞不定的地方。这个原则想清楚了方案选型就不会跑偏。另外别追求一步到位。我见过太多团队一上来就想搭一套完美的评测体系结果搭了三个月还没跑起来。正确的做法是先跑起来最简单的版本——哪怕就是几个case加一个LLM裁判——然后在使用中迭代。评测体系是长出来的不是设计出来的。
网站建设高端定制企业官网