Chapter 09

推理成本控制:Budget Forcing 与智能路由

推理模型的 token 成本可能是普通模型的 10-50 倍。系统化的成本控制策略让推理模型在生产中真正具备可行性。

成本构成深度分析

输入 Token(Input Token)
用户消息 + system prompt + 对话历史。费率较低(约为输出 token 的 1/3 到 1/5)。可以通过 Prompt Caching 大幅降低(缓存命中时费率约为普通输入的 10%)。
思考 Token(Thinking Token)
Extended Thinking 中 thinking block 消耗的 token。按输出 token 费率计费(较贵)。通常是成本的大头——一次复杂推理可能产生 5000-20000 个思考 token,相当于 20-80 次普通 API 调用的输出成本。
输出 Token(Output Token)
最终 text block 中用户可见的答案。费率最高。由于推理模型"已经想清楚了",最终答案通常比普通模型更简洁,输出 token 数反而较少。
成本对比示例(claude-sonnet-4-6 估算,2025 年价格): 普通模式(不思考): 输入: 200 tokens × $3/M = $0.0006 输出: 500 tokens × $15/M = $0.0075 合计: 约 $0.008 / 次 推理模式(budget=5000): 输入: 200 tokens × $3/M = $0.0006 思考: 4000 tokens × $15/M = $0.060 输出: 300 tokens × $15/M = $0.0045 合计: 约 $0.065 / 次(约8×) 推理模式(budget=20000): 思考: 15000 tokens × $15/M = $0.225 合计: 约 $0.23 / 次(约29×) 结论:思考 token 是成本主导因素,必须精细控制

Budget Forcing:动态思考预算分配

import anthropic
import re
from typing import Optional

client = anthropic.Anthropic()

class ThinkingBudgetOptimizer:
    """根据问题难度动态调整 thinking budget,平衡质量与成本"""

    # 难度 → budget 映射(单位:tokens)
    BUDGET_MAP = {
        "trivial":  0,      # 禁用思考(极简问答、格式转换)
        "easy":    1000,   # 轻量推理(基础计算、简单比较)
        "medium":  5000,   # 中等推理(多步骤问题、代码审查)
        "hard":    12000,  # 深度推理(数学竞赛、复杂架构设计)
        "expert":  25000,  # 专家级(博士级科学、定理证明)
    }

    def classify_difficulty(self, question: str) -> str:
        """基于规则的难度分类(可替换为小模型分类器)"""
        q = question.lower()
        word_count = len(question.split())

        # 极简问题特征(不需要推理)
        trivial_patterns = ['翻译', 'translate', '什么是', 'how to spell']
        if word_count < 8 or any(p in q for p in trivial_patterns):
            return "trivial"

        # 专家级问题特征
        expert_keywords = ['证明', 'prove', 'theorem', '定理', '∫', '∑', '∀', '∃']
        if any(k in question for k in expert_keywords) and word_count > 30:
            return "expert"

        # 难问题特征
        hard_keywords = ['算法复杂度', '系统设计', '优化', '竞赛', '推导', '比较分析']
        has_code = '```' in question or word_count > 100
        if any(k in q for k in hard_keywords) or (has_code and word_count > 50):
            return "hard"

        # 中等问题(默认)
        if word_count > 20:
            return "medium"
        return "easy"

    def ask(self, question: str, force_difficulty: Optional[str] = None) -> dict:
        """智能提问:自动分级,返回答案和成本信息"""
        difficulty = force_difficulty or self.classify_difficulty(question)
        budget = self.BUDGET_MAP[difficulty]

        thinking_config = (
            {"type": "enabled", "budget_tokens": budget}
            if budget > 0
            else {"type": "disabled"}
        )

        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=budget + 2000,
            thinking=thinking_config,
            messages=[{"role": "user", "content": question}]
        )

        # 提取思考 token 数量
        thinking_tokens = sum(
            len(b.thinking.split())
            for b in response.content
            if hasattr(b, 'thinking')
        )

        answer = next(b.text for b in response.content if b.type == "text")
        return {
            "answer": answer,
            "difficulty": difficulty,
            "budget": budget,
            "actual_thinking_tokens": thinking_tokens,
            "total_output_tokens": response.usage.output_tokens,
        }

