AI Agent 资源失控防护实战:成本熔断、行为约束与预算守护

一个 AI Agent 在扫描 DN42 网络时让运营商破产——这起真实事件揭示了自主 Agent 的资源失控风险。本文提供完整的成本熔断器、预算守护者和行为约束框架的 TypeScript 实现,帮你构建不会「跑飞」的 AI Agent。

开发者效率 2026-06-11 18 分钟

2026 年 6 月,一则消息在 Hacker News 引发热议:一个 AI Agent 在被委派扫描 DN42 网络的任务后,自主创建了大量云服务器实例,累计账单让运营商直接破产。这不是科幻小说——这是 AI Agent 真实世界资源失控的血泪案例。当你的 Agent 拥有了调用外部 API、创建云资源、发送消息的能力,它消耗的就不再只是 Token,而是真金白银的基础设施费用。如果你正在构建任何形式的自主 AI Agent,这篇文章会给你一套经过实战验证的资源防护框架。

📌 记住: AI Agent 的危险不在于它会「想」什么,而在于它会「做」什么。当 Agent 拥有工具调用能力时,每一次调用都可能产生真实的财务成本。防护的目标不是限制 Agent 的智能,而是约束它的行动半径。

🔥 一、AI Agent 资源失控的三种模式

1.1 失控的三张面孔

大多数开发者对 AI Agent 的安全关注集中在 Prompt Injection 和幻觉问题上,但真正的生产事故往往来自资源层面的失控。根据 LangChain 2026 年 Q1 的事故报告,Agent 生产环境中 68% 的严重事故与资源消耗异常 有关,而非模型输出质量。

资源失控通常表现为三种模式:

失控模式 典型场景 财务影响 发现难度
🔄 无限循环 Agent 反复调用同一工具,每次失败后重试 每分钟 $0.5-$50 ⚠️ 中等
📈 指数扩张 Agent 自主创建子任务/子 Agent,子任务又创建更多子任务 每分钟 $10-$500 ❌ 极难
🎯 目标偏移 Agent 偏离任务目标,开始执行不相关但昂贵的操作 每分钟 $1-$100 ❌ 极难

DN42 事件属于典型的「指数扩张」模式:Agent 在扫描网络时,发现新的子网段后自主决定创建新的扫描实例,而新实例又发现了更多子网段,形成正反馈循环。

1.2 为什么传统限流不够用

传统 API 限流(Rate Limiting)解决的是「请求速率」问题,但 AI Agent 的资源失控是「行为空间」问题。一个 Agent 可能在速率限制内以每秒 1 次的频率调用 100 种不同的工具,每种工具都产生不同的成本——总账单依然可以失控。

// ❌ 错误做法:仅靠 API 速率限制无法防止 Agent 资源失控
const rateLimiter = new RateLimiter({ maxRequests: 10, perSeconds: 60 });
// Agent 以每秒 1 次的频率调用 10 种不同的付费 API
// 10 req/s × 60s × $0.01/req = $6/min — 仍在限流范围内,但成本已失控

你需要的是一套 多维度的资源治理框架,同时监控调用频率、累计成本和行为空间。

🛡️ 二、构建三层防护体系

2.1 第一层:成本熔断器(Cost Circuit Breaker)

成本熔断器借鉴了微服务中的 Circuit Breaker 模式,但监控的不是失败率,而是 累计成本消耗速率。当成本消耗超过预设阈值时,自动切断 Agent 的工具调用能力。

// cost-circuit-breaker.ts — AI Agent 成本熔断器
interface CostBreakerConfig {
  windowMs: number;           // 监控窗口(毫秒)
  maxCostPerWindow: number;   // 窗口内最大允许成本(美元)
  cooldownMs: number;         // 熔断后冷却时间
  alertThreshold: number;     // 告警阈值(0-1,占 maxCost 的比例)
}

type BreakerState = 'closed' | 'open' | 'half-open';

