少样本与思维链:让模型学会思考 本节摘要:告诉模型「做什么」是提示,告诉它「怎么想」才是工程。同一个模型、同一道题、同一份数据,准确率从 78% 跳到 91% 的差距,不来自更好的模型,而来自更好的推理策略。本节带你吃透 LLM 推理范式的完整谱系:少样本(Few-Shot)为什么用例子比写指令更管用、思维链(Chain-of-Thought,CoT)为什么「先写草稿再答题」能多拿 1040 分、自洽性(Self-Consistency)如何用多次采样投票抵消单链误差、思维树(Tree-of-Thought,ToT)如何分叉探索并剪枝,以及把推理与工具调用交织的 ReAct 范式。读完本节,你能为数学题、规划类问题搭出一条「先便宜试一次,不行再升级」的推理管线。
本节摘要:告诉模型「做什么」是提示,告诉它「怎么想」才是工程。同一个模型、同一道题、同一份数据,准确率从 78% 跳到 91% 的差距,不来自更好的模型,而来自更好的推理策略。本节带你吃透 LLM 推理范式的完整谱系:少样本(Few-Shot)为什么用例子比写指令更管用、思维链(Chain-of-Thought,CoT)为什么「先写草稿再答题」能多拿 10~40 分、自洽性(Self-Consistency)如何用多次采样投票抵消单链误差、思维树(Tree-of-Thought,ToT)如何分叉探索并剪枝,以及把推理与工具调用交织的 ReAct 范式。读完本节,你能为数学题、规划类问题搭出一条「先便宜试一次,不行再升级」的推理管线。
对应原课程:Phase 11 · Lesson 02 ·
few-shot-cot(原英文phases/11-llm-engineering/02-few-shot-cot/docs/en.md)。
阅读完本节,你应当能够:
你做了一个数学辅导应用,提示就一句话:「解这道应用题」。GPT-5 在 GSM8K(标准小学数学基准,约 8500 道题)上已经能答对 94%,你以为到顶了——其实没有,思维链还能再加 3~4 个点。
只多加五个字「让我们一步步思考」,准确率跳到 91%;再加几道带解答过程的范例,冲到 95%。同一个模型、同一个温度、同一份 API 开销,差别只在于你给了模型一张草稿纸。
这不是花招,这是推理的运作方式。人类做不出「一步登天」的多步心算,Transformer 也不行。当你强迫模型先生成中间 token,这些 token 就成了下一个 token 的上下文——每一步推理都在喂养下一步,模型是真的一步步算到答案的。
「一步步思考」只是起点,不是终点。如果采样 5 条推理路径再多数投票会怎样?如果让模型探索一棵「可能性树」、给每个分支打分再剪枝呢?如果把推理与工具调用交织起来呢?这些都是有论文、有实测提升的真实技术,本节全部从零实现。
零样本(Zero-Shot)只给任务不给示例,少样本(Few-Shot)先给若干输入输出示例。Wei 等人(2022)在 8 个基准上测过:情感分类这种简单任务,两者差距在 2% 以内;多步算术与符号推理这类复杂任务,少样本能提升 10~25%。
直觉是:示例是被压缩的指令。与其描述输出格式,不如直接展示;与其解释推理过程,不如直接演示。模型对示例的模式匹配,比解读抽象指令更可靠。
少样本占优的场景:对格式敏感的任务、分类、结构化抽取、领域行话、任何需要模型套用特定模式的任务。
零样本占优的场景:简单事实问答、示例会束缚创意的创作类任务、找好示例比写好指令更难的任务。
示例并不平等。Liu 等人(2022)发现:挑选与目标输入相似的示例,在分类任务上比随机选高出 5~15%。三条原则:
多数任务的最优示例数是 3~5 个。少于 3 个,模型信号不足;超过 5 个,边际收益递减且浪费上下文 token。对多标签分类,每类一个示例即可。
思维链由 Wei 等人(2022)在 Google Brain 提出。核心很简单:不只问模型要答案,先让它写出推理步骤。
机械层面为什么有效?Transformer 每生成一个 token,它就成为下一个 token 的上下文。没有 CoT 时,模型必须把全部推理压进单次前向传播的隐藏状态里;有了 CoT,模型把中间计算外化为 token,每一步推理都在扩展有效计算深度。
GSM8K 基准(小学数学,约 8500 题)实测:
| 模型 | 零样本 | 零样本 CoT | 少样本 CoT |
|---|---|---|---|
| GPT-4o | 78% | 91% | 95% |
| GPT-5 | 94% | 97% | 98% |
| o4-mini(推理模型) | 97% | — | — |
| Claude Opus 4.7 | 93% | 97% | 98% |
| Gemini 3 Pro | 92% | 96% | 98% |
| Llama 4 70B | 80% | 89% | 94% |
| DeepSeek-V3.1 | 89% | 94% | 96% |
⚠️ 推理模型的注意事项:OpenAI 的 o 系列(o3、o4-mini)和 DeepSeek-R1 这类模型,在给出答案前内部已经跑过思维链。给推理模型加「让我们一步步思考」是多余的,有时反而起反作用——它早就做过了。
思维链有两种风味:
零样本 CoT:在提示末尾追加「让我们一步步思考」,无需示例。Kojima 等人(2022)证明这一句话在算术、常识、符号推理任务上都能稳定提分。
少样本 CoT:提供带推理步骤的示例,比零样本 CoT 更有效,因为模型看到了你期望的精确推理格式。
CoT 反而有害的场景:简单事实回忆(「法国首都是哪?」)、单步分类、速度比准确率更重要的任务。CoT 每次查询多花 50~200 个推理 token,高吞吐低复杂度的任务上这是纯浪费。
Wang 等人(2023)提出自洽性。洞见是:单条 CoT 链可能含推理错误;但如果用 temperature > 0 采样 N 条独立推理路径,再对最终答案做多数投票,误差会相互抵消。
自洽性把 GSM8K 准确率从单链的 56.5% 提到 N=40 时的 74.4%(原始 PaLM 540B 实验)。在 GPT-5 上提升很小(97%→98%),因为基线已经饱和。它最亮的场景是基线 CoT 准确率在 60~85% 的模型——单链错误频繁但非系统性的甜区。推理模型(o 系列、R1)的内置采样已涵盖此机制。
代价是 N 次采样意味着 N 倍 API 成本与延迟。实践中 N=5 能吃下大部分收益,N=3 是有意义投票的下限,N>10 对多数任务边际收益已微乎其微。
Yao 等人(2023)提出思维树。CoT 沿一条线性路径走,ToT 则分叉多条分支,在继续前先评估哪条最有希望。
ToT 有三个组件:
在「24 点游戏」(用 4 个数字做算术凑出 24)上,GPT-4 用标准提示只解出 7.3% 的题,用 CoT 只有 4.0%(这里 CoT 反而更差,因为搜索空间太大),用 ToT 高达 74%。
ToT 很贵:树里每个节点都要一次 LLM 调用。分支因子 3、深度 3 的树最多要 39 次调用。只用于搜索空间大但可评估的问题——规划、解谜、带约束的创造性解题。
Yao 等人(2022)把推理轨迹与动作结合起来。模型在「思考」(生成推理)与「行动」(调用工具、搜索、计算)之间交替。
ReAct 在知识密集型任务上胜过纯 CoT,因为它能把推理锚定到真实数据。在 HotpotQA(多跳问答)上,ReAct + GPT-4 达到 35.1% 精确匹配,而纯 CoT 只有 29.4%。真正的威力在于:推理错误会被观察纠正——模型能在执行中途更新计划。
ReAct 是现代 AI Agent 的基石。每个 Agent 框架(LangChain、CrewAI、AutoGen)都实现了某种「思考-行动-观察」循环的变体。完整的 Agent 我们放到后续章节,本节只讲提示范式本身。
提示变复杂后,结构能防止模型把各段搞混。三种做法:
XML 标签(Claude 上效果最好,各家都还行):
<context> 你正在审阅一个 pull request。 代码库用 TypeScript 和 React。 </context> <task> 审阅下面的 diff,找出 bug、安全问题、风格违规。 </task> <diff> {diff 内容} </diff> <output_format> 每个问题列出:文件、行号、严重度(致命/警告/提示)、描述。 </output_format>
Markdown 标题(通用):
## 角色 某金融科技公司的资深安全工程师。 ## 任务 分析这个 API 端点的漏洞。 ## 输入 {api 代码} ## 规则 - 聚焦 OWASP Top 10 - 每个发现评级:致命、高、中、低 - 附上修复步骤
分隔符(极简但有效):
---输入--- {用户文本} ---输入结束--- ---指令--- 把上面内容总结成 3 个要点。 ---指令结束---
有些任务单条提示搞不定。提示链把它拆成多步,前一步的输出成为下一步的输入。
提示链胜过单提示有三个原因:
| 技术 | 最适合 | GSM8K 准确率(GPT-5) | API 调用 | token 开销 | 复杂度 |
|---|---|---|---|---|---|
| 零样本 | 简单任务 | 94% | 1 | 无 | 极低 |
| 少样本 | 格式匹配 | 96% | 1 | 200~500 token | 低 |
| 零样本 CoT | 快速推理增益 | 97% | 1 | 50~200 token | 极低 |
| 少样本 CoT | 单次调用最大准确率 | 98% | 1 | 300~600 token | 低 |
| 自洽性(N=5) | 高风险推理 | 98.5% | 5 | 5 倍 token | 中 |
| 推理模型(o4-mini) | CoT 即插即用替代 | 97% | 1 | 隐藏(内部 2~10 倍) | 极低 |
| 思维树 | 搜索/规划问题 | 不适用(24 点 74%) | 10~40+ | 10~40 倍 token | 高 |
| ReAct | 知识接地推理 | 不适用(HotpotQA 35.1%) | 3~10+ | 可变 | 高 |
| 提示链 | 复杂多步任务 | 96%(管线) | 2~5 | 2~5 倍 token | 中 |
选哪种取决于三个因素:准确率要求、延迟预算、成本容忍度。多数生产系统用少样本 CoT + 3 样本自洽性兜底就能覆盖 90% 的场景。
我们要做一个数学题求解器,把少样本、思维链、自洽性投票揉进同一条管线,再为硬题加上思维树。完整代码见原课程 code/advanced_prompting.py,这里给出关键骨架。
第一个组件管理少样本示例,并为给定问题挑出最相关的。
GSM8K_EXAMPLES = [ { "question": "珍妮特的鸭子每天下 16 个蛋。她每天早饭吃 3 个,烤松饼用 4 个。剩下每个蛋在农贸市场卖 2 美元。她每天在市场赚多少?", "reasoning": "鸭子每天下 16 个蛋。吃 3 个、烤 4 个,用掉 3 + 4 = 7 个。剩下 16 - 7 = 9 个。每个卖 2 美元,所以每天赚 9 × 2 = 18 美元。", "answer": "18" }, # ... 其余示例同理 ]
每个示例有三部分:问题、推理链、最终答案。正是那条推理链,把普通少样本示例变成了 CoT 少样本示例。
把系统消息、带推理链的少样本示例、目标问题拼成一条提示。
def build_cot_prompt(question, examples, num_examples=3): system = ( "你是一个数学题求解器。" "对每道题,先展示逐步推理,再在最后一行" "用『答案是 [数字]』的格式给出最终数值答案。" ) example_text = "" for ex in examples[:num_examples]: example_text += f"问: {ex['question']}\n" example_text += f"答: {ex['reasoning']} 答案是 {ex['answer']}。\n\n" user = f"{example_text}问: {question}\n答:" return system, user
格式约束「答案是 [数字]」至关重要——没有它,自洽性无法跨样本抽取并比较答案。
采样 N 条推理路径,取多数答案。
def self_consistency_solve(question, examples, client, model, n_samples=5): system, user = build_cot_prompt(question, examples) answers, reasonings = [], [] for _ in range(n_samples): response = client.chat.completions.create( model=model, messages=[{"role": "system", "content": system}, {"role": "user", "content": user}], temperature=0.7 ) text = response.choices[0].message.content reasonings.append(text) answer = extract_answer(text) if answer is not None: answers.append(answer) vote_counts = Counter(answers) best = vote_counts.most_common(1)[0][0] if vote_counts else None confidence = vote_counts[best] / len(answers) if best else 0 return best, confidence, reasonings, vote_counts
temperature=0.7 很关键。温度为 0 时,N 条样本会完全相同,自洽性失去意义。你需要足够的随机性产生多样的推理路径,但又不能乱到模型胡言乱语。
线性推理失败的问题上,ToT 探索多种思路,评估哪个方向最有希望。
def tree_of_thought_solve(question, client, model, breadth=3, depth=3): thoughts = generate_initial_thoughts(question, client, model, breadth) scored = [(t, evaluate_thought(t, question, client, model)) for t in thoughts] scored.sort(key=lambda x: x[1], reverse=True) for _ in range(1, depth): next_thoughts = [] for thought, _ in scored[:2]: # 只扩展最优的两条 for ext in extend_thought(thought, question, client, model, breadth): next_thoughts.append((ext, evaluate_thought(ext, question, client, model))) scored = sorted(next_thoughts, key=lambda x: x[1], reverse=True) best_thought = scored[0][0] if scored else "" return extract_answer(best_thought), best_thought
评估器本身是一次 LLM 调用:你问模型「在 0.0 到 1.0 之间,这条推理路径解题的希望有多大?」。这正是 ToT 的关键洞见——模型评估自己的部分解。
把所有技术揉进一条带升级策略的管线。
def solve_with_escalation(question, examples, client, model): system, user = build_cot_prompt(question, examples) single = call_llm(client, model, system, user, temperature=0.0) sc_answer, confidence, _, _ = self_consistency_solve( question, examples, client, model, n_samples=5) if confidence >= 0.8: return sc_answer, "self_consistency", confidence tot_answer, _ = tree_of_thought_solve(question, client, model) return tot_answer, "tree_of_thought", None
升级逻辑是:先用便宜的(单次 CoT),若自洽性置信度低于 0.8(5 票中不到 4 票一致),再升级到 ToT。这样平衡了成本与准确率——多数问题便宜解决,难题才动用更多算力。
LangChain 内置了提示模板与输出解析,简化少样本与 CoT:
# from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate # from langchain_openai import ChatOpenAI # example_prompt = PromptTemplate( # input_variables=["question","reasoning","answer"], # template="问: {question}\n答: {reasoning} 答案是 {answer}。") # few_shot_prompt = FewShotPromptTemplate( # examples=examples, example_prompt=example_prompt, # suffix="问: {input}\n答: 让我们一步步思考。", # input_variables=["input"]) # llm = ChatOpenAI(model="gpt-4o", temperature=0.7) # chain = few_shot_prompt | llm # result = chain.invoke({"input": "一列火车 2 小时行驶 120 公里..."})
LangChain 还有 ExampleSelector 类做语义相似度选择:
# from langchain_core.example_selectors import SemanticSimilarityExampleSelector # from langchain_openai import OpenAIEmbeddings # selector = SemanticSimilarityExampleSelector.from_examples(examples, OpenAIEmbeddings(), k=3)
DSPy 把提示策略当作可优化的模块。与其手搓 CoT 提示,你定义一个签名,让 DSPy 优化:
# import dspy # dspy.configure(lm=dspy.LM("openai/gpt-4o", temperature=0.7)) # class MathSolver(dspy.Module): # def __init__(self): # self.solve = dspy.ChainOfThought("question -> answer") # def forward(self, question): # return self.solve(question=question) # solver = MathSolver() # result = solver(question="珍妮特的鸭子每天下 16 个蛋...")
DSPy 的 ChainOfThought 自动加推理轨迹;dspy.majority 实现自洽性:
# result = dspy.majority([solver(question=q) for _ in range(5)], field="answer")
| 特性 | 从零(本节) | LangChain | DSPy |
|---|---|---|---|
| 提示格式控制 | 完全 | 基于模板 | 自动 |
| 自洽性 | 手动投票 | 手动 | 内置(dspy.majority) |
| 示例选择 | 自定义逻辑 | ExampleSelector |
dspy.BootstrapFewShot |
| 思维树 | 自定义树搜索 | 社区链 | 非内置 |
| 提示优化 | 手动迭代 | 手动 | 自动编译 |
| 最适合 | 学习、自定义管线 | 标准工作流 | 研究、优化 |
本节产出两个可复用文件(位于原课程 outputs/):
prompt-reasoning-chain.md:一个生产级提示模板,带自洽性的少样本 CoT。填入你的示例与问题域即可用。skill-cot-patterns.md:一个决策框架,按任务类型、准确率要求、成本约束选择合适的推理技术。Python 代码(code/advanced_prompting.py)是一条独立推理管线,把 call_llm 换成真实 API 即可投产。
测出差距:取 10 道 GSM8K 题,分别用零样本、少样本、零样本 CoT、少样本 CoT 求解,记录各方法准确率。哪个技术在你的模型上提升最大?
示例选择实验:同一批 10 题,对比随机选示例 vs 手挑相似示例,测量准确率差。在什么点上,示例质量开始比数量更重要?
自洽性成本曲线:在 20 道 GSM8K 题上跑 N=1、3、5、7、10 的自洽性,画出准确率 vs 成本(总 token)曲线,找出你模型的拐点。
搭一个 ReAct 循环:给管线加一个计算器工具。当模型生成数学表达式时,用 Python 的 eval()(在沙箱里)执行并把结果喂回去,测量工具接地的推理是否胜过纯 CoT。
ToT 用在创作上:把思维树求解器改造成创作任务——「写一个既好笑又悲伤的六字故事」,用 LLM 当评委。分叉探索是否比单次生成产出更好的创意?
下一节,我们将进入「结构化输出」——如何用 JSON Schema、约束解码让模型产出机器可解析的确定格式,这是函数调用与 Agent 工具链的地基。