Prompt Caching:降低输入成本

class CachedReasoningService:
    """使用 Prompt Caching 降低重复系统提示的成本"""

    def __init__(self, system_prompt: str, budget: int = 5000):
        self.budget = budget
        # 系统提示封装为可缓存格式
        # 缓存有效:系统提示必须 ≥ 1024 tokens(Claude 的最低缓存阈值)
        self.system = [{
            "type": "text",
            "text": system_prompt,
            "cache_control": {"type": "ephemeral"}  # 标记为可缓存(5分钟TTL)
        }]

    def query(self, question: str) -> dict:
        response = client.messages.create(
            model="claude-sonnet-4-6",
            max_tokens=self.budget + 2000,
            thinking={"type": "enabled", "budget_tokens": self.budget},
            system=self.system,
            messages=[{"role": "user", "content": question}]
        )

        usage = response.usage
        # 缓存命中时,cache_read_input_tokens 费率是普通输入的 10%
        # 第一次调用:cache_creation_input_tokens > 0(写入缓存,略贵)
        # 后续调用:cache_read_input_tokens > 0(命中缓存,大幅省钱)
        is_cache_hit = usage.cache_read_input_tokens > 0

        answer = next(b.text for b in response.content if b.type == "text")
        return {
            "answer": answer,
            "cache_hit": is_cache_hit,
            "cache_read_tokens": usage.cache_read_input_tokens,
            "cache_write_tokens": usage.cache_creation_input_tokens,
            "normal_input_tokens": usage.input_tokens,
            "output_tokens": usage.output_tokens,
        }

# 成本节省估算:
# 系统提示 = 2000 tokens,每日 100 次查询
# 不缓存:100 × 2000 × $3/M = $0.60/天
# 缓存命中(90%):9 × 2000 × $3/M(写入)+ 91 × 2000 × $0.3/M(读取)
#                = $0.054 + $0.055 = $0.109/天
# 节省:约 82%

智能模型路由:三层路由策略

from enum import Enum
from dataclasses import dataclass

class RouteConfig:
    """模型路由配置:任务类型 × 复杂度 → (模型, 思考配置)"""

    # 规则:行 = 任务类型,列 = 复杂度
    ROUTES = {
        # 对话类(不需要推理)
        ("chat",       "low"):    ("claude-haiku-4-5-20251001", None),
        ("chat",       "medium"): ("claude-sonnet-4-6", None),
        ("chat",       "high"):   ("claude-sonnet-4-6", None),

        # 推理类(需要 Extended Thinking)
        ("reasoning",  "low"):    ("claude-sonnet-4-6",
                                      {"type": "enabled", "budget_tokens": 2000}),
        ("reasoning",  "medium"): ("claude-sonnet-4-6",
                                      {"type": "enabled", "budget_tokens": 8000}),
        ("reasoning",  "high"):   ("claude-sonnet-4-6",
                                      {"type": "enabled", "budget_tokens": 20000}),

        # 数学类(使用最强的推理配置)
        ("math",       "low"):    ("claude-sonnet-4-6",
                                      {"type": "enabled", "budget_tokens": 5000}),
        ("math",       "high"):   ("claude-opus-4-6",
                                      {"type": "enabled", "budget_tokens": 32000}),

        # 代码类(中等推理)
        ("code",       "low"):    ("claude-sonnet-4-6", None),
        ("code",       "high"):   ("claude-sonnet-4-6",
                                      {"type": "enabled", "budget_tokens": 10000}),
    }

    def route(self, task_type: str, complexity: str) -> tuple:
        key = (task_type, complexity)
        return self.ROUTES.get(key,
                               ("claude-sonnet-4-6",
                                {"type": "enabled", "budget_tokens": 5000}))

