Prompt Engineering——从随意提问到工程化调用
前言
在上一篇文章中,我们理解了大模型为什么会产生幻觉。其中一个关键的缓解手段,就是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 我在课程问答项目中的做法
StringpromptTemplate=""" 你是一个课程答疑助手,只能根据以下课程内容回答学生的问题。 如果课程内容中没有相关信息,请直接说"课程中未提及此内容",不要编造答案。 ## 课程内容 {context} ## 学生问题 {question} ## 回答要求 - 基于课程内容回答,引用原文时标注出处(章节名) - 如果涉及代码,使用代码块格式包裹 - 回答简洁,不超过300字 - 输出格式保持纯文本,不需要markdown标题 """;设计考量:
- “标注出处”——方便用户回溯原文验证,也方便我做质量审查
- “代码用代码块”——课程内容大量涉及代码,格式要求保证可读性
- “不超过300字”——克制模型过度回答的倾向,当答案不需要300字时,这个限制防止它"为了凑字数而编造"
- “不需要markdown标题”——前端渲染时直接用样式控制层级,不需要模型自作主张
四、Prompt模板管理——硬编码 vs 配置化
疑问:Prompt直接写在代码里不行吗?为什么需要模板管理?
回答:Demo阶段写死没问题,但生产环境需要考虑迭代效率和团队协作。
4.1 硬编码的问题
// ❌ 硬编码:改一句话需要重新部署Stringprompt="你是客服助手..."+context+"..."+question;// ✅ 配置化:改Prompt不需要重新部署Stringprompt=promptTemplateManager.get("course_qa").replace("{context}",context).replace("{question}",question);4.2 我的方案
对于课程问答这个Demo项目,我选择了"轻量配置化"——Prompt存放在配置文件中,启动时加载到内存:
# prompts.ymlcourse_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拼接的最佳实践。
