先看现象。promptfoo 0.95 的 assert 里用 contains 判断输出是否包含「退款成功」,中文用例全部失败,英文用例正常。改成 regex 也失败,输出肉眼看着确实有这几个字,试过把断言值缩短成「退款」仍然不通过,报告里只显示 expected to contain。

原因不止一个,得分开看。模型输出里插了零宽字符,肉眼看不出来但字符串比对不过。少数情况下是断言用的是全角标点,输出里是半角,没有做规范化。

少数情况下是contains 默认区分大小写,英文部分大小写不一致。

不容易想到的是,provider 返回的是数组结构,断言作用在了整个对象上。

先把模型原始输出打出来看码点

python -c "s=open('out.txt').read();print([hex(ord(c)) for c in s if ord(c)<32 or ord(c)==8203])"

再往下用规范化后的文本再做 contains 断言

python -c "import unicodedata;s=unicodedata.normalize('NFKC',open('out.txt').read());print('退款成功' in s)"

随后把断言改成对齐码点的正则

python -c "import re;print(bool(re.search('退款\s*成功',open('out.txt').read())))"

等前面都确认过,再在配置里改用 javascript 断言后重跑

npx promptfoo eval -c promptfooconfig.yaml

怎么算真的好了,看的是同一批现象。同一个用例在 promptfoo 里通过,报告里断言成功的条目与人工核对一致,英文用例没有回退。

提醒一点。中文比对失败先查零宽字符和全角半角,NFKC 规范化之后再断言,比反复改断言关键词有效。同一份配置在测试环境跑通,不代表线上没事,两边的默认值经常不一样。

作者 9823189