人工智能(十三)- Prompt 工程完全指南:从原理到实战

4 阅读19分钟

你有没有过这种感觉:同样一个问题,别人用 AI 能得到一份结构清晰、可直接用的答案;你用 AI 却只能得到一段看似正确、实则含糊的"正确的废话"?

区别往往不在模型,而在提问的方式

这篇文章,我们就从零开始,把 Prompt 工程(Prompt Engineering)讲透——既讲清楚"为什么这么写有效",也给出可以直接复制粘贴、拿来就能跑的代码和模板。


目录


一、先搞清楚:什么是 Prompt 工程?

1.1 一句话定义

Prompt 工程(Prompt Engineering)是一门通过设计、优化和精炼输入给大语言模型(LLM)的文本指令(Prompt),引导模型生成高准确性、高相关性、高质量输出的学科与艺术。

拆成三个关键词:

关键词通俗解释
Prompt(提示词)你发给 AI 的那段文字——问题、指令、上下文、示例都算
Engineering(工程)不是随手一写,而是有方法论、可复用、可迭代、可测量
引导(Guide)AI 不是"读懂"你,而是被你的文字"引导"到正确的输出空间

1.2 它为什么重要?

对程序员而言,你可以把大模型理解为一个能力极强但没有明确 KPI 的外包同事

  • 你丢一句"帮我推广产品"——他写出来的东西大概率是废话;
  • 你给他明确的背景、目标、受众、风格、输出格式——他很可能给你一份超预期的成果。

大模型的天花板是"模型能力",你的天花板是"Prompt 质量"。 大多数人离模型的能力上限,差的不是算力,而是一个好 Prompt。

1.3 Prompt 工程到底在工程什么?

     用户需求
        │
        ▼
  ┌─────────────┐       反复迭代         ┌──────────────┐
  │  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 个小习惯

  1. 把形容词换成数字 —— "简短" → "不超过 100 字";"几个要点" → "3~5 个要点"
  2. 把开放问题换成封闭问题 —— "你觉得怎么样?" → "请从 A/B/C 三个方案中选出最优并说明理由"
  3. 把意图变成可验收标准 —— "写得专业一点" → "使用行业术语,避免口语化表达,引用至少 2 个数据来源"
  4. 明确输出格式 —— "列一下" → "用 Markdown 表格输出,列名为:优点 / 缺点 / 适用场景"

四、结构化篇:用 Prompt 框架系统化地表达需求

前面讲了"要清晰",但如果每次都要现想怎么写,还是容易漏关键信息。Prompt 框架就是一个可复用的模板,把该说的都说到。

4.1 经典 6 要素框架:BGSTAO

Background 背景 · Goal 目的 · Style 风格 · Tone 语气 · Audience 受众 · Output 输出

要素作用示例片段
背景(Background)告诉模型"我们在聊什么事"我公司名叫阿里云百炼,即将推出 Zephyr Z9 手机……
目的(Goal)告诉模型"我要什么结果"写一条微博帖子,引导用户点击购买链接
风格(Style)写作风格参考参考小米、黑米等成功数码品牌的推广风格
语气(Tone)情感和语调有说服力、略带激情但不浮夸
受众(Audience)读者是谁18~30 岁年轻人,对数码产品敏感
输出(Output)交付物形态单条微博,≤140 字,包含 1~2 个 emoji 和 1 个话题标签

4.2 完整对比:不用框架 vs 用框架

❌ 不用框架:

我想推广公司的新产品。我的公司名为阿里云百炼,
新产品名为 Zephyr Z9,是一款轻薄便携的手机。
帮我创建一条微博帖子,简洁而深具影响力。

✅ 使用框架:

#背景#
我司阿里云百炼即将推出新品"Zephyr Z9"——一款主打轻薄便携、
高性能配置的智能手机。主要竞品为小米、荣耀同价位机型。

#目的#
创建一条微博推广帖(≤140 字),引导用户点击产品链接进行购买。

#风格#
参考小米、黑米等数码品牌成功爆款微博的写作风格——
短句、节奏感强、金句开头。

