📌 背景:分类只是第一步
在上一篇文章中,我分享了如何用 LLM 把 14,088 条地铁乘客反馈分类到马斯洛需求层次中。
但分类只是第一步。知道"这条反馈属于舒适层"还不够,运营方真正想知道的是:到底什么东西让乘客不舒服?
比如这条反馈:
"南京地铁的空调能不能调高点?每次坐都冻成狗,夏天穿裙子根本扛不住。"
分类结果:舒适层 + 负面
但具体问题是什么?答案是:列车空调
再比如这条:
"工作人员帮我找回了钱包,真的太感谢了!"
分类结果:尊重层 + 正面
具体要素是:安检服务 / 失物招领
这就是本文要解决的问题——从口语化的微博文本中,精准提取 3-7 个字的核心服务要素。
🤔 为什么是"3-7 字关键词"?
地铁运营方需要的不是长篇大论,而是可以直接定位到具体设施或服务的名词短语。
| 反馈原文 | 理想提取结果 | 为什么 |
|---|---|---|
| "工作人员帮我找回了钱包,太感谢了" | 安检服务 | 指向具体服务类型 |
| "早晚高峰挤得怀疑人生,门都关不上" | 车厢拥挤 | 可量化、可改进的问题 |
| "报站声音太小,戴着耳机根本听不清" | 报站音量 | 具体可调节的参数 |
| "毕业季的车厢装饰太浪漫了吧" | 毕业季装饰 | 具体可复制的活动 |
| "S6号线什么时候通车啊" | 线路开通 | 具体可追踪的进展 |
核心原则:
- 长度 3-7 个汉字(最精简的名词短语)
- 必须指向具体可改进的服务要素
- 禁止形容词和情感词(如"很好""太烂")
- 禁止只输出"南京地铁"(没有信息量)
🛠️ 第一版提示词:过于自由,结果失控
我最初写的提示词很简单:
请从以下乘客反馈中提取 1-3 个南京地铁的具体服务要素:
{text}
结果惨不忍睹:
| 反馈原文 | 模型输出 | 问题 |
|---|---|---|
| "空调太冷了" | "空调温度过低,希望调高" | 输出整句话,不是关键词 |
| "工作人员态度特别好" | "南京地铁工作人员" | 太长,且包含冗余信息 |
| "每次坐都觉得很干净" | "干净" | 形容词,不是可操作的服务要素 |
| "换乘太远了走得腿疼" | "换乘体验差" | 包含评价词"差" |
问题分析:
- 没有约束输出格式:模型不知道要输出短语而不是句子
- 没有示例:模型不理解什么是"服务要素"
- 没有长度限制:输出结果参差不齐
- 没有禁止修饰词:模型会输出"好""差"等形容词
🎯 第二版:加格式约束 + 加示例
我开始细化要求:
请从乘客反馈中提取南京地铁的具体服务特征要素,要求:
1. 每个要素 3-7 个汉字
2. 必须是可以被地铁管理部门直接处理的具体问题
3. 采用最精简的名词短语
4. 禁止任何修饰词和情感词
5. 用中文逗号分隔,不要解释
示例:
输入:"坐南京地铁十几年了真有感情" → 输出:地铁历史
输入:"毕业季的车厢装饰太浪漫了" → 输出:毕业季装饰
输入:"列车空调冷得像冰窖" → 输出:列车空调
输入:"安检员帮我找回了钱包" → 输出:安检服务
待分析内容:{text}
效果:输出格式规范了,大部分能输出正确的短语形式。
但新问题出现了:
- 模型经常输出"无法提取"或"无"
- 当乘客反馈比较隐晦时,模型不知道该不该提取
- 多要素场景下,模型有时会把几个要素混在一起
💡 第三版:增加"转化示例",教模型推理
我意识到:模型需要看到从口语到要素的映射过程。
于是我在 system prompt 中增加了完整的推理框架:
messages = [
{
"role": "system",
"content": """你作为南京地铁服务分析专家,请严格从乘客反馈中提取南京地铁的具体服务特征要素。请遵守:
核心原则:
1. 所有输出关键词必须直接指向南京地铁的具体属性/服务/特征
2. 表面现象→本质问题:将乘客的具体遭遇映射到南京地铁的可改进点
3. 采用最精简的名词短语(3-5个汉字)
4. 禁止任何修饰词和情感词
输出要求:
- 必须包含"地铁"或能明确对应其服务的词汇
- 每个要素3-7个汉字
- 用中文逗号分隔,不要解释"""
},
{
"role": "user",
"content": f"""请提取南京地铁的具体服务要素:
【乘客反馈原文】
{text}
【分类信息】
需求层次:{demand_level}
情感倾向:{sentiment}
【转化示例】
输入:"坐南京地铁十几年了真有感情"
→ 输出:地铁历史
输入:"毕业季的车厢装饰太浪漫了"
→ 输出:毕业季装饰
输入:"列车空调冷得像冰窖"
→ 输出:列车空调
输入:"安检员帮我找回了钱包"
→ 输出:安检服务
【当前任务】
请提取1-3个南京地铁具体服务要素:"""
}
]
这一版的效果:
- 提取成功率从约 70% 提升到 95%
- 输出格式几乎 100% 符合要求
- 多要素场景下能正确分离(如"拥挤+空调差" → "车厢拥挤,列车空调")
📊 最终提示词结构
经过迭代,我总结出要素提取提示词的黄金结构:
1. 【System Prompt:角色与原则】
- 赋予专家身份
- 明确核心原则(表面现象→本质问题)
- 规定输出格式(长度、禁止项)
2. 【User Prompt:任务与示例】
- 提供原文
- 提供分类信息(需求层次+情感倾向,帮助模型理解语境)
- 提供转化示例(4个典型案例)
- 明确当前任务
3. 【后处理】
- 按逗号分割
- 过滤长度不符合的
- 去重
💡 四个核心经验
1. 提供分类信息作为上下文
在提取要素时,我会把该条反馈的"需求层次"和"情感倾向"也传给模型。这让模型更容易理解语境——比如知道这是"舒适层+负面",模型会更倾向于提取环境相关要素而非服务相关要素。
2. 转化示例要覆盖不同场景
我的 4 个示例分别覆盖了:
- 隐晦表达 → 本质问题("有感情"→"地铁历史")
- 正面评价 → 具体活动("装饰浪漫"→"毕业季装饰")
- 负面抱怨 → 具体设施("冷得像冰窖"→"列车空调")
- 服务互动 → 服务类型("找回钱包"→"安检服务")
3. 后处理过滤很重要
即使提示词很完善,模型偶尔还是会输出不符合要求的內容。我在代码中加入后处理:
factors = [
f.strip()
for f in result.replace(",", ",").split(",")
if f.strip() and 2 <= len(f.strip()) <= 7
]
return factors if factors else ["无法提取"]
4. 长度限制要合理
经过测试,3-7 汉字是最优区间:
- 太短(1-2字):信息量不足,如"空调"无法区分是列车空调还是站厅空调
- 太长(8字以上):不够精简,如"车厢空调温度太低"可以简化为"列车空调"
🔧 完整代码
def analyze_factors_with_openai(text, demand_level, sentiment):
"""使用OpenAI API分析情感因素"""
messages = [
{
"role": "system",
"content": """你作为南京地铁服务分析专家,请严格从乘客反馈中提取南京地铁的具体服务特征要素。请遵守:
核心原则:
1. 所有输出关键词必须直接指向南京地铁的具体属性/服务/特征
2. 表面现象→本质问题:将乘客的具体遭遇映射到南京地铁的可改进点
3. 采用最精简的名词短语(3-5个汉字)
4. 禁止任何修饰词和情感词
输出要求:
- 必须包含"地铁"或能明确对应其服务的词汇
- 每个要素3-7个汉字
- 用中文逗号分隔,不要解释"""
},
{
"role": "user",
"content": f"""请提取南京地铁的具体服务要素:
【乘客反馈原文】
{text}
【分类信息】
需求层次:{demand_level}
情感倾向:{sentiment}
【转化示例】
输入:"坐南京地铁十几年了真有感情"
→ 输出:地铁历史
输入:"毕业季的车厢装饰太浪漫了"
→ 输出:毕业季装饰
输入:"列车空调冷得像冰窖"
→ 输出:列车空调
输入:"安检员帮我找回了钱包"
→ 输出:安检服务
【当前任务】
请提取1-3个南京地铁具体服务要素:"""
}
]
try:
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=messages,
temperature=0.1,
max_tokens=50
)
result = response.choices[0].message.content.strip()
factors = [
f.strip()
for f in result.replace(",", ",").split(",")
if f.strip() and 2 <= len(f.strip()) <= 7
]
return factors if factors else ["无法提取"]
except Exception as e:
print(f"API处理出错: {str(e)}")
return ["API错误"]
📈 提取结果统计
对 13,247 条有效分类数据进行要素提取,统计结果:
| 需求层次-情感 | 有效提取数 | 高频要素 Top 3 |
|---|---|---|
| 舒适层-负面 | 3,201 | 列车空调(584)、车厢拥挤(390)、噪音异味(182) |
| 尊重层-正面 | 2,481 | 暖心祝福(546)、服务贴心(161)、工作人员(152) |
| 保障层-负面 | 1,756 | 设施不足(295)、换乘不便(232)、电梯缺失(178) |
| 基础层-负面 | 1,782 | 列车故障(179)、安全隐患(285)、延误(150) |
| 共鸣层-正面 | 1,458 | 归属感(320)、怀旧感(280)、城市自豪(195) |
🔗 完整代码与项目
完整实现已开源在 GitHub:
👉 nanjing-metro-analysis/scripts/03_topic_extraction/
包含:
- 完整的提示词模板
- 批量处理 14,000 条数据的工程代码
- 统计分析与可视化
📮 写在最后
要素提取看似简单,但要保证输出格式稳定、可解析,需要精心设计提示词和后处理逻辑。核心思路是:给模型明确的角色、清晰的示例、严格的格式约束。
下一篇将分享《拒绝拍脑袋!Sentence-BERT 文本聚类如何用轮廓系数自动寻优?》,教你如何把几千个要素聚合成十几个主题。
本文是"南京地铁乘客需求分析"系列的第二篇。