前言
在上一篇文章中,我们理解了大模型为什么会产生幻觉。其中一个关键的缓解手段,就是Prompt Engineering。
你可能会觉得:"Prompt Engineering 不就是写好提示词吗?这有什么可学的?" 但真正做过大模型应用开发的人都知道------同样一个模型,换一种提问方式,答案质量天差地别。这不是玄学,而是一套可以被拆解、验证、复用的工程方法。
面试官看到你简历上写了"Prompt Engineering",通常会追问一句:"你设计的Prompt模板是怎么想的?" 本文就是帮你准备好这个问题的完整答案。
本文核心问题:
- 为什么同样的模型,换个问法答案就不一样?Prompt到底是如何影响模型输出的?
- 角色设定、Few-shot、Chain of Thought 分别解决什么问题?各自的底层逻辑是什么?
- 输出格式约束怎么做?JSON模式 vs 自然语言约束?
- Prompt模板如何管理?硬编码 vs 配置化的优劣?
- 如何评估一个Prompt的好坏?A/B测试怎么做?
- 你在课程问答项目中实际使用的Prompt模板是什么?经历了哪些迭代?
- 什么场景下应该把复杂Prompt拆成多步?
- Prompt Engineering 有天花板吗?什么时候该转向微调或其他方案?
读完本文,你将能把"写Prompt"这件事从感觉变成方法,面试时能讲清楚每一步设计的底层考量。
一、为什么Prompt是"工程"而不是"聊天"?
疑问:不就是跟AI说话吗?为什么还要"工程化"?
回答:因为大模型对输入极为敏感。同一个问题,三种问法,三种结果。
1.1 一个真实的对比实验
我在做课程问答助手时,测试过三种不同的Prompt:
问法A(随意):
"Java线程池怎么配置?"
→ 模型给了一个通用回答,和课程内容无关,还编了两个不存在的参数名
问法B(加角色):
"你是一个Java课程助教。Java线程池怎么配置?"
→ 语气更像老师了,但内容还是通用的,没有引用课程
问法C(加角色+约束+课程文档):
"你是课程答疑助手,只能根据以下课程内容回答。如果内容中没有答案,说'课程未提及'。课程内容:{文档}。问题:Java线程池怎么配置?"
→ 准确从课程文档中提取了答案,标注了章节出处
同样的模型,三种Prompt,三种质量。 这就是Prompt Engineering要解决的问题------不是"会说话",而是"用最小的Token成本,稳定地获得最高质量的输出"。
1.2 Prompt是如何影响模型输出的?
从上一篇我们知道,大模型是逐Token预测下一个词的概率。Prompt改变了这个概率分布:
没有Prompt约束时:
"Java线程池..." → 所有可能的续写竞争 → 最通用的那类回答概率最高
有Prompt约束时:
"你是课程助教。课程内容:{文档}。Java线程池..."
→ "基于课程"的续写概率被大幅拉高
→ "编造外部知识"的续写概率被压制
Prompt就是在引导概率流向------让模型在正确的语义空间中采样。
二、Prompt三大核心技巧的底层逻辑
疑问:角色设定、Few-shot、Chain of Thought 是Prompt的三大法宝。它们各自解决什么问题?为什么有效?
2.1 角色设定------缩小语义搜索空间
不做角色设定:
模型在"整个互联网文本分布"中采样 → 输出泛化、风格随机
做角色设定:
"你是一个10年经验的Java架构师"
→ 模型在"技术专家文本"的子空间中采样
→ 输出更专业、更有结构
本质 :角色设定是一种软约束------它没有硬性规定输出内容,但大幅缩小了模型采样的语义空间。这不是让模型"扮演角色",而是告诉模型"应该从哪种语言模式的分布中采样"。
2.2 Few-shot------用示例教会模型模式
Zero-shot(不提供例子):
"将以下句子翻译成JSON:姓名张三,年龄25"
→ 模型可能输出:{"name":"张三","age":25} ← 猜对了
→ 也可能输出:{"姓名":"张三","年龄":25} ← 猜错了格式
Few-shot(提供2-3个例子):
"示例1:姓名李四,年龄30 → {"name":"李四","age":30}
示例2:姓名王五,年龄22 → {"name":"王五","age":22}
现在:姓名张三,年龄25 → "
→ 模型几乎100%输出:{"name":"张三","age":25}
本质 :Few-shot 是一种模式示范。大模型有很强的模式识别和复制能力。给它2-3个示例,它就能准确复现你想要的格式。这比用自然语言描述格式要求可靠得多。
2.3 Chain of Thought------强迫模型"慢思考"
不用COT:
"一个班有30个学生,男生比女生多4个,男生有多少?"
→ 模型直接输出:"17个" ← 可能对也可能错
用COT(加一句"请逐步推理"):
同一个问题 + "请逐步推理"
→ "设女生x人,男生x+4人。x+(x+4)=30,2x=26,x=13。男生=13+4=17。"
→ 准确率大幅提升
本质 :COT 把"一步跳到答案"变成了"多步推演"。每一步的推演结果都成为下一步的输入------模型在每一步都在基于自己的推理结果继续推理,而不是一次性猜测最终答案。这和人类的"打草稿"是同一个道理。
三、输出格式约束------让模型说"人话"
疑问:怎么让模型稳定输出JSON?用自然语言说"请输出JSON格式"够吗?
回答:不够。自然语言约束有模糊性,结构化约束更可靠。
3.1 三种约束方式对比
| 方式 | 示例 | 成功率 | 适用场景 |
|---|---|---|---|
| 自然语言 | "请以JSON格式输出" | 80-90% | 简单结构 |
| Few-shot + 示例格式 | 给出2-3个期望输出示例 | 95%+ | 大部分场景 |
| JSON Mode(API原生) | response_format={"type":"json_object"} |
99%+ | 严格要求JSON |
3.2 我在课程问答项目中的做法
java
String promptTemplate = """
你是一个课程答疑助手,只能根据以下课程内容回答学生的问题。
如果课程内容中没有相关信息,请直接说"课程中未提及此内容",不要编造答案。
## 课程内容
{context}
## 学生问题
{question}
## 回答要求
- 基于课程内容回答,引用原文时标注出处(章节名)
- 如果涉及代码,使用代码块格式包裹
- 回答简洁,不超过300字
- 输出格式保持纯文本,不需要markdown标题
""";
设计考量:
- "标注出处"------方便用户回溯原文验证,也方便我做质量审查
- "代码用代码块"------课程内容大量涉及代码,格式要求保证可读性
- "不超过300字"------克制模型过度回答的倾向,当答案不需要300字时,这个限制防止它"为了凑字数而编造"
- "不需要markdown标题"------前端渲染时直接用样式控制层级,不需要模型自作主张
四、Prompt模板管理------硬编码 vs 配置化
疑问:Prompt直接写在代码里不行吗?为什么需要模板管理?
回答:Demo阶段写死没问题,但生产环境需要考虑迭代效率和团队协作。
4.1 硬编码的问题
java
// ❌ 硬编码:改一句话需要重新部署
String prompt = "你是客服助手..." + context + "..." + question;
// ✅ 配置化:改Prompt不需要重新部署
String prompt = promptTemplateManager.get("course_qa")
.replace("{context}", context)
.replace("{question}", question);
4.2 我的方案
对于课程问答这个Demo项目,我选择了"轻量配置化"------Prompt存放在配置文件中,启动时加载到内存:
yaml
# prompts.yml
course_qa:
system: "你是课程答疑助手,只能根据课程内容回答..."
user_template: |
## 课程内容
{context}
## 学生问题
{question}
选型考量:项目当前只有一个Prompt模板,过度设计没必要。但用配置文件意味着后续如果需要做A/B测试(对比两个Prompt的效果),只需要改配置重启即可,不用动代码。
五、如何评估一个Prompt的好坏?
疑问:我怎么知道改了一句话,Prompt是变好了还是变坏了?凭感觉吗?
回答:不能凭感觉。需要一套可量化的评估方法。
5.1 评估维度
| 维度 | 衡量标准 | 课程问答项目中的做法 |
|---|---|---|
| 准确性 | 回答与课程内容是否一致 | 人工抽查50条,判断引用是否正确 |
| 诚实性 | 不知道时是否说"不知道" | 用课程外的10个问题测试,统计幻觉率 |
| 稳定性 | 同一问题多次询问是否一致 | 5个问题各问3次,统计不一致的比例 |
| 格式合规 | 是否遵守输出格式约束 | 检查代码块包裹率、字数超标率 |
5.2 A/B测试怎么做?
1. 准备两份Prompt:A版(当前版本)、B版(优化版本)
2. 准备50个测试问题(覆盖正常问题、边界问题、课程外问题)
3. 用同一个模型,分别套用A版和B版Prompt
4. 对每个回答打分(准确性、诚实性、格式合规)
5. 对比两版的总分和分项表现
6. B版如果全面优于A版 → 上线B版
5.3 我在项目中实际经历的迭代
V1:"你是一个课程答疑助手。{context}。{question}。"
→ 问题:模型偶尔还是编造内容
V2:"你是课程答疑助手,只能根据课程内容回答。如果内容中没有答案,
请说'课程中未提及'。{context}。{question}。"
→ 改善:编造大幅下降,但对代码格式控制不好
V3:在V2基础上加"代码用代码块包裹"和"不超过300字"
→ 最终版:满足项目需求
六、什么时候需要拆解复杂Prompt?
疑问:一个Prompt搞定所有事情不行吗?为什么要拆成多步?
回答:复杂任务拆成多步,每步只做一件事,准确率比一个长Prompt高。
单一复杂Prompt:
"分析这段代码的问题,给出修改建议,并输出修改后的完整代码"
→ 模型可能遗漏问题、建议笼统、代码和原文对不上
拆成多步:
步骤1:"分析这段代码的问题(只列问题,不修改)"
步骤2:拿到问题列表后,逐个问"针对问题1,给出修改方案"
步骤3:拿到所有方案后,"请整合所有修改,输出完整代码"
→ 每步输出质量有保障,最后结果可用
什么场景该拆? 如果一个问题需要模型同时完成"判断+检索+生成+格式化",且你发现单一Prompt输出不稳定------就值得拆成多步。
七、Prompt Engineering的天花板
疑问:Prompt Engineering能解决一切吗?什么时候该用其他方案?
回答:有天花板。当Prompt已经足够好但效果仍不达标时,考虑以下进阶方案:
| 场景 | Prompt的局限 | 进阶方案 |
|---|---|---|
| 需要模型"学会"新的知识 | 靠Prompt灌输效率低 | RAG(检索增强生成) |
| 需要模型遵循非常特定的格式/风格 | Prompt无法100%保证 | 微调(Fine-tuning) |
| 需要模型执行复杂的多步操作 | Prompt过长导致注意力分散 | Agent(工具调用+任务规划) |
| 需要模型展示可靠的计算能力 | 概率生成不保证数值正确 | 外挂计算工具(Function Call) |
Prompt Engineering是基础,不是终点。 绝大多数场景下,好的Prompt设计 + RAG已经足够。只有当这些方法都到天花板了,才需要考虑微调等更重的方案。
总结
- Prompt不是"聊天",是引导概率流向------同样的模型,不同的Prompt,答案质量天差地别
- 角色设定缩小语义搜索空间,让模型在相关分布中采样
- Few-shot比自然语言描述更可靠------模型学"模式"比学"指令"更准确
- Chain of Thought强迫模型"打草稿",把一步猜测变成多步推演,准确率大幅提升
- 输出格式约束需多层保障:自然语言 + Few-shot示例 + API级约束
- Prompt模板配置化让迭代不依赖重新部署,为A/B测试铺路。Demo阶段建议轻量配置即可
- 评估Prompt靠数据不靠感觉------准确性、诚实性、稳定性三个维度量化对比
- Prompt Engineering有天花板------该上RAG和微调时不要死磕Prompt
下一篇预告:AI理论学习(五)------RAG检索增强生成:让大模型学会"开卷作答"。拆解RAG为什么能同时解决幻觉和知识陈旧问题,以及文档切分策略、检索精度优化、Prompt拼接的最佳实践。