
一、背景与动机1.1 问题LLM模型输出具有随机性传统的字符串匹配工具不匹配新场景LLM模型根据不同的prompt会输出不同的结果甚至只是针对prompt的微调结果也会改变传统assert output“expected”进行精确匹配的断言方式不适用具有随机性的LLM模型1.2 现有方案的不足直接调用 API成本高、用时长、在CI中的表现不可控若使用人工审核判断标准比较主观复制性差只测试准确率缺失对于幻觉、语气、以及违禁词的检测维度1.3我的方案要完成什么像使用普通插件一样测试LLM效果能离线运行CI中秒级完成能够检测到模型效果的退化不止是“PASS”or“FAIL”。二、核心设计2.1四种语义断言该项目用于LLM模型生成的文字内容测试。一共设计四种断言断言用法原理mentions是否提到某个概念语义相似度not_mentions是否未提及某个概念语义相似度取反tone是否为某种语气多模板相似度匹配grounded_in是否为幻觉NLI蕴含判断2.2 链式调用assert_behavior(output).mentions(退款).tone(共情)每个断言方法返回self。保证一次输出可以有多个维度的检测免去多次断言的调用assert_behavior(output).mention(退款) assert_behavior(output).not_mention(退款) assert_behavior(output).tone(共情)2.3 回归基线检测漂移行为保存基线baseline记录基线模型的输出分数和状态对比基线探测模型输出的测试结果是否存在漂移statue_regression和score_drop从测试状态和测试得分两个维度进行评判status_regression记录测试结果是否存在从pass变为fail的情况若有则抛出错误score_drop若得分下降超过某一阈值若有则抛出错误2.4整体架构测试层pytest 测试文件 ↓ 断言层BehaviorAssertion四种断言 ↓ 模型层similarity.py / nli.py / tone.py ↓ 基线层BaselineRecorder / BaselineComparator ↓ CI 层GitHub Actions HTML 报告三、技术选型与决策3.1 语义相似度使用的是sentence-transformerparaphrase-multilingual-MiniLM-L12-v2模型考虑指标sentence-transformerLLM API成本0按token计算运算用时秒级受算力、网络影响存在波动输出结果结果确定具有随机性CICI可用只需要下载对应模型需要联网paraphrase-multilingual-MiniLM-L12-v2模型是适用于多语种的小模型120MB可用于英语、中文、法语等文本的分析且小模型可以放在本地环境上使用CPU运行。虽然分析效果比不上大模型但是项目的可复制性好易于推广。踩坑记录初版使用的模型是all-MiniLM-L6-v2是应用最广泛的小模型之一但是由于不支持中文才进行了更换。3.2 幻觉检测幻觉检测使用NLI模型mDeBERTa-v3-base-xnli-multilingual-nli-2mil7。同样是适用于多语的小模型。Why NLINLI模型可以判断出文本和支持文本之间的逻辑关系比单独的相似度查询更具有实用性。NLI判断结果 entailment前提与结果是逻辑包含关系 neutral前提与结果是中立关系无关系 contradiction前提与结果是逻辑相反关系 sentenct-transformer 查询文本和支持文本之间的相似度是多少 e.g.相似度高但是存在相反关系 支持退款 VS 不支持退款 -sentence-transformer会判断成功因为相似度高3.3 语气判断语气判断是使用了一系列语气模板文本进行匹配的。使用现有模型进行相似度计算的成本远小于设计一个分类器本地模型运行速度极快。语气支持数据有修改可以快速响应不用去分类器中慢慢调参。四、实现细节4.1语义相似度的封装def semantic_similarity(output,target): emb1model.encode(output,convert_to_tensorTrue)#model是用全局变量缓存的模型需要转化为tensor张量方便util计算 emb2model.encode(target,convert_to_tensorTrue) rawutil.cos_sim(emb1,emb2).item()#计算余弦相似度 return (raw1)/2#映射到0~10.5以下表示不相关4.2 NLI幻觉检测def check_entailment(output,context) nli_get_nli() result nli({textoutput,text_pair:context})#判断output和context的关系 labelLABEL_MAP.get(result[label],result[label])#防御性写法 return {label:label,score:result[label]}#若context output任意为空则返回“neutral”和0.0踩坑记录# 错误会导致包含关系的句子检测为矛盾 #手动拼 [SEP]是 BERT 时代的手动拼接方式对 DeBERTa 类模型不适用 nli(f{premise} [SEP] {hypothesis}) # 正确传字典 nli({text: premise, text_pair: hypothesis})修复记录一开始output的设计是专业回复语气辅助文本混合在一起的。这就导致grounded_in的判定会把语气文本识别为neutral进而导致整个幻觉检测失效。所以后面还是要拆分出来对语气文本单独检判断避免幻觉测试。4.3 基线的数据结构json { “test_refund_response::tone::共情”:#key函数名::断言类型 ::target { test:test_refund_response, assertion:tone, target:共情, status:passed, score:0.8421 “timestamp”:datetime.now().isoformat(timespecseconds) } }4.4 漂移检测的两种类型if base[status]passed and cur[status]failed:#状态变化 ... if (base[score]-cur[score])/base[score]0.10:#分数降低 ...4.5 链式调用的局限性链式调用最大的局限性是调用链上游的断言抛出错误下游的断言则不会再执行。即如果要全面检测模型的效果还是要分别调用。链式调用更适合快速跑通模型效果时使用而在完整的功能测试里还是要使用分别调用。4.6 HTML报告使用pytest-html直接生成在表格中自定义列用于记录“断言类型”踩坑记录之前的资料都是pytest-html3.x的版本一般使用时需要导入from py.xml import html def html_report(cells,target): cells.insert(2,html.th(target))但pytest-html4.2.0不需要导入了更新过后的写法如下def html_report(cell,target): cell.insert(2,fth{target}/th)#插入第三列,target是列名4.7 pytest插件集成pytest_addoption:添加--llm-save-baseline和--llm-compare-baselinepytest_configrue:创建recordor用于保存需要提交到html的数据详细内容见4.3基线的数据结构五、CI集成5.1 Github Actions workflow缓存hugging face模型安装依赖项准备NLI模型准备sentence-transformer模型跑测试对比模型效果上传html报告发现漂移就阻断六、坑的总结sentence-transformer模型选择grounded_in断言中需要隐去语气文本NLI手动拼 [SEP]需要锁 huggingface_hub1.5.0,2.0。这一条是因为在下载NLI模型和sentence-transformer模型到本地时使用了不同的下载工具的最新版本这两个工具的最新版本要求不一样的huggingface_hub版本。所以就导致了在要切换sentence-transformer模型时失败了。后续将huggingface_hub改到两个工具都适配的版本号就能解决该问题。CI 里 Python 代码缩进的坑python -c 多行代码 在 YAML 里会报 IndentationError。用 heredoc 写到临时文件再执行run: | cat download_nli.py EOF from huggingface_hub import snapshot_download snapshot_download( repo_idMoritzLaurer/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7, local_dir./models/mDeBERTa-v3-base-xnli-multilingual-nli-2mil7, local_dir_use_symlinksFalse, ) EOF python download_nli.py七、未来方向7.1 局限语气检测用模板匹配准确率有限经常出现两个语气得分非常接近模型误判的情况NLI 对长文本判断不稳定链式调用会打断记录7.2 未完待续语气检测换成分类模型支持多轮对话测试支持更多断言类型事实一致性、毒性检测等八、附录8.1 项目地址https://github.com/Hiddleston-12/pytest-llm-assert8.2 技术栈清单Python 3.11 / pytest / pytest-html / sentence-transformers / transformers / GitHub Actions8.3 参考资料pytest 官方文档Writing pluginssentence-transformers 文档HuggingFace NLI 模型卡片