Prompt 工程入门

角色设定、Few-shot、指令设计、Chain-of-Thought——从零到写出高质量 Prompt 的完整指南

17 min read Part of AI Foundation · Ch. 5
← 上一层级:学习路径 · Part 01 · AI 基础概念

Prompt 工程入门

flowchart LR
  A["Prompt 工程入门"]
  A --> B["分类:基础概念"]
  A --> C["关键词:AI"]
  A --> D["关键词:LLM"]
  A --> E["关键词:Prompt"]
  A --> F["关键词:Few-shot"]

同样的模型,Prompt 不同,输出质量可能天差地别。Prompt 工程的本质,不是“写咒语”,而是把任务说清楚、把边界讲明白、把输出设计成可控的接口。


这篇文章会讲什么

前面几篇讲了模型的原理、训练、记忆。但作为应用开发者,你在真实项目里最常打交道的,往往不是训练流程,也不是模型结构,而是每次请求时送进去的那段文本——Prompt。

很多人刚接触 LLM 时,会把 Prompt 理解成一句“对模型说的话”。这个理解没错,但还不够完整。
在工程视角里,Prompt 更像是:

  • 你给模型的任务说明书
  • 你和模型之间的接口契约
  • 你约束输出格式和行为边界的控制层
  • 你在不重新训练模型的前提下,最大化模型表现的主要手段

也正因为如此,同样一个模型,Prompt 稍微改一改,结果可能完全不同:

  • 模糊的 Prompt,输出容易发散、啰嗦、跑题
  • 清晰的 Prompt,输出更稳定、更短、更可解析
  • 没有边界的 Prompt,模型容易乱补全
  • 加了示例和约束的 Prompt,模型更像“按 SOP 办事”

本文会从最基础的概念讲起,覆盖:

  • Prompt Engineering 到底是什么
  • 角色设定为什么有效
  • Few-shot 为什么能显著改善格式和稳定性
  • 指令设计该怎么写才清楚
  • Chain-of-Thought 什么时候有用,什么时候反而拖慢或扰乱输出
  • 常见错误、修复思路和生产环境实践

读完之后,你应该能做到两件事:

  1. 写出比“随便问一句”明显更稳定的 Prompt
  2. 理解 Prompt 不是玄学,而是一套可拆解、可测试、可迭代的方法

延伸阅读


先说结论:Prompt 不是“加点魔法词”,而是“减少模型猜测”

很多入门文章会把 Prompt 工程讲得很神秘,好像只要找到某种神奇写法,模型就会突然变聪明。

更准确的理解应该是:

Prompt 工程的核心,不是赋予模型新能力,而是减少模型误解任务的空间。

模型本来就有很多能力,但默认情况下,它并不知道:

  • 你现在到底要它做什么
  • 你希望答案多长
  • 输出应该是自由文本还是 JSON
  • 它该不该解释过程
  • 它遇到不确定信息时应该保守还是大胆补全
  • 它这次是扮演老师、客服、分析师,还是代码审查员

如果这些都不说清楚,模型就只能“猜你的意思”。
而 Prompt 工程,就是把这些本来需要模型猜的部分,尽量显式化。


1. 什么是 Prompt Engineering,为什么重要

先看定义:Prompt Engineering 是通过设计输入文本和消息结构,引导模型产生期望输出的技术。它是成本最低、反馈最快、最适合迭代的模型控制方式。


为什么 Prompt 这么重要

在大模型应用里,Prompt 往往是你最先、也最频繁能动的那个杠杆。

维度说明
零边际成本改 Prompt 不需要重新训练,改完就生效
快速迭代几分钟就能试一版,适合 A/B 测试
稳定性控制好 Prompt 能明显减少跑题、啰嗦、格式错误
工程价值Prompt 设计直接影响自动化、解析、用户体验

但更重要的是,Prompt 其实决定了“模型现在在做哪一种任务”。

同一个问题,如果你写成:

  • “帮我看看这个需求”
  • “请作为产品经理,列出 3 个风险点”
  • “请输出 JSON,字段为 risks / assumptions / next_steps”

对模型来说,这几乎是三种不同的任务形式。

所以 Prompt 的作用不是“装饰问题”,而是:

把任务从模糊意图,转换成模型更容易稳定执行的指令。


Prompt 不只是“一段话”,而是一组输入元素

很多教程会把 Prompt 简化成一句用户输入,但在真实系统中,一次完整请求通常由多部分组成:

System Prompt:角色、规则、长期约束
User Prompt:当前任务、输入数据、输出要求
Examples(可选):Few-shot 示例
Context(可选):对话历史、检索结果、工具返回内容

所以更准确地说,Prompt Engineering 不是只改一句话,而是在设计:

  • 哪些信息应该放 system
  • 哪些应该放 user
  • 哪些应该作为示例
  • 哪些应该作为动态上下文
  • 这些信息的顺序和组织方式是什么

这也是为什么生产环境里的 Prompt 工程,常常和“上下文工程(context engineering)”分不开。


Prompt 的价值边界:它能放大表现,但不能凭空造能力

这里有一个必须讲清楚的边界。

Prompt 可以:

  • 更好地激活模型已有能力
  • 提高输出稳定性
  • 减少格式错误
  • 降低幻觉概率
  • 改善任务表现

但 Prompt 不能:

  • 凭空注入模型根本没有的知识
  • 让小模型瞬间变成大模型
  • 让模型在没有依据时可靠地知道最新事实
  • 从根本上消除所有推理错误

所以更准确的说法是:

模型的能力上限由训练决定,Prompt 决定的是你能把这部分能力稳定发挥到什么程度。


2. 角色 Prompt(Role Prompt):为什么“你是谁”会改变回答

先看定义:角色设定能让模型进入更合适的行为模式,从而改变回答的风格、粒度、边界和专业视角。


为什么角色会有效

角色 Prompt 有效,不是因为模型真的“变成了某个人”,而是因为训练数据里本来就包含大量带角色特征的文本模式。

例如:

  • 技术支持的说话方式
  • 教师的解释方式
  • 律师的谨慎表达
  • 产品经理的结构化思考
  • 程序员的术语和代码风格

当你告诉模型:

你是资深 Python 工程师

你实际上是在向它发出一个强信号:

  • 应该优先激活哪一类语言模式
  • 应该用什么颗粒度解释问题
  • 什么内容更 relevant
  • 什么回答风格更像“合格的专家”

所以角色设定的本质是:

缩小模型需要猜测的回答风格空间。


弱角色和强角色的差别

弱角色

你是一个有帮助的助手。

这类 Prompt 当然也能用,但问题是:

  • 太泛
  • 没有专业边界
  • 风格不明确
  • 无法约束“有帮助”到底意味着什么

强角色

你是某科技公司的资深技术支持工程师,擅长 Python 和数据分析。
你的回答风格:简洁、专业、带必要代码示例。
优先给出可执行建议;不确定时明确说明需要进一步确认。
不回答与当前技术问题无关的内容。

这类角色 Prompt 更强,不是因为字更多,而是因为它补齐了四件事:

  1. 身份:你是谁
  2. 领域:你擅长什么
  3. 风格:你该怎么说
  4. 边界:你不该做什么

角色 Prompt 最适合放什么

角色 Prompt 特别适合承载以下信息:

  • 角色身份
  • 目标受众
  • 语气风格
  • 安全边界
  • 回答优先级
  • 高层行为原则

例如:

  • 面向小白,避免术语
  • 回答先给结论,再给原因
  • 优先输出可执行步骤
  • 不确定时说不确定,不要猜测
  • 不输出内部推理,只给简明依据

这些设定很适合放在 system prompt 里,因为它们相对稳定、需要每轮生效。


角色 Prompt 的常见误区

1. 角色写得太空

例如“你是世界上最强的专家”。
这类说法听起来厉害,但对模型约束很弱。

2. 角色堆太多

比如同时要求模型是:

  • 战略顾问
  • 数据分析师
  • 心理咨询师
  • 法务顾问
  • 创意总监

角色越混,输出越容易混乱。

3. 角色里塞太多知识

角色 Prompt 适合放规则和风格,不适合充当大型知识库。
如果知识经常变化,应该考虑 RAG,而不是无限拉长 system prompt。


3. Few-shot Prompting:为什么“示范”比“命令”更稳

先看定义:Few-shot 的本质不是给模型补知识,而是给模型看“这道题应该怎么做、输出应该长什么样”。


为什么 Few-shot 这么有效

很多任务失败,并不是模型完全不会,而是它不知道:

  • 你要的结果是什么格式
  • 哪些边界情况应该拒绝
  • 哪种表达风格最符合预期
  • 同类样本中最重要的模式是什么

Few-shot 的作用,就是直接给它看几个例子,让它少猜一点。

你可以把它理解成:

  • 指令是在“讲要求”
  • 示例是在“做演示”

