Prompt 不是玄学:写给前端的 Prompt 工程指南

上一篇我们搞懂了 LLM 的工作原理------它只会「预测下一个 token」。那么问题来了:既然它的全部能力就是续写,我们怎么让它乖乖按我们的意图输出?答案就是 Prompt 工程。这篇我们来系统拆解:怎么把"和模型说话"变成一门可控的工程手艺。


0. 为什么 Prompt 是你的"第二门编程语言"

上一篇结尾留了一个钩子:作为应用开发者,你控制模型行为的主要手段是 Prompt,不是训练、不是微调。这意味着什么?

意味着 Prompt 就是你写给模型的代码

写前端时,你用 JavaScript 命令浏览器;做 AI 应用时,你用 Prompt 命令模型。区别在于:JavaScript 是精确的形式语言,少一个分号可能就报错;而 Prompt 是自然语言,它的"语法"是模糊的、概率性的------这恰恰是它难掌握的地方,也是"Prompt 工程"之所以是一门"工程"的原因。

很多前端同学第一次写 Prompt 的体验是:有时候效果好得惊人,有时候又完全跑偏,而且你不知道为什么。这篇要解决的,就是把这个"不知道为什么"变成"我知道为什么,而且能稳定复现"。


1. 先破除一个迷思:Prompt 不是"咒语"

网上有很多「神级 prompt」分享,看起来像魔法咒语------一大段神秘的设定、各种"你必须..."、"你是一个拥有 20 年经验的..."。很多人收藏了一堆,用的时候照抄,却发现效果忽好忽坏。

Prompt 不是咒语,是沟通。 你在和一个非常聪明、但没有上下文、且倾向于"猜你意思"的同事交接任务。想想你在公司里怎么给同事提需求的:

  • 说清楚目标(要做什么)
  • 给足背景(为什么做、限制条件)
  • 提供参考(以前怎么做的、期望的样子)
  • 规定交付格式(写成文档还是表格)

给模型写 Prompt,核心就是这四件事。把它当成一次"需求交接",你已经掌握了 80%。


2. Prompt 的骨架:四个组成部分

任何一个高质量 Prompt,拆开来看基本都由四块组成。我用前端的方式来类比:

组成部分 作用 前端类比
指令(Instruction) 告诉模型要做什么 组件的 props / 函数签名
上下文(Context) 提供背景信息 组件的 context / 全局配置
示例(Examples) 用样例示范期望输出 Storybook 里的 story
输出格式(Format) 约束返回的结构 函数的 return 类型 / TS 接口

下面逐个拆解。

2.1 指令:一句话说清要做什么

这是 Prompt 的核心。好的指令有几个特征:

  • 动词开头,明确动作:「分类」「提取」「翻译」「总结」「生成」------别让模型猜你要什么。
  • 单一目标:一个 Prompt 最好只解决一件事。想让模型同时"总结并翻译并评分",往往会每件都做不好。
  • 正向表述:说"做什么"比说"不做什么"更有效。模型对"不要"的理解不如对"要"的理解可靠。

对比一下:

arduino 复制代码
不好的:"帮我看看这段文字"
(做什么?看什么?看完输出啥?全是谜)

好的:"把下面这段用户反馈分类为「bug 反馈」「功能建议」「使用疑问」「其他」之一,只输出类别名称。"
(动作=分类,范围=四选一,输出=只输出类别名)

2.2 上下文:给模型补足"你才知道的信息"

模型见多识广,但它不知道你的业务。任何跟你的具体场景相关的信息,都需要你喂给它:

  • 你在做什么产品、面向谁
  • 处理的数据是什么格式、来自哪
  • 有哪些约束(字数、语气、敏感词)

前端类比:这就像 React 的 Context Provider------你不注入,子组件(模型)就拿不到这些信息,只能用它的"默认值"(通用知识),而通用知识往往不符合你的业务。

2.3 示例(Few-shot):用样例教学

这是 Prompt 工程里最强大的技巧之一,单独一节讲(见第 3 节)。

2.4 输出格式:定义"返回类型"

这是工程化应用中最关键的一环。如果你的程序要解析模型的输出,你必须严格约束格式,否则模型可能今天返回 JSON、明天返回一段散文。

最基础的方式是直接描述:

xml 复制代码
请按以下格式输出,不要输出任何其他内容:
类别:<分类结果>
置信度:<0-1的数字>