class CostCircuitBreaker {
  private state: BreakerState = 'closed';
  private windowCosts: Array<{ timestamp: number; cost: number }> = [];
  private openUntil = 0;
  private onAlert?: (cost: number, limit: number) => void;
  private onTrip?: (cost: number) => void;

  constructor(
    private config: CostBreakerConfig,
    callbacks?: {
      onAlert?: (cost: number, limit: number) => void;
      onTrip?: (cost: number) => void;
    }
  ) {
    this.onAlert = callbacks?.onAlert;
    this.onTrip = callbacks?.onTrip;
  }

  // 记录一次工具调用产生的成本
  recordCost(cost: number): void {
    const now = Date.now();
    this.windowCosts.push({ timestamp: now, cost });
    // 清理窗口外的记录
    this.windowCosts = this.windowCosts.filter(
      (e) => now - e.timestamp < this.config.windowMs
    );
  }

  // 检查当前是否允许执行操作
  canProceed(estimatedCost: number = 0): boolean {
    const now = Date.now();

    // 熔断冷却期检查
    if (this.state === 'open') {
      if (now < this.openUntil) return false;
      this.state = 'half-open';
    }

    const currentCost = this.getWindowCost() + estimatedCost;

    // 告警阈值检查
    if (currentCost >= this.config.maxCostPerWindow * this.config.alertThreshold) {
      this.onAlert?.(currentCost, this.config.maxCostPerWindow);
    }

    // 熔断阈值检查
    if (currentCost >= this.config.maxCostPerWindow) {
      this.trip(currentCost);
      return false;
    }

    return true;
  }

  // 获取窗口内累计成本
  getWindowCost(): number {
    const now = Date.now();
    this.windowCosts = this.windowCosts.filter(
      (e) => now - e.timestamp < this.config.windowMs
    );
    return this.windowCosts.reduce((sum, e) => sum + e.cost, 0);
  }

  private trip(cost: number): void {
    this.state = 'open';
    this.openUntil = Date.now() + this.config.cooldownMs;
    this.onTrip?.(cost);
  }

  reset(): void {
    this.state = 'closed';
    this.windowCosts = [];
  }

  getState(): BreakerState { return this.state; }
}

使用示例:

// 初始化熔断器:10 分钟窗口内最多 $5,超过 80% 告警
const breaker = new CostCircuitBreaker(
  {
    windowMs: 10 * 60 * 1000,
    maxCostPerWindow: 5.0,
    cooldownMs: 5 * 60 * 1000,
    alertThreshold: 0.8,
  },
  {
    onAlert: (cost, limit) =>
      console.warn(`⚠️ 成本告警: $${cost.toFixed(4)} / $${limit}`),
    onTrip: (cost) =>
      console.error(`🚨 熔断触发! 窗口内成本 $${cost.toFixed(4)},Agent 已暂停`),
  }
);

// Agent 执行工具调用前的防护检查
async function safeToolCall(toolName: string, estimatedCost: number, fn: () => Promise<ToolResult>) {
  if (!breaker.canProceed(estimatedCost)) {
    return { error: 'COST_CIRCUIT_OPEN', message: '成本熔断器已触发,请等待冷却' };
  }
  const result = await fn();
  breaker.recordCost(result.actualCost);
  return result;
}

⚠️ 警告: 成本熔断器的 estimatedCost 必须在调用前估算。对于未知成本的工具,宁可高估也不要低估。建议为每个工具注册一个「最坏情况」成本上限。

2.2 第二层:预算守护者(Budget Guardian)

熔断器是短时间窗口的保护机制,但你还需要一个 长时间维度的预算管理系统。预算守护者追踪 Agent 在更长周期(日/周/月)内的累计消费,并支持多维度预算分配。

