为什么你的Prompt总是不靠谱
很多开发者上手LLM的第一件事,就是写一段自然语言指令扔给模型。能跑通就觉得可以了,跑不通就加几句话再试。这种试错法在原型阶段没问题,但一旦进入生产环境,你会面临一个核心问题:怎么保证Prompt在1000次调用里都稳定输出?
Anthropic官方文档明确指出,Prompt工程的前提是先定义成功标准和评估方法,而不是上来就调措辞。换句话说,没有eval的Prompt优化就是盲人摸象。
这篇文章拆解5种在生产环境里验证过的Prompt模式,每种都附带使用场景、代码示例和评估思路。你看完能直接拿去用,或者对照检查自己的Prompt缺了什么。
模式一:结构化XML标签
用XML标签把Prompt的不同部分隔开,是目前各大模型厂商都推荐的做法。Anthropic的Claude系列从早期就支持这个模式,OpenAI的GPT-4也能良好遵循。
核心思路:把指令、上下文、示例、输出格式分别用标签包裹,让模型清楚地知道每段内容的边界。
<task>
从用户输入中提取产品名称、数量和地址。
</task>
<rules>
1. 如果缺少任一字段,输出 "INSUFFICIENT_INFO"
2. 产品名称保留原文,不翻译
3. 数量只保留数字
</rules>
<examples>
输入: "帮我下单三箱农夫山泉送到朝阳区"
输出: {"product": "农夫山泉", "quantity": 3, "address": "朝阳区"}
</examples>
<input>
{user_input}
</input>
<output_format>
JSON with keys: product, quantity, address
</output_format>
为什么有效?因为LLM的注意力机制对结构化标记有天然的识别优势。标签相当于给模型画了阅读路线图,减少了不同指令之间的干扰。
评估方法:准备20-30条测试输入,其中故意包含缺失字段、多余信息、边界情况(如数量写”两”而非”2″)。统计输出JSON的合法率和字段准确率。目标是合法率100%,准确率95%以上。
模式二:Few-Shot示例驱动
零样本(Zero-shot)在某些简单任务上够用,但一旦涉及格式控制或分类判断,Few-Shot的稳定性远超零样本。Google Research 2022年的研究表明,在文本分类任务上,3-shot的准确率比零样本平均高12%-18%。
关键不是示例数量,而是示例的代表性。3个好示例的效果往往胜过10个随机示例。
选示例的三个原则:
- 覆盖边界情况:别只放简单正例,放一两个容易判断错误的
- 格式完全一致:示例的输出格式必须和你期望的最终格式一模一样
- 难度递进:第一个简单,第二个中等,第三个带干扰项
分类以下用户反馈为: BUG / FEATURE / QUESTION
反馈: "点击导出按钮没反应"
分类: BUG
反馈: "希望能支持暗黑模式"
分类: FEATURE
反馈: "你们的数据导出支持Excel格式吗"
分类: QUESTION
反馈: "{actual_input}"
分类:
一个常见错误:示例放太多导致token浪费。一般3-5个就够,超过8个边际收益急剧下降。
模式三:思维链引导
对于需要推理的任务(数学计算、逻辑判断、多步骤决策),直接让模型输出答案不如让它先推理再回答。OpenAI在2023年的研究中发现,Chain-of-Thought在多步推理任务上能将错误率降低30%-50%。
实现方式有两种:
方式A:显式标记
请逐步分析以下问题,在<reasoning>标签中写出推理过程,在<answer>标签中给出最终答案。
问题: {question}
方式B:自然语言引导
分析以下问题。先列出已知条件,再逐步推理,最后给出答案。
问题: {question}
注意:思维链会增加输出token,也就意味着更高的成本和延迟。简单分类任务不需要CoT,用了反而浪费。判断标准是:如果人类解决这个问题也需要想几步,那就用。
模式四:角色约束+输出格式控制
让模型扮演特定角色不是噱头,它确实能改变输出的语气、深度和准确性。但关键不在角色本身,而在于角色带来的知识边界约束。
你是一个专精于SQL优化的数据库工程师。
你的知识范围:
- PostgreSQL和MySQL的查询优化
- 索引策略和执行计划分析
- 不回答与SQL无关的问题
当用户的问题超出你的知识范围时,回复:
"这个问题超出了我的专业范围,我只能回答SQL优化相关的问题。"
输出格式:
<analysis>问题分析</analysis>
<solution>优化方案</solution>
<sql>优化后的SQL语句</sql>
角色约束的真正价值是减少幻觉。当你告诉模型”你只知道X”,它在面对超出范围的问题时更倾向于承认不知道,而不是编造答案。
模式五:Prompt链式调用
复杂任务拆成多步,每步用独立Prompt处理,前一步的输出作为后一步的输入。这比把所有要求塞进一个超长Prompt效果好得多。
典型场景:长文档摘要 → 关键信息提取 → 结构化输出。
# 第一步:摘要
prompt_1 = f"将以下文档摘要为200字以内:\n{document}"
# 第二步:提取关键信息
prompt_2 = f"从以下摘要中提取3个关键数据点:\n{summary}"
# 第三步:格式化输出
prompt_3 = f"将以下信息转为Markdown表格:\n{key_points}"
链式调用的好处:每步的Prompt都短小聚焦,更容易调试和评估。出问题时能精确定位是哪一步错了,而不是面对一个500字的Prompt猜哪里出了问题。
代价是多一次API调用。但如果第一步处理的是长文本,后续步骤只处理摘要,总token消耗可能比单次超长Prompt还低。
怎么评估Prompt效果
这是大多数团队忽略的环节。没有评估,你永远不知道今天的改动是改善还是退化。
最小可行评估方案:
- 准备30-50条测试输入,覆盖正常、边界、 adversarial三种情况
- 对每条输入定义期望输出(或至少定义可接受的输出范围)
- 每次修改Prompt后,跑全部测试用例
- 记录四个指标:合法输出率、准确率、平均延迟、平均token消耗
- 任何一项指标退化超过5%,就需要回退改动
Anthropic官方推荐的做法更系统:先定义success criteria,再构建eval dataset,最后才进入Prompt迭代。但如果你团队小、节奏快,上面这个最小方案足够帮你避开大部分坑。
三个常见踩坑
坑一:把所有要求塞进System Prompt。 System Prompt和User Prompt的权重不同。把具体任务指令放在User Prompt里,System Prompt只放全局角色和规则,效果通常更好。
坑二:频繁修改措辞但不做回归测试。 改一个词可能让某些case变好,同时让另一些case变差。没有测试集就是在赌博。
坑三:盲目追求零样本。 Few-shot不是能力不足的表现,而是工程上的稳定性保障。生产环境里,稳定性比优雅重要。
从今天开始做的一件事
如果你现在有一个正在用的Prompt,做一件事:给它配10条测试输入和期望输出。下次改动Prompt时跑一遍,看看结果是否变差。这个习惯能帮你省掉无数个”为什么突然不好用了”的深夜debug。
