Token Budget(词元预算):我给 AI Agent 装了一个"智能油耗表"

5 阅读3分钟

背景:Agent 在盲飞


title: Token Budget(词元预算):我给 AI Agent 装了一个"智能油耗表" tags: [AI, Agent, TypeScript, 设计模式, 架构]

背景:Agent 在盲飞

2026年的AI圈有一个奇怪的现象:每个人都让 Agent 干活,但没有人教 Agent 怎么省钱。

你的 Agent 调 GPT-5.5 的单价 ¥30/百万token,它不知道。你的 Agent 跑了一整晚深度推理,它不觉得有什么问题。账单出来了你才看到——事后诸葛亮。

现有的"成本控制"方案全是事后统计:

  • OpenAI 用量仪表盘:花完了才知道
  • LangSmith 追踪:调式工具,不是控制工具
  • API Key 限额:硬封顶,不分任务优先级

问题的本质:Agent 没有预算自治能力。


Token Budget(词元预算):一个设计模式

核心就一条规则:每次调 LLM 前,先问预算。

class TokenBudget {
  canSpend(task): { allow: boolean, suggestion: string, model?: string }
  selectModel(taskType): string
  recordSpend(task, tokens, model): void
}

模型分级

等级模型费用用途
PREMIUMDeepSeek V4 Reasoning¥8/百万深度推理、战略分析
STANDARDDeepSeek V4 Fast¥2/百万分析、写作、总结
CHEAPDeepSeek V3 Lite (免费)¥0日常对话、格式化

预算策略

日预算满 → 自动降级。预算紧张 → 低优先级任务拦截。单次超限 → 建议拆分。深度推理预算不够 → 提示"明日再跑"。

验证通过的 6 个场景

场景A: 充裕预算 + 深度推理     → ✅ 允许
场景B: 简单格式化             → ✅ 允许,用免费模型
场景C: 大量消耗到95%          → ✅ 自动降级
场景D: 预算紧张 + 中等任务     → 🚫 拦截
场景E: 预算紧张 + 免费任务     → ✅ 自动切免费
场景F: 单次任务超限            → 🚫 拦截(建议拆分)

完整代码实现

核心类 200 行,MIT 协议,GitHub 开源:

export type TaskType = 'deep_reasoning' | 'analysis' | 'drafting' | 'simple_chat' | 'formatting'
export type TaskPriority = 'high' | 'medium' | 'low'
export type ModelTier = 'PREMIUM' | 'STANDARD' | 'CHEAP'

interface TaskSpec {
  type: TaskType
  estimatedTokens: number
  priority?: TaskPriority
}

interface BudgetConfig {
  dailyLimit?: number
  monthlyLimit?: number
  perTaskLimit?: number
  autoDowngrade?: boolean
  alertThreshold?: number
  preferredTier?: ModelTier
}

export class TokenBudget {
  private config: Required<BudgetConfig>
  private usedDaily = 0
  private usedMonthly = 0
  private log: SpendRecord[] = []

  private readonly MODEL_TIER_COST: Record<ModelTier, { model: string; costPerM: number }> = {
    PREMIUM: { model: 'DeepSeek V4 Reasoning', costPerM: 8 },
    STANDARD: { model: 'DeepSeek V4 Fast', costPerM: 2 },
    CHEAP: { model: 'DeepSeek V3 Lite (free)', costPerM: 0 },
  }

  constructor(config?: BudgetConfig) {
    this.config = {
      dailyLimit: config?.dailyLimit ?? 100000,
      monthlyLimit: config?.monthlyLimit ?? 3_000_000,
      perTaskLimit: config?.perTaskLimit ?? 50000,
      autoDowngrade: config?.autoDowngrade ?? true,
      alertThreshold: config?.alertThreshold ?? 0.1,
      preferredTier: config?.preferredTier ?? 'STANDARD',
    }
  }

  canSpend(task: TaskSpec): { allow: boolean; suggestion: string; model?: string } {
    if (task.estimatedTokens > this.config.perTaskLimit) {
      return { allow: false, suggestion: `单次任务超限(${task.estimatedTokens} > ${this.config.perTaskLimit}),建议拆分` }
    }
    const remaining = this.config.dailyLimit - this.usedDaily
    const pctUsed = this.usedDaily / this.config.dailyLimit

    if (remaining <= 0) {
      if (this.config.autoDowngrade && this.isCheapTask(task.type)) {
        return { allow: true, suggestion: '日预算已用完,自动降级到免费模型', model: 'DeepSeek V3 Lite (free)' }
      }
      return { allow: false, suggestion: '今日AI额度已用完,请先充值或限制预算上限' }
    }

    if (task.estimatedTokens > remaining && !this.isCheapTask(task.type)) {
      return { allow: false, suggestion: '预算紧张(余额不足),请充值或等明天' }
    }

    return { allow: true, suggestion: '通过预算检查' }
  }