对于模型来说,演示往往比抽象要求更有约束力。


0-shot、Few-shot、Many-shot

类型说明适用场景
0-shot只给任务,不给例子简单任务、强模型、边界清晰
Few-shot给 1–5 个高质量示例格式化、抽取、分类、改写
Many-shot给更多示例小模型、复杂映射、强约束任务

在很多场景里,一个好的 few-shot 示例,比再多写几句说明更有效。
因为模型看到的是:

输入长这样,输出就应该长这样。


一个简单例子:提取信息

只有指令

请从下面文本中提取公司名、融资轮次和金额。

这条指令本身不差,但模型可能会输出:

  • 自由文本
  • 缺字段
  • 多解释几句
  • 格式每次都不一样

加 Few-shot 之后

示例1
输入:星云科技完成数千万人民币 A 轮融资,由晨光资本领投。
输出:
{
  "company": "星云科技",
  "round": "A轮",
  "amount": "数千万人民币"
}

示例2
输入:蓝海智能近日宣布获得天使轮投资,金额未披露。
输出:
{
  "company": "蓝海智能",
  "round": "天使轮",
  "amount": "未披露"
}

这时模型会更容易学会:

  • 输出 JSON
  • 字段固定
  • “未披露”也要填字段
  • 不要额外解释

Few-shot 最大的价值,往往就在这种结构约束上。


怎么选示例才有效

1. 少而精

3 个高质量示例,通常比 10 个杂乱示例更有效。

2. 覆盖边界情况

不要只给“标准样本”,还应考虑:

  • 缺失字段
  • 异常输入
  • 应拒绝的情况
  • 容易混淆的类型

3. 格式高度一致

如果示例 A 用 JSON,示例 B 用列表,示例 C 用自然语言,模型就会困惑到底该模仿哪一种。

4. 和当前任务分布接近

示例越接近真实任务,效果通常越好。
拿新闻分类的例子去教法律文书抽取,帮助有限。


Few-shot 的副作用

Few-shot 虽然强,但也不是越多越好。

1. 占用上下文

示例本身会消耗 token,挤压真正任务输入的空间。

2. 过拟合示例风格

模型可能会模仿示例表面的语气和结构,导致泛化变差。

3. 把错误模式一并教进去

示例写错,模型会认真学错。

所以很实用的经验法则是:

先用 zero-shot + 清晰指令;不稳定,再加 few-shot。


4. 指令设计(Instruction Design):让模型少猜、多做

先看定义:好的指令不是“更礼貌”,而是“更清楚、更具体、更可执行”。


为什么模糊指令一定会带来模糊输出

模型不是读心术。
如果你说:

  • “写点东西”
  • “分析一下”
  • “帮我优化”
  • “弄成表格”

这些话在人和人之间也许还能靠上下文补全,但模型往往只能自己猜。

而一旦进入“猜”的状态,输出稳定性就会迅速下降。

所以好的指令设计,本质上是在回答四个问题:

  1. 你要模型做什么
  2. 你希望输出长什么样
  3. 你希望它遵守哪些边界
  4. 它不知道时应该怎么处理

模糊 vs 清晰

模糊写法更清晰的写法
写点东西写一段 200 字产品介绍,面向第一次接触该产品的用户
分析一下列出 3 个优点和 3 个缺点,每点不超过 20 字
用 JSON输出 JSON,字段为 name、price、stock,缺失值填 null
优化这段文案保持原意不变,改得更简洁,适合 App 首页 Banner

你会发现,清晰写法往往多补了这些维度:

  • 目标
  • 长度
  • 受众
  • 格式
  • 缺失值处理
  • 约束条件

一个好指令的四个关键部件

1. 任务目标

告诉模型这次到底是:

  • 总结
  • 改写
  • 抽取
  • 分类
  • 评审
  • 翻译
  • 规划
  • 生成结构化数据

2. 输入范围

告诉它应该基于什么材料回答,以及不能越界到哪里。

3. 输出格式

告诉它答案应该长什么样。
格式越明确,后续自动化越容易。

4. 失败策略

最容易被忽略的一点是:
模型不知道的时候怎么办?

例如你可以明确规定:

  • 不确定就说“不确定”
  • 找不到字段就填 null
  • 信息不足时先列缺失项,不要编造
  • 超出范围就拒答

这一步对降低幻觉非常重要。


约束不是“附加项”,而是 Prompt 的一半

很多 Prompt 写失败,不是任务目标没写,而是约束没写。

比如:

