提示词优化实战:少样本学习Few-shot Learning

今天我就用这个真实案例,讲讲少样本学习(Few-shot Learning)的坑和优化技巧。


原始代码:问题出在哪?

from openai import OpenAI

client = OpenAI(
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)

# 示例数据
examples_data = {
    '退货': '我想退掉昨天买的衣服',
    '换货': '这个商品有质量问题,能换吗',
    '咨询': '这个手机支持 5G 吗',
    '投诉': '你们的服务太差了'
}

# 构建 messages
messages = [
    {"role": "system", "content": "你是客服助手,将用户问题分类..."}
]

for key, value in examples_data.items():
    messages.append({"role": "user", "content": value})
    messages.append({"role": "assistant", "content": key})

# 提问
for q in questions:
    response = client.chat.completions.create(
        model="qwen3.5-plus",
        messages=messages + [{"role": "user", "content": q}]
    )

乍一看没问题,对吧?有系统提示、有示例、有问答对。

但实际跑起来,模型经常分错。为什么?


问题一:示例和问题的格式不一致

仔细看代码,示例是这样的:

用户:我想退掉昨天买的衣服
助手:退货

但提问的时候是这样的:

用户:按照示例,回答这段文本的分类类别:{用户问题}

模型懵了:前面每个用户消息后面都跟了助手回答,怎么最后一个没有?

更严重的是,提问时还加了个前缀:“按照示例,回答…”,示例里可没有这句话!模型没见过这种格式,自然容易跑偏。


问题二:示例太简单,缺乏上下文

看看原始示例:

'退货': '我想退掉昨天买的衣服'
'换货': '这个商品有质量问题,能换吗'

问题:

  • 示例太短了,就一句话
  • 没有体现不同场景的表达差异
  • 用户实际说话可能更复杂,比如:“我上周在你们店买的那件蓝色衬衫,洗了一次就掉色,能不能退?”

好的示例应该覆盖多种表达方式


问题三:系统提示太模糊

原始系统提示:

你是客服助手,将用户问题分类为退货、换货、咨询、投诉

问题:

  • 没有说明每类的判断标准
  • 没有说明输出格式
  • 没有边界情况处理(比如用户同时想退货又想投诉)

优化方案:三步走

Step 1:统一格式

确保示例和提问的格式完全一致

# 示例
用户:{用户问题}
助手:{类别}

# 提问
用户:{用户问题}
助手:

提问时不要加任何前缀,让模型自然续写。

Step 2:丰富示例

每个类别准备 2-3 个不同表达方式的示例:

examples = [
    # 退货 - 多种表达方式
    ("我上周买的那件衬衫,洗了一次就掉色,能不能退?", "退货"),
    ("这个商品和描述不符,我要申请退货", "退货"),
    ("尺码不合适,想退掉重新买", "退货"),
    
    # 换货 - 强调质量问题
    ("收到货发现包装破损,能换个新的吗?", "换货"),
    ("这个耳机一边没声音,可以换货吗", "换货"),
    
    # 咨询 - 询问信息
    ("这款手机支持无线充电吗?", "咨询"),
    ("请问发货地是哪里?大概几天能到?", "咨询"),
    ("有优惠券可以用吗?", "咨询"),
    
    # 投诉 - 表达不满
    ("客服态度太差了,我要投诉!", "投诉"),
    ("物流一个星期了还没到,怎么回事?", "投诉"),
]

特点:

  • 每类 2-3 个示例,覆盖不同表达
  • 包含具体细节(“洗了一次就掉色”、“一边没声音”)
  • 贴近真实用户说话方式
Step 3:强化系统提示
system_prompt = """你是电商客服意图识别专家。

任务:将用户问题分类为以下 4 类之一:
- 退货:用户要求退回商品并退款
- 换货:用户要求更换商品(通常因质量问题)
- 咨询:用户询问商品信息、物流、优惠等
- 投诉:用户表达不满或要求处理问题

输出规则:
- 只输出类别名称,不要解释
- 如果同时涉及多类,按用户核心诉求分类
- 无法确定时输出"转人工"

以下是示例:"""

优化后的完整代码

from openai import OpenAI

client = OpenAI(
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key="sk-xxx"  # 替换为你的 API Key
)

# 系统提示
system_prompt = """你是电商客服意图识别专家。

任务:将用户问题分类为以下 4 类之一:
- 退货:用户要求退回商品并退款
- 换货:用户要求更换商品(通常因质量问题)
- 咨询:用户询问商品信息、物流、优惠等
- 投诉:用户表达不满或要求处理问题

输出规则:
- 只输出类别名称,不要解释
- 如果同时涉及多类,按用户核心诉求分类
- 无法确定时输出"转人工"

以下是示例:"""

# 丰富的示例数据
examples = [
    ("我上周买的那件衬衫,洗了一次就掉色,能不能退?", "退货"),
    ("这个商品和描述不符,我要申请退货", "退货"),
    ("收到货发现包装破损,能换个新的吗?", "换货"),
    ("这个耳机一边没声音,可以换货吗", "换货"),
    ("这款手机支持无线充电吗?", "咨询"),
    ("请问发货地是哪里?大概几天能到?", "咨询"),
    ("客服态度太差了,我要投诉!", "投诉"),
    ("物流一个星期了还没到,怎么回事?", "投诉"),
]

