3.2 高级 Prompt 策略:CoT、Few-shot、ReAct
在上一节中,我们掌握了 Prompt 的基础知识——角色设定、指令结构、输出格式约束,以及"清晰、具体、有上下文"三大原则。这些是写好 Prompt 的地基。但当地基打好之后,你会遇到一个新问题:面对复杂的推理任务、陌生的输出格式、需要调用外部工具的场景,仅靠"把指令写清楚"已经不够了。模型需要更高级的引导策略。
本节介绍三种最实用的高级 Prompt 策略,它们分别解决三类不同的问题:
- CoT(Chain-of-Thought,思维链)——让模型展示推理过程,解决"直接给答案容易出错"的问题。
- Few-shot(少样本提示)——给模型举几个例子,解决"光说不练,模型get不到你要什么"的问题。
- ReAct(Reasoning + Acting,推理+行动)——让模型边想边做,解决"模型知识不足或需要外部信息"的问题。
掌握了这三种策略,你就拥有了应对绝大多数实际任务的 Prompt 工具箱。
3.2.1 CoT:思维链——让模型"展示解题过程"
生活中的类比
想象你找一位数学老师帮你做题。如果你只说"给我答案",老师可能直接报一个数字——但这个数字未必对。更好的做法是说:"请把解题过程写出来。"老师一步步推导:先列方程,再消元,最后求解。即使中间某步有小错,你也能看到错在哪里;更重要的是,分步推理本身就是降低出错率的有效手段。
CoT 就是这个思路:不要让模型直接吐出最终答案,而是要求它把推理过程一步步写出来。研究表明,对于需要多步推理的任务(数学题、逻辑题、因果分析),让模型"展示过程"能显著提升正确率。
基本用法:一句话触发
最简单的 CoT 用法,只需在 Prompt 末尾加一句话:
一个商店原有苹果 48 个,上午卖出 12 个,下午又进了 20 个,
傍晚卖出 15 个。现在商店还有多少个苹果?
请一步步推理,最后给出答案。这一句"请一步步推理",就是触发 CoT 的开关。模型会输出类似这样的内容:
步骤1:原有 48 个苹果,上午卖出 12 个,还剩 48 - 12 = 36 个。
步骤2:下午又进了 20 个,现在有 36 + 20 = 56 个。
步骤3:傍晚卖出 15 个,最终还剩 56 - 15 = 41 个。
答案:现在商店还有 41 个苹果。注意:不加"请一步步推理"时,模型可能直接输出"41"——答案碰巧对了,但你看不到它怎么算的,万一算错了也无从检查。加了这句话,不仅正确率更高,你还能审计推理过程。
Zero-shot CoT vs Few-shot CoT
CoT 有两种常见用法:
| 类型 | 做法 | 适用场景 |
|---|---|---|
| Zero-shot CoT | 只加一句"请一步步推理",不提供示例 | 通用场景,快速上手 |
| Few-shot CoT | 在 Prompt 中给出一个带推理过程的示例,再让模型照做 | 对推理格式有要求,或任务较特殊 |
Few-shot CoT 示例:
Q:小明有 15 颗糖,给了小红 7 颗,又从妈妈那里得到 5 颗。小明现在有几颗糖?
A:小明原有 15 颗,给出 7 颗后剩 15-7=8 颗,又得到 5 颗后是 8+5=13 颗。
答案:13
Q:图书馆原有图书 320 本,上周借出 85 本,本周归还 40 本又新购 60 本。
现在有多少本?
A:模型看到上面的示例后,会模仿同样的推理格式来回答新问题。这就是"举一个例子,让模型照着做"。
代码示例:用 CoT 解数学题
import openai # 导入 OpenAI SDK
# 构造一个带 CoT 触发语的 Prompt
# "请一步步推理"是关键——它激活模型的链式推理能力
cot_prompt = """
问题:一个水池有两个进水管和一个出水管。
A管单独进水,6小时注满;B管单独进水,8小时注满;
C管单独出水,12小时排空。三管同时打开,几小时能注满?
请一步步推理,最后给出答案。
"""
# 调用模型,temperature 设为 0 以获得最确定的推理路径
# (CoT 推理适合用低温度,减少随机性干扰)
response = openai.chat.completions.create(
model="gpt-4", # 使用 GPT-4 进行推理
temperature=0, # 温度设为 0,输出更稳定
messages=[
{"role": "user", "content": cot_prompt} # 用户消息即上面的 Prompt
]
)
# 打印模型的推理过程
print(response.choices[0].message.content)
# 输出会包含分步推导:
# "A管每小时进 1/6,B管每小时进 1/8,C管每小时排 1/12..."
# 最终算出三管同时打开约 4.8 小时注满CoT 的适用场景
CoT 在以下任务中效果最明显:
- 数学计算与应用题——分步列式比直接算更可靠
- 逻辑推理题——如"谁说谎、谁说真话"类谜题
- 因果分析与多步推断——如"A导致B,B又影响C,最终结果是什么"
- 代码调试与算法设计——先分析问题再给方案
需要注意的是,CoT 并非万能。对于简单的事实问答("法国首都是哪里"),加不加 CoT 都一样——模型本来就知道答案,推理过程反而多余。CoT 的价值在于需要多步推理的复杂任务。
3.2.2 Few-shot:少样本提示——给模型"举几个例子"
生活中的类比
假设你在公司新入职,领导让你写周报。你问:"周报长什么样?"领导如果说"你看着写就行",你大概率会迷茫。但如果领导说"你看看上个月的周报,照着那个格式写",你立刻就有方向了。
这就是 Few-shot 的核心思想:与其费力描述你要什么,不如直接给几个例子让模型照做。人类学习新事物靠示范,大模型也一样——看到示例后,它能快速理解你期望的格式、语气和逻辑。
Zero-shot vs Few-shot
| 类型 | 做法 | 何时用 |
|---|---|---|
| Zero-shot | 只给指令,不给示例 | 任务简单、指令足够清晰时 |
| One-shot | 给 1 个示例 | 格式简单,主要需要示范输出格式 |
| Few-shot | 给 2-5 个示例 | 任务复杂或输出格式有特殊要求 |
基本用法
假设你想让模型做情感分类,把评论分为"正面/负面/中性":
请对以下评论进行情感分类。
评论:这款手机续航很棒,用了一天还有 30% 的电。
分类:正面
评论:物流太慢了,等了一周才到。
分类:负面
评论:手机壳收到了,颜色和图片一样,没有多余的感觉。
分类:中性
评论:屏幕显示效果还可以,但是拍照比预期差不少。
分类:模型看到三个例子后,会自动学习到"分类"的标签体系是"正面/负面/中性",并按照这个模式对第四条评论输出"负面"。
这就是 Few-shot 的魔力:你不需要定义什么是正面、什么是负面,模型从例子中自己归纳。
代码示例:Few-shot 做文本分类
import openai # 导入 OpenAI SDK
# 构造一个 Few-shot Prompt,包含 3 个示例
# 每个示例都是"输入→输出"对,让模型学习格式和分类逻辑
few_shot_prompt = """
请对用户评论进行情感分类(正面/负面/中性)。
评论:这款耳机音质非常好,戴着也很舒服。
分类:正面
评论:用了一周就坏了,质量太差。
分类:负面
评论:包装一般,产品功能正常,没什么特别的感觉。
分类:中性
评论:售后服务响应很快,问题很快就解决了。
分类:
"""
# 调用模型,temperature 设为 0 保证分类结果稳定一致
response = openai.chat.completions.create(
model="gpt-4", # 使用 GPT-4
temperature=0, # 分类任务用低温度,避免随机性
messages=[
{"role": "user", "content": few_shot_prompt} # 发送 Few-shot Prompt
]
)
# 输出模型的分类结果
print(response.choices[0].message.content)
# 预期输出:"正面"Few-shot 的设计要点
选例子时需要注意以下几点:
- 示例要有代表性——覆盖你要分类的各种情况(正例、反例、边界情况)
- 示例之间要有区分度——如果几个例子长得太像,模型学不到模式
- 示例数量适中——通常 2-5 个足够,太多会浪费 Token 且可能干扰
- 示例顺序会影响结果——把最典型的例子放在最后,模型对最近的内容更敏感
Few-shot + CoT 组合
当任务既需要复杂推理、又需要统一格式时,可以把 Few-shot 和 CoT 结合起来——每个示例都包含完整的推理过程:
Q:小明有 15 颗糖,给了小红 7 颗,又从妈妈那里得到 5 颗。现在有几颗?
A:原有 15 颗,给出 7 颗剩 8 颗,又得到 5 颗是 13 颗。答案:13。
Q:教室里有 24 把椅子,搬走了 6 把,又搬来 10 把。现在有几把?
A:原有 24 把,搬走 6 把剩 18 把,又搬来 10 把是 28 把。答案:28。
Q:书架上有 50 本书,借出 22 本,又买来 15 本。现在有几本?
A:模型会模仿示例的推理格式,先分步计算再给出答案。这种"带推理过程的少样本"在数学应用题、逻辑推理等任务上效果尤其好。
3.2.3 ReAct:推理+行动——让模型"边想边做"
生活中的类比
想象你在厨房里做一道没做过的菜。你不是先把所有步骤在脑子里想清楚再动手,而是:看一眼菜谱(思考)→ 拿出食材(行动)→ 发现少了一味调料(观察)→ 上网查替代品(思考)→ 去冰箱找替代调料(行动)→ 尝一下味道(观察)→ 调整盐量(行动)……思考与行动交替进行,根据每一步的反馈决定下一步。
ReAct(Reasoning + Acting)就是这个模式:让模型不是一口气推理到底,而是推理一步、执行一个动作、观察结果,再基于新信息继续推理。这使模型能够调用外部工具(搜索、计算器、数据库),弥补自身知识的不足。
ReAct 的三要素
ReAct 的每一次循环包含三个部分:
| 要素 | 含义 | 作用 |
|---|---|---|
| Thought(思考) | 模型分析当前状态,决定下一步做什么 | 推理与规划 |
| Action(行动) | 调用外部工具执行具体操作 | 获取新信息 |
| Observation(观察) | 工具返回的结果,作为下一步推理的输入 | 反馈与修正 |
这个 Thought → Action → Observation 的循环会反复进行,直到模型认为已经收集到足够信息,给出最终答案。
ReAct 的价值
为什么不能直接让模型回答?因为大模型有两个固有局限:
- 知识截止——模型的训练数据有截止日期,无法回答"今天北京天气如何"
- 计算不准——模型不擅长精确数值计算,容易在数学上出错
ReAct 通过让模型调用外部工具来解决这两个问题:用搜索引擎获取实时信息,用计算器做精确运算,用 API 查询数据库。模型负责"想"——决定什么时候用什么工具、怎么解读结果;工具负责"做"——执行确定性操作。
ReAct Prompt 示例
你是一个具备工具调用能力的助手。请使用以下格式回答问题:
Thought: 分析当前情况,思考下一步需要做什么
Action: 调用工具,格式为 工具名[参数]
Observation: 工具返回的结果
...(以上步骤可重复多次)
Thought: 我已经获得了足够的信息
Final Answer: 最终答案
可用工具:
- search[关键词]: 搜索互联网获取信息
- calculator[表达式]: 执行数学计算
- weather[城市]: 查询指定城市的天气
问题:北京和上海今天的温差是多少度?模型会按 ReAct 格式逐步推理:
Thought: 我需要分别查询北京和上海的天气,然后计算温差。
Action: weather[北京]
Observation: 北京今日气温 32°C
Thought: 已获得北京气温,接下来查询上海天气。
Action: weather[上海]
Observation: 上海今日气温 35°C
Thought: 现在我有两个城市的气温,需要计算温差 35-32。
Action: calculator[35-32]
Observation: 3
Thought: 我已经获得所有需要的信息,可以给出最终答案。
Final Answer: 北京今天 32°C,上海今天 35°C,温差为 3°C。注意整个过程中的关键点:模型不是一次性给出答案,而是先思考需要哪些信息(查天气),逐个调用工具获取结果,再用计算器算温差,最后综合给出答案。每一步都基于上一步的观察结果。
代码示例:ReAct 模式实现
# ReAct 模式的简化实现——展示 Thought/Action/Observation 循环逻辑
react_prompt = """
你是一个能调用工具的助手。请按以下格式回答:
Thought: [你的思考]
Action: [工具调用]
Observation: [工具返回结果]
(可重复多次)
Thought: [最终思考]
Final Answer: [最终答案]
可用工具:
- search[关键词]: 搜索互联网
- calculator[表达式]: 数学计算
- weather[城市]: 查询天气
"""
# 定义可用工具的映射表——工具名对应实际函数
# 当模型输出 Action: weather[北京] 时,调用对应的函数
tools = {
"search": lambda query: f"搜索结果:{query} 的相关信息...", # 模拟搜索
"calculator": lambda expr: str(eval(expr)), # 用 eval 执行数学表达式
"weather": lambda city: {"北京": "32°C", "上海": "35°C"}.get(city, "未知"), # 模拟天气查询
}
def run_react_loop(question, max_steps=5):
"""
ReAct 主循环:反复执行 Thought→Action→Observation 直到模型给出最终答案。
参数:
question: 用户问题
max_steps: 最大循环次数,防止无限循环
"""
messages = [
{"role": "system", "content": react_prompt}, # 系统消息设定 ReAct 格式
{"role": "user", "content": question} # 用户消息即问题
]
for step in range(max_steps): # 最多循环 max_steps 次
# 调用模型,获取下一步的 Thought 和 Action
response = openai.chat.completions.create(
model="gpt-4",
temperature=0, # 低温度保证推理稳定
messages=messages # 传入完整对话历史
)
reply = response.choices[0].message.content # 获取模型输出
# 检查是否已得出最终答案
if "Final Answer" in reply:
return reply # 模型认为已获得足够信息,返回最终答案
# 解析 Action,调用对应工具获取 Observation
if "Action:" in reply:
# 从回复中提取工具名和参数(简化版解析逻辑)
action_line = [l for l in reply.split("\n") if "Action:" in l][0]
action_part = action_line.split("Action:")[1].strip() # 如 "weather[北京]"
tool_name = action_part.split("[")[0].strip() # 提取工具名 "weather"
tool_arg = action_part.split("[")[1].rstrip("]") # 提取参数 "北京"
# 调用工具并获取结果
observation = tools[tool_name](tool_arg) # 执行工具调用
# 把模型输出和工具结果都加入对话历史
messages.append({"role": "assistant", "content": reply})
messages.append({"role": "user", "content": f"Observation: {observation}"})
return "达到最大步数,未能得出最终答案。"
# 运行示例
result = run_react_loop("北京和上海今天的温差是多少度?")
print(result)
# 模型会逐步调用 weather 工具查两地气温,再用 calculator 算温差
# 最终输出:"北京今天 32°C,上海今天 35°C,温差为 3°C。"这段代码展示了 ReAct 的核心逻辑:一个"推理-行动-观察"的循环。模型每轮输出 Thought 和 Action,代码解析 Action 调用对应工具,把 Observation 拼回对话历史,让模型基于新信息继续推理。这也是后续章节中 AI Agent 的基本骨架。
3.2.4 常见误区
在实际使用这三种策略时,初学者容易踩一些坑。以下是几个最常见的误区:
误区1:所有任务都用 CoT
CoT 的价值在于分步推理。但对于简单的事实问答("中国的首都是哪里?"),加"请一步步推理"只会让输出变得啰嗦,白白消耗 Token,对正确率没有任何帮助。CoT 适用于需要多步推理的复杂任务——判断标准是:这道题人也需要想几步才能做出来。
误区2:Few-shot 示例越多越好
有人觉得给 10 个、20 个示例模型会学得更好。实际上,示例太多不仅浪费 Token,还可能让模型困惑——尤其是示例之间有细微差异时。通常 3-5 个高质量示例的效果远好于 20 个平庸示例。关键是示例的质量和代表性,而非数量。
误区3:ReAct 中工具描述含糊
ReAct 效果很大程度取决于工具描述是否清晰。如果工具说明写成 search: 搜索东西,模型不知道什么情况该用、参数怎么传。好的工具描述应该是 search[关键词]: 搜索互联网获取实时信息,适合查询新闻、天气、最新事件——说清楚用途、参数格式和适用场景。
误区4:忽视 temperature 的设置
CoT 推理时 temperature 建议设 0(追求稳定正确),Few-shot 分类同理。但 Self-Consistency(多次采样投票)反而需要较高 temperature(如 0.7),才能产生有差异的推理路径。不同策略对随机性的需求不同,不能一刀切。
误区5:Prompt 写一次就不管了
高级策略往往需要迭代调试。CoT 的推理格式、Few-shot 的示例选择、ReAct 的工具描述——第一次写出来效果不好是常态。养成"写 Prompt → 测试 → 分析输出 → 调整 → 再测"的习惯,是提升效果的关键。
3.2.5 三种策略对比与选择
| 维度 | CoT | Few-shot | ReAct |
|---|---|---|---|
| 核心机制 | 让模型展示推理过程 | 给模型看几个示例 | 推理与行动交替循环 |
| 解决问题 | 直接给答案容易出错 | 模型不懂你要什么格式 | 模型知识不足或需外部信息 |
| 生活类比 | "展示解题过程" | "举几个例子" | "边想边做" |
| 适用场景 | 数学、逻辑、因果分析 | 分类、格式化输出、翻译风格 | 需搜索、计算、查API的任务 |
| 计算成本 | 低(单次调用) | 低(单次调用,Prompt略长) | 中高(多次调用+工具执行) |
| 关键技巧 | 加"请一步步推理" | 选有代表性、有区分度的示例 | 工具描述要清晰具体 |
选择策略的决策思路:
任务需要外部信息或工具?
├── 是 -> ReAct
└── 否 -> 任务需要统一输出格式?
├── 是 -> Few-shot(或 Few-shot + CoT)
└── 否 -> 任务需要多步推理?
├── 是 -> CoT
└── 否 -> 基础 Prompt 足矣三种策略并非互斥,实际项目中经常组合使用。比如用 Few-shot 给 ReAct 提供工具调用的示例,或用 Few-shot + CoT 同时解决格式和推理两个问题。根据任务特点灵活搭配,才是高级 Prompt 工程的核心。
3.2.6 本节小结
- CoT(思维链):通过"请一步步推理"触发,让模型展示推理过程而非直接给答案,适用于数学计算、逻辑推理等需要多步思考的任务。简单加一句话就能显著提升复杂任务的正确率。
- Few-shot(少样本提示):在 Prompt 中提供 2-5 个输入-输出示例,让模型从例子中归纳格式和规则,适用于分类、格式化输出等需要统一标准的任务。关键是示例的代表性和区分度。
- ReAct(推理+行动):让模型按 Thought → Action → Observation 循环工作,边推理边调用外部工具获取信息,适用于需要实时数据、精确计算或外部 API 的任务。ReAct 是现代 AI Agent 的核心推理模式。
这三种策略构成了高级 Prompt 工程的基础工具箱。CoT 解决"想得对",Few-shot 解决"做得对",ReAct 解决"知道得够"。在实际项目中,根据任务特点选择或组合使用,是提升大模型应用效果的关键技能。
上一节我们学习了 Prompt 的基本要素和写作原则,本节进一步掌握了 CoT、Few-shot、ReAct 三种高级策略。然而,无论是基础 Prompt 还是高级策略,它们都写在"用户消息"里。在一个完整的大模型应用中,还有一类特殊的 Prompt——System Prompt(系统提示词),它在整个对话中始终生效,定义了模型的角色、边界和行为规范。下一节(3.3)我们将专门讨论 System Prompt 的设计方法,看看如何用它为模型设定"人格"和"底线"。