Prompt 工程教程:五要素法 + Few-shot + CoT + JSON 输出
🎯 本节目标
Section titled “🎯 本节目标”学完这一节,你能:
- 写出结构化 Prompt(五要素框架),不再写“帮我整理下客户反馈”这种让模型抓瞎的话
- 用 Few-shot 示例 和 CoT 思维链 提升复杂任务准确率,还能讲清“为啥这样有效”
- 让模型稳定输出 JSON(这是 FDE 把 AI 从“聊天玩具”变成“工程系统”的关键)
提示词工程在 FDE JD 出现率 55%,是技术栈第一项。后面所有模块(RAG / Agent / 评估)都建立在它之上。
🍎 生活化类比
Section titled “🍎 生活化类比”写 Prompt = 点外卖。
回想你点外卖填的那张单子,为啥要勾那么多选项?因为一句“我要吃饭”没法让店家知道你到底要啥。一份清楚的外卖单,有五块东西:
- 给谁下单(角色):给厨师下单,还是给跑腿小哥下单?
- 现在啥情况(背景):朋友聚餐?一个人吃?赶时间?
- 具体要什么(任务):两份番茄炒蛋、一份米饭
- 不要什么(约束):不要辣、不要香菜
- 怎么给你(输出格式):打包盒、要不要餐具
少一块会怎样?少“背景”,店家不知道份量做多大;少“约束”,可能做出你吃不了的味道;少“输出格式”,直接给你端盘子里——可你在家没法用。写 Prompt 也一样,五块都给齐,模型才稳。
📖 概念讲解
Section titled “📖 概念讲解”Prompt 五要素框架(点外卖版)
Section titled “Prompt 五要素框架(点外卖版)”把点外卖那五块,翻译成写 Prompt:
| 点外卖 | Prompt 五要素 | 邮件分类举例 |
|---|---|---|
| 给谁下单 | 角色 | 你是客服邮件分类专家 |
| 现在啥情况 | 背景 | 我们是卖电子产品的电商 |
| 具体要什么 | 任务 | 把每封邮件归到 5 类之一 |
| 不要什么 | 约束 | 只能归一类,不确定归“其他” |
| 怎么给你 | 输出格式 | JSON: {category, confidence} |
最容易搞混的是背景和任务。一句话记住:背景是状态,任务是动作。“我们是电商”是背景(陈述现状),“请分类”是任务(动词开头)。背景告诉模型“在什么场景下判断”,任务告诉模型“具体产出什么”。
Few-shot:给学徒看几个成品
Section titled “Few-shot:给学徒看几个成品”复杂任务,光描述规则模型常常学不会。这时别再费劲解释了——直接拿几个成品给它看。
就像师傅带徒弟:你口头描述“什么叫好的木匠活”,徒弟半天听不懂;但你塞给他三把做好的椅子,他照着刨,立马就会了。模型也一样,这招叫 Few-shot(少样本):在 Prompt 里塞 2-5 个“输入→输出”的成品例子,它会照着模式模仿。
例子1: "这手机太好用了!" → positive例子2: "质量差,后悔买" → negative例子3: "还行吧,一般般" → neutral现在判断: "续航惊人但拍照糊" → ?为什么是 3-5 个,不是 1 个也不是 30 个?
- 太少(1-2 个):模型只见过一次,没抓准规律,可能蒙
- 正好(3-5 个):模型看几遍就总结出套路,稳定模仿
- 太多(>10 个):一是费钱(每个例子都算 token),二是模型反而会“死记例子”,换个稍不同的说法就懵(过拟合)
CoT(思维链):让模型把计算过程写出来
Section titled “CoT(思维链):让模型把计算过程写出来”数学题、逻辑推理、多步决策这类任务,你直接问答案,模型容易蒙错。让它先把过程写出来再给答案,准确率能从 50% 跳到 90%+。
类比:考试时把计算过程写出来,vs 心算直接写答案。心算错一步整道题全错;写过程的话,每步都能检查,而且写着写着思路就顺了。
# 不用 CoT(容易蒙错)prompt = "小明有 5 个苹果,吃了 2 个,又买了 3 个,现在有几个?"
# 用 CoT(准确率高)prompt = """小明有 5 个苹果,吃了 2 个,又买了 3 个,现在有几个?请一步步思考:步骤1:步骤2:最终答案:"""但说实话:模型并没有真的“在思考”。 它本质是个“字接字”的语言模型,CoT 只是逼它先输出一段推理文字。重点是:模型生成下一个字时,前面所有字(包括它自己刚写的推理)都会被一起算进去当输入。所以“先写过程 → 再下结论”这个顺序,会让最终结论落在更合理的字上。看起来像思考,本质是“输出文字 → 这些文字又帮下一句输出得更准”的滚雪球效应。
什么时候用 CoT?推理类任务(数学、逻辑、多步决策)。分类、抽取这种简单任务别用,白白多花 token 还变慢。
结构化 JSON 输出:三种“约束程度”
Section titled “结构化 JSON 输出:三种“约束程度””企业应用里,模型输出不能是一段话,必须是代码能解析的 JSON。三种做法,按“管得多严”排序:
方法 1:口头要求(松,可能违约) —— 在 Prompt 里写一句“请输出 JSON”
system = "你是数据抽取器。输出必须是合法 JSON,不要任何解释。格式:{name, age, city}"user = "张三今年 28 岁,住在上海"# 大部分时候输出 JSON,偶尔会调皮加一句"好的,这是结果:"像口头约定:大多数时候守约,偶尔违约。简单任务够用,但你不能 100% 信它。
方法 2:response_format(中,基本不违约) —— 调用时强制 JSON 模式
response = client.chat.completions.create( model="glm-4-flash", messages=[...], response_format={"type": "json_object"} # 像签合同:必须输出 JSON)像签合同:模型底层被强制走 JSON 模式,基本不会违约。生产环境首选。
方法 3:Function Calling(严,直接锁死格式) —— 定义一个“工具”,让模型按工具的参数 schema 输出。像装了把锁:模型必须按你定义的字段名、类型填,一点不差。模块 3 详讲,现在知道有这招就行。
怎么选? 小练习用方法 1;做产品先上方法 2;要跟其他系统对接、字段必须严丝合缝,上方法 3。
🛠️ 动手实操:做一个邮件分类器(分三步加难度)
Section titled “🛠️ 动手实操:做一个邮件分类器(分三步加难度)”我们做 FDE 入门最经典的小项目——客服邮件分类器,但分三步迭代:先写五要素版,再加 Few-shot 提准确率,最后用 JSON 强制输出。你会直观看到“每加一层,效果稳一点”。
Step 0:确认上节的 chat() 还能用
Section titled “Step 0:确认上节的 chat() 还能用”确保 llm.py 还在 ~/fde-learn 目录里(上节封装的函数)。这节站在它肩膀上。
Step 1:五要素版(基础分类)
Section titled “Step 1:五要素版(基础分类)”新建 email_classifier.py:
from llm import chatimport json
# 五要素版的 system(行尾【】标了对应哪个要素,方便对照)SYSTEM_V1 = """你是客服邮件分类专家。【角色】
我们是卖电子产品的电商,每天收几百封客户邮件。【背景】
请把每封邮件归到下面 5 类中的 1 类:【任务】- complaint:投诉(不满、索赔、情绪激动)- inquiry:咨询(问功能、价格、流程)- return:退货退款- technical:技术问题(bug、报错、不能用)- other:其他
每封只能归一类,不确定归 other。【约束】
输出 JSON: {"category": "类别", "confidence": 0.0-1.0, "reason": "一句话理由"}【输出格式】"""
def classify(email: str) -> dict: """分类一封邮件""" result = chat(email, system=SYSTEM_V1) try: return json.loads(result) except json.JSONDecodeError: return {"category": "other", "confidence": 0.0, "reason": "解析失败"}
if __name__ == "__main__": test_emails = [ "我上周买的耳机充不进电,要求换货!", # 期望 return / technical "请问你们的企业版支持多少人同时用?", # 期望 inquiry "服务太差了!等了三天没人理,要投诉!", # 期望 complaint ] for email in test_emails: print(f"邮件: {email}") print(f"分类: {classify(email)}\n")运行 python email_classifier.py。
✅ 大概率分类对。但遇到边界邮件(比如“产品不能用,要求退款”)会左右摇摆——这时候该 Few-shot 上场。
Step 2:加 Few-shot(让边界邮件也稳)
Section titled “Step 2:加 Few-shot(让边界邮件也稳)”把 SYSTEM 换成 V2,在五要素末尾追加几个“输入→输出”的成品例子:
# Few-shot 版:五要素 + 3 个成品例子SYSTEM_V2 = SYSTEM_V1 + """
参考下面 3 个例子,照这个模式分类:例子1: "耳机用了三天就坏了,充不进电,要求换货" → {"category": "return", "confidence": 0.9, "reason": "明确要求换货"}例子2: "你们的 Pro 版和 Max 版有啥区别?" → {"category": "inquiry", "confidence": 0.95, "reason": "询问产品差异"}例子3: "app 一直闪退,根本登录不进去" → {"category": "technical", "confidence": 0.9, "reason": "报 bug 不能用"}"""
def classify_v2(email: str) -> dict: """Few-shot 版分类""" result = chat(email, system=SYSTEM_V2) try: return json.loads(result) except json.JSONDecodeError: return {"category": "other", "confidence": 0.0, "reason": "解析失败"}
# 把 main 里的 classify(email) 换成 classify_v2(email) 再跑一遍✅ 边界邮件会明显稳——这就是“师傅带徒弟看几个成品”的效果。3 个例子足够,加到 10 个反而会让模型只认这几个特定说法。
Step 3:JSON 强制输出(让解析不再失败)
Section titled “Step 3:JSON 强制输出(让解析不再失败)”Step 1/2 偶尔会解析失败(模型加了废话)。从源头解决:在 llm.py 末尾追加一个 chat_json() 函数,用方法 2 强制 JSON。
# 加到 llm.py 末尾def chat_json(prompt: str, system: str = "你是中文助手", model: str = "glm-4-flash") -> str: """ 强制 JSON 输出版 chat。 response_format={"type": "json_object"} 让模型底层走 JSON 模式, 不会再加"好的,这是结果:"这种废话。 """ client = OpenAI( api_key=os.getenv("GLM_API_KEY"), base_url="https://open.bigmodel.cn/api/paas/v4/" ) response = client.chat.completions.create( model=model, messages=[ {"role": "system", "content": system}, {"role": "user", "content": prompt} ], response_format={"type": "json_object"} # 签合同:必须 JSON ) return response.choices[0].message.content然后 email_classifier.py 把 from llm import chat 改成 from llm import chat_json,classify_v2 里调用换成 chat_json,再跑一遍。
✅ 这次解析失败几乎不再出现。生产环境永远用
response_format或 Function Calling,别赌模型自觉。
⚠️ 常见错误
Section titled “⚠️ 常见错误”| 问题 | 原因 | 解决 |
|---|---|---|
| 模型不输出 JSON,带一堆解释 | 只口头要求 | 升级到 response_format={"type": "json_object"}(方法 2) |
| JSON 解析失败 | 模型加了 ```json 标记 | 用 result.strip('\’).replace(‘json\n’,‘’)` 清洗,或上方法 2 |
| 分类不准 / 左右摇摆 | 规则模糊 | 加 3-5 个 Few-shot 例子,把边界说清 |
| 简单任务用 CoT 反而慢 | 不需要推理却加了“一步步思考” | 分类、抽取别用 CoT,只推理任务用 |
| Few-shot 加 10+ 个更差 | 过拟合 + 费 token | 砍到 3-5 个,挑边界例子 |
1. 五要素里,"背景" 和 "任务" 有啥区别?各举一个邮件分类里的例子。
背景 是“现在啥情况”(陈述现状)。例如:“我们是电商,每天收几百封邮件”。 任务 是“具体要模型干啥”(动词开头)。例如:“把每封邮件归到 5 类之一”。 一句话:背景是状态,任务是动作。
2. Few-shot 为什么 3-5 个最好?给 30 个会怎样?
太少(1-2 个)模型没抓准规律;3-5 个模型看完能总结出套路,稳定模仿;太多(30 个)一是费钱(每个例子都算 token),二是模型容易“死记例子”,换个稍不同的输入就懵(过拟合)。
3. CoT 提升准确率的真正原理是什么?模型真的"在思考"吗?
不是真思考。模型本质是“字接字”的语言模型,CoT 只是逼它先生成一段推理文字。重点是:模型每次生成下一个字时,前面所有的字(包括它自己刚生成的推理)都会被一起算进去当输入。所以“先写过程 → 再下结论”这个输出顺序,会让最终结论落在更合理的字上。看起来像思考,本质是“输出文字 → 这些文字又帮助下一句输出得更准”的滚雪球效应。
🚀 实战小项目:结构化信息抽取
Section titled “🚀 实战小项目:结构化信息抽取”选一份真实文档(简历 / 产品说明 / 合同条款),用 五要素 Prompt + JSON 输出,抽取结构化字段。验收:对 10 份文档,字段准确率 ≥ 80%(自己人工核对)。进阶:准确率不够的字段,加 Few-shot 例子补强。
📚 总结 & 下一节
Section titled “📚 总结 & 下一节”3 句话回顾:
- Prompt 五要素 = 点外卖:角色 / 背景 / 任务 / 约束 / 输出格式,五块都给齐模型才稳
- Few-shot(看成品)和 CoT(写过程)分别解决“规则难描述”和“推理复杂”,前提是用对地方
- JSON 输出三档约束:口头要求(松)→ response_format(中,生产首选)→ Function Calling(严)
下一节:进入模块 2,用低代码平台(Coze / Dify) 把这些能力快速做成能给客户用的应用——30 分钟交付。
🏋️ 配套自练
Section titled “🏋️ 配套自练”- Anthropic Prompt 互动教程 — 重点做第 6 章(思维链)和第 7 章(Few-shot),带答案(免费)
- PromptClaude 练习场 — 写 prompt 立即被 Claude 打分,看哪里能改进(免费、需 Anthropic Key)
- PromptEval 每日一练 — 5 个能力轨道,从基础到护栏,每天一道(免费)
🔗 延伸阅读
Section titled “🔗 延伸阅读”- Prompt Engineering Guide(promptingguide.ai)
- OpenAI Prompt Engineering 最佳实践
- Anthropic Prompt Engineering
← 上一节:第一次调通 API | 下一节:Coze 与 Dify 低代码 →