这道题本质上是问怎么让大模型输出严格落在封闭标签集合内,说白了就是可控生成问题。我的思路是分层防御,从软约束到硬约束逐层加固,因为单一方案总会有漏洞。
先说提示工程,这是最轻量的软约束。比如做个客服分类,我在系统Prompt里写明“只允许输出退款、换货、投诉、咨询、其他之一”,再加几个[[Few-shot]]示例。但这样其实靠不住,模型可能不听话,尤其标签相似时容易混淆。所以它适合快速验证或标签少的场景,生产环境我绝不敢只靠这个。
再一个就是解码约束,这才是硬约束。核心是自定义Logits Processor,生成第一步就把非法标签的token概率置为负无穷,只让模型从合法token里选。举个例子,标签是“退款”,我提前拿到“退款”对应的token ID,解码时其他token全屏蔽。这样模型根本没机会输出非法内容。但这里有个坑:如果标签是短语,比如“申请退款”,每个token都得单独约束,可能生成出“申请退货”,那就得配合prefix_allowed_tokens_fn做更细的状态机。实现时我会特别注意tokenizer的编码方式,确保约束覆盖完整。
然后是后处理,作为兜底。模型输出后,如果不在标签集合里,我用编辑距离或语义相似度做模糊匹配,匹配不上就返回“其他”。这层主要防意外,但别指望它解决所有问题,匹配错了反而更糟。
最根本的还是微调,也就是[[SFT]]。构造一批高质量分类数据,标签分布均衡,让模型内化这个任务。如果有资源,还可以用[[RLHF]]或[[DPO]]对输出非法标签的行为打低分。或者给每个标签分配一个特殊token,降低混淆。微调能提升基础能力,但成本高,而且标签一变就得重训。
所以实际落地我倾向组合用:SFT打底,让模型本身更可靠;解码约束保底线,确保输出永远合法;后处理兜底,处理边界情况。具体选型要看场景:标签少且快速验证,提示工程加后处理就够了;生产环境要求高可靠,必须上约束解码加微调;如果标签动态变化,那就以后处理为主,避免频繁重训。
这里有个风险:约束解码会限制模型能力,如果标签集合覆盖不全,模型可能被迫输出错误标签。上线前我会用大量测试样本跑一遍,看非法输出比例和模糊匹配的准确率,同时监控用户反馈。
另外,如果标签集合特别大,比如几千个,约束解码的效率会下降,因为要维护一个巨大的合法token列表。我可能会考虑把标签先聚类,分步解码,先预测大类,再细化到具体标签,这样既能保证精度,又控制计算开销。
所以总体来看,我会把这个问题看成系统工程,不是靠一个技巧解决,而是分层防御、各司其职。