# 使用示例
router = RouteConfig()
model, thinking = router.route("math", "high")
print(f"使用模型: {model}, 思考预算: {thinking}")

budget_tokens vs 质量的权衡曲线

通过实验数据,我们可以看到思考预算与质量之间的关系:

MATH-500 准确率 vs budget_tokens(实验观测,示意): 准确率 96% │ ████ 92% │ ████████████ 86% │ ████████████████ 78% │ ████████ 65% │ ████ 50% │██ (禁用思考) └──────────────────────────────────────────────── 0 1K 2K 4K 8K 16K 32K budget 关键观察(经验规律): 0 → 1000 tokens:准确率快速从 50% 提升到 65% 1000 → 4000 tokens:高性价比区间(每千 token 提升 ~6%) 4000 → 8000 tokens:稳步提升(推荐默认区间) 8000 → 16000 tokens:收益递减(每千 token 提升 ~1-2%) 16000+:边际收益极小(仅对极端难题有帮助) 结论:5000-8000 tokens 是大多数业务场景的甜点区间
避免的反模式:固定高预算

一些工程师为了"简单",对所有请求设置相同的 budget_tokens=20000。这会导致:简单问题浪费大量成本(即使预算高,模型不用那么多,但等待时间和计费会增加);极难问题可能仍然不够用。正确做法是根据问题类型动态分配预算,如本章的 ThinkingBudgetOptimizer 所示。

成本监控与告警

from datetime import datetime, timedelta
from collections import defaultdict

class CostMonitor:
    """推理成本实时监控"""

    # 2025 年 claude-sonnet-4-6 估算费率(美元/百万 token)
    RATES = {
        "input": 3.0,
        "cache_write": 3.75,
        "cache_read": 0.30,
        "output": 15.0,  # thinking token 也按此费率
    }

    def __init__(self, daily_budget_usd: float = 10.0):
        self.daily_budget = daily_budget_usd
        self.daily_cost = 0.0
        self.request_count = 0
        self.reset_time = datetime.now() + timedelta(days=1)

    def record_usage(self, usage) -> dict:
        """记录一次请求的用量,返回成本信息"""
        # 重置每日计数器
        if datetime.now() > self.reset_time:
            self.daily_cost = 0.0
            self.request_count = 0
            self.reset_time = datetime.now() + timedelta(days=1)

        # 计算本次成本
        cost = (
            usage.input_tokens * self.RATES["input"] +
            usage.cache_creation_input_tokens * self.RATES["cache_write"] +
            usage.cache_read_input_tokens * self.RATES["cache_read"] +
            usage.output_tokens * self.RATES["output"]
        ) / 1_000_000

        self.daily_cost += cost
        self.request_count += 1

        # 成本告警
        budget_usage = self.daily_cost / self.daily_budget
        if budget_usage > 0.9:
            print(f"警告:已使用 {budget_usage:.0%} 日预算")
        elif budget_usage > 0.7:
            print(f"提示:已使用 {budget_usage:.0%} 日预算")

        return {
            "request_cost_usd": cost,
            "daily_total_usd": self.daily_cost,
            "budget_usage_pct": budget_usage,
            "requests_today": self.request_count,
        }
本章小结

成本控制四板斧:(1) 动态思考预算(ThinkingBudgetOptimizer,难度分级);(2) Prompt Caching(固定系统提示 80%+ 成本节省);(3) 模型路由(简单任务用 Haiku,复杂任务用 Sonnet+thinking);(4) 成本监控(实时告警,防止超支)。大多数生产场景 budget=5000-8000 是质量和成本的最优区间——更高预算的边际收益递减明显。下一章完整端到端实战项目,将本教程所有内容综合应用。