上个月帮一个朋友调试他的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"现象)。

后来我分了几个层次处理:

  1. 如果文档长度在模型上下文窗口的50%以内,直接全量塞进去。
  2. 如果在50%-80%之间,先让模型做摘要,再基于摘要回答问题。
  3. 如果超过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%——因为新提示词牺牲了那个类别的召回。

现在我养成了几个习惯:

  1. 提示词用Git管理,每次修改有commit记录和原因说明。
  2. 每个任务维护一个测试集(大概50-100条样本),改提示词后必须跑一遍测试集,对比指标变化。
  3. 重要任务准备两三个候选提示词,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应用开发,建议从上面那个模板开始用,然后根据实际任务去调整。踩坑是难免的,但至少可以少踩几个。

Logo

脑启社区是一个专注类脑智能领域的开发者社区。欢迎加入社区,共建类脑智能生态。社区为开发者提供了丰富的开源类脑工具软件、类脑算法模型及数据集、类脑知识库、类脑技术培训课程以及类脑应用案例等资源。

更多推荐