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。这就是为什么「云服务创建」必须设为
confirm或deny级别,绝不能设为auto。
💡 五、总结与最佳实践
构建不会「跑飞」的 AI Agent,核心原则可以归结为三条:
- 预算先于能力。 在给 Agent 接入任何新工具之前,先评估该工具的最坏情况成本,并设置相应的预算规则。宁可过于保守,也不要事后补救。
- 分级授权而非一刀切。 低风险操作(读文件、搜索)自动放行,中风险操作(API 调用)设预算上限,高风险操作(创建资源、删除数据)必须人工审批或直接禁用。
- 可观测性是最后一道防线。 即使前两层防护全部失效,实时监控和告警也能让你在损失扩大前手动介入。为每个 Agent 实例部署独立的 Metrics 端点。
相关工具与框架推荐:
- 📊 LangSmith — LLM 应用的可观测性平台,支持 Agent 调用链追踪与成本分析
- 🔒 Guardrails AI — Python 框架,提供输入/输出校验和 Agent 行为约束
- 💰 Helicone — LLM API 代理层,支持成本追踪、速率限制和预算告警
- 🛡️ NeMo Guardrails — NVIDIA 出品的 Agent 安全框架,支持可编程的行为约束
- 📈 OpenTelemetry — 可观测性标准,用于 Agent 行为追踪和成本指标收集