Chapter 05

推理提示工程:激发高质量思维链

推理模型的提示工程与普通模型截然不同。很多普通模型的"最佳实践"在推理模型上适得其反。本章系统梳理差异与最佳实践。

核心差异:推理模型不需要 CoT 触发词

普通模型需要"Let's think step by step"来激活推理;对于已经内置了推理能力的模型(o1、Claude Extended Thinking、DeepSeek-R1),这类触发词不仅无用,有时还会干扰模型的内部推理过程,让模型把思维链输出到 text block 而非内部 thinking 中,降低推理质量。

普通 LLM 提示最佳实践

  • 加入 CoT 触发词("step by step")
  • 提供 Few-shot 推理示例
  • 详细指定推理步骤格式
  • 长而详细的 System Prompt 有帮助
  • 用 XML 标签指导输出结构
  • 明确说明"先思考再回答"

推理模型提示最佳实践

  • 不需要 CoT 触发词(已内置)
  • Few-shot 效果有限,甚至会限制推理路径
  • 不要规定推理步骤的格式
  • 简洁的 System Prompt 更好
  • 只约束最终输出格式,不约束思考过程
  • 提供问题背景和约束,让模型自己决定怎么推理

System Prompt 设计准则

推理模型的 System Prompt 要提供背景约束,而不是指导推理过程。以下是正确与错误的对比:

import anthropic

client = anthropic.Anthropic()

# ❌ 错误:告诉模型如何推理(会干扰内部思维链)
bad_system = """你是一个数学助手。
解题时请按以下步骤:
1. 首先分析题目类型
2. 选择合适的方法
3. 逐步计算,每步注明理由
4. 最后验证答案
请一步步思考后给出答案,用 <answer> 标签包裹最终答案。"""
# 问题:指定了推理格式,模型会在 text block 里写推理过程,
#        而不是放在 thinking block,导致输出冗长且推理质量下降

# ✅ 正确:只提供背景和输出约束
good_system = """你是一个数学助手,专注于高中数学竞赛题。
回答时,最终答案用 \\boxed{} 格式表示。"""
# 优点:简洁,只约束输出格式,让模型自由推理

# ✅ 正确(多领域场景):提供背景但不约束推理方式
expert_system = """你是一位资深软件架构师,有 15 年分布式系统经验。
请基于以下约束给出建议:
- 团队规模:5人
- 月预算:5000 RMB(云服务费)
- 要求:99.9% 可用性
回答格式:JSON,包含 recommendation、rationale、risks 三个字段。"""

response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=12000,
    thinking={"type": "enabled", "budget_tokens": 6000},
    system=good_system,  # 简洁的系统提示
    messages=[{"role": "user", "content": "解方程:x² - 7x + 12 = 0"}]
)

约束输出格式而不限制推理

这是推理模型提示工程的核心原则:自由思考,约束输出。在 thinking block 中,模型可以随意探索;在 text block 中,输出必须符合指定格式。

import json

# 场景:代码审查,需要结构化 JSON 输出
system_code_review = """你是一个代码审查专家。
审查完成后,只输出以下 JSON 格式(不要其他内容):
{
  "bugs": [{"line": 数字, "severity": "high/medium/low", "description": "问题描述"}],
  "suggestions": ["优化建议1", "优化建议2"],
  "overall_score": 0-10,
  "summary": "一句话总结"
}"""

def review_code(code: str) -> dict:
    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=10000,
        thinking={"type": "enabled", "budget_tokens": 6000},
        system=system_code_review,
        messages=[{"role": "user",
                   "content": f"请审查以下代码:\n```python\n{code}\n```"}]
    )
    # 模型会在 thinking 中自由分析,在 text 中输出符合格式的 JSON
    text = next(b.text for b in response.content if b.type == "text")
    return json.loads(text)

# 场景:决策分析,需要结构化输出
def analyze_decision(scenario: str, options: list[str]) -> dict:
    system = """你是战略顾问。分析完成后输出 JSON:
{"recommended": "选项名", "scores": {"选项名": 分数}, "key_factors": ["因素1"], "risks": ["风险1"]}"""

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=10000,
        thinking={"type": "enabled", "budget_tokens": 5000},
        system=system,
        messages=[{"role": "user",
                   "content": f"场景:{scenario}\n选项:{options}"}]
    )
    text = next(b.text for b in response.content if b.type == "text")
    return json.loads(text)

MCTS 在推理中的应用:Test-Time Search

Monte Carlo Tree Search(蒙特卡洛树搜索)是一种用于解决复杂搜索问题的算法,最初用于围棋 AI(AlphaGo)。2024-2025 年,研究者将 MCTS 应用于推理模型,实现了更强大的 Test-Time Compute 扩展。