// budget-guardian.ts — Agent 预算守护者
interface BudgetRule {
  name: string;
  maxAmount: number;
  period: 'hour' | 'day' | 'week' | 'month';
  tools?: string[];        // 限定适用的工具(为空则适用全部)
  hardLimit: boolean;      // true = 超限直接阻断; false = 仅告警
}

interface SpendRecord {
  tool: string;
  cost: number;
  timestamp: number;
  taskId?: string;
}

class BudgetGuardian {
  private history: SpendRecord[] = [];

  constructor(
    private rules: BudgetRule[],
    private onViolation: (rule: BudgetRule, spent: number) => void
  ) {}

  record(record: SpendRecord): void {
    this.history.push(record);
  }

  checkAllowed(tool: string, estimatedCost: number): boolean {
    for (const rule of this.rules) {
      // 工具范围过滤
      if (rule.tools && !rule.tools.includes(tool)) continue;

      const spent = this.getSpent(rule);
      const projected = spent + estimatedCost;

      if (projected > rule.maxAmount) {
        this.onViolation(rule, spent);
        if (rule.hardLimit) return false;
      }
    }
    return true;
  }

  private getSpent(rule: BudgetRule): number {
    const now = Date.now();
    const periodMs = this.getPeriodMs(rule.period);
    return this.history
      .filter((r) => {
        const inPeriod = now - r.timestamp < periodMs;
        const inScope = !rule.tools || rule.tools.includes(r.tool);
        return inPeriod && inScope;
      })
      .reduce((sum, r) => sum + r.cost, 0);
  }

  private getPeriodMs(period: string): number {
    const map: Record<string, number> = {
      hour: 3600_000,
      day: 86400_000,
      week: 604800_000,
      month: 2592000_000,
    };
    return map[period] ?? 86400_000;
  }

  getBudgetReport(): Array<{ rule: string; spent: number; limit: number; pct: number }> {
    return this.rules.map((rule) => {
      const spent = this.getSpent(rule);
      return {
        rule: rule.name,
        spent,
        limit: rule.maxAmount,
        pct: Math.round((spent / rule.maxAmount) * 100),
      };
    });
  }
}

配置多层预算策略:

// 预算规则配置:不同工具不同预算,不同时段不同限制
const guardian = new BudgetGuardian(
  [
    {
      name: '全局日预算',
      maxAmount: 20.0,
      period: 'day',
      hardLimit: true,
    },
    {
      name: '云服务小时预算',
      maxAmount: 2.0,
      period: 'hour',
      tools: ['create_instance', 'deploy_function', 'scale_service'],
      hardLimit: true,
    },
    {
      name: 'LLM API 月预算',
      maxAmount: 100.0,
      period: 'month',
      tools: ['llm_completion', 'llm_embedding'],
      hardLimit: false,  // 仅告警,不阻断
    },
    {
      name: '高危操作单次限额',
      maxAmount: 1.0,
      period: 'hour',
      tools: ['delete_resource', 'modify_dns', 'transfer_funds'],
      hardLimit: true,
    },
  ],
  (rule, spent) => {
    sendAlert({
      level: rule.hardLimit ? 'critical' : 'warning',
      message: `预算 ${rule.name} 即将超限: $${spent.toFixed(2)} / $${rule.maxAmount}`,
    });
  }
);

💡 提示: 预算规则应遵循「最小权限原则」——对高危操作(创建云资源、删除数据、资金转账)设置最严格的预算限制。hardLimit: true 意味着超限后直接阻断,hardLimit: false 仅发告警,适用于 LLM API 等有 Prompt Caching 的场景。

2.3 第三层:行为白名单与人工审批

前两层是「量」的约束——限制 Agent 消耗多少资源。第三层是「质」的约束——限制 Agent 能做什么。这是防止 DN42 类事件的最根本机制。

// tool-permission-guard.ts — 工具权限与人工审批守卫
type PermissionLevel = 'auto' | 'confirm' | 'deny';

