LLM 封闭标签如何强约束?
提示工程、解码约束与微调三种手段对比
原题:假设你有一个大型语言模型(LLM),希望其输出严格限定在一个预定义的封闭标签集合内,应采用哪些技术手段(如提示工程、解码约束、微调等)来确保输出的准确性和一致性?
模型微调
30 秒回答
- 提示工程(Few-shot + 格式约束)是基础手段
- 约束解码(Constrained Decoding)是核心保障
- 微调/SFT提升指令遵循能力
- Function Calling/JSON Schema是工程实践标准方案
回答与解析
答案要点
- 提示工程(Few-shot + 格式约束)是基础手段
- 约束解码(Constrained Decoding)是核心保障
- 微调/SFT提升指令遵循能力
- Function Calling/JSON Schema是工程实践标准方案
- 后处理校验作为兜底机制
核心思路
封闭标签输出本质是可控生成问题,需多层防护:提示工程 → 模型能力 → 解码约束 → 后处理校验。
技术手段(按优先级)
1. 提示工程(基线)
- Zero-shot:明确指令 + 标签列表 + "只能从以下选择"
- Few-shot:提供输入→标签的示例,稳定输出格式
- 加格式约束:"输出必须是以下之一:[A, B, C],不要解释"
2. 约束解码(强保障)
- 原理:在解码每一步,只保留合法标签token的logits,非法token概率置-∞
- 实现:用FSM(有限状态机)或CFG(上下文无关文法)定义合法输出空间
- 工具:Outlines、Guidance、lm-format-enforcer等库
- 优势:100%保证输出合法性,不依赖模型"自觉"
3. Function Calling / JSON Schema(工程标准)
- OpenAI/Claude等原生支持,强制输出符合预定义schema
- 底层即约束解码实现,但API封装易用
- 适合标签需附带置信度、理由等结构化场景
4. 微调/SFT(能力强化)
- 构造指令-标签数据对,训练模型指令遵循能力
- 配合LoRA高效微调,适合领域标签(如医疗诊断分类)
- 注意:单独SFT无法100%保证封闭性,需配合解码约束
5. 后处理校验(兜底)
- 正则匹配提取标签,非法输出fallback到默认/重试
- 多标签场景用投票或一致性检查
选型建议
| 场景 | 推荐方案 |
|---|---|
| 快速验证 | Few-shot + 后处理 |
| 生产部署 | Function Calling / 约束解码库 |
| 领域定制 | SFT + 约束解码双层保障 |
| 动态标签 | 约束解码(无需改模型) |
口语版讲法(约4分钟)
- 一句话定位:封闭标签输出本质是可控生成问题
- 提示工程:基线手段但不够可靠
- 约束解码:核心保障,保证100%合法
- 微调与后处理:辅助和兜底
- 选型取舍与风险提醒
这道题其实问的是,怎么让大模型输出严格限定在我们指定的几个标签里,比如客服场景里,用户问退款问题,模型只能输出“退款中、已退款、退款失败”这几个状态,不能自由发挥。这本质上是可控生成问题,我有几层手段,从轻到重,但真正落地的时候,我不会只依赖一层。
先说提示工程,这是最基础的。我会在系统提示里明确给出标签列表,加上类似“只能从以下选择,不要解释”的指令。再给几个例子,就是 Few-shot,让模型模仿。但说实话,这层只能管住大部分情况,模型还是可能跑偏,尤其面对复杂表述或者对抗性输入时,它可能会自己发明一个标签。所以提示工程只是基线,不能当唯一保障。
再一个,约束解码,这是核心。原理很简单:在模型生成每一步,只保留合法标签对应的 token 概率,其他 token 的概率直接置成负无穷。比如标签是“退款中”和“已退款”,那模型生成“退”之后,下一个 token 只能是“款”,不能是别的。这能 100% 保证输出合法,因为它从解码层面就锁死了。实现上可以用 有限状态机 或者上下文无关文法来定义合法输出空间,有现成的库比如 Outlines、Guidance。生产环境我一般会用这层做主力。
然后微调,也就是 SFT,可以提升模型对指令的遵循能力。比如我收集一批指令-标签对,让模型学会在特定格式下输出。但注意,单独微调也不能保证百分之百,尤其模型没见过的新变体。所以我会把微调和约束解码结合起来,微调让模型更听话,约束解码兜住最后的底线。
另外还有 Function Calling,现在很多模型原生支持,你给它一个 JSON Schema,它必须输出符合 schema 的结构。底层其实就是约束解码,但 API 封装得很好用。如果标签还需要附带置信度或者理由,这个方案就特别合适。
最后是后处理校验,用正则匹配提取标签,如果匹配不到就 fallback 到默认标签或者重试。这层是兜底,不能依赖它,但加上去能提高鲁棒性。
选型上,我的经验是分场景。快速验证,用提示工程加后处理就够了。生产部署,我会优先用约束解码库或者 Function Calling,因为可靠性高。如果领域很特殊,比如医疗诊断分类,我还会加一层微调,让模型更理解领域语义。另外有个前提:标签集必须是静态且有限的,如果标签动态变化,那约束解码就比微调灵活得多,因为不需要重新训练模型。
这里有个常见失败场景:你用了约束解码,但模型第一个 token 就生成了非法字符,比如空格或者标点,那解码器可能直接卡住。所以我会在约束文法里允许一些空白和标点前缀,或者用更宽松的规则先过滤。还有,如果标签本身有歧义,比如“退款中”和“退款处理中”语义相近,模型可能混淆,那就需要靠微调或者更好的标签设计来区分。
另外,如果标签集特别大,比如上千个,约束解码的性能会下降,因为每一步要过滤的 token 太多。这时候我会考虑用分层标签,先输出大类再输出小类,或者用向量检索先缩小候选集,再让模型从候选中选。这其实变成了检索加精排的思路。
所以总的来说,我的做法是多层防护:提示工程打底,约束解码做核心保障,微调提升能力,后处理兜底。具体用哪几层,取决于你对可靠性的要求、标签的规模和动态性。我更倾向于在解码层下功夫,因为它最可控,不依赖模型“自觉”。
关键一句:当标签集非常大时,约束解码性能可能下降,需要分层或检索辅助。
面试官还可能这样问
- 问法 1 · 场景切入
假设你做电商客服,需要把用户问题分类到“售后/物流/退货”等十几个固定类别里,怎么确保LLM只输出这些标签,不会乱说别的?
- 问法 2 · 层层追问
让LLM输出固定标签,你一般怎么做?……如果模型偶尔输出不在集合里的词怎么办?……有没有办法从解码层面强制它只生成合法标签?
- 问法 3 · 直球架构
设计一个系统,要求LLM输出严格限定在预定义的封闭标签集合内,你会从提示工程、解码约束、微调、后处理这几个层面分别怎么设计?优先级怎么排?