#语气#
有说服力,略带激情但避免浮夸;不使用"震撼""王炸"等过度词汇。

#受众#
18~30 岁年轻人,尤其是对数码新品敏感、追求性价比的年轻女性。

#输出#
- 单条微博,≤140 字
- 包含 1~2 个 emoji
- 以 1 个行动号召(CTA)结尾
- 附上 1 个话题标签 #...#

差异: 用了框架后,模型明确知道"这是一个有字数限制、有风格参考、有 CTA 要求、有平台调性"的任务,而不是泛泛地写广告。

4.3 框架是活的,不是死的

你不必每次都 6 要素都写。按任务需求增减即可:

  • 写代码 → 背景 + 目的 + 输出(+ 约束)就够了
  • 写文案 → 6 要素完整用
  • 做翻译 → 背景(术语领域)+ 目的(源/目标语言)+ 风格(正式/口语)+ 输出
  • 做数据分析 → 背景(数据结构)+ 目的(分析维度)+ 输出(表格/图表描述)

五、进阶技巧:让 AI 输出从"能用"到"好用"

掌握了框架之后,下面这几个技巧能让你的 Prompt 再上一个台阶。

技巧 1:Few-shot(给模型看几个例子)

原理:大模型擅长"模仿"。给它几个输入-输出示例,它会照葫芦画瓢,输出格式、风格、颗粒度都会显著更稳定。

反例:不给样例

把下面句子翻译成电商风格的商品标题:
"这是一款轻薄便携的笔记本电脑,续航 12 小时。"

模型的输出每次可能都不一样,风格飘忽。

正例:给 2~3 个样例

请将输入的产品描述改写为爆款电商标题,严格参考下面示例的风格。

示例 1:
输入:一款可以放进口袋的迷你风扇,USB 充电。
输出:【口袋神器】迷你 USB 风扇|出门清凉,随身带走!

示例 2:
输入:一副蓝牙耳机,降噪,续航 8 小时。
输出:【8 小时续航】主动降噪蓝牙耳机|沉浸音乐,清静一整天!

现在请处理:
输入:这是一款轻薄便携的笔记本电脑,续航 12 小时。
输出:

效果:模型会严格模仿"【卖点】主句|场景句"的结构。

Few-shot 的使用原则

  • 示例数量:2~5 个最佳,太多占 Token 还可能引入噪声
  • 示例要多样:覆盖不同情况,防止模型"过拟合"到某一种
  • 示例格式要一致:输入、输出分隔符要统一,模型会模仿这个结构

技巧 2:设定任务步骤(分步指令)

原理:复杂任务一次性丢给模型,它倾向于"一口气给答案",漏步骤、错推理。拆成步骤后,模型会按部就班执行。

示例:小学应用题

题目:小明周日 8 点整出发步行去爷爷家,速度 50 米/分钟。走了 12 分钟后,爸爸发现小明忘带作业,骑车去追,速度 200 米/分钟。追上后爸爸带上小明继续骑车,小明坐自行车走的路程是他走路路程的 5 倍。问小明几点到爷爷家?

不带步骤的 Prompt: 直接问→模型可能算错(常见错误:没算清"追上"时刻)。

带步骤的 Prompt:

#背景#
(题目原文...)

#目的#
计算小明到爷爷家的时间。

#任务步骤#
1. 先计算爸爸追上小明时的时刻 T1 和此时小明距出发点的距离 D1。
2. 再计算从 T1 开始,小明坐自行车还要走多少距离 D2(D2 = 5 × D1)
   以及这段路需要多少分钟 T2。
3. 最终到达时间 = 8:00 + 12 分钟 + T1 持续时长 + T2。

请按步骤给出计算过程和最终答案。

效果:模型会按 1/2/3 依次展开计算,正确率大幅提升(正确答案:8:36)。

技巧 3:用分隔符区分内容单元

原理:当 Prompt 里有多段不同性质的内容(指令、上下文、示例、用户输入),模型容易混淆。用特殊分隔符明确边界,模型解析会稳定很多。

常用分隔符(选一个你喜欢的即可)