不要编造不存在的功能。
不要超过 200 字。
不要输出解释,只输出 JSON。
若信息不足,请明确写“无法判断”。

这些约束的价值在于:

  • 把错误空间缩小
  • 让输出更可控
  • 降低后处理复杂度
  • 降低“看起来像对,其实不对”的风险

从工程角度说,约束越清晰,模型的自由度越低,输出越稳定。


5. 输出格式设计:为什么“好解析”比“文采好”更重要

先看定义:在应用开发里,Prompt 设计的重点往往不是“让模型说得多漂亮”,而是“让模型输出能被系统稳定消费”。


对人看的输出,和对程序看的输出,是两回事

如果回答只是给人看,自然语言可读性很重要。
但如果回答还要被后端程序接着处理,那格式稳定性就比“写得优美”更重要。

例如这些任务:

  • 信息抽取
  • 分类标签
  • 函数调用参数生成
  • 审批流判断
  • Agent 的下一步动作选择

它们都更依赖结构化输出


常见格式控制方式

格式适合场景
JSON抽取、接口返回、程序消费
列表步骤说明、要点总结
Markdown 表格对比、展示
分块输出摘要 / 分析 / 建议
固定字段模板审核、评分、打标签

例如:

请输出 JSON,格式为:
{
  "summary": "",
  "risks": [],
  "next_steps": []
}
若某项缺失,使用空数组或 null,不要省略字段。

这种写法往往比“请结构化回答”强很多,因为它把“结构化”具体化了。


一条非常实用的经验

如果结果要被程序读取,就不要只写“请用 JSON”,而要把字段、缺失值规则、嵌套层级都写清楚。

否则模型很可能会:

  • 多写解释
  • 少字段
  • 字段名漂移
  • 在 JSON 外包一层自然语言
  • 有时输出数组,有时输出对象

这些问题都不是模型“不会”,而是 Prompt 没把接口约定写清楚。


6. Chain-of-Thought(CoT):什么时候该“先分析再回答”

先看定义:对于多步推理任务,让模型先拆解再作答通常会更稳;但在生产环境里,重点不是一味追求冗长思维链,而是设计合适的中间步骤和输出结构。


CoT 为什么有效

复杂任务之所以容易错,一个重要原因是模型会“跳答案”。

比如一道多步题,直接要求最终结果时,模型可能在中途省略了关键推导。
而如果让它:

  • 先识别问题类型
  • 再列已知条件
  • 再给推导过程
  • 最后给结论

正确率通常会更高。

这背后的原因很直观:

复杂问题拆成小步后,每一步都更容易做对。


“Let’s think step by step” 为什么有用

经典写法是:

Let's think step by step.

或者:

请一步步分析后再回答。

这类提示会诱导模型生成中间过程,从而减少直接跳结论带来的错误。

但要注意,真正有效的往往不是这句英文魔法本身,而是它背后代表的策略:

让模型先展开任务结构,再输出结果。


更稳的做法:结构化推理,而不是泛泛“多想想”

与其只说“请一步步思考”,很多时候更好的写法是:

请按以下步骤回答:
1. 判断问题类型
2. 列出已知条件
3. 给出分析过程
4. 输出最终结论

或者:

先给出简短分析,再输出最终答案。

这样做有两个好处:

  • 推理过程更可控
  • 输出更容易检查和裁剪

在产品环境里,你通常并不需要模型生成很长、很散的“思维独白”,而是需要:

  • 可用的中间结构
  • 可审查的依据
  • 可截断、可复用的结果

什么时候适合用 CoT

适合不太适合
数学题、逻辑题简单分类
多步规划单字段抽取
需要解释依据对延迟极度敏感的任务
复杂代码分析高吞吐批处理中的轻任务

简单说:

任务越复杂、步骤越长、错误越可能出在中间,CoT 就越有价值。


CoT 的代价和风险

1. 输出更长

会增加 token 成本和延迟。

2. 有时会“越想越偏”

不是所有任务都适合展开过程,某些简单任务反而会被复杂化。

3. 不等于真实思维

模型生成的步骤不一定完全等价于它内部“真正如何得到答案”,所以不要把所有中间文本都当作可靠推理证据。

因此更实用的原则是:

对复杂任务,要设计“有用的中间步骤”;对简单任务,不要强迫模型长篇推理。


7. 常见错误与修复:为什么 Prompt 看起来没错,结果却不稳

很多 Prompt 失败,不是因为模型太笨,而是因为输入里留下了太多歧义。


