我写了大半年的提示词,才意识到自己一直在裸跑
上个月帮一个朋友调试他的LLM项目,他写的提示词我在屏幕前看了足足两分钟,心里只有一个想法:这哥们儿真的是在裸奔。提示词里没有任何结构,上下文、指令、示例、输出格式全混在一段自然语言里。我问他为什么不写规范一点,他说"网上那些教程教的也都是这样写啊"。
这让我想起自己刚开始用ChatGPT那会儿——一段自然语言问过去,模型答了就走,答得不好就换个问法重试。当时不知道有个东西叫"结构化提示词",更不知道里面其实有一堆坑在等着。后来接手了一个真实的LLM应用项目,开始认真研究提示词工程,才发现这玩意儿的水比我以为的深得多。
今天把我踩过的几个大坑和最后沉淀下来的提示词模板整理出来。如果你是刚开始用LLM做应用的开发人员,应该能少走点弯路。
坑一:把示例当装饰,而不是当约束
最初我写few-shot提示词的时候,示例(example)的位置是放在提示词末尾的"装饰位"。结构大概是这样:
请把以下评论分类为正面或负面。
评论:这个产品太垃圾了
分类:
后来我加了一个示例,放在最末尾:
评论:这个东西很棒
分类:正面
评论:这个产品太垃圾了
分类:
我以为这就叫few-shot了。结果模型输出是"负面",但换一条评论:
评论:我不确定这个产品好不好用
分类:正面 # 模型给的
不对吧?这种"模糊"评价凭什么分到正面?问题就出在示例上——我只给了模型一个"明显正面"和一个"明显负面"的样本,模型学到的不是分类规则,而是"句子里有明显情感词就算正面"。这其实是把示例当成了展示,而不是当成约束。
正确的做法是:示例要覆盖边界情况和典型情况,而且示例的位置应该紧跟在指令后面,而不是放在最后。我的修改版本:
你的任务是把电商评论分类为"正面"或"负面"。
分类规则:
- 明确表达喜欢、推荐、满意 → 正面
- 明确表达讨厌、不满、失望 → 负面
- 表达犹豫、不确定、提到部分问题 → 负面(默认)
- 完全无法判断内容 → 输出"无法判断"
示例1:
评论:这个东西太棒了,强烈推荐
分类:正面
示例2:
评论:质量还行吧,但价格偏贵
分类:负面
示例3:
评论:包装不错,物流也快
分类:正面
评论:我不确定这个产品好不好用
分类:
这次模型老老实实输出"负面"。规则明确,示例覆盖了边界情况,模型才真正学到分类逻辑。
坑二:以为加了"请按JSON输出"就完事了
这是最经典的坑。我做的一个项目需要从商品描述里抽结构化字段,提示词结尾加了一句"请以JSON格式输出",就以为稳了。结果程序一跑:
{
"product_name": "智能手表",
"price": 299
}
希望对您有帮助。
模型很"贴心"地给我加了一堆客套话。Markdown代码块、前后说明、客套结尾,全给我包圆了。我的代码做JSON解析的时候直接报错。
第一次遇到这种情况的时候我以为是偶然,换个模型试试,结果同样的问题。后来研究了一下发现:模型没有收到"只输出JSON"的强约束,它默认为"对话要友好"。
解决办法不是简单地写"只输出JSON",而是把整个输出格式给框死:
你的任务是从商品描述中抽取字段。
抽取字段:
- product_name: 商品名称
- price: 价格(数字,单位元)
- category: 商品类别
输出要求(重要,必须严格遵守):
1. 只输出JSON对象,不要任何额外文字
2. 不要使用Markdown代码块标记
3. 不要加任何解释、问候、客套话
4. 字段缺失时使用null,不要猜测
5. 不要在JSON前后加任何内容
开始抽取:
商品描述:[商品描述文本]
再进一步,把JSON结构也作为约束写进去:
输出JSON结构:
{
"product_name": "string",
"price": "number",
"category": "string|null"
}
这样模型输出的内容才能被程序可靠地解析。
坑三:用"自然语言思考"代替"明确的步骤"
我之前做的一个数据处理任务,需要把不规则的客户地址规范化为"省/市/区/详细地址"的标准格式。最初的提示词:
请把以下地址规范化为标准格式:
[地址]
模型输出五花八门:有的用斜杠分隔,有的用逗号,有的全角逗号,有的字段顺序不一样。原因是模型"自由发挥"的空间太大了。
后来我改成显式步骤:
请按照以下步骤处理地址:
步骤1:识别地址中包含的省、市、区县信息
步骤2:识别详细地址(街道、门牌号等)
步骤3:按照"省/市/区/详细地址"的格式输出
步骤4:检查每个字段是否完整,缺失的字段用"未知"填充
输出格式:省/市/区/详细地址
字段之间用斜杠/分隔
待处理地址:[地址]
效果立刻不一样。模型不再"创造"格式了,而是按步骤走。这个经验后来被我用在很多地方:把任务拆成明确的步骤,比单纯描述目标可靠得多。
坑四:上下文窗口不够时不知道如何取舍
做一个长文档分析项目的时候,我把整个文档(3万字)塞进提示词里,模型返回的效果一塌糊涂。原因是上下文太长时,模型对中间和靠后位置的信息关注度会下降(俗称"lost in the middle"现象)。
后来我分了几个层次处理:
- 如果文档长度在模型上下文窗口的50%以内,直接全量塞进去。
- 如果在50%-80%之间,先让模型做摘要,再基于摘要回答问题。
- 如果超过80%,用RAG方案,先检索相关片段,再喂给模型。
具体的工程做法是:先用嵌入模型把文档切成chunk,存到向量数据库。查询时先把问题也做嵌入,召回top-k个最相关的chunk,再把这些chunk喂给模型生成答案。
这部分内容展开讲能写一整篇。这里我只想说:不要试图把一个大文档硬塞进提示词,绝大多数情况下效果都不会好。
坑五:忽视"温度"参数的影响
我之前做内容生成的时候,温度参数(temperature)一直是默认值0.7。结果发现同一个提示词,相同模型,连续运行两次的输出差异巨大——第一版写得很文艺,第二版写得很直白,第三版又跳到另一个方向。
后来我才搞清楚:temperature控制输出的随机性。值越低,输出越确定;越高,输出越多样。
不同任务需要不同的temperature:
- 数据抽取、分类、格式严格的任务:temperature=0
- 代码生成、SQL生成:temperature=0-0.3
- 创意写作、头脑风暴:temperature=0.7-1.0
- 对话、问答:temperature=0.5-0.7
很多人忽视这个参数,但实际项目里它对稳定性的影响巨大。同一段代码生成,temperature=0和temperature=0.7的输出质量可能差几个档次。
坑六:没有版本管理和测试用例
这是我做项目的时候踩过最深的一个坑。提示词改来改去,过了两周回头看,已经想不起来当时为什么这样写、这样写解决了什么问题。
更糟糕的是,提示词调整后我没有回归测试的意识。有一次我优化了情感分析的提示词,分类准确率从85%提到了88%,结果上线后某个类别的准确率从90%掉到了70%——因为新提示词牺牲了那个类别的召回。
现在我养成了几个习惯:
- 提示词用Git管理,每次修改有commit记录和原因说明。
- 每个任务维护一个测试集(大概50-100条样本),改提示词后必须跑一遍测试集,对比指标变化。
- 重要任务准备两三个候选提示词,AB测试一段时间再决定用哪个。
这套流程听起来有点重,但实际做项目时是必要的。提示词不是一次性写完就完事的东西,它更像是需要持续迭代的代码资产。
沉淀下来的一个通用模板
踩了这么多坑之后,我现在的提示词都遵循一个固定结构:
# 角色
你是一个[具体角色],擅长[具体能力]。
# 任务
[用一句话明确任务目标]
# 规则
1. [规则1]
2. [规则2]
3. [规则3]
(按需要列出,必须严格遵守)
# 示例
示例1:
输入:[输入]
输出:[输出]
示例2:
输入:[输入]
输出:[输出]
# 输出格式
[明确输出格式,包括字段、类型、约束]
# 待处理内容
[实际要处理的内容]
这种结构的好处是:每个部分职责清晰,模型可以稳定地按结构理解;我可以针对某一部分单独优化而不影响其他部分;版本管理时diff清晰,团队其他人接手也容易。
举一个实际例子,用这个模板做合同关键信息抽取:
# 角色
你是一个法律文档分析师,擅长从合同文本中抽取关键信息。
# 任务
从合同文本中抽取以下信息:合同双方、签订日期、合同金额、付款方式、合同期限。
# 规则
1. 严格基于文本内容抽取,不要推测或补充
2. 涉及金额时保留原始数字和单位
3. 日期统一转换为YYYY-MM-DD格式
4. 字段缺失时使用null
# 示例
示例1:
输入:本协议由甲方ABC科技有限公司与乙方XYZ贸易公司于2024年3月15日签订,合同总金额为人民币50万元。
输出:{
"party_a": "ABC科技有限公司",
"party_b": "XYZ贸易公司",
"sign_date": "2024-03-15",
"amount": "50万元",
"payment_method": null,
"duration": null
}
# 输出格式
严格输出JSON对象,不要任何额外文字:
{
"party_a": "string|null",
"party_b": "string|null",
"sign_date": "YYYY-MM-DD|null",
"amount": "string|null",
"payment_method": "string|null",
"duration": "string|null"
}
# 待处理内容
[合同文本]
这个模板在实际项目里跑了大半年,稳定可靠。
写在最后
提示词工程不是玄学,它更像是"给模型写一份说明书"。说明书写得好,模型才能稳定输出你想要的结果。
回过头看,这一年我最大的认知转变是:提示词不是一段自然语言,而是一份需要维护的工程产物。它有结构、有版本、有测试用例、有迭代记录。把它当作代码来管理,你才能在大规模应用里获得稳定的输出质量。
如果你刚开始接触LLM应用开发,建议从上面那个模板开始用,然后根据实际任务去调整。踩坑是难免的,但至少可以少踩几个。
更多推荐


所有评论(0)