你有没有过这种感觉:同样一个问题,别人用 AI 能得到一份结构清晰、可直接用的答案;你用 AI 却只能得到一段看似正确、实则含糊的"正确的废话"?
区别往往不在模型,而在提问的方式。
这篇文章,我们就从零开始,把 Prompt 工程(Prompt Engineering)讲透------既讲清楚"为什么这么写有效",也给出可以直接复制粘贴、拿来就能跑的代码和模板。
目录
- 一、先搞清楚:什么是 Prompt 工程?
- 二、理论底座:为什么"提问方式"能决定 AI 的回答质量?
- 三、基础篇:写一个"能用"的 Prompt
- 四、结构化篇:用 Prompt 框架系统化地表达需求
- 五、进阶技巧:让 AI 输出从"能用"到"好用"
- 六、高级技巧:解决复杂推理与幻觉问题
- 七、实战:用 Python 调用大模型 + 迭代优化 Prompt
- 八、常见坑位与排查清单
- 九、总结:一张图看懂 Prompt 工程心法
一、先搞清楚:什么是 Prompt 工程?
1.1 一句话定义
Prompt 工程 (Prompt Engineering)是一门通过设计、优化和精炼 输入给大语言模型(LLM)的文本指令(Prompt),引导模型生成高准确性、高相关性、高质量输出的学科与艺术。
拆成三个关键词:
| 关键词 | 通俗解释 |
|---|---|
| Prompt(提示词) | 你发给 AI 的那段文字------问题、指令、上下文、示例都算 |
| Engineering(工程) | 不是随手一写,而是有方法论、可复用、可迭代、可测量 |
| 引导(Guide) | AI 不是"读懂"你,而是被你的文字"引导"到正确的输出空间 |
1.2 它为什么重要?
对程序员而言,你可以把大模型理解为一个能力极强但没有明确 KPI 的外包同事:
- 你丢一句"帮我推广产品"------他写出来的东西大概率是废话;
- 你给他明确的背景、目标、受众、风格、输出格式------他很可能给你一份超预期的成果。
大模型的天花板是"模型能力",你的天花板是"Prompt 质量"。 大多数人离模型的能力上限,差的不是算力,而是一个好 Prompt。
1.3 Prompt 工程到底在工程什么?
markdown
用户需求
│
▼
┌─────────────┐ 反复迭代 ┌──────────────┐
│ Prompt 设计 │ ───────────────────▶ │ 模型输出 │
└─────────────┘ └──────────────┘
▲ │
│ 评估 / 反馈 │
└──────────────────────────────────────┘
所谓"工程",就是把**"我碰运气问一句"** 变成 "我有一套稳定可复用的方法去问"。
二、理论底座:为什么"提问方式"能决定 AI 的回答质量?
要写好 Prompt,得先对模型"在干什么"有个最基本的心智模型。
2.1 大模型本质:一个"超级联想接龙"机器
LLM 的核心机制可以简化成一句话:
给它一段文字,它基于概率预测下一个最合理的词,一个词一个词地续写下去。
比如你输入:
从前有一座山,山里有一座
模型会根据训练语料里海量"接龙"经验,输出概率最高的词------大概率是"庙"。
重点:它不是在"思考你真正想问什么",而是在**"给定你这段话,模型训练中见过的最可能的续写是什么"**。
2.2 由此推出三条 Prompt 铁律
这三条是后面所有技巧的根源,请先记住:
① 你给的上下文越清晰,模型"续写方向"越收敛
模糊输入 → 概率分布发散 → 每次输出都不一样,质量波动大 清晰输入 → 概率分布收敛 → 输出稳定、贴近需求
② 模型擅长"模仿",不擅长"无中生有"
你给示例(Few-shot),它能模仿得很像; 你不给示例,它就只能按训练语料的"平均水平"输出。
③ 模型"不会思考",但可以被"引导着思考"
直接问复杂问题 → 它倾向于"一步给出答案",很容易错; 要求它"先分析再回答" → 它会在输出中真的展开推理过程,准确率大幅提升。(这就是后面要讲的思维链 CoT)
2.3 一个关键概念:Token(令牌)
模型并不按"字"处理文本,而是按 Token。
- 一个中文汉字 ≈ 1~2 个 Token
- 一个英文单词 ≈ 1~2 个 Token
- 模型有上下文长度限制(如 8K / 32K / 128K tokens),超过就截断或报错
- 计费也按 Token:输入 Token + 输出 Token 都算钱
这意味着:Prompt 不是越长越好。清晰、结构化、无冗余,才是目标。
三、基础篇:写一个"能用"的 Prompt
3.1 第一原则:清晰、具体、无歧义
想象你给一个新来的实习生派活:
- 只说"帮我推广下新产品" → 他一脸懵,随便写一段交差
- 告诉他产品名、卖点、目标受众、平台、字数、语气 → 他能写出可直接发布的文案
给 AI 派活,完全是同一个道理。
对比示例 1:推广文案
| 模糊 Prompt | 清晰 Prompt |
|---|---|
| 我想推广公司的新产品。我的公司名为阿里云百炼,新产品名为 Zephyr Z9,是一款轻薄便携的手机。帮我创建一条微博帖子。 | 请为我司"阿里云百炼"最新推出的"Zephyr Z9"轻薄便携手机设计一条吸引眼球的微博推广帖。 内容需彰显 Zephyr Z9 的独特卖点:极致轻薄设计、高性能配置、用户便利性,同时融入创意元素提升观众兴趣与互动意愿。 记得提及阿里云百炼品牌声誉,激发受众好奇心,引导他们探索更多产品信息或直接进行购买。 贴文需简洁有力,符合微博平台风格与字数限制(≤140 字)。 |
差别在哪? 右侧 Prompt 明确了:
- 产品卖点(轻薄、高性能、便利性)
- 平台约束(微博风格、字数限制)
- 目的(引导点击/购买)
- 风格偏好(创意、吸引眼球)
模型"续写空间"被大幅收敛,输出质量自然稳定。
对比示例 2:技术任务
| 模糊 Prompt | 清晰 Prompt |
|---|---|
你是 PHP 专家,帮我实现 ${require},考虑边界和错误处理。 |
作为资深 PHP 专家,请针对需求 ${require} 给出一个完整的实现方案,并在回答中明确包含: 1. 实现步骤 :函数/类/数据结构选择与设计 2. 边界分析 :识别潜在边缘场景并说明处理方式 3. 错误处理 :异常捕获与健壮性设计 4. 安全考量 :SQL 注入、XSS、权限等威胁的防范措施 5. 性能优化 :算法复杂度、缓存策略、资源管理 请以 Markdown 输出,代码用 ```php 代码块包裹。 |
关键变化 :清晰 Prompt 把"考虑边界和错误处理"这种口号化的要求 ,拆成了 5 个具体的、可验收的输出模块------模型就没办法偷懒了。
3.2 写清晰 Prompt 的 4 个小习惯
- 把形容词换成数字 ------ "简短" → "不超过 100 字";"几个要点" → "3~5 个要点"
- 把开放问题换成封闭问题 ------ "你觉得怎么样?" → "请从 A/B/C 三个方案中选出最优并说明理由"
- 把意图变成可验收标准 ------ "写得专业一点" → "使用行业术语,避免口语化表达,引用至少 2 个数据来源"
- 明确输出格式 ------ "列一下" → "用 Markdown 表格输出,列名为:优点 / 缺点 / 适用场景"
四、结构化篇:用 Prompt 框架系统化地表达需求
前面讲了"要清晰",但如果每次都要现想怎么写,还是容易漏关键信息。Prompt 框架就是一个可复用的模板,把该说的都说到。
4.1 经典 6 要素框架:BGSTAO
B ackground 背景 · G oal 目的 · S tyle 风格 · T one 语气 · A udience 受众 · Output 输出
| 要素 | 作用 | 示例片段 |
|---|---|---|
| 背景(Background) | 告诉模型"我们在聊什么事" | 我公司名叫阿里云百炼,即将推出 Zephyr Z9 手机...... |
| 目的(Goal) | 告诉模型"我要什么结果" | 写一条微博帖子,引导用户点击购买链接 |
| 风格(Style) | 写作风格参考 | 参考小米、黑米等成功数码品牌的推广风格 |
| 语气(Tone) | 情感和语调 | 有说服力、略带激情但不浮夸 |
| 受众(Audience) | 读者是谁 | 18~30 岁年轻人,对数码产品敏感 |
| 输出(Output) | 交付物形态 | 单条微博,≤140 字,包含 1~2 个 emoji 和 1 个话题标签 |
4.2 完整对比:不用框架 vs 用框架
❌ 不用框架:
我想推广公司的新产品。我的公司名为阿里云百炼,
新产品名为 Zephyr Z9,是一款轻薄便携的手机。
帮我创建一条微博帖子,简洁而深具影响力。
✅ 使用框架:
markdown
#背景#
我司阿里云百炼即将推出新品"Zephyr Z9"------一款主打轻薄便携、
高性能配置的智能手机。主要竞品为小米、荣耀同价位机型。
#目的#
创建一条微博推广帖(≤140 字),引导用户点击产品链接进行购买。
#风格#
参考小米、黑米等数码品牌成功爆款微博的写作风格------
短句、节奏感强、金句开头。
#语气#
有说服力,略带激情但避免浮夸;不使用"震撼""王炸"等过度词汇。
#受众#
18~30 岁年轻人,尤其是对数码新品敏感、追求性价比的年轻女性。
#输出#
- 单条微博,≤140 字
- 包含 1~2 个 emoji
- 以 1 个行动号召(CTA)结尾
- 附上 1 个话题标签 #...#
差异: 用了框架后,模型明确知道"这是一个有字数限制、有风格参考、有 CTA 要求、有平台调性"的任务,而不是泛泛地写广告。
4.3 框架是活的,不是死的
你不必每次都 6 要素都写。按任务需求增减即可:
- 写代码 → 背景 + 目的 + 输出(+ 约束)就够了
- 写文案 → 6 要素完整用
- 做翻译 → 背景(术语领域)+ 目的(源/目标语言)+ 风格(正式/口语)+ 输出
- 做数据分析 → 背景(数据结构)+ 目的(分析维度)+ 输出(表格/图表描述)
五、进阶技巧:让 AI 输出从"能用"到"好用"
掌握了框架之后,下面这几个技巧能让你的 Prompt 再上一个台阶。
技巧 1:Few-shot(给模型看几个例子)
原理:大模型擅长"模仿"。给它几个输入-输出示例,它会照葫芦画瓢,输出格式、风格、颗粒度都会显著更稳定。
反例:不给样例
arduino
把下面句子翻译成电商风格的商品标题:
"这是一款轻薄便携的笔记本电脑,续航 12 小时。"
模型的输出每次可能都不一样,风格飘忽。
正例:给 2~3 个样例
请将输入的产品描述改写为爆款电商标题,严格参考下面示例的风格。
示例 1:
输入:一款可以放进口袋的迷你风扇,USB 充电。
输出:【口袋神器】迷你 USB 风扇|出门清凉,随身带走!
示例 2:
输入:一副蓝牙耳机,降噪,续航 8 小时。
输出:【8 小时续航】主动降噪蓝牙耳机|沉浸音乐,清静一整天!
现在请处理:
输入:这是一款轻薄便携的笔记本电脑,续航 12 小时。
输出:
效果:模型会严格模仿"【卖点】主句|场景句"的结构。
Few-shot 的使用原则
- 示例数量:2~5 个最佳,太多占 Token 还可能引入噪声
- 示例要多样:覆盖不同情况,防止模型"过拟合"到某一种
- 示例格式要一致:输入、输出分隔符要统一,模型会模仿这个结构
技巧 2:设定任务步骤(分步指令)
原理:复杂任务一次性丢给模型,它倾向于"一口气给答案",漏步骤、错推理。拆成步骤后,模型会按部就班执行。
示例:小学应用题
题目:小明周日 8 点整出发步行去爷爷家,速度 50 米/分钟。走了 12 分钟后,爸爸发现小明忘带作业,骑车去追,速度 200 米/分钟。追上后爸爸带上小明继续骑车,小明坐自行车走的路程是他走路路程的 5 倍。问小明几点到爷爷家?
不带步骤的 Prompt: 直接问→模型可能算错(常见错误:没算清"追上"时刻)。
带步骤的 Prompt:
markdown
#背景#
(题目原文...)
#目的#
计算小明到爷爷家的时间。
#任务步骤#
1. 先计算爸爸追上小明时的时刻 T1 和此时小明距出发点的距离 D1。
2. 再计算从 T1 开始,小明坐自行车还要走多少距离 D2(D2 = 5 × D1)
以及这段路需要多少分钟 T2。
3. 最终到达时间 = 8:00 + 12 分钟 + T1 持续时长 + T2。
请按步骤给出计算过程和最终答案。
效果:模型会按 1/2/3 依次展开计算,正确率大幅提升(正确答案:8:36)。
技巧 3:用分隔符区分内容单元
原理:当 Prompt 里有多段不同性质的内容(指令、上下文、示例、用户输入),模型容易混淆。用特殊分隔符明确边界,模型解析会稳定很多。
常用分隔符(选一个你喜欢的即可)
arduino
### === >>> """...""" <tag>...</tag>
不用分隔符
arduino
请简短总结以下影评。曾经意气风发的张志强在生活的重压下,中年"失速"偏离了
原本的生活轨迹......(一大段影评)......重新定义人生新的方向。
模型可能把"请简短总结以下影评"也当成内容的一部分。
使用分隔符
markdown
请简短总结以下影评,要求 3 句话以内。
###
(影评正文放这里)
###
更进阶:用 XML 标签(Claude、GPT 都支持)
xml
<instruction>
请根据用户需求,从产品库中推荐 3 个产品并给出理由。
</instruction>
<user_need>
我想买一款 2000 元以内、续航长、适合商务办公的蓝牙耳机。
</user_need>
<product_database>
(产品 JSON 数据...)
</product_database>
<output_format>
Markdown 表格,列名:产品名 | 价格 | 推荐理由
</output_format>
这在构建复杂 Agent / RAG 应用时几乎是标配。
技巧 4:明确输出格式(让结果可被程序消费)
程序员尤其要关注这一点------AI 的输出要直接进代码流程,不能是"自由发挥的散文"。
markdown
#输出格式#
请严格按以下 JSON 格式输出,不要包含任何额外文字,
不要使用 ```json 代码块包裹:
{
"title": "string, 不超过 30 字",
"tags": ["string", "string"],
"summary": "string, 100 字以内",
"score": "integer, 1-10"
}
配合代码解析:
python
import json
import re
def safe_json_parse(text: str) -> dict:
"""安全解析 LLM 返回的 JSON,容错多种常见格式。"""
# 1) 尝试去掉 ```json ... ``` 包裹
match = re.search(r"```(?:json)?\s*(\{.*?\})\s*```", text, re.DOTALL)
if match:
text = match.group(1)
# 2) 兜底:截取第一个 { 到最后一个 }
if not text.strip().startswith("{"):
start = text.find("{")
end = text.rfind("}")
if start != -1 and end != -1:
text = text[start:end + 1]
return json.loads(text)
六、高级技巧:解决复杂推理与幻觉问题
6.1 思维链(Chain of Thought, CoT)
核心思想:让模型在给答案之前,先把"思考过程"写出来。
这是研究界验证过、成本几乎为零、效果却非常显著的一个技巧。
Zero-shot CoT:最简单的版本
只要在 Prompt 末尾加一句魔法咒语:
arduino
Let's think step by step.
(让我们一步一步思考。)
模型就会展开推理过程,准确率在数学/逻辑题上常能提升 10~40%。
对比示例:JSON 校验任务
json
{
"web-app": {
"servlet": [
{ "servlet-name": "cofaxEmail", "servlet-class": "...", "init-param": {...} },
{ "servlet-name": "cofaxTools", "servlet-class": "...", "init-param": {
"templatePath": "...", "log": 1,
"logLocation": "...", "logMaxSize": ""
}}
],
"servlet-mapping": {
"cofaxEmail": "/cofaxutil/aemail/*",
"cofaxTools": "/tools/*"
}
}
}
需求:判断是否满足三条规则------
- 每个 servlet 都有 init-param
- servlet-mapping 中的元素都能在 servlet 中找到
- cofaxTools 里以 "log" 开头的参数应有 3 个,且名为 "log" 的参数值 < 10
| 不使用 CoT | 使用 CoT |
|---|---|
#输出# 如果全部符合输出 "符合要求",否则输出 "不符合要求"。 |
#输出# 1. 先分别针对 3 条规则,逐条列出判断过程和结论; 2. 再给最终结论"符合要求 / 不符合要求"。 |
效果 :加了 CoT 的模型会清楚列出"规则 1 满足,因为......;规则 2 满足,因为......;规则 3 不满足,因为以 log 开头的只有 3 个但 logMaxSize 是空字符串不算数字......"------出错也容易发现在哪一步错的,便于调试。
6.2 Prompt Chaining(提示链)
核心思想 :把一个大任务拆成多个小任务,分多轮调用模型,每一轮的输出作为下一轮的输入。
场景:生成一篇深度技术博客
| 单次 Prompt | Prompt Chain(分 4 步) |
|---|---|
| "帮我写一篇关于 Kubernetes 网络的技术博客" → 通常又长又泛,重点不清 | Step 1 :让模型列出目标读者关心的 5 个核心问题 Step 2 :基于这 5 个问题生成大纲 Step 3 :按大纲逐节写正文 Step 4:让模型扮演读者审稿,提出 3 条改进建议 |
代码示例
python
def chain_generate_blog(topic: str) -> str:
# Step 1: 挖掘读者关心的问题
questions = llm(f"作为 Kubernetes 新手,我想学习 {topic},"
f"请列出我最关心的 5 个核心问题。")
# Step 2: 生成大纲
outline = llm(f"基于以下问题生成一份技术博客大纲:\n{questions}")
# Step 3: 按大纲逐节写
article = llm(f"按以下大纲撰写一篇技术博客,面向初学者:\n{outline}")
# Step 4: 自我审稿
review = llm(f"请以苛刻的技术审稿人视角,给出 3 条改进建议:\n{article}")
# Step 5: 应用建议
return llm(f"原文:\n{article}\n\n审稿建议:\n{review}\n\n请修订并输出终稿。")
优势:每一步目标单一,模型表现稳定;且每一步都可以单独 Debug、替换。
6.3 角色扮演(Role Prompting)
在 Prompt 开头给模型"设定身份",会显著影响输出的专业度和风格。
你是一位拥有 15 年经验的 Java 架构师,擅长分布式系统和高并发场景。
请以这个身份回答下列问题......
原理:模型会从训练语料里调用"符合这个身份"的表达方式和知识深度。这不是玄学,是实证有效的。
常用角色模板:
你是一位资深 XX 工程师......→ 技术问题你是一位严格的代码审查者......→ Code Review你是一位擅长用比喻解释复杂概念的老师......→ 面向小白的讲解你是一位产品经理,擅长挖掘用户真实需求......→ 需求分析
6.4 自一致性(Self-Consistency)
对一个需要推理的问题,用同一个 Prompt 让模型回答 N 次 (温度 temperature 调高一点),然后投票选出现最多的答案。
python
from collections import Counter
def self_consistency(prompt: str, n: int = 5) -> str:
answers = [llm(prompt, temperature=0.8) for _ in range(n)]
# 对每个答案提取最终结论,投票
final_answers = [extract_final_answer(a) for a in answers]
return Counter(final_answers).most_common(1)[0][0]
适用场景:数学题、逻辑题、分类任务。代价是 Token 用量变成 N 倍。
6.5 结构化输出 + Function Calling
现代 LLM(GPT-4、Claude、Qwen 等)都支持"函数调用"或 JSON Schema 约束输出,这才是工程化的最佳实践。示意:
python
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
)
# 模型直接返回结构化参数:{"city": "北京", "unit": "celsius"}
对程序员的意义 :不再需要用正则去"猜"模型的输出,类型安全的 AI 接口成为可能。
七、实战:用 Python 调用大模型 + 迭代优化 Prompt
7.1 最简可运行代码(以 OpenAI 兼容接口为例)
python
# pip install openai
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1", # 以百炼为例
)
def ask(prompt: str, system: str = "你是一位乐于助人的 AI 助手。",
temperature: float = 0.7) -> str:
resp = client.chat.completions.create(
model="qwen-plus",
messages=[
{"role": "system", "content": system},
{"role": "user", "content": prompt},
],
temperature=temperature,
)
return resp.choices[0].message.content
if __name__ == "__main__":
prompt = """
#背景#
我是一名 3 年经验的 Java 后端工程师,想在下半年转型做 AI 应用开发。
#目的#
给我列一个 3 个月的学习路线图。
#输出#
Markdown 表格,列名:周次 | 学习主题 | 产出物
"""
print(ask(prompt))
7.2 Prompt 迭代方法论(PDCA 循环)
写 Prompt 是个高度实验性的过程。不要指望一次写对,而要建立稳定的优化循环:
sql
┌─────────── Plan 设计 ─────────┐
│ 明确任务目标、验收标准 │
└─────────────┬─────────────────┘
▼
┌─────────── Do 执行 ───────────┐
│ 写 Prompt → 调用模型 → 拿结果 │
└─────────────┬─────────────────┘
▼
┌─────────── Check 评估 ────────┐
│ 对比验收标准,打分 / 找 bad case│
└─────────────┬─────────────────┘
▼
┌─────────── Act 调整 ──────────┐
│ 针对 bad case 调 Prompt │
└─────────────┬─────────────────┘
▼
重复直到稳定
7.3 一个可复用的 A/B 测试脚本
python
def ab_test(prompt_a: str, prompt_b: str, test_cases: list[dict], n: int = 3):
"""对同一批测试用例跑两个 Prompt,统计胜率。"""
score_a = score_b = 0
for case in test_cases:
user_input = case["input"]
expected = case["expected"] # 人工标注的标准答案
for _ in range(n):
out_a = ask(prompt_a.format(input=user_input))
out_b = ask(prompt_b.format(input=user_input))
# 用模型当裁判(LLM-as-judge)
judge = ask(f"""
标准答案:{expected}
候选 A:{out_a}
候选 B:{out_b}
请判断哪个更接近标准答案,只回答 A 或 B 或 TIE。
""", temperature=0)
if "A" in judge: score_a += 1
elif "B" in judge: score_b += 1
print(f"A 胜场:{score_a},B 胜场:{score_b}")
关键实践:
- 每次只改一个变量(控制变量法)
- 固定测试用例集(回归测试)
- 把 Prompt 版本化(像代码一样写入 Git)
八、常见坑位与排查清单
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 输出每次都不一样 | temperature 太高 / Prompt 太模糊 |
降到 0~0.3;加约束条件 |
| 模型"胡说八道"(幻觉) | 缺少事实依据;任务超出知识范围 | 用 RAG 给它提供上下文;明确说"不知道就答不知道" |
| JSON 解析失败 | 模型输出了说明文字 / Markdown 包裹 | 显式要求"只输出 JSON,不要任何其他文字";用 Function Calling |
| 中文里夹英文 | 没指定输出语言 | 明确"用中文输出,所有术语保留英文原词" |
| 一写长 Prompt 就忽略后面的 | 模型"近因效应" / 上下文太长 | 关键指令放开头和结尾各一次;或拆成多轮 |
| 输出太啰嗦 | 没限制长度 / 没给示例 | "不超过 200 字";给一个简洁的 Few-shot 示例 |
| 模型不按格式输出 | 没用分隔符 / 示例不够 | 加 XML 标签;补 2~3 个 Few-shot 示例 |
| 推理题经常错 | 没触发 CoT | 加"请一步步推理后再给答案" |
Prompt 工程不是玄学,是一门可以刻意练习的工程学科。 愿你在 AI 时代,成为那个**既会写代码、也会"写提示"**的新型程序员。