Prompt Engineering 实战:让 LLM 真正听话的技巧
TL;DR:LLM 不是指令跟随机器,是概率下一词预测器。理解这一点,才能写出真正有效的 prompt。
认知基础:LLM 是什么
LLM(大语言模型)的本质是概率模型:给定前文,预测下一个 token 的概率分布。它从海量文本学到的是"什么样的文字在统计上应该接在这段文字后面",而不是在执行你的指令。
这个认知影响一切后续的 prompt 设计决策。
System Prompt 的作用域与局限
System prompt 是对话开始时设置的"角色说明",在 Chat 格式下位于 system role。
它能做什么:
- 设定角色和语气(“你是一个严格的代码审查员”)
- 建立输出格式约定(“始终用 JSON 回复”)
- 提供领域背景知识
- 设定行为边界
它做不到的:
- 完全阻止越狱(jailbreak)
- 保证长对话中指令始终被遵循(指令遗忘)
- 覆盖训练时形成的深层倾向(sycophancy)
# 好的 System Prompt 结构
You are a senior backend engineer specializing in Go.
## Your role
- Review code for correctness, performance, and idioms
- Point out potential race conditions and nil pointer issues
## Output format
For each issue found:
1. **Severity**: [Critical/Major/Minor]
2. **Location**: file:line
3. **Issue**: brief description
4. **Fix**: concrete suggestion
## Constraints
- Only comment on actual problems, not style preferences
- If the code looks correct, say "LGTM" with a brief reason
Instruction Following vs Role Playing vs In-context Learning
三种不同的机制,适合不同场景:
| 方式 | 原理 | 适合场景 |
|---|---|---|
| Instruction Following | 直接描述任务要求 | 明确的单步任务 |
| Role Playing | 让模型扮演某角色 | 需要特定风格/视角 |
| In-context Learning | 给例子让模型归纳 | 有规律但难描述的任务 |
Zero-shot / One-shot / Few-shot
选择逻辑:
# Zero-shot:任务简单或模型能力强时
prompt = "将以下英文翻译为中文:Hello, world!"
# One-shot:给一个示例校准风格
prompt = """
翻译风格示例:
EN: The system encountered an unexpected error.
CN: 系统遭遇意外错误。
请翻译:The connection timed out after 30 seconds.
"""
# Few-shot:任务有微妙规律时
prompt = """
判断以下句子情感(考虑反讽):
"这个产品真是太棒了,我的电脑崩了三次。" -> 负面
"虽然外观普通,但性能超出预期。" -> 正面
"又是一个让人热血沸腾的周一早晨。" -> 负面(反讽)
请判断:这次更新解决了旧 bug,又引入了两个新 bug。
"""
Few-shot 的注意事项:
- 例子数量:3-8 个通常足够,过多反而混淆
- 例子质量 > 数量:边缘案例比重复普通例子更有价值
- 顺序影响:模型对最后几个例子权重更高
Chain-of-Thought(CoT):让模型先想再说
CoT 核心思路:强迫模型生成中间推理步骤,而不是直接输出答案。
# 不带 CoT(容易出错的复杂推理)
Q: 一家店有 23 个苹果,卖了 20 个,又进货了 6 个,现在有多少?
A: 9
# 带 CoT(更准确)
Q: 一家店有 23 个苹果,卖了 20 个,又进货了 6 个,现在有多少?
请一步步思考。
A:
初始:23 个
卖出:23 - 20 = 3 个
进货:3 + 6 = 9 个
答案:9 个
Zero-shot CoT:简单加一句 “Let’s think step by step” 或 “请一步步思考” 就能显著提升推理质量。
Self-consistency:对同一问题生成多条推理路径,投票选最常见答案,准确率更高。
Structured Output
强制模型输出结构化数据,减少解析不确定性。
方法一:JSON mode(OpenAI API)
response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"},
messages=[
{"role": "system", "content": "你是数据提取器,始终返回 JSON"},
{"role": "user", "content": f"从以下文本提取人名和日期:{text}"}
]
)
方法二:Function Calling / Tool Use
tools = [{
"type": "function",
"function": {
"name": "extract_person",
"description": "提取人物信息",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string"},
"age": {"type": "integer"},
"occupation": {"type": "string"}
},
"required": ["name"]
}
}
}]
方法三:Prompt 约束(不依赖 API 特性)
要求输出严格的 JSON 格式,不要添加任何额外文字,直接输出 JSON 对象。
参数实际意义
# Temperature:控制随机性
# 0.0 = 贪心解码,总选概率最高的词(适合代码、事实查询)
# 0.7 = 适度随机(适合写作、对话)
# 1.0+ = 高随机性(适合创意、头脑风暴)
temperature = 0.7
# Top_p(nucleus sampling):只从累计概率达到 p 的词集中采样
# 与 temperature 类似效果,通常只用一个
top_p = 0.9
# 实践建议:
# - 代码生成:temperature=0, top_p=1
# - 文本摘要:temperature=0.3
# - 创意写作:temperature=0.8-1.0
# - 不要同时调 temperature 和 top_p
常见失败模式
1. 幻觉(Hallucination)
模型生成听起来正确但实际错误的信息。
- 对策:要求模型引用来源;对事实敏感的任务加 RAG;要求模型表达不确定性
2. 指令遗忘(Instruction Drift)
长对话中模型逐渐忽略 system prompt 中的约束。
- 对策:重要指令在 user message 中重复;定期重置对话;使用 sliding window 时保留 system 部分
3. 越狱(Jailbreak)
通过角色扮演、假设情景等绕过安全限制。
- 对策:输出层二次审查;不要把安全完全依赖 prompt;使用模型自带的安全过滤
4. Sycophancy(讨好倾向)
模型倾向于同意用户,即使用户是错的。
# 对策:明确要求批判性审查
system_prompt = """你是严格的代码审查员。即使用户认为代码没问题,
你也必须独立评估,发现问题就直说。不要因为用户的判断而改变你的结论。"""
调试方法
A/B 测试 prompt:
import anthropic
def eval_prompt(system_prompt, test_cases):
client = anthropic.Anthropic()
scores = []
for case in test_cases:
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
system=system_prompt,
messages=[{"role": "user", "content": case["input"]}]
)
score = evaluate_with_llm(response.content[0].text, case["expected"])
scores.append(score)
return sum(scores) / len(scores)
score_v1 = eval_prompt(prompt_v1, test_cases)
score_v2 = eval_prompt(prompt_v2, test_cases)
print(f"V1: {score_v1:.2f}, V2: {score_v2:.2f}")
用更强模型评估弱模型输出:
def evaluate_with_llm(output, expected_criteria):
judge_prompt = f"""
评估以下输出是否满足标准(1-5分):
输出:{output}
标准:{expected_criteria}
只输出数字 1-5,不要解释。
"""
response = strong_model.complete(judge_prompt)
return int(response.strip())
实用模板
# 通用 System Prompt 框架
## 角色
你是 [具体角色描述]。
## 任务
你的主要任务是 [任务描述]。
## 输出格式
[具体格式要求,最好给示例]
## 约束条件
- [限制1]
- [限制2]
- 如果遇到 [边界情况],则 [处理方式]
## 示例(可选)
Input: [示例输入]
Output: [示例输出]
总结
Prompt Engineering 的本质是设计信息环境,让模型的概率分布偏向你想要的输出。关键原则:
- 明确 > 隐晦:不要期望模型猜测你的意图
- 示例 > 描述:给一个例子胜过描述十条规则
- 结构化输出:减少后处理的不确定性
- CoT 推理:复杂任务让模型先想再说
- 测试驱动:建立评估集,量化 prompt 改进效果