Skip to main content

为什么预算控制很重要

在循环中调用付费 API 的 AI 代理可能迅速累积大量费用。预算控制让您能够:
  • 限制每个会话的总消费上限
  • 设置按域名的限额(例如,在 api.weather.com 上每次会话最多 100 sats)
  • 在每笔支付前接收回调
  • 随时获取完整的消费报告

全局预算

import { L402Client, BlinkWallet } from "l402-kit";

const client = new L402Client({
  wallet: new BlinkWallet(process.env.BLINK_API_KEY!, process.env.BLINK_WALLET_ID!),
  budgetSats: 500, // 本次会话最多 500 sats
});
当 402 响应包含 priceSats 字段且该金额将超过剩余预算时,客户端会在付款之前抛出 BudgetExceededError —— 不会花费任何 satoshis。

按域名预算

const client = new L402Client({
  wallet,
  budgetSats: 2000,
  budgetPerDomain: {
    "api.weather.com": 100,
    "api.finance.com": 500,
  },
});
按域名限额与全局限额独立检查 —— 两者都必须通过,支付才能继续。

回调

const client = new L402Client({
  wallet,
  budgetSats: 1000,
  onSpend: (sats, url) => {
    console.log(`✓ Paid ${sats} sats → ${url}`);
    // 记录到您的遥测系统、更新仪表板等
  },
  onBudgetExceeded: (url, sats) => {
    console.warn(`✗ Blocked: ${sats} sats requested by ${url} — budget exhausted`);
    // 发送警报、通知 Slack 等
  },
});
onBudgetExceeded / on_budget_exceeded 会在抛出 BudgetExceededError 之前被调用 —— 适用于日志记录或发送警报。

消费报告

const report = client.spendingReport();

if (report) {
  console.log(`Total spent: ${report.total} sats`);
  console.log(`Remaining:   ${report.remaining} sats`);
  console.log("By domain:", report.byDomain);
  // { "api.weather.com": 42, "api.finance.com": 105 }

  for (const tx of report.transactions) {
    console.log(`  ${tx.ts}  ${tx.sats} sats  ${tx.url}`);
  }
}
当未配置预算时,spendingReport() 返回 null / None

处理 BudgetExceededError

import { BudgetExceededError } from "l402-kit";

try {
  const res = await client.fetch("https://api.example.com/premium");
} catch (err) {
  if (err instanceof BudgetExceededError) {
    console.log(`Need ${err.required} sats, only ${err.remaining} remaining`);
    // 优雅降级 —— 返回缓存数据、跳过此步骤等
  }
}

并发注意事项

当预算限额重要时,请勿在并发的 Promise.all 调用中共享同一个 L402Client 实例。BudgetTracker.check()record() 之间存在一个 await(即 Lightning 付款)。两个并发的 client.fetch() 调用可能在任何一个记录消费之前都通过了预算检查 —— 这意味着合并费用可能暂时超出您的预算上限一笔付款的金额。安全模式 —— 顺序调用:
for (const url of urls) {
  const res = await client.fetch(url); // 逐一等待
}
风险模式 —— 并行调用:
// 两者可能都在调用 budget.record() 之前通过了 budget.check()
const results = await Promise.all(urls.map(url => client.fetch(url)));
并行工作负载的缓解方案: 保守地设置 budgetSats(例如设为真实限额的 80%),以吸收一次并发支付导致的超支。如需严格执行,请按顺序处理调用。

完整选项参考

选项TypeScriptPython默认值描述
全局预算budgetSatsbudget_sats无限制会话的最大 sats 数
按域名budgetPerDomainbudget_per_domain{}域名 → 最大 sats 的映射
消费钩子onSpendon_spend每次支付后调用
超限钩子onBudgetExceededon_budget_exceeded抛出错误前调用