###     ===     >>>     """..."""     <tag>...</tag>

不用分隔符

请简短总结以下影评。曾经意气风发的张志强在生活的重压下,中年"失速"偏离了
原本的生活轨迹……(一大段影评)……重新定义人生新的方向。

模型可能把"请简短总结以下影评"也当成内容的一部分。

使用分隔符

请简短总结以下影评,要求 3 句话以内。

###
(影评正文放这里)
###

更进阶:用 XML 标签(Claude、GPT 都支持)

<instruction>
请根据用户需求,从产品库中推荐 3 个产品并给出理由。
</instruction>

<user_need>
我想买一款 2000 元以内、续航长、适合商务办公的蓝牙耳机。
</user_need>

<product_database>
(产品 JSON 数据...)
</product_database>

<output_format>
Markdown 表格,列名:产品名 | 价格 | 推荐理由
</output_format>

这在构建复杂 Agent / RAG 应用时几乎是标配。

技巧 4:明确输出格式(让结果可被程序消费)

程序员尤其要关注这一点——AI 的输出要直接进代码流程,不能是"自由发挥的散文"。

#输出格式#
请严格按以下 JSON 格式输出,不要包含任何额外文字,
不要使用 ```json 代码块包裹:

{
  "title": "string, 不超过 30 字",
  "tags": ["string", "string"],
  "summary": "string, 100 字以内",
  "score": "integer, 1-10"
}

配合代码解析:

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 末尾加一句魔法咒语:

Let's think step by step.
(让我们一步一步思考。)

模型就会展开推理过程,准确率在数学/逻辑题上常能提升 10~40%。

对比示例: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/*"
    }
  }
}

需求:判断是否满足三条规则——

  1. 每个 servlet 都有 init-param
  2. servlet-mapping 中的元素都能在 servlet 中找到
  3. cofaxTools 里以 "log" 开头的参数应有 3 个,且名为 "log" 的参数值 < 10
不使用 CoT使用 CoT
#输出#
如果全部符合输出 "符合要求",否则输出 "不符合要求"。
#输出#
1. 先分别针对 3 条规则,逐条列出判断过程和结论;
2. 再给最终结论"符合要求 / 不符合要求"。

效果:加了 CoT 的模型会清楚列出"规则 1 满足,因为……;规则 2 满足,因为……;规则 3 不满足,因为以 log 开头的只有 3 个但 logMaxSize 是空字符串不算数字……"——出错也容易发现在哪一步错的,便于调试。

6.2 Prompt Chaining(提示链)

核心思想:把一个大任务拆成多个小任务,分多轮调用模型,每一轮的输出作为下一轮的输入。

场景:生成一篇深度技术博客

单次 PromptPrompt Chain(分 4 步)
"帮我写一篇关于 Kubernetes 网络的技术博客" → 通常又长又泛,重点不清Step 1:让模型列出目标读者关心的 5 个核心问题
Step 2:基于这 5 个问题生成大纲
Step 3:按大纲逐节写正文
Step 4:让模型扮演读者审稿,提出 3 条改进建议

代码示例

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 调高一点),然后投票选出现最多的答案

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 约束输出,这才是工程化的最佳实践。示意:

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 兼容接口为例)

# 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 是个高度实验性的过程。不要指望一次写对,而要建立稳定的优化循环:

  ┌─────────── Plan 设计 ─────────┐
  │  明确任务目标、验收标准          │
  └─────────────┬─────────────────┘
                ▼
  ┌─────────── Do 执行 ───────────┐
  │  写 Prompt → 调用模型 → 拿结果  │
  └─────────────┬─────────────────┘
                ▼
  ┌─────────── Check 评估 ────────┐
  │  对比验收标准,打分 / 找 bad case│
  └─────────────┬─────────────────┘
                ▼
  ┌─────────── Act 调整 ──────────┐
  │  针对 bad case 调 Prompt       │
  └─────────────┬─────────────────┘
                ▼
              重复直到稳定

7.3 一个可复用的 A/B 测试脚本

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 时代,成为那个**既会写代码、也会"写提示"**的新型程序员。