实习日志 / 中文 / 测评设计 / 给报案 Agent 设计一套测评
给报案 Agent 设计一套测评
中文 | English
Scenario 8 的验收要求很具体:10 类场景(5 类车险、3 类房屋、2 类财产),80% 的报案不需要再联系;严重程度分流由员工盲评;欺诈提醒在测试集上零误报;整个报案少于 5 分钟、少于 10 个问题。
这些数字只有放在一套站得住的测评里才有意义。被测的是一个会聊天的 Agent,同一句话每次跑的结果都不一样;它面对的客户,也只能由另一个模型来扮演。测评要先回答几件事:考的是什么样的旅程,标准答案从哪来、写成什么样才能让机器比对;扮演客户的模型怎样才像一个真人,又不会替 Agent 把题做了;分数怎么算,公布出去的数字包含什么、能说明什么。
这套测评分两部分:一批能反复跑、每次条件都一样的受控语料,以及一台跑语料的测试机。语料和它的标准文档是一位队友做的,要求和评审意见是我写的。
一个测试包
一个包是一次完整的报案旅程,一张图片或一份单据单独不算包。每个包四部分:标准答案、客户的第一句话、客户知道的全部事实、要上传的材料。另有一份 setup.json,写测试机开跑前要把系统准备成什么样。
CTL-M-001/
input/
initial_message.txt
facts.json
materials.json
materials/
photo-rear-bumper.jpg
repair-quote.pdf
oracle/
labels.json
setup.json
provenance.json
review.json
模型能看的放在 input/,答案放在 oracle/,两个目录分开。input/materials.json 只写文件名、证据类型和客户什么时候有这份文件;每张图片的生成用途、提示词、单据上印了哪些字段,都会泄露答案,所以挪进只有检查器和打分器读的 provenance.json。
第一句话按六种风格写:plain、many_facts、emotional、typos、non_native、wrong_product_term(比如把财产险说成 "house insurance")。风格只改措辞,不改事实,"water came through the ceiling" 可以变成 "water started coming through my ceiling!!",不能变成 "water was pouring"。日期一律写绝对日期。写"昨晚"的话,每次跑都是不同的一天,事故日期跟着挪,一个本来无辜的包就可能撞上日期检查。
答案从规则推出来,并且要测得出误报
第一批 3 个样例包(0.1 版)交上来,我提的几条意见都落在标准答案怎么写上,后来成了 0.2 版的改动。
没有无辜对照。 只有"该被提醒"的包,测不出误报,而题目要的恰恰是零误报。0.2 加了专门的无辜对照:看起来可疑,按规则不该提醒。
| 对照 | 为什么不提醒 |
|---|---|
reprint_date_after_loss | 补打的收据,打印日期在事故之后,购买日期在事故之前;只看购买日期 |
same_day_purchase | 当天买、当天丢,至少要差一天 |
one_prior_claim | 过去一年只有一宗别的理赔,规则要两宗 |
non_date_inconsistency | 和日期无关的普通不一致,分流 standard,不提醒 |
欺诈和冲突混在一起。 欺诈标签的意思是"这一单需不需要人看一眼",不等于"这是欺诈",也不能因为有某个边界条件就自动打上。0.2 里标签只从规则的几项检查推出来:收据日期晚于事故、维修报价早于事故是强信号;一年内两宗以上别的理赔、保单生效 30 天内出险是弱信号。每一项检查都要标一个结果,hit、clear、not_checked 或 not_applicable。not_checked 永远不等于 clear:日期看不清、在缺失的那一页上、查询失败、系统没有这项数据,都只能算没查,打分时单独统计。一个强信号或两个弱信号,才是 review_required。收据日期晚于事故,哪怕故事里给了无辜的解释,也照样提醒,解释随提醒一起交给员工,系统不替人判断。
答案是一段话。 打分器读不了散文。0.2 的 oracle 改成结构化的:分流的级别、必须出现的理由、允许出现的理由;每一项复核检查一个结果;断言分成三种裁判,state 看系统状态,text 看文字,human 留给人。事实也分两组:expected_facts 是系统在这条路径上必须收齐的,跑完必须是"已确认"且值对得上;optional_facts 是好的 Agent 可能会多问的,问了就得对。
缺页看不出来。 一份两页的报价单只给了第一页,单据本身看不出少了什么。0.2 的每一页都印 "Page X of N"。
图片只经过 AI 看。 语料里的照片是生成的,生成的图不一定画出了包里假设的东西。0.2 的 review.json 把助手的检查和人的签字分开记,check_corpus.py --freeze 在每张图都有一个具名的人签字之前不会通过。0.2 还加了一道盲看:只把图片发给产品自己用的视觉模型,不给事实、不给用途,记下它自己看出来了什么。一张电视屏幕碎裂的特写,被它认成了玻璃茶几,那张图就重做了。
扮演客户的模型要像一个真人
测试机用一个模型扮演客户,和 Agent 走完全程。这里最容易出的问题是测试本身在作弊:扮演客户的模型如果读到了标准答案,就会"恰好"说出 Agent 需要的话,旅程顺得不像话,分数好看,什么也证明不了。所以客户的可见范围要划清楚,按字段划分,不按文件划分。模拟客户能看到的是:
initial_message:原样发送,错别字也保留;persona:性格和说话方式;facts:客户知道的全部事实,不在里面的事就当客户不知道;materials里的文件本身,外加每个文件一句人话标签,比如"车尾照片""修车厂的报价单"。这些标签每个包手写一次,不能从系统的类型代码转出来,真人描述手里的东西时不会说incident_photo。
它绝对不能看到的是:系统的证据类型代码 evidence_kind,生成图片用的 prompt 和 purpose,只用来制作其他文件的原图,setup,以及整个 oracle。
客户能看的内容和标注放在不同的文件或对象里,防止哪天代码出错,把答案塞进客户的提示词。再配一个测试:oracle 或 setup 里的任何一个值,比如预期的分流级别,只要出现在客户模型的提示词里,测试就失败。
客户的行为也照真人来:只根据 facts 回答,问到包里没有的就按人设说不知道;不主动说 Agent 没问的事;不纠正 Agent,不暗示它该问什么,不主动递没被要的文件,除非人设里写了它会这么做。人设里写着"以后才有"的材料,第一次被要时就这样说,到了包里描述的时机再上传。
看屏幕做决定,看网络做记录
读 oracle 是一种偷看,另一种更隐蔽:读系统接口返回的数据。返回里有很多真人看不到的东西,customer_next_step.required_items 列着还缺哪些内容,turn_record 记着这一轮的判断,动态表单的要求、当前的案件表单都在里面。测试机拿这些去决定下一句说什么,和读答案没有区别。
所以每一个决定都只依据屏幕上看得到的内容:Agent 回复的文字、回复下的小标签、侧边栏、待办条、按钮和弹窗。交给客户模型的只有最新一条回复和需要操作的卡片上的可见文字,JSON 一概不给。网络数据全部录下来,用来留证和打分,但不让客户模型碰到。
环境也要干净。每个包开跑前通过种子路由装入自己的 setup,用全新的客户,新的匿名会话,或者按 claimant_session 的要求用新账号;anonymous_then_sign_in 就是先匿名开始,产品要求登录时再登录。包与包之间浏览器存储、账号、案件都不共用。跑的地方是 scripts/start-dev.ps1 起的本地环境,真实的 MongoDB、MinIO 和配置的模型;后端如果报告自己是受控运行时,也就是那个按关键词回话的替身,测试机拒绝启动。这套东西不进 CI,手动跑或定时跑。
打分是事后的、机械的
打分在旅程跑完以后单独进行,这时才读 oracle,按规则机械比对。模型可以帮忙写运行小结,但不能决定分数。每个包记录:
- 分流:员工端显示的级别和理由,对照预期的级别、必须的理由、允许的理由;
- 复核信号:触发没有,误报和漏报分开统计,无辜对照单独报告;
- 事实:每个关键事实的值对不对,事故时间单独统计;
- 旅程:拿没拿到案件编号,客户发了几条消息,Agent 重复问了几次,有几次回复让客户无话可答,重试了几次,卡在哪一步、为什么,每轮耗时的中位数和最长值;
- 员工端:客户途中的问题和联系请求,在员工的待答清单里是什么样子。
系统里还有没做完的能力,比如当时的复核信号和员工端的分流显示。遇到这些,这个包记为"系统未实现",不算测试机失败,也不能悄悄跳过,并且不计入通过和失败的统计。公布出去的数字,只包含真正测到的内容。
每个包留下客户看到的完整对话、每一步的截图、网络日志、客户模型的提示词和回复、打分表。运行产出放在仓库之外:运行记录是证据,和 fixture 分开放。
分数说明了什么
语料一旦冻结成 v1,就不再为了让结果好看去改答案或材料;真有错,出新版本,在 manifest 的变更记录里写明原因。失败的运行留在结果里,从不删除。
还有一点要说清楚:受控语料的标签,和系统要实现的是同一套规则,所以通过只能说明代码照规则做了。规则本身对不对,要由理赔员工的盲评另外衡量。
顺序也定好了:先做种子文件格式和种子路由、客户可见范围的划分和那条泄漏测试;再让一个车险包从第一句话一路跑到拿到案件编号,加上员工端读取;然后是 10 个包,带打分表和每个包的耗时、费用;10 个稳定了,再跑全部 100 个。100 个包的配比也有了提议:车险 50、房屋 30、财产 20,其中 17 个该提醒,13 个无辜对照。