进阶方式是用结构化输出(Structured Outputs / JSON Schema),这会在第 4 章专门讲。现在你只需要记住一个原则:想要机器可用,就必须定义格式。


3. Few-shot:用示例教模型"照着做"

3.1 Zero-shot vs Few-shot

给模型任务的方式,按"给不给示例"分两类:

  • Zero-shot(零样本):只描述任务,不给示例。模型靠自己的理解力去猜。
  • Few-shot(少样本):给几个输入-输出的范例,模型"照葫芦画瓢"。

前端类比:Zero-shot 是你给同事一段口头描述就让他干活;Few-shot 是你给他两三个做好的样板,说"照这个来"。后者几乎总是更靠谱,尤其当你要求特定格式或特定风格时。

3.2 一个具体例子

假设我们要做情感分析,把用户评论分成正面/负面/中性。

Zero-shot 版本:

arduino 复制代码
判断下面评论的情感倾向(正面/负面/中性):
"这个页面加载太慢了,等得我花都谢了"

模型大概率能做对,但输出格式不可控------它可能回"负面",也可能回"这条评论的情感倾向是负面的"。

Few-shot 版本:

arduino 复制代码
判断评论的情感倾向。

评论:"界面很好看,用起来很顺手" → 正面
评论:"经常闪退,太烂了" → 负面
评论:"功能还行,就是没有夜间模式" → 中性

评论:"这个页面加载太慢了,等得我花都谢了" →

给完三个例子,模型不仅知道任务,还从示例中学会了输出格式(只要一个词)。你甚至不需要显式说"只输出一个词"。

3.3 Few-shot 的几条经验

  • 示例数量:通常 2-5 个就够,不是越多越好。太多会占上下文、增加成本,边际收益递减。
  • 示例要有多样性:如果全是正面例子,模型可能学会"什么都判正面"。正负中各给一个,覆盖你要区分的情况。
  • 示例顺序会影响结果:模型对最后的示例印象更深(这是注意力机制的特性),把和当前输入最相似的示例放后面。
  • 示例就是"隐式规范":你想让模型输出的格式、语气、粒度,最好在示例里体现出来,比口头描述有效得多。

3.4 什么时候用 Few-shot

场景 建议
任务简单、模型能力强 Zero-shot 足够,省 token
要求特定输出格式 Few-shot,示例比描述有效
任务有特殊规则或风格 Few-shot,用示例传递"潜规则"
任务是分类/抽取等"有标准答案"的 Few-shot 效果稳定

4. Chain-of-Thought:让模型"打草稿"

4.1 一个反直觉的现象

你让模型算一道稍微复杂的数学题,或者做一个需要推理的判断,它直接给答案时经常出错。但如果你让它先把推理过程写出来,正确率会大幅提升。

这不是玄学,这和 LLM 的本质有关------还记得上一篇说的吗?模型是「预测下一个 token」。当它直接预测"答案是什么"时,它是在"蒙"。但当它先预测"第一步推理是什么",再基于第一步预测"第二步是什么"......每一步都给后续推理铺了路,错误率就降下来了。

前端类比:这就像你让一个人心算 37 乘 24,他直接报答案容易错;但你让他在纸上列竖式一步步算,就不容易错。Chain-of-Thought 就是让模型"列竖式"。

4.2 怎么触发 CoT

最简单的方式,在指令里加一句:

复制代码
请一步步思考,然后再给出最终答案。

或者用 Few-shot 的方式,给几个带推理过程的范例:

ini 复制代码
问题:小明有 5 个苹果,给了小红 2 个,又买了 3 个,现在有几个?
推理:小明原来有 5 个,给出 2 个后剩 5-2=3 个,又买 3 个,3+3=6 个。
答案:6

模型看到这种"先推理后作答"的模式,就会照做。

4.3 CoT 的适用边界

CoT 不是万能的,它有明确的适用场景:

  • 适合:数学计算、逻辑推理、多步骤决策、需要权衡的问题。
  • 不适合:简单的分类、翻译、摘要------这些任务"想太多"反而可能引入错误,还白烧 token。

还有一点要权衡:CoT 会增加输出长度,也就增加成本和延迟。你的应用需不需要它,得拿效果和成本来换算。这也是为什么后面"评测"那一章很重要------你得能量化"加 CoT 到底提升多少"。


5. System Prompt:全局规则的大本营