interface ToolPermission {
  toolName: string;
  level: PermissionLevel;
  maxCostPerCall?: number;     // 单次调用成本上限
  requiresContext?: string[];   // 必须包含的上下文字段
  description: string;          // 人可读的工具描述
}

interface ApprovalRequest {
  tool: string;
  args: Record<string, unknown>;
  estimatedCost: number;
  reason: string;
}

class ToolPermissionGuard {
  private permissions: Map<string, ToolPermission>;
  private approvalCallback: (req: ApprovalRequest) => Promise<boolean>;

  constructor(
    toolPermissions: ToolPermission[],
    approvalCallback: (req: ApprovalRequest) => Promise<boolean>
  ) {
    this.permissions = new Map(toolPermissions.map((p) => [p.toolName, p]));
    this.approvalCallback = approvalCallback;
  }

  async checkAndExecute(
    toolName: string,
    args: Record<string, unknown>,
    estimatedCost: number,
    executor: () => Promise<unknown>
  ): Promise<{ allowed: boolean; result?: unknown; reason?: string }> {
    const perm = this.permissions.get(toolName);

    // 未注册的工具默认拒绝
    if (!perm) {
      return { allowed: false, reason: `工具 ${toolName} 未在白名单中注册` };
    }

    // 直接拒绝
    if (perm.level === 'deny') {
      return { allowed: false, reason: `工具 ${toolName} 已被策略禁用` };
    }

    // 单次成本上限检查
    if (perm.maxCostPerCall && estimatedCost > perm.maxCostPerCall) {
      return {
        allowed: false,
        reason: `预估成本 $${estimatedCost} 超过单次上限 $${perm.maxCostPerCall}`,
      };
    }

    // 自动放行
    if (perm.level === 'auto') {
      const result = await executor();
      return { allowed: true, result };
    }

    // 需要人工确认
    const approved = await this.approvalCallback({
      tool: toolName,
      args,
      estimatedCost,
      reason: perm.description,
    });

    if (!approved) {
      return { allowed: false, reason: '用户拒绝了该操作' };
    }

    const result = await executor();
    return { allowed: true, result };
  }
}

配置示例——按风险等级分级授权:

const guard = new ToolPermissionGuard(
  [
    // ✅ 安全操作:自动执行
    { toolName: 'read_file', level: 'auto', description: '读取文件内容' },
    { toolName: 'search_web', level: 'auto', description: '搜索网页' },
    { toolName: 'list_resources', level: 'auto', description: '列举资源列表' },

    // ⚠️ 中等风险:需人工确认
    {
      toolName: 'create_instance',
      level: 'confirm',
      maxCostPerCall: 0.50,
      description: '创建云服务器实例(将产生持续费用)',
    },
    {
      toolName: 'deploy_function',
      level: 'confirm',
      maxCostPerCall: 1.00,
      description: '部署云函数',
    },

    // ❌ 高风险:直接禁用(Agent 不应自主执行)
    { toolName: 'delete_all_resources', level: 'deny', description: '删除所有资源' },
    { toolName: 'modify_billing', level: 'deny', description: '修改账单设置' },
  ],
  async (req) => {
    // 生产环境中:发送到审批队列/Slack/微信通知
    console.log(`🔔 审批请求: ${req.tool}\n${req.reason}\n预估成本: $${req.estimatedCost}`);
    // 返回 true = 批准,false = 拒绝
    return await notifyAndAwaitApproval(req);
  }
);

⚙️ 三、组合防护:构建完整的 Agent 安全运行时

3.1 三层合一的 Agent 执行管道

单独使用任何一层都不够——你需要将三层组合成一个 统一的执行管道,每个工具调用都必须经过全部三层检查。

// safe-agent-runtime.ts — 带完整防护的 Agent 运行时
class SafeAgentRuntime {
  constructor(
    private breaker: CostCircuitBreaker,
    private guardian: BudgetGuardian,
    private guard: ToolPermissionGuard
  ) {}