MCTS 的四步骤
选择(Selection):从根节点出发,按策略选择最有价值的子节点;扩展(Expansion):在叶节点生成新的候选推理步骤;模拟(Simulation/Rollout):从新节点快速运行到终态,评估价值;反向传播(Backpropagation):将终态价值更新到路径上所有节点。
MCTS vs Tree-of-Thought
ToT 是固定深度的贪心搜索(每层只保留 top-k);MCTS 是动态的自适应搜索——在有希望的节点上花更多时间,在无望的节点上及时剪枝。MCTS 在需要长期规划的任务(定理证明、代码生成)上效果更好。
过程奖励模型(Process Reward Model, PRM)
为 MCTS 提供中间步骤评分的模型。与结果奖励模型(ORM)不同,PRM 能评估每个推理步骤的质量(不需要等到最终答案)。PRM 是将 MCTS 应用于 LLM 推理的关键组件,但训练 PRM 需要人工标注步骤级别的质量分数,成本较高。
import math
import random
from dataclasses import dataclass, field
from typing import List, Optional

@dataclass
class MCTSNode:
    """MCTS 搜索树节点"""
    state: str              # 当前推理状态(已有的思维链)
    parent: Optional['MCTSNode'] = None
    children: List['MCTSNode'] = field(default_factory=list)
    visits: int = 0          # 该节点被访问的次数
    value: float = 0.0       # 累积价值

    def ucb_score(self, c: float = 1.4) -> float:
        """UCB1 分数:平衡开发(exploitation)与探索(exploration)"""
        if self.visits == 0:
            return float('inf')  # 未访问节点优先探索
        exploit = self.value / self.visits  # 平均价值(开发)
        explore = c * math.sqrt(math.log(self.parent.visits) / self.visits)  # 探索奖励
        return exploit + explore

def mcts_reasoning(problem: str, n_simulations: int = 20) -> str:
    """
    MCTS 推理搜索(简化版,展示算法结构)
    在生产环境中,通常使用专门的推理框架(如 OpenR)
    """
    root = MCTSNode(state=f"问题:{problem}\n推理:")

    for _ in range(n_simulations):
        # 1. 选择:从根到叶,选择 UCB 分数最高的节点
        node = root
        while node.children:
            node = max(node.children, key=lambda n: n.ucb_score())

        # 2. 扩展:生成新的推理步骤(通过 LLM 采样)
        next_steps = generate_reasoning_steps(node.state, n=3)
        for step in next_steps:
            child = MCTSNode(state=node.state + step, parent=node)
            node.children.append(child)

        # 3. 模拟:从新节点快速生成完整答案并评分
        selected = random.choice(node.children)
        final_answer = complete_reasoning(selected.state)
        reward = evaluate_answer(problem, final_answer)  # PRM 或结果验证

        # 4. 反向传播:更新路径上所有节点的价值
        current = selected
        while current is not None:
            current.visits += 1
            current.value += reward
            current = current.parent

    # 返回访问次数最多的路径(最可靠的推理路径)
    best_child = max(root.children, key=lambda n: n.visits)
    return complete_reasoning(best_child.state)

难题分解:DECOMP 策略

对于极度复杂的问题,即使推理模型有时也需要帮助拆解。DECOMP 策略(先分解,再各个击破)可以将超出模型单次推理能力的问题变为可处理的:

def decomp_solve(complex_problem: str) -> str:
    """DECOMP 策略:分解 → 逐步解决 → 综合"""

    # Step 1: 用快速模型分解问题(不需要推理模型)
    decompose_response = client.messages.create(
        model="claude-haiku-4-5-20251001",  # 用便宜的快速模型分解
        max_tokens=1024,
        messages=[{"role": "user", "content":
            f"""将以下复杂问题分解为 3-5 个必须按序解决的子问题。
每个子问题必须比原问题简单。用编号列表格式输出。

复杂问题:{complex_problem}

子问题列表:"""}]
    )
    # 解析子问题列表
    lines = decompose_response.content[0].text.strip().split('\n')
    sub_problems = [
        line.strip().lstrip("0123456789. ))")
        for line in lines if line.strip()
    ]

    # Step 2: 用推理模型逐个解决子问题(每步都参考前面的结果)
    sub_solutions = []
    for i, sp in enumerate(sub_problems):
        context = "\n".join([f"子问题{j+1}解答:{s}" for j, s in enumerate(sub_solutions)])
        sol_response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=8000,
            thinking={"type": "enabled", "budget_tokens": 4000},
            messages=[{"role": "user", "content":
                f"""原始大问题:{complex_problem}

{f"已解决的前置子问题:{chr(10)}{context}{chr(10)}" if context else ""}
请解决子问题 {i+1}:{sp}"""}]
        )
        answer = next(b.text for b in sol_response.content if b.type == "text")
        sub_solutions.append(answer)

    # Step 3: 综合所有子答案
    final_response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=4000,
        messages=[{"role": "user", "content":
            f"""原始问题:{complex_problem}

子问题及解答:
{chr(10).join([f"{i+1}. {sp}:{sol}" for i, (sp, sol) in enumerate(zip(sub_problems, sub_solutions))])}

请基于以上分析,给出原始问题的完整最终答案:"""}]
    )
    return final_response.content[0].text