上一篇我们讲了 system / user / assistant 三个角色。这里展开讲:怎么写好 system prompt

5.1 System Prompt 该放什么

System prompt 是你应用的"宪法",定义了整个会话的基调。适合放在这里的内容:

  • 角色设定:你是谁、服务谁、用什么语气("你是一个严谨的技术客服,回答简洁专业")
  • 全局规则:贯穿整个会话的硬约束("不确定时明确说不知道,不要编造")
  • 输出规范:默认的格式和长度要求
  • 安全边界:不能做什么("不回答与产品无关的问题")

前端类比:它是你应用的根配置或全局 middleware------所有请求都会过一遍这些规则。

5.2 写 System Prompt 的几条原则

1. 具体胜过抽象

arduino 复制代码
不好:"你是一个好助手"
好:"你是一个前端技术答疑助手,面向有 1-3 年经验的前端开发者。回答时优先给代码示例,解释要简洁,涉及原理时附上 MDN 链接。"

2. 用结构化格式

长 system prompt 建议用分段或列表写,而不是一整坨:

diff 复制代码
你是一个代码审查助手。

职责:审查用户提交的 JavaScript 代码。
审查维度:
- 安全性(XSS、注入风险)
- 性能(不必要的重渲染、内存泄漏)
- 可读性(命名、结构)
输出格式:按维度列出问题,每条给出代码位置、问题描述、修改建议。
约束:如果代码没有问题,直接回复"代码看起来没问题",不要编造问题。

分段写的好处不只是模型理解得更好,也方便你自己维护和迭代------后面调试 prompt 时,你能精确定位是哪一段出了问题。

3. 设定"不知道"的兜底

LLM 最大的毛病之一是"幻觉"------不知道也硬编一个答案。在 system prompt 里明确约束"不确定时说不知道",能显著降低幻觉率。这一条几乎每个生产应用都该有。

5.3 System 和 User 的边界

一个常见错误是:把本该放在 system 里的全局规则,反复写在每条 user 消息里。这样既浪费 token,又容易不一致。

原则:稳定的、全局的规则进 system;变化的、一次性的信息进 user


6. 常见反模式:这些坑你一定会踩

6.1 指令冲突:自相矛盾

arduino 复制代码
不好:"请详细全面地回答,但不超过 50 个字。"

模型收到矛盾指令时会"猜"你更想要哪个,结果不可控。要么放宽字数限制,要么调整"详细"的要求。

6.2 模糊表述:缺少可执行标准

arduino 复制代码
不好:"帮我优化一下这段文案"
(优化方向?目标受众?保持原意还是可以重写?)

好:"把这段文案改写得更适合社交媒体传播:口语化、每句不超过 15 个字、保留核心卖点。"

6.3 没有输出约束:格式漂移

这是工程化最大的坑。你不约束格式,模型就自由发挥------这次返回 JSON,下次返回"好的,结果是:..."。你的代码一解析就崩。

铁律:只要输出要给程序处理,就必须用格式约束(描述格式、给示例、或用 Structured Outputs)。

6.4 过度堆砌角色设定

arduino 复制代码
不好:"你是一个拥有 30 年经验的谷歌首席工程师,MIT 博士,精通所有编程语言,获奖无数......"

冗长的头衔堆砌对效果提升有限,反而浪费 token。模型不需要你"夸"它,它需要你明确告诉它做什么、怎么做

6.5 期望模型"记住"你没说的

arduino 复制代码
不好:"按我们之前说的格式输出"
(它不记得你脑子里想的格式,你得显式写出来)

模型没有读心术。任何期望它"应该知道"的东西,都要写出来。


7. Prompt 调试方法:像管理代码一样管理 Prompt

Prompt 工程的"工程"二字,体现在你怎么管理和迭代它。这一节帮你建立正确的工作流。

7.1 变量化:把 Prompt 拆成模板

不要把整个 Prompt 写成一大段死文本。把可变的部分抽出来当变量,固定部分当模板------这和你写前端组件是一个思路。

js 复制代码
// 伪代码:把 Prompt 当组件来设计
const prompt = buildPrompt({
  system: SYSTEM_PROMPT,           // 固定的全局规则
  context: userContext,            // 动态的上下文
  examples: FEW_SHOT_EXAMPLES,     // 固定的示例库
  userInput: currentUserInput,     // 用户本次输入
  format: OUTPUT_FORMAT_SPEC,      // 固定的格式约束
});

