📌 痛点:模型输出太自由,后端解析直接崩溃
在用 LLM 做文本分类或信息抽取时,最让人头疼的不是准确率,而是输出格式不可控。
你可能会遇到这些情况:
- 让模型输出“基础层”,它偏偏输出“我认为这属于基础层。”
- 让模型输出逗号分隔的关键词,它给你一行一个,或者用中文逗号、英文逗号混着来。
- 让模型输出不超过5个字,它给你整出一句话。
如果你的后端代码用正则或 split 来解析,程序就直接崩了。
我在用 DeepSeek 和 OpenAI 处理 14,000 条地铁反馈时,就彻底解决了这个问题。这篇文章分享一套“Prompt 设计 + 参数调优 + 后处理兜底”的组合拳,让你的大模型输出像函数返回值一样稳定。
🎯 目标:让模型只输出“列车空调”,而不是“空调太冷了希望改进”
以情感因素提取为例,我的需求是:输入乘客反馈,输出 1-3 个 3-7 汉字的核心服务要素,用中文逗号分隔。
期望输出示例:
列车空调,车厢拥挤
现实中的“自由发挥”:
好的,为您提取的服务要素是:列车空调、车厢拥挤。
或者:
1. 列车空调
2. 车厢拥挤
🛠️ 第一道防线:Prompt 中的“格式强约束”
我把约束直接写进了 System Prompt,并且用“核心原则”的方式反复强调:
system_content = """你作为南京地铁服务分析专家,请严格从乘客反馈中提取南京地铁的具体服务特征要素。请遵守:
核心原则:
1. 所有输出关键词必须直接指向南京地铁的具体属性/服务/特征
2. 表面现象→本质问题:将乘客的具体遭遇映射到南京地铁的可改进点
3. 采用最精简的名词短语(3-5个汉字)
4. 禁止任何修饰词和情感词
输出要求:
- 每个要素3-7个汉字
- 用中文逗号分隔,不要解释"""
关键技巧:
- 用“禁止”比用“请勿”更强硬:模型更倾向于遵守否定式指令。
- 明确分隔符:“用中文逗号分隔”比“用逗号分隔”更精确,避免中英文标点混用。
- 禁止解释:这句话能省下大量输出 token,并且防止模型在前面加废话。
🎯 第二道防线:Few-Shot 示例比规则更有效
LLM 是“模仿大师”,给它看标准答案比给它念规则管用得多。我在 User Prompt 中加入了“转化示例”:
user_content = f"""
【转化示例】
输入:"坐南京地铁十几年了真有感情"
→ 输出:地铁历史
输入:"毕业季的车厢装饰太浪漫了"
→ 输出:毕业季装饰
输入:"列车空调冷得像冰窖"
→ 输出:列车空调
输入:"安检员帮我找回了钱包"
→ 输出:安检服务
【当前任务】
请提取1-3个南京地铁具体服务要素:{text}
"""
为什么有效?
- 示例展示了输入和输出的直接映射,没有中间分析过程(分析过程在 System Prompt 里)。
- 示例覆盖了不同场景:隐晦表达、正面评价、负面抱怨、服务互动。
⚙️ 第三道防线:参数调优——用低温度锁死随机性
即使 Prompt 完美,模型仍可能“抽风”。参数设置是最后一道软件层面的防线。
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
temperature=0.1, # 极低温度,确保输出稳定
max_tokens=50 # 限制输出长度,防止模型“发挥”
)
temperature=0.1:分类和抽取任务不需要创造性,越低越稳定。我测试过,从 0.3 降到 0.1 后,格式违规率下降了约 40%。max_tokens=50:一个要素 5 个字,3 个要素加逗号最多 20 字。给 50 token 的冗余足够,但又不至于让模型长篇大论。
🧹 第四道防线:后处理——代码层面的最终兜底
即使前三道防线做到极致,偶尔仍会有“漏网之鱼”。这时就需要在代码里做防御性解析。
result = response.choices[0].message.content.strip()
# 1. 统一分隔符:中文逗号 → 英文逗号
result = result.replace(",", ",")
# 2. 按逗号分割
factors = [f.strip() for f in result.split(",")]
# 3. 过滤掉长度不符合要求的(2-7个汉字)
factors = [f for f in factors if 2 <= len(f) <= 7]
# 4. 如果过滤后为空,返回默认值
return factors if factors else ["无法提取"]
为什么这样做?
- 统一分隔符后,无论模型输出的是“A,B”还是“A,B”,都能正确分割。
- 长度过滤能去掉“好的”这类残留词。
- 返回默认值保证程序不会因为空列表而报错。
💡 总结:格式化输出的四层防御体系
| 防线 | 方法 | 作用 |
|---|---|---|
| 第一层 | System Prompt 强约束 | 告诉模型“必须怎样”和“禁止怎样” |
| 第二层 | Few-Shot 示例 | 用标准答案引导模型模仿 |
| 第三层 | 低 temperature + max_tokens | 从参数层面锁定输出稳定性 |
| 第四层 | 代码后处理 | 最终兜底,确保程序不崩溃 |
这套组合拳下来,我在 14,000 条数据上的格式合规率达到了 99% 以上。
🔗 完整代码
完整实现已开源在 GitHub:
👉 nanjing-metro-analysis/scripts/03_topic_extraction/extract_factors.py