  selectModel(taskType: TaskType): string {
    const pct = this.usedDaily / this.config.dailyLimit
    if (pct > 0.8 && taskType !== 'deep_reasoning') return 'DeepSeek V4 Fast'
    if (pct > 0.6 && this.isCheapTask(taskType)) return 'DeepSeek V3 Lite (free)'
    if (taskType === 'deep_reasoning') return 'DeepSeek V4 Reasoning'
    if (taskType === 'simple_chat' || taskType === 'formatting') return 'DeepSeek V3 Lite (free)'
    return this.config.preferredTier === 'STANDARD' ? 'DeepSeek V4 Fast' : 'DeepSeek V4 Reasoning'
  }

  recordSpend(task: string, tokens: number, model: string): void {
    this.usedDaily += tokens
    this.usedMonthly += tokens
    this.log.push({ time: Date.now(), task, tokens, model })
  }

  getStats(): BudgetStats {
    return {
      usedDaily: this.usedDaily, dailyLimit: this.config.dailyLimit,
      usedMonthly: this.usedMonthly, monthlyLimit: this.config.monthlyLimit,
      dailyPercent: `${((this.usedDaily / this.config.dailyLimit) * 100).toFixed(1)}%`,
      todayCost: (this.usedDaily * 0.000008).toFixed(4),
      monthlyCost: (this.usedMonthly * 0.000008).toFixed(4),
      autoDowngrade: this.config.autoDowngrade,
      records: this.log.slice(-10).map(r => ({ ...r, time: new Date(r.time).toLocaleString() })),
    }
  }

  resetDaily(): void { this.usedDaily = 0 }
  private isCheapTask(t: TaskType): boolean { return ['simple_chat', 'formatting'].includes(t) }
}

怎么用

npm install @wenrl2006/token-budget --registry=https://npm.pkg.github.com
const budget = new TokenBudget({ dailyLimit: 100000, autoDowngrade: true })

// 每次调 LLM 前:
const task = { type: 'deep_reasoning', estimatedTokens: 8000, priority: 'high' }
if (budget.canSpend(task).allow) {
  const model = budget.selectModel(task.type)  // 预算够 → 满血版
  const result = await callLLM(input, model)
  budget.recordSpend('分析用户输入', 8000, model)
}

这不是一个包,是一个设计模式

Token Budget 不是产品。它是设计模式——像 MVC 一样,应该渗透到每一个 Agent 的血脉里。

当前 Agent 框架里缺的不是 API Key 管理,也不是用量追踪。缺的是一个在 LLM 调用之前就拦住你的判断层。Token Budget 补的就是这一层。

以后写 Agent 框架,第一步不是配 API Key,是 new TokenBudget(…)

GitHub: github.com/wenrl2006/t… Package: @wenrl2006/token-budget

2026年的AI圈有一个奇怪的现象:每个人都让 Agent 干活,但没有人教 Agent 怎么省钱。

你的 Agent 调 GPT-5.5 的单价 ¥30/百万token,它不知道。你的 Agent 跑了一整晚深度推理,它不觉得有什么问题。账单出来了你才看到——事后诸葛亮。

现有的"成本控制"方案全是事后统计:

  • OpenAI 用量仪表盘:花完了才知道
  • LangSmith 追踪:调式工具,不是控制工具
  • API Key 限额:硬封顶,不分任务优先级

问题的本质:Agent 没有预算自治能力。


Token Budget(词元预算):一个设计模式

核心就一条规则:每次调 LLM 前,先问预算。

class TokenBudget {
  canSpend(task): { allow: boolean, suggestion: string, model?: string }
  selectModel(taskType): string
  recordSpend(task, tokens, model): void
}

模型分级

等级模型费用用途
PREMIUMDeepSeek V4 Reasoning¥8/百万深度推理、战略分析
STANDARDDeepSeek V4 Fast¥2/百万分析、写作、总结
CHEAPDeepSeek V3 Lite (免费)¥0日常对话、格式化

预算策略

日预算满 → 自动降级。预算紧张 → 低优先级任务拦截。单次超限 → 建议拆分。深度推理预算不够 → 提示"明日再跑"。

验证通过的 6 个场景

场景A: 充裕预算 + 深度推理     → ✅ 允许
场景B: 简单格式化             → ✅ 允许,用免费模型
场景C: 大量消耗到95%          → ✅ 自动降级
场景D: 预算紧张 + 中等任务     → 🚫 拦截
场景E: 预算紧张 + 免费任务     → ✅ 自动切免费
场景F: 单次任务超限            → 🚫 拦截(建议拆分)

完整代码实现

核心类 200 行,MIT 协议,GitHub 开源:

export type TaskType = 'deep_reasoning' | 'analysis' | 'drafting' | 'simple_chat' | 'formatting'
export type TaskPriority = 'high' | 'medium' | 'low'
export type ModelTier = 'PREMIUM' | 'STANDARD' | 'CHEAP'

interface TaskSpec {
  type: TaskType
  estimatedTokens: number
  priority?: TaskPriority
}