# 构建 messages
messages = [{"role": "system", "content": system_prompt}]

for text, label in examples:
    messages.append({"role": "user", "content": text})
    messages.append({"role": "assistant", "content": label})

# 测试问题
test_questions = [
    "我买的鞋子尺码小了,能退吗?",
    "这个充电宝充一次就没电了,质量问题",
    "你们店满多少包邮?",
    "打了三次客服电话都没人接,太失望了",
    "今天天气怎么样",  # 无关内容,测试边界
]

# 逐个测试
for q in test_questions:
    messages.append({"role": "user", "content": q})
    
    response = client.chat.completions.create(
        model="qwen3.5-plus",
        messages=messages,
        temperature=0.1,  # 分类任务用小温度
        max_tokens=20
    )
    
    print(f"输入:{q}")
    print(f"输出:{response.choices[0].message.content}")
    print("-" * 50)
    
    # 移除最后一个用户消息,保持 messages 不变
    messages.pop()

关键改进:

  1. temperature=0.1:分类任务要稳定
  2. max_tokens=20:防止模型啰嗦
  3. messages.pop():每次提问后移除,保持示例不变
  4. 提问时不加任何前缀

效果对比

优化前:

输入:我买的鞋子尺码小了,能退吗?
输出:换货  # 错,应该是退货

输入:今天天气怎么样
输出:咨询  # 瞎猜

优化后:

输入:我买的鞋子尺码小了,能退吗?
输出:退货  # 正确

输入:今天天气怎么样
输出:转人工  # 正确识别为无关内容

我踩过的坑(血泪教训)

坑 1:示例越多越好?

错。我一开始加了 20 个示例,结果模型反而更困惑。8-12 个高质量示例 > 30 个低质量示例

坑 2:示例要完全覆盖所有情况?

不用。示例的作用是展示格式和典型特征,不是覆盖所有情况。边界情况交给模型自己泛化。

坑 3:temperature 设成 0 最稳定?

也不是。temperature=0 在某些模型上会有奇怪行为,0.1-0.3 是分类任务的甜点区。

坑 4:系统提示越长越好?

大错特错。系统提示超过 500 字,模型反而会忽略后面的内容。简洁、结构化才是王道。


进阶技巧:动态示例选择

如果你的场景更复杂,可以试试动态选择示例

def select_relevant_examples(query, examples, k=4):
    """根据查询内容,选择最相关的 k 个示例"""
    # 简单版:用关键词匹配
    keywords = {
        '退货': ['退', '退款', '不要了', '尺码不合适'],
        '换货': ['换', '质量问题', '破损', '故障'],
        '咨询': ['请问', '支持吗', '多少钱', '多久'],
        '投诉': ['投诉', '太差', '失望', '举报'],
    }
    
    scores = []
    for text, label in examples:
        score = sum(1 for kw in keywords.get(label, []) if kw in query)
        scores.append((score, text, label))
    
    scores.sort(reverse=True)
    return [(text, label) for _, text, label in scores[:k]]

# 使用
relevant_examples = select_relevant_examples(query, examples)

这样可以让示例更贴近当前问题,进一步提升准确率。


通用模板:直接套用

最后给个通用模板,你可以直接改改用:

from openai import OpenAI

client = OpenAI(
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
    api_key="sk-xxx"
)

# 1. 写清楚系统提示(任务 + 类别定义 + 输出规则)
system_prompt = """你是 [领域] 专家。

任务:将输入分类为以下类别之一:
- 类别 1:定义说明
- 类别 2:定义说明
- 类别 3:定义说明

输出规则:
- 只输出类别名称
- 无法确定时输出 [兜底选项]

以下是示例:"""

# 2. 准备 8-12 个高质量示例(每类 2-3 个)
examples = [
    ("示例文本 1", "类别 1"),
    ("示例文本 2", "类别 2"),
    # ...
]

# 3. 构建 messages
messages = [{"role": "system", "content": system_prompt}]
for text, label in examples:
    messages.append({"role": "user", "content": text})
    messages.append({"role": "assistant", "content": label})

# 4. 提问(不要加前缀!)
messages.append({"role": "user", "content": "你的问题"})

response = client.chat.completions.create(
    model="qwen3.5-plus",
    messages=messages,
    temperature=0.1,
    max_tokens=20
)

print(response.choices[0].message.content)

写在最后

搞完这个案例,我最大的感受是:提示词优化不是玄学,是有方法论的

核心就三点:

  1. 格式一致:示例和提问长一个样
  2. 示例精简:短小、特征明显、覆盖主要场景
  3. 指令清晰:告诉模型做什么、怎么做、输出什么

别指望一次就完美,多测试、多迭代。同一个模型,提示词改一改,准确率能从 60% 提升到 95%。

最后给个建议:把你的提示词和示例存到版本控制里,每次改动都记录效果。这样你才能知道什么改法有效,什么改法是瞎折腾。

有问题欢迎在评论区交流。如果觉得有用,点个赞再走呗 👍


参考链接:

  • Qwen API 文档:https://help.aliyun.com/zh/dashscope/
  • 少样本学习论文:https://arxiv.org/abs/2005.14165
  • 提示词工程指南:https://platform.openai.com/docs/guides/prompt-engineering
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