Skip to content

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 末尾加一句话:

text
一个商店原有苹果 48 个,上午卖出 12 个,下午又进了 20 个,
傍晚卖出 15 个。现在商店还有多少个苹果?

请一步步推理,最后给出答案。

这一句"请一步步推理",就是触发 CoT 的开关。模型会输出类似这样的内容:

text
步骤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 示例

text
Q:小明有 15 颗糖,给了小红 7 颗,又从妈妈那里得到 5 颗。小明现在有几颗糖?
A:小明原有 15 颗,给出 7 颗后剩 15-7=8 颗,又得到 5 颗后是 8+5=13 颗。
   答案:13

Q:图书馆原有图书 320 本,上周借出 85 本,本周归还 40 本又新购 60 本。
   现在有多少本?
A:

模型看到上面的示例后,会模仿同样的推理格式来回答新问题。这就是"举一个例子,让模型照着做"。

代码示例:用 CoT 解数学题

python
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 个示例任务复杂或输出格式有特殊要求

基本用法

假设你想让模型做情感分类,把评论分为"正面/负面/中性":

text
请对以下评论进行情感分类。

评论:这款手机续航很棒,用了一天还有 30% 的电。
分类:正面

评论:物流太慢了,等了一周才到。
分类:负面

评论:手机壳收到了,颜色和图片一样,没有多余的感觉。
分类:中性

评论:屏幕显示效果还可以,但是拍照比预期差不少。
分类:

模型看到三个例子后,会自动学习到"分类"的标签体系是"正面/负面/中性",并按照这个模式对第四条评论输出"负面"。

这就是 Few-shot 的魔力:你不需要定义什么是正面、什么是负面,模型从例子中自己归纳

代码示例:Few-shot 做文本分类

python
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 的设计要点

选例子时需要注意以下几点:

  1. 示例要有代表性——覆盖你要分类的各种情况(正例、反例、边界情况)
  2. 示例之间要有区分度——如果几个例子长得太像,模型学不到模式
  3. 示例数量适中——通常 2-5 个足够,太多会浪费 Token 且可能干扰
  4. 示例顺序会影响结果——把最典型的例子放在最后,模型对最近的内容更敏感

Few-shot + CoT 组合

当任务既需要复杂推理、又需要统一格式时,可以把 Few-shot 和 CoT 结合起来——每个示例都包含完整的推理过程:

text
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 的价值

为什么不能直接让模型回答?因为大模型有两个固有局限:

  1. 知识截止——模型的训练数据有截止日期,无法回答"今天北京天气如何"
  2. 计算不准——模型不擅长精确数值计算,容易在数学上出错

ReAct 通过让模型调用外部工具来解决这两个问题:用搜索引擎获取实时信息,用计算器做精确运算,用 API 查询数据库。模型负责"想"——决定什么时候用什么工具、怎么解读结果;工具负责"做"——执行确定性操作。

ReAct Prompt 示例

text
你是一个具备工具调用能力的助手。请使用以下格式回答问题:

Thought: 分析当前情况,思考下一步需要做什么
Action: 调用工具,格式为 工具名[参数]
Observation: 工具返回的结果
...(以上步骤可重复多次)
Thought: 我已经获得了足够的信息
Final Answer: 最终答案

可用工具:
- search[关键词]: 搜索互联网获取信息
- calculator[表达式]: 执行数学计算
- weather[城市]: 查询指定城市的天气

问题:北京和上海今天的温差是多少度?

模型会按 ReAct 格式逐步推理:

text
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 模式实现

python
# 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 三种策略对比与选择

维度CoTFew-shotReAct
核心机制让模型展示推理过程给模型看几个示例推理与行动交替循环
解决问题直接给答案容易出错模型不懂你要什么格式模型知识不足或需外部信息
生活类比"展示解题过程""举几个例子""边想边做"
适用场景数学、逻辑、因果分析分类、格式化输出、翻译风格需搜索、计算、查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 的设计方法,看看如何用它为模型设定"人格"和"底线"。