核心差异:推理模型不需要 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 扩展。
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 的实用技巧
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 是利用推理模型规划能力的最佳方式。