这样做的好处:改一处不影响全局,不同部分可以独立迭代和测试。

7.2 A/B 对比:一次只改一个变量

调试 Prompt 时最大的陷阱是"同时改了好几处"------效果变了,但你不知道是哪个改动起的作用。

正确做法:控制变量,一次只改一个地方

  • 改了 few-shot 示例?其他都不动,对比前后效果。
  • 调了 temperature?prompt 不变,看输出差异。
  • 用了 CoT?和不用的版本放一起跑同一批输入。

这也是为什么你需要一批固定的测试输入("黄金集"),每次改完 prompt 都用同一批数据跑一遍,才能客观比较。这一点会在第 10 章"评测"里展开。

7.3 版本管理:Prompt 也是资产

Prompt 不是写完就扔的一次性文本,它是你应用的核心资产,而且一定会反复改。所以:

  • 把 Prompt 单独抽出来,不要和业务逻辑混在一起。
  • 用 Git 管理你的 prompt,每次改动有 commit message 说明改了什么、为什么。
  • 记录每个版本的"测试集表现",这样你能知道改了之后是变好还是变差。

前端类比:你不会把 API 配置硬编码散落在各处还不留版本记录------Prompt 也一样。很多团队甚至会专门搭一个 prompt 管理平台。对你个人来说,至少做到:prompt 独立成文件 + Git 管理。

7.4 一个实用的调试清单

当模型输出不符合预期时,按这个顺序排查:

  1. 指令清楚吗? 动词是否明确,目标是否单一。
  2. 格式约束了吗? 有没有明确说"只输出 XXX"。
  3. 要不要加示例? 试试用 few-shot 替代纯描述。
  4. 要不要 CoT? 如果是推理任务,加一句"一步步思考"。
  5. 是不是 temperature 太高? 需要稳定输出时降到 0-0.3。
  6. 上下文够不够? 模型是不是缺少必要的背景信息。
  7. 有没有指令冲突? 仔细读一遍,有没有自相矛盾的要求。

8. 小结与下一步

这篇我们把"和模型说话"拆成了一套可控的方法论:

  • Prompt = 指令 + 上下文 + 示例 + 输出格式,四块各司其职。
  • Few-shot 用示例教模型格式和风格,比口头描述更可靠。
  • Chain-of-Thought 让模型先推理再作答,适合复杂推理任务,但要权衡成本。
  • System Prompt 是全局规则的大本营,具体、结构化、带兜底。
  • 反模式要避开:指令冲突、模糊表述、无格式约束、过度堆砌、期望模型读心。
  • 调试要工程化:变量化、控制变量 A/B、版本管理。

核心理念一句话:Prompt 不是写出来的,是调出来的。 像调代码一样调 prompt------有假设、有对照、有度量。

下一篇我们正式进入代码实战:用 Next.js + Vercel AI SDK 把这些 prompt 变成真实可运行的应用,并打通流式输出这条链路。从"在 Playground 里调"到"在代码里跑",这是从"懂概念"到"能做产品"的关键一跃。我们下篇见。

相关推荐
爱丶不疚37 分钟前
在 dsh 仓库里扒到的宝藏工作流:详解 .agents/notes 决策沉淀系统
前端·agent·vibecoding
武子康38 分钟前
从 Pi 学习设计自己的 Agent Harness:一条可验证的垂直生产线
人工智能·llm·agent
HAHAXX840 分钟前
从传统RPA到AI增强RPA:内网离线部署、元素自愈与Prompt工程落地实践
人工智能·prompt·rpa
两万五千个小时40 分钟前
DeepSeek Harness 上下文压缩:长对话管理
人工智能·程序员·架构
有脚就行40 分钟前
第24篇-Go-gRPC推理服务-高性能跨语言通信
开发语言·人工智能·后端·golang
hyunbar77742 分钟前
玩转LlamaIndex:流式输出与并发执行
人工智能
喜欢睡觉42 分钟前
从"送花"讲懂 JavaScript:对象、数据类型与代理模式
前端
哈__42 分钟前
Windows部署Next AI Draw.io:用大模型生成并修改draw.io图表
人工智能·draw.io
2301_7803567042 分钟前
【技术解码】全视通“物联数据中台”如何打通医养结合全链路?
大数据·人工智能·物联网