ARTICLE DETAIL

资讯详情

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

大模型测评实战:从零跑通 Kev,给“概率模型”做 4 类自动化测试

大模型测评实战:从零跑通 Kev,给“概率模型”做 4 类自动化测试 这两年测试工程师接触的大模型大多都在“写”写用例、写脚本、写日志总结、写缺陷分析。Kev 这类模型反过来。它不负责组织一段看起来很有道理的话而是给你一份状态和几个固定问题返回每个选项的概率。比如一条测试执行记录你可以问它这个失败是不是环境波动该归给前端、服务端还是测试数据问题影响属于什么等级它不会写一大段解释直接给出结构化结果。很多同学看到这里第一反应是那不就是一个“AI 判断器”吗是。但从测试的角度更重要的第二反应应该是既然它是一个判断器那我要怎么测它这篇不聊怎么把它接进发布审批更不教你拿一个 0.8 的概率直接替人做决定。我们只做一件事把 Kev 当成一个本地 HTTP 服务跑通并验证四类最基础、也最容易被忽略的测试。你最后会得到一套能复用的最小测试资产一条真实的state questions请求一组 API 契约断言一组“多问题会不会串、选项换序会不会翻”的行为测试一个能跟着模型版本、问题文案和温度参数一起回归的冻结样本集。1. 先理解它不是聊天接口而是类型化决策接口普通大模型接口返回的是文本。你要么让人读要么再找一个模型读很容易变成“模型评模型”。Kev 的输入有两部分状态state一段模型需要读取的事实可以是文本也可以是结构化对象问题questions多个带类型的判断题。它支持三类问题问题类型适合测什么你要断言什么noul是否满足、是否异常、是否需要升级返回的是true的概率必须在 01 之间choice故障归属、工单路由、缺陷类别候选项集合不能丢概率和应接近 1score风险等级、严重程度、规则满足度分数对应有序等级不能把它当成天然的 15 分这也是它和“让大模型评价一句话好不好”的根本区别问题、选项和返回形状都可以被测试代码固定下来。官方接口约定就是把一份state和多道类型化问题送入/v1/systemone返回每个问题的概率结果。2. 第一步先把本地服务跑起来准备 Python 3.12 或 3.13以及uv。进入项目目录后执行gitclone https://github.com/jaredpalmer/kev.gitcdkev uvsync--extraserve uv run--extraserve python-mkev.serve--runjaredpalmer/kev-4b--port8008服务启动后先别急着做微调。测试同学的第一件事应该是确认服务能不能稳定返回、返回结构是否符合你后续要依赖的契约。提醒一句本地能启动不等于生产可用。模型大小、设备、输入长度和并发都会影响耗时这一步的目标只是建立一个可重复调用的被测对象。3. 第二步发出第一条“可测试”的决策请求下面不用虚构一个完整业务故事就取一条测试执行记录。重点不是模型给出的归属到底对不对而是你从此拥有了一条可以重复跑、可以写断言的请求。importhttpx BASEhttp://127.0.0.1:8008REQUEST{model:kev-latest,state:{case:订单取消后库存没有在 30 秒内回补,observed:接口返回 200库存查询仍为原值重试后恢复,facts:[库存回补由异步消息触发,压测环境消息积压 4 分钟,该用例过去两周出现过 3 次]},questions:{is_flaky:{type:noul,instructions:根据现有事实这次失败是否更像环境波动而非功能缺陷},fault_owner:{type:choice,instructions:当前最该由哪个方向先排查,criteria:{service:服务逻辑或异步消费异常,environment:测试环境或基础设施异常,test_data:测试数据或前置条件异常}},impact_level:{type:score,instructions:按当前证据评估影响等级。,criteria:[可观察,需要跟进,需要立即处理]}}}responsehttpx.post(f{BASE}/v1/systemone,jsonREQUEST,timeout120)response.raise_for_status()print(response.json()[answers])注意一个很容易犯的错误不要把模型的首个答案当成预期结果。这里的预期结果首先应是协议预期例如“choice的候选项没有丢失”“概率分布可用”“score有对应的等级图例”。语义正确性放到后面的人工标注样本里再验证。4. 第三步先写接口契约测试别一上来只看 HTTP 200很多 AI 服务的冒烟测试止步于status_code 200。对决策模型不够。下面这组断言不关心本次到底判成“环境”还是“服务”只检查响应是否仍满足你的依赖约定。它也应该是模型、SDK 或服务版本升级后的第一道回归。importmathimporthttpxdefask(path/v1/systemone,bodyREQUEST):resphttpx.post(f{BASE}{path},jsonbody,timeout120)assertresp.status_code200assertresp.headers.get(x-typesafe-request-id)returnresp.json()deftest_decision_response_contract():resultask()answersresult[answers]# noul服务承诺返回“true”的概率flakyanswers[is_flaky]assertflaky[type]noulassert0.0flaky[noul]1.0# choice候选项齐全、概率可归一、答案来自候选项owneranswers[fault_owner]expectedset(REQUEST[questions][fault_owner][criteria])assertowner[type]choiceassertset(owner[probabilities])expectedassertmath.isclose(sum(owner[probabilities].values()),1.0,abs_tol0.03)assertowner[choice]inexpectedassert0.0owner[confidence]1.0# score不要只取一个数等级映射也属于响应契约levelanswers[impact_level]assertlevel[type]scoreassertset(level[legend])set(level[probabilities])这里有个测试思路值得记住模型的答案允许变化接口承诺不能随便变化。例如你换了模型权重fault_owner从environment变成service这未必立刻是 Bug但如果probabilities少了一个候选项、noul被改成了自然语言、分数等级缺失那后面依赖它的用例分流、报表和脚本都会坏。5. 第四步测“多题并发”有没有串题也测选项顺序会不会影响答案Kev 的一个核心卖点是多个问题共享同一份状态但各自隔离。对测试同学来说这不是一句架构描述而是一条可以直接写进自动化的行为要求。项目服务提供了两个很适合教学和验收的辅助接口/v1/systemone/separate把同一批问题拆成多个单独请求/v1/systemone/permute把一个choice问题的候选项多次换序。先看隔离。相同状态下“一问一请求”和“多问一请求”的核心选择不应该无缘无故翻转deftest_batch_and_separate_are_consistent():batchedask(/v1/systemone)separateask(/v1/systemone/separate)assert(batched[answers][fault_owner][choice]separate[answers][fault_owner][choice])再测顺序稳定性。这个测试不是要求每次概率小数点后四位完全相同而是为了抓住“只把选项顺序换了第一名却翻了”的问题deftest_choice_order_is_stable():body{request:REQUEST,question:fault_owner,n_perm:6}resultask(/v1/systemone/permute,body)assertresult[argmax_stable],result[runs]如果这两个测试失败先不要急着改阈值。先检查三件事问题文案是否把多个意图塞到了一道单选题里候选项是否重叠你的状态是否缺了决定性的事实。模型“飘”有时确实存在但更多时候是测试设计把一个不可判的问题伪装成了可判问题。6. 第五步概率能不能信要靠校准测试不靠直觉最容易被误用的一句话是“它给了 0.8说明有 80% 把握。”只有当你自己的数据上模型打到 0.8 左右的样本最终大约也有八成被人工确认时这句话才成立。模型准确率高不代表概率校准得好。最小做法并不复杂先准备 3050 条已经人工裁决过的测试记录覆盖四类样本正常、边界、反例、未知。每条记录保留当时的状态、问题、正确选项和人工裁决日期。不要把事后排查结论塞回状态里否则离线分数会虚高。下面的 Brier 分数函数就能先跑起来。数值越小代表预测概率与实际结果的差距越小它不是唯一指标却比只看“猜对了多少次”更适合检查概率有没有被高估。defbrier_score(labels:list[int],probs:list[float])-float:assertlen(labels)len(probs)returnsum((label-prob)**2forlabel,probinzip(labels,probs))/len(labels)# labels人工裁决1 表示“确实是环境波动”# probs答案中 answers[is_flaky][noul] 的概率print(brier_score(labels,probs))所谓“冻结”不是把 Excel 锁起来不许更新而是这一批样本一旦作为回归基线就不能一边调模型一边拿它训练或反复改标注。模型版本、问题文案、候选项、温度参数任一个变化都重新跑一遍。新样本可以持续补但要和旧冻结集分开管理。7. 微调是最后一步不是第一步Kev 支持基于自有 JSONL 继续训练这确实是它的价值之一。但对测试团队而言微调前至少要先完成两件事用冻结样本集证明问题定义与标注口径本身是一致的用上面的四层回归证明服务返回、问题隔离和概率行为没有基础性问题。否则很容易出现一种假象模型微调后“准确率提升了”其实只是它记住了某位同学的标签习惯或者你的评测样本已经被训练数据污染。真正适合进入 JSONL 的不是“这个缺陷很严重”这种散文式总结而是可复核的记录当时可见的状态、结构化问题、候选项、人工确认的标签、以及标签依据。这样你以后换模型、换团队成员仍能解释这条样本为什么这么判。8. 给测试工程师的最小检查清单第一次接触这类模型不需要先造一个庞大的 AI 测试平台。把下面六项跑通就已经超过大多数“只看演示效果”的接入方式了本地接口能稳定返回类型化答案noul、choice、score都有契约断言多问题和单问题请求的关键结论能对照候选项换序不会导致结果莫名翻转3050 条人工样本能算出自己的概率质量模型、问题文案或温度参数变化后会自动重跑冻结集。Kev 的价值不是让测试工程师从此相信模型替你判断而是提供了一种更容易被测试的 AI 接口形态问题固定、输出固定、概率可记录、行为可回归。当你把它当作一个被测服务而不是一个“会给建议的黑盒”模型才真正开始进入测试工程能够掌控的范围。
返回列表