提示词工程不是「把话说得更客气」,而是把模型的输出空间约束到你想要的形状。这篇讲的都是可验证的技巧,每条都能说清「为什么有效」。
一、先理解模型在做什么
模型本质在「预测下一个 token」。所以提示词的作用是提高某些 token 的概率。有效的提示词都在做这件事:
- 给出上下文:「你是 XX 角色,用户是 XX」——把后续文本的分布拉向该领域
- 给出范例:几组输入→输出示例,模型会自动模仿格式与风格
- 给出结构:要求输出 JSON,就大大降低了输出散文的概率
- 给出约束:「不要提 XX」——直接压低相关 token 的概率
二、结构化输出:为什么一定要给 schema
# 弱:只说「输出 JSON」
"请把信息整理成 JSON"
1. 强:给出完整 schema + 示例
"""从下面文本抽取信息,输出 JSON,严格遵循此结构:
{
"name": "string,人名",
"age": "integer,年龄,未知填 null",
"skills": ["string"],
"confidence": "number,0-1,抽取把握度"
}
示例输入:张三,28 岁,会 Python 和 Go
示例输出:{"name":"张三","age":28,"skills":["Python","Go"],"confidence":0.95}
文本:{text}"""| 做法 | 效果 | 说明 |
|---|---|---|
| 只写「输出 JSON」 | 格式常错(缺引号、多注释) | 模型不知道键名叫什么 |
| 给 schema | 格式正确率高很多 | 键名、类型都明确了 |
| 给示例 | 再上一档 | 模型直接模仿示例,比理解描述更准 |
| 用 API 的 structured output | 几乎不会错 | 如 OpenAI 的 response_format |
三、Few-shot:示例的选择比数量重要
# 差:3 个几乎一样的示例
examples = [
("苹果", "水果"),
("香蕉", "水果"),
("橘子", "水果"),
]
1. 好:覆盖不同情况(含边界与难例)
examples = [
("苹果", "水果"),
("螺丝刀", "工具"), # 不同类别
("iPhone", "电子产品"), # 易混淆的(不是水果)
("番茄", "蔬菜"), # 有争议的(明确按烹饪用途分类)
]- 示例要覆盖边界:只给简单正例,模型遇到难例就乱猜
- 3-8 个足够:再多收益递减,且占 token
- 示例顺序影响输出:把最想模仿的格式放最后(近因效应)
- 示例要一致:格式、详略程度必须统一,否则模型学到混乱的规范
四、Chain of Thought:什么时候有用,什么时候没用
| 任务类型 | CoT 是否有用 | 原因 |
|---|---|---|
| 数学计算、逻辑推理 | 很有用 | 中间步骤减少了「一步到位」的误差 |
| 多条件判断 | 有用 | 逐条检查比一次性判断可靠 |
| 简单分类、抽取 | 没用甚至有害 | 增加 token 与出错面,还可能引入无关推理 |
| 创意写作 | 用「先列大纲」代替 | 直接 CoT 会让文字变得像分析报告 |
# 有效写法:要求「先分析再结论」
"""分析下面这段合同条款是否对甲方不利。
先逐条列出关键条款及其影响,再给出结论。
输出格式:
## 条款分析
(逐条)
## 结论
(一句话:对甲方有利 / 不利 / 中性,附理由)"""五、角色与约束:写「你是」比「请你」有效
# 弱
"请你帮我审查这段代码"
1. 强
"""你是一名有 10 年经验的后端工程师,负责代码评审。
审查下面这段代码,重点关注:
1. 并发安全(是否有竞态)
2. 错误处理(是否有未捕获的异常)
3. 资源泄漏(连接/文件是否正确释放)
只报告确实存在的问题,不要提出「建议优化」这类主观意见。
如果某方面没有问题,明确说「无问题」。
代码:
{code}"""为什么有效:角色描述把输出拉向「专业、精确、有取舍」的分布,而「请你帮我」没有提供任何约束。
六、输出格式强约束:用「禁止清单」
# 输出总是带「好的,我来帮你...」这类废话时
"""规则:
- 不要开场白(禁止「好的」「当然」「以下是」)
- 不要总结段
- 直接输出 JSON,不要用 markdown 代码块包裹
输出:"""| 问题 | 约束写法 |
|---|---|
| 开头有客套话 | 「不要开场白,直接从内容开始」 |
| 结尾有总结 | 「不要总结段」 |
| JSON 被 “` 包裹 | 「不要用代码块包裹,直接输出 JSON」 |
| 回答太长 | 「最多 3 句话」或「不超过 50 字」 |
| 用了不确定的表述 | 「不确定就写 null,不要猜」 |
七、温度与采样参数:按任务类型调
| 任务 | temperature | 说明 |
|---|---|---|
| 抽取、分类、代码生成 | 0 – 0.3 | 要稳定,不要「创意」 |
| 问答、解释 | 0.3 – 0.7 | 允许一定表达变化 |
| 创意写作、起名 | 0.8 – 1.2 | 需要多样性 |
| 需要确定性复现 | 0 + seed | 配合 seed 参数 |
注意:temperature 只影响采样随机性,不能解决「模型不知道」的问题——知识缺失只能靠 RAG 或上下文补充。
八、失败模式的针对性修法
| 失败模式 | 表现 | 针对性修法 |
|---|---|---|
| 幻觉 | 编造不存在的 API / 数据 | 提供资料 + 「没有就说没有」+ 要求标注来源 |
| 格式崩 | JSON 缺括号、多注释 | 给 schema + 示例 + 用 structured output |
| 过度解释 | 简单问题写一大段 | 「最多 2 句」+ 示例展示简洁答案 |
| 拒绝回答 | 该答的说「我无法」 | 去掉过度保守的措辞,明确「你要回答」 |
| 忽略部分要求 | 多条要求只做到前两条 | 编号列出要求 + 结尾再复述一遍关键约束 |
| 语言混用 | 中文问题答英文 | 「用中文回答」+ 示例用中文 |
九、迭代方法:不要凭感觉改
# 维护一个「提示词测试集」,每次改动都跑一遍
CASES = [
{"input": "苹果多少钱", "expect_contains": ["无法", "没有价格"]},
{"input": "翻译 hello", "expect": "你好"},
{"input": "", "expect_contains": ["请提供"]},
1. 20-30 条起步
]
def evaluate(prompt_template, cases):
passed = 0
for c in cases:
out = call_llm(prompt_template.format(**c))
if check(out, c): # 按 expect 规则判断
passed += 1
return passed / len(cases)提示词改动最常见的误区:改了一条规则修好了 A 场景,却弄坏了 B 场景,因为没测 B。有测试集才能知道「是不是真的变好了」。
十、总结:有效的提示词都在做这四件事
- 收窄输出空间:给 schema、给示例、给禁用清单——让「错」的输出概率降低
- 提供判断依据:给资料、给规则、给分类标准——让模型不必猜
- 把复杂任务拆成步骤:只在需要推理的任务上用 CoT,简单任务用了反而糟
- 有测试集才能迭代:改完要验证「A 修好了,B 有没有坏」
- 温度按任务调:抽取用 0,创作用 1,别一套参数走天下