错误 1:目标不清

表现

模型输出泛泛而谈,不知道重点是什么。

修复

把任务从“分析一下”改成:

  • 分析什么
  • 从哪个角度分析
  • 输出几条
  • 每条多长

错误 2:没有格式契约

表现

有时列表,有时段落;有时 JSON,有时自然语言。

修复

明确写出:

  • 输出格式
  • 字段名
  • 缺失值处理
  • 是否允许解释文本

错误 3:角色设定太空或太混

表现

回答风格漂移,时而像客服,时而像老师,时而像营销文案。

修复

角色不要多而杂,要明确:

  • 身份
  • 领域
  • 语气
  • 边界

错误 4:Few-shot 示例质量差

表现

模型认真模仿了错误示例,结果越改越离谱。

修复

少而精,只保留:

  • 格式最稳的
  • 边界最关键的
  • 最接近真实任务的

错误 5:Prompt 太长,信息优先级混乱

表现

真正重要的任务被埋掉,模型抓不到重点。

修复

把 Prompt 按优先级重写:

  1. 角色 / 规则
  2. 当前任务
  3. 输出格式
  4. 约束
  5. 示例
  6. 辅助说明

不是所有信息都值得放进主 Prompt。


错误 6:把 Prompt 当“一次性作品”,不做测试

表现

偶尔有效,但换个输入就崩。

修复

把 Prompt 当成产品接口来测:

  • 典型样本
  • 边界样本
  • 脏数据
  • 空输入
  • 对抗输入

Prompt 工程最容易被忽视的一点是:

它需要评测,而不只是灵感。


8. 五要素黄金公式:一条高质量 Prompt 通常包含什么

先看定义:高质量 Prompt 往往由五个核心要素组成:角色、目标、格式、约束、示例。

[角色] 你是谁,你的专业领域是什么
[目标] 这次具体要完成什么任务
[格式] 输出长什么样
[约束] 不要做什么,不确定时怎么办
[示例](可选)给几个高质量输入-输出演示

这五个要素不是每次都必须齐全,但它们构成了一个非常好用的检查框架。


模板示例

角色:你是客服助手,负责解答产品使用问题。
目标:根据用户问题,给出准确、简洁的答复。
格式:先给一句话结论,再分点说明(如有需要)。
约束:不知道的就说“需要转接人工”;不回答与产品无关的问题;不要编造政策。
示例:
用户:怎么重置密码?
助手:您可以通过“忘记密码”链接重置。步骤:1. 点击登录页的“忘记密码” 2. 输入注册邮箱 3. 查收邮件并点击链接 4. 设置新密码。

这个模板之所以有效,不是因为它“完整”,而是因为它把模型最容易猜错的几个地方都说清楚了。


9. 一个更工程化的理解:Prompt 不是文案,而是接口设计

当你把 Prompt 用在生产系统里,最应该建立的心态不是“我在写一段自然语言”,而是:

我在设计一个给模型调用的输入接口。

这意味着你需要考虑:

  • 输入字段是否稳定
  • 输出是否可解析
  • 失败时是否有保底行为
  • 换一个用户输入后会不会崩
  • 是否方便评测和迭代
  • 是否能和 RAG、工具调用、对话记忆拼起来

一旦你用这种视角看 Prompt,很多事情就会变简单:

  • 角色 = 默认配置
  • 约束 = 规则校验
  • 示例 = 单元测试样本
  • 格式 = 返回值 schema
  • 评测 = 回归测试

Prompt 工程真正成熟的标志,不是你能写出多华丽的提示词,而是:

你能不能把模型输出变成一个稳定、可测、可维护的系统组件。


核心概念速查

概念一句话
Prompt Engineering通过设计输入和上下文组织方式,引导模型稳定完成任务
Role Prompt用角色设定约束风格、边界和专业视角
Few-shot用少量高质量示例教模型当前任务的做法和格式
Instruction Design把任务、格式、约束和失败策略说清楚
Output Contract让模型输出可解析、可复用的结构化结果
CoT对复杂任务先分析再作答,提升多步任务稳定性
五要素角色 + 目标 + 格式 + 约束 + 示例

下一篇

入门之后,真正难的不是“写一个 Prompt”,而是把 Prompt 系统化

  • 如何做结构化输出
  • 如何管理 Prompt 模板版本
  • 如何把 system、memory、RAG、tool use 拼成一套稳定链路
  • 如何做评测、回归测试和迭代优化
  • 什么样的 Prompt 设计才适合生产环境