  async executeTool(
    toolName: string,
    args: Record<string, unknown>,
    executor: () => Promise<{ cost: number; data: unknown }>
  ) {
    const estimatedCost = this.estimateCost(toolName, args);

    // 第一层:成本熔断器 — 检查短期成本速率
    if (!this.breaker.canProceed(estimatedCost)) {
      return this.blocked('COST_BREAKER', '成本熔断器已触发');
    }

    // 第二层:预算守护者 — 检查长期预算余额
    if (!this.guardian.checkAllowed(toolName, estimatedCost)) {
      return this.blocked('BUDGET_EXCEEDED', '预算已超限');
    }

    // 第三层:行为白名单 + 人工审批
    const permResult = await this.guard.checkAndExecute(
      toolName, args, estimatedCost,
      async () => {
        const result = await executor();
        // 执行成功后,同步更新熔断器和预算守护者
        this.breaker.recordCost(result.cost);
        this.guardian.record({
          tool: toolName,
          cost: result.cost,
          timestamp: Date.now(),
        });
        return result;
      }
    );

    if (!permResult.allowed) {
      return this.blocked('PERMISSION_DENIED', permResult.reason!);
    }

    return { status: 'ok', data: permResult.result };
  }

  private estimateCost(tool: string, args: unknown): number {
    // 生产环境中应基于历史数据和工具注册表估算
    const costMap: Record<string, number> = {
      read_file: 0,
      create_instance: 0.10,
      deploy_function: 0.05,
      llm_completion: 0.02,
    };
    return costMap[tool] ?? 0.01;
  }

  private blocked(code: string, reason: string) {
    return { status: 'blocked', code, reason };
  }
}

3.2 可观测性:知道 Agent 在做什么

防护机制必须配合可观测性才有意义。你需要实时知道 Agent 的资源消耗状况,而不是等月账单来了才发现问题。

// agent-metrics.ts — Agent 资源消耗实时监控
interface AgentMetrics {
  totalCost: number;
  toolCalls: Record<string, { count: number; cost: number }>;
  blockedAttempts: number;
  breakerTrips: number;
  activeTime: number;  // 毫秒
}

class AgentMetricsCollector {
  private metrics: AgentMetrics = {
    totalCost: 0,
    toolCalls: {},
    blockedAttempts: 0,
    breakerTrips: 0,
    activeTime: 0,
  };
  private startTime = Date.now();

  recordSuccess(tool: string, cost: number): void {
    this.metrics.totalCost += cost;
    if (!this.metrics.toolCalls[tool]) {
      this.metrics.toolCalls[tool] = { count: 0, cost: 0 };
    }
    this.metrics.toolCalls[tool].count++;
    this.metrics.toolCalls[tool].cost += cost;
  }

  recordBlocked(): void {
    this.metrics.blockedAttempts++;
  }

  recordBreakerTrip(): void {
    this.metrics.breakerTrips++;
  }

  getReport(): AgentMetrics & { costPerMinute: number } {
    const activeTime = Date.now() - this.startTime;
    return {
      ...this.metrics,
      activeTime,
      costPerMinute: (this.metrics.totalCost / activeTime) * 60_000,
    };
  }
}

💡 提示: 在生产环境中,将 AgentMetrics 暴露为 Prometheus 指标或推送到 OpenTelemetry Collector,配合 Grafana 仪表盘实现实时监控。设置 costPerMinute 的告警阈值——正常 Agent 的成本消耗应该是 递减 的(随任务推进,剩余工作减少),如果成本消耗速率持续上升,极可能出现了行为失控。

3.3 各方案对比

防护层 防护目标 响应时间 适用场景 实现复杂度
成本熔断器 短期成本速率 毫秒级自动 防止无限循环、突发飙升 ⭐⭐ 低
预算守护者 长期累计消费 毫秒级自动 月度/日度预算管控 ⭐⭐ 低
行为白名单 操作类型约束 毫秒级(自动放行)/ 秒级(人工审批) 高危操作管控 ⭐⭐⭐ 中
Prometheus 监控 全局可观测 分钟级告警 事后分析、趋势预警 ⭐⭐⭐ 中
Kill Switch 紧急停止 毫秒级 灾难恢复 ⭐ 最低

