核心矛盾
推理模型的困境:
- 启用 Extended Thinking 后,Claude 的
content里有thinkingblock 和textblock - 如果要求模型直接输出 JSON,它会尝试在
textblock 里输出 JSON,但思维链可能干扰格式 - OpenAI o1 系列暂不支持 response_format: json_object 和 json_schema
解耦方案:Two-Stage 架构
Stage 1: 推理阶段(Extended Thinking)
输入: 原始问题
模型: claude-sonnet-4-6 + thinking enabled
输出: thinking block(推理过程)+ text block(自由文本分析)
Stage 2: 结构化提取阶段(不用推理模型)
输入: Stage 1 的 text block
模型: claude-haiku-4-5-20251001(快速便宜)
约束: response_format 强制 JSON
输出: 标准 JSON 结构
import anthropic
from pydantic import BaseModel
from typing import List
client = anthropic.Anthropic()
class CodeReviewResult(BaseModel):
bugs: List[dict]
suggestions: List[str]
overall_score: int
summary: str
def reasoning_then_extract(code: str) -> CodeReviewResult:
# Stage 1: 深度推理分析
reasoning_response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=8000,
thinking={"type": "enabled", "budget_tokens": 5000},
messages=[{"role": "user",
"content": f"请深度审查以下代码,找出所有 bug 和改进点:\n```\n{code}\n```"}]
)
analysis_text = next(b.text for b in reasoning_response.content if b.type == "text")
# Stage 2: 结构化提取(用轻量模型)
extract_response = client.messages.create(
model="claude-haiku-4-5-20251001",
max_tokens=2000,
system="""将以下代码审查分析提取为 JSON。
严格输出 JSON,不要任何其他内容:
{"bugs": [{"line": 数字, "severity": "high/medium/low", "description": "描述"}],
"suggestions": ["建议1"],
"overall_score": 0-10,
"summary": "一句话总结"}""",
messages=[{"role": "user",
"content": f"审查分析:\n{analysis_text}"}]
)
return CodeReviewResult.model_validate_json(extract_response.content[0].text)
单模型方案:在 System Prompt 中分离推理与输出
如果不想用两阶段,可以让模型自己管理格式:
system = """你是代码审查专家。
工作方式:
1. 在 <analysis> 标签内自由思考和分析(不限格式)
2. 在 <result> 标签内输出严格的 JSON(不含注释)
示例格式:
<analysis>
这段代码... [任意分析内容]
</analysis>
<result>
{"bugs": [...], "score": 8}
</result>"""
def extract_json_from_result_tag(text: str) -> dict:
import re
match = re.search(r'<result>\s*(.*?)\s*</result>', text, re.DOTALL)
if match:
return json.loads(match.group(1))
raise ValueError("No <result> tag found")
Instructor 库:自动化结构化提取
import instructor
from pydantic import BaseModel, Field
from typing import List
# Instructor 封装 Claude 客户端(兼容 thinking mode)
client_with_instructor = instructor.from_anthropic(
anthropic.Anthropic(),
mode=instructor.Mode.ANTHROPIC_TOOLS
)
class TechStackRecommendation(BaseModel):
recommended_stack: List[str] = Field(description="推荐的技术栈列表")
reasoning: str = Field(description="选择理由")
risks: List[str] = Field(description="潜在风险")
alternatives: List[str] = Field(description="备选方案")
result = client_with_instructor.chat.completions.create(
model="claude-sonnet-4-6",
max_tokens=4000,
response_model=TechStackRecommendation,
messages=[{"role": "user",
"content": "为一个日活 100 万的实时聊天应用推荐技术栈"}]
)
print(result.recommended_stack)
print(result.reasoning)
Two-Stage 架构的成本优化
Stage 1 使用推理模型(贵),Stage 2 使用 haiku(便宜)。由于推理分析往往只有几百 token,Stage 2 成本极低。整体成本比让推理模型直接输出 JSON 低约 20-30%,且更可靠。
本章小结
推理 + 结构化输出的最佳实践:Two-Stage(推理 → 提取)或 XML 标签隔离推理区域。Instructor 库简化了提取层的代码。下一章进入推理模型评估体系。