一、问题背景:为什么“随便写个Prompt”在生产环境会出事
最近在做一个商品评论摘要模块,输入是用户对某款蓝牙耳机的多条评论,输出是一段不超过80字的摘要,要求包含“音质、续航、佩戴”三个维度的倾向性。
一开始团队里有人写了个极简Prompt:
总结以下评论。
上线后问题很明显:
- 有的输出80字,有的输出300字,前端卡片直接被撑爆;
- 有的只总结“音质”,漏掉续航和佩戴;
- 中文里混英文,甚至出现“根据评论,音质 good”这种中英夹杂;
- 最要命的是,Token消耗波动极大,按OpenAI计费,单条从180 Token到900 Token都有,成本不可控。
于是我们决定做一次系统的Prompt Engineering对比实验。目标很明确:在保证输出质量的前提下,把单条平均Token压到可预测范围内。
二、环境与版本
实验环境如下:
- Python 3.11.9
- openai==1.52.0
- transformers==4.46.3
- vLLM 0.6.3(用于本地Qwen推理)
- 模型:
- GPT-4o mini(2024-07-18版本,temperature=0.2,top_p=0.9,max_tokens=256)
- Qwen2.5-7B-Instruct(本地A100 40G,bfloat16,temperature=0.2,top_p=0.9,max_tokens=256)
- 数据集:200条真实蓝牙耳机评论,每条评论平均长度约120字,最多15条评论合并为一个输入。
- 评估指标:
- 格式合规率:输出是否为纯中文、是否≤80字、是否含三个维度;
- 人工评分:3人盲评,1-5分,取平均;
- Token消耗:prompt_tokens + completion_tokens;
- 成本:按GPT-4o mini输入$0.15/1M tokens、输出$0.60/1M tokens估算。
三、方案设计:6版Prompt的演进
我把6版Prompt分成三组:
- 基线组
- P0:零样本,极简指令:“总结以下评论。”
-
P1:零样本+长度约束:“用不超过80字总结以下评论,包含音质、续航、佩戴。”
-
结构化组
- P2:P1 + 输出格式要求:“输出为纯中文,不要英文,不要分点。”
-
P3:P2 + JSON Schema:“输出JSON:{“summary”: “...”, “sound”: “...”, “battery”: “...”, “comfort”: “...”}”
-
Few-shot组
- P4:P3 + 1个示例;
- P5:P3 + 2个示例,且示例覆盖“正面+负面”两种倾向。
核心实现代码如下,统一封装调用函数:
import os
import json
import time
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def call_gpt(prompt: str, model="gpt-4o-mini-2024-07-18"):
start = time.time()
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.2,
top_p=0.9,
max_tokens=256,
)
latency = time.time() - start
usage = resp.usage
return {
"text": resp.choices[0].message.content,
"prompt_tokens": usage.prompt_tokens,
"completion_tokens": usage.completion_tokens,
"total_tokens": usage.total_tokens,
"latency": latency,
}
Prompt模板统一用字符串拼接,便于对比:
PROMPTS = {
"P0": "总结以下评论。\n\n{reviews}",
"P1": "用不超过80字总结以下评论,包含音质、续航、佩戴。\n\n{reviews}",
"P2": "用不超过80字总结以下评论,包含音质、续航、佩戴。输出为纯中文,不要英文,不要分点。\n\n{reviews}",
"P3": """用不超过80字总结以下评论,包含音质、续航、佩戴。
输出JSON,字段为summary、sound、battery、comfort,值均为中文。
不要输出JSON以外的内容。
评论:
{reviews}""",
"P4": """用不超过80字总结以下评论,包含音质、续航、佩戴。
输出JSON,字段为summary、sound、battery、comfort,值均为中文。
不要输出JSON以外的内容。
示例:
评论:音质不错,低音有力,续航大概6小时,戴久了耳朵疼。
输出:{"summary":"音质好,续航6小时,佩戴久了不适","sound":"好","battery":"6小时","comfort":"久戴不适"}
评论:
{reviews}""",
"P5": """用不超过80字总结以下评论,包含音质、续航、佩戴。
输出JSON,字段为summary、sound、battery、comfort,值均为中文。
不要输出JSON以外的内容。
示例1:
评论:音质不错,低音有力,续航大概6小时,戴久了耳朵疼。
输出:{"summary":"音质好,续航6小时,佩戴久了不适","sound":"好","battery":"6小时","comfort":"久戴不适"}
示例2:
评论:音质一般,高音刺耳,续航只有4小时,佩戴很轻几乎无感。
输出:{"summary":"音质一般高音刺耳,续航4小时,佩戴轻无感","sound":"一般","battery":"4小时","comfort":"轻无感"}
评论:
{reviews}""",
}
四、踩坑与优化:那些文档不会告诉你的细节
坑1:JSON Schema不是越细越好。
P3一开始我写了完整JSON Schema,包括type、required、description,结果模型经常把Schema本身也输出出来。后来改成“只描述字段和值”,格式合规率从72%升到94%。
坑2:Few-shot示例顺序影响很大。
P5里如果两个示例都是正面,模型对负面评论会强行总结成“音质好”。把第二个示例改成负面后,负面样本的评分从2.8升到4.1。
坑3:max_tokens=256对P3/P4/P5不够。
JSON输出容易在字段间加换行和空格,completion_tokens经常冲到220以上。后来把max_tokens提到320,但平均completion_tokens反而下降了,因为模型不再被截断后“硬凑”。
坑4:Qwen2.5-7B-Instruct对英文指令更敏感。
同样的Prompt,中文版在Qwen上格式合规率只有81%,把指令改成英文、示例保留中文后,合规率升到93%。但GPT-4o mini上中英文差异不大,所以最终线上用了两套Prompt。
坑5:Token消耗的大头其实是输入。
200条评论合并后,输入平均在900-1200 Token。P5因为加了2个示例,输入多了约180 Token,但输出反而更短、更稳定,总Token只增加18%。
五、效果数据:6版Prompt的硬核对比
在200条测试集上,GPT-4o mini的结果如下(人工评分取3人平均):
| Prompt | 格式合规率 | 人工评分 | 平均Prompt Token | 平均Completion Token | 平均总Token | 单条成本(USD) |
|---|---|---|---|---|---|---|
| P0 | 61% | 2.4 | 1020 | 210 | 1230 | 0.000279 |
| P1 | 78% | 3.1 | 1035 | 165 | 1200 | 0.000254 |
| P2 | 86% | 3.5 | 1050 | 140 | 1190 | 0.000241 |
| P3 | 94% | 4.0 | 1080 | 155 | 1235 | 0.000255 |
| P4 | 96% | 4.2 | 1180 | 145 | 1325 | 0.000264 |
| P5 | 98% | 4.5 | 1260 | 135 | 1395 | 0.000273 |
几个关键结论:
- P0最便宜但最不可用:格式合规率61%,人工评分2.4,线上会直接崩。
- P3是性价比拐点:格式合规率94%,评分4.0,总Token只比P0多0.4%。
- P5质量最好但成本最高:评分4.5,总Token比P3多13%,单条成本多7%。
- P4和P5差距不大:如果预算紧张,P4是更稳的选择。
在Qwen2.5-7B-Instruct上,P5的格式合规率93%,评分4.1,但P3只有81%和3.4。所以小模型上Few-shot的收益更明显。
六、总结
这次Prompt Engineering实验让我最深的体会是:Prompt不是越长越好,也不是越短越省。 真正影响成本和质量的,是“输出结构是否被约束”和“示例是否覆盖边界情况”。
最终线上方案:
- GPT-4o mini用P4,Qwen2.5-7B-Instruct用P5英文指令版;
- max_tokens=320,temperature=0.2;
- 输入评论先按时间排序,超过15条做截断;
- 单条平均总Token控制在1350以内,比最初P0的1230只多10%,但格式合规率从61%升到96%。
如果你也在做类似的信息抽取或摘要任务,建议先跑P3,再根据格式合规率决定要不要加Few-shot。不要一上来就堆示例,Token烧得心疼,效果还不一定好。