避免 Over-thinking 的实用技巧

问题简单时降低或禁用 budget_tokens
对于简单查询,设置 budget_tokens=1000 甚至禁用(type: "disabled"),防止模型"想太多"。过度思考不仅浪费 token,有时还会让模型把简单问题复杂化("Analysis Paralysis")。
明确说明"不需要深度分析"
对于需要快速检索类的问题,可以在提示中加入"请直接回答,不需要详细分析",引导模型快速给出答案。推理模型会将这类限制性指令内化到思维链中,主动缩短推理。
用难度分级路由(Routing)
通过关键词或小模型对问题进行难度预估,简单问题(<10词,无数学符号,无代码)用普通模型,复杂问题才启用推理模式。这是生产环境中成本控制的核心策略,下一章会详细介绍。
Reflexion:让模型反思自己的答案
Reflexion(Shinn et al. 2023)是让模型对自己的答案进行批评和反思的框架:先生成答案 → 生成自我批评 → 基于批评修正答案。这种"先回答,再反思"的模式比"一次性推理"更稳健,特别适合复杂推理任务。
def reflexion_solve(problem: str, max_reflections: int = 2) -> dict:
    """
    Reflexion 框架:推理 → 自我批评 → 修正
    即使不使用扩展思考,也能通过反思提升质量
    """
    answer = ""

    for i in range(max_reflections + 1):
        # 阶段 1:生成(或修正)答案
        generate_prompt = problem if i == 0 else (
            f"{problem}\n\n之前的答案:{answer}\n"
            f"已识别的问题:{reflection}\n\n请修正答案:"
        )
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=8000,
            thinking={"type": "enabled", "budget_tokens": 4000},
            messages=[{"role": "user", "content": generate_prompt}]
        )
        answer = next(b.text for b in response.content if b.type == "text")

        if i < max_reflections:
            # 阶段 2:自我批评(无需推理模型,用小模型即可)
            reflect_response = client.messages.create(
                model="claude-haiku-4-5-20251001",
                max_tokens=512,
                messages=[{"role": "user", "content":
                    f"问题:{problem}\n\n回答:{answer}\n\n请指出这个回答的具体错误或不足(如果有的话):"}]
            )
            reflection = reflect_response.content[0].text
            # 如果批评说"没有问题",提前结束
            if "没有" in reflection or "正确" in reflection:
                break

    return {"answer": answer, "reflections_used": i}
推理模型提示工程的高频错误

在 System Prompt 中过度约束推理过程:写"请先分析问题,然后分步骤推导,最后给出答案"——这反而干扰了模型的内在推理机制。推理模型的 thinking block 有自己的思考节奏,人为指定步骤格式会降低质量。System Prompt 只需提供必要背景(领域、角色、输出格式),不应指挥推理节奏。

把 CoT 触发词("让我们一步一步思考")带入推理模型:这些 Few-shot CoT 技巧是为普通 LLM 设计的,推理模型的扩展思考已经内置了这个能力。加入这些词不会提升质量,反而可能干扰模型的推理路径。

MCTS 的成本失控:MCTS 的计算成本与树的深度和宽度呈指数关系。深度 4、宽度 4 意味着潜在 256 次 LLM 调用。生产环境中使用 MCTS 必须设置严格的节点预算上限和提前终止条件,否则单次查询成本可能超过 $10。

本章小结

推理模型的提示工程核心原则:简洁的 System Prompt(提供背景,不指导推理)+ 只约束最终输出格式(不限制思考过程)+ 不需要 CoT 触发词。MCTS 提供了更强大的 Test-Time Search 框架,但成本较高。DECOMP 策略将超大问题拆解为可管理的子问题。Reflexion 框架通过自我批评迭代提升答案质量。下一章将推理模型集成到 Agent 架构中——Plan-and-Execute 是利用推理模型规划能力的最佳方式。