interface BudgetConfig {
  dailyLimit?: number
  monthlyLimit?: number
  perTaskLimit?: number
  autoDowngrade?: boolean
  alertThreshold?: number
  preferredTier?: ModelTier
}

export class TokenBudget {
  private config: Required<BudgetConfig>
  private usedDaily = 0
  private usedMonthly = 0
  private log: SpendRecord[] = []

  private readonly MODEL_TIER_COST: Record<ModelTier, { model: string; costPerM: number }> = {
    PREMIUM: { model: 'DeepSeek V4 Reasoning', costPerM: 8 },
    STANDARD: { model: 'DeepSeek V4 Fast', costPerM: 2 },
    CHEAP: { model: 'DeepSeek V3 Lite (free)', costPerM: 0 },
  }

  constructor(config?: BudgetConfig) {
    this.config = {
      dailyLimit: config?.dailyLimit ?? 100000,
      monthlyLimit: config?.monthlyLimit ?? 3_000_000,
      perTaskLimit: config?.perTaskLimit ?? 50000,
      autoDowngrade: config?.autoDowngrade ?? true,
      alertThreshold: config?.alertThreshold ?? 0.1,
      preferredTier: config?.preferredTier ?? 'STANDARD',
    }
  }

  canSpend(task: TaskSpec): { allow: boolean; suggestion: string; model?: string } {
    if (task.estimatedTokens > this.config.perTaskLimit) {
      return { allow: false, suggestion: `单次任务超限(${task.estimatedTokens} > ${this.config.perTaskLimit}),建议拆分` }
    }
    const remaining = this.config.dailyLimit - this.usedDaily
    const pctUsed = this.usedDaily / this.config.dailyLimit

    if (remaining <= 0) {
      if (this.config.autoDowngrade && this.isCheapTask(task.type)) {
        return { allow: true, suggestion: '日预算已用完,自动降级到免费模型', model: 'DeepSeek V3 Lite (free)' }
      }
      return { allow: false, suggestion: '今日AI额度已用完,请先充值或限制预算上限' }
    }

    if (task.estimatedTokens > remaining && !this.isCheapTask(task.type)) {
      return { allow: false, suggestion: '预算紧张(余额不足),请充值或等明天' }
    }

    return { allow: true, suggestion: '通过预算检查' }
  }

  selectModel(taskType: TaskType): string {
    const pct = this.usedDaily / this.config.dailyLimit
    if (pct > 0.8 && taskType !== 'deep_reasoning') return 'DeepSeek V4 Fast'
    if (pct > 0.6 && this.isCheapTask(taskType)) return 'DeepSeek V3 Lite (free)'
    if (taskType === 'deep_reasoning') return 'DeepSeek V4 Reasoning'
    if (taskType === 'simple_chat' || taskType === 'formatting') return 'DeepSeek V3 Lite (free)'
    return this.config.preferredTier === 'STANDARD' ? 'DeepSeek V4 Fast' : 'DeepSeek V4 Reasoning'
  }

  recordSpend(task: string, tokens: number, model: string): void {
    this.usedDaily += tokens
    this.usedMonthly += tokens
    this.log.push({ time: Date.now(), task, tokens, model })
  }

  getStats(): BudgetStats {
    return {
      usedDaily: this.usedDaily, dailyLimit: this.config.dailyLimit,
      usedMonthly: this.usedMonthly, monthlyLimit: this.config.monthlyLimit,
      dailyPercent: `${((this.usedDaily / this.config.dailyLimit) * 100).toFixed(1)}%`,
      todayCost: (this.usedDaily * 0.000008).toFixed(4),
      monthlyCost: (this.usedMonthly * 0.000008).toFixed(4),
      autoDowngrade: this.config.autoDowngrade,
      records: this.log.slice(-10).map(r => ({ ...r, time: new Date(r.time).toLocaleString() })),
    }
  }

  resetDaily(): void { this.usedDaily = 0 }
  private isCheapTask(t: TaskType): boolean { return ['simple_chat', 'formatting'].includes(t) }
}

怎么用

npm install @wenrl2006/token-budget --registry=https://npm.pkg.github.com
const budget = new TokenBudget({ dailyLimit: 100000, autoDowngrade: true })

// 每次调 LLM 前:
const task = { type: 'deep_reasoning', estimatedTokens: 8000, priority: 'high' }
if (budget.canSpend(task).allow) {
  const model = budget.selectModel(task.type)  // 预算够 → 满血版
  const result = await callLLM(input, model)
  budget.recordSpend('分析用户输入', 8000, model)
}

这不是一个包,是一个设计模式

Token Budget 不是产品。它是设计模式——像 MVC 一样,应该渗透到每一个 Agent 的血脉里。

当前 Agent 框架里缺的不是 API Key 管理,也不是用量追踪。缺的是一个在 LLM 调用之前就拦住你的判断层。Token Budget 补的就是这一层。

以后写 Agent 框架,第一步不是配 API Key,是 new TokenBudget(…)

GitHub: github.com/wenrl2006/t… Package: @wenrl2006/token-budget