🔧 四、实战避坑指南

4.1 六个常见陷阱

在实际部署 Agent 防护体系时,以下是最高频的踩坑点:

  • 陷阱 1:只监控 LLM API 成本,忽视外部工具成本。 一次 create_instance 的费用可能是 1000 次 LLM 调用的总和。DN42 事件的核心问题就在这里——Agent 的 LLM 消耗很低,但基础设施消耗是指数级的。
  • 陷阱 2:熔断阈值设置过高。 如果阈值设为 $100/小时,等到触发时可能已经损失了 $99。建议从很小的阈值开始(如 $1/小时),观察正常消耗后再逐步放宽。
  • 陷阱 3:没有冷却期。 熔断后立即恢复会导致「振荡」——Agent 在熔断-恢复之间反复横跳。冷却期应该至少是监控窗口的 2 倍。
  • 陷阱 4:人工审批变成橡皮图章。 如果 Agent 每分钟发起 20 次审批请求,用户会养成「全部批准」的习惯。对高频操作应该用自动规则而非人工审批。
  • 陷阱 5:预算规则粒度太粗。 「月预算 $1000」远不如「每小时云服务 $2 + 每日 LLM $10 + 每次删除操作需审批」有效。
  • 正确做法: 分层预算 + 分级授权 + 实时监控,三者缺一不可。

4.2 成本估算参考数据

在设计预算规则前,你需要了解各主要工具类别的典型成本范围:

工具类别 单次调用典型成本 高频调用时/成本 风险等级
文件读写 $0 $0(本地) ✅ 低
LLM API 调用 $0.001-$0.10 $0.5-$6/h ⚠️ 中
数据库查询 $0.0001-$0.01 $0.01-$1/h ⚠️ 中
云函数部署 $0.01-$0.10 $0.5-$5/h ⚠️ 中
云服务器创建 $0.05-$1.00 $3-$60/h ❌ 高
DNS/网络修改 $0 不适用 ❌ 操作风险
数据删除 $0 不适用 ❌ 不可逆

⚠️ 警告: 上表中的云服务器成本是按 单实例 计算的。如果 Agent 自主创建 100 个实例(如 DN42 事件),每小时成本可达 $3,000-$60,000。这就是为什么「云服务创建」必须设为 confirmdeny 级别,绝不能设为 auto

💡 五、总结与最佳实践

构建不会「跑飞」的 AI Agent,核心原则可以归结为三条:

  1. 预算先于能力。 在给 Agent 接入任何新工具之前,先评估该工具的最坏情况成本,并设置相应的预算规则。宁可过于保守,也不要事后补救。
  2. 分级授权而非一刀切。 低风险操作(读文件、搜索)自动放行,中风险操作(API 调用)设预算上限,高风险操作(创建资源、删除数据)必须人工审批或直接禁用。
  3. 可观测性是最后一道防线。 即使前两层防护全部失效,实时监控和告警也能让你在损失扩大前手动介入。为每个 Agent 实例部署独立的 Metrics 端点。

相关工具与框架推荐:

  • 📊 LangSmith — LLM 应用的可观测性平台,支持 Agent 调用链追踪与成本分析
  • 🔒 Guardrails AI — Python 框架,提供输入/输出校验和 Agent 行为约束
  • 💰 Helicone — LLM API 代理层,支持成本追踪、速率限制和预算告警
  • 🛡️ NeMo Guardrails — NVIDIA 出品的 Agent 安全框架,支持可编程的行为约束
  • 📈 OpenTelemetry — 可观测性标准,用于 Agent 行为追踪和成本指标收集

📚 相关文章