AI Agent 开发实战(十三):Agent 上生产——容错、成本与限流

Demo 跑通了,不代表生产就能用。Agent 上生产面临三个核心问题:容错(LLM 不稳定怎么办)、成本(Token 烧钱怎么办)、限流(并发爆了怎么办)。本文给出经过实战验证的解法,也是本系列的收官篇。


一、生产环境的三个硬约束

Demo 阶段:正确性优先,偶尔失败无所谓
生产阶段:稳定性 > 成本 > 正确性
约束风险不处理的后果
容错LLM 超时 / 格式错乱 / API 503用户看到报错,流程中断
成本每次调用 0.01~0.5 元,日调用 10 万次月账单 3~150 万
限流突发流量打满 API Rate Limit429 错误,服务不可用

三个约束互相关联:容错需要重试,重试增加成本,限流控制成本但牺牲可用性。下面逐一拆解。


二、容错:让 Agent 在 LLM 不稳定时仍能工作

2.1 LLM 调用的三类故障

1. 网络层故障:超时、连接拒绝、DNS 解析失败
2. API 层故障:429 限流、500 服务端错误、401 凭证过期
3. 内容层故障:输出格式不对、幻觉、Token 超限截断

2.2 重试策略:不是简单的 for 循环

// ❌ 错误示范:固定间隔重试
for (int i = 0; i < 3; i++) {
    try {
        return llm.call(prompt);
    } catch (Exception e) {
        Thread.sleep(1000); // 固定等1秒
    }
}

// ✅ 正确示范:指数退避 + 抖动
public <T> T callWithRetry(Supplier<T> action, int maxRetries) {
    for (int i = 0; i <= maxRetries; i++) {
        try {
            return action.get();
        } catch (RetryableException e) {
            if (i == maxRetries) throw e;
            long base = (long) Math.pow(2, i) * 1000;       // 1s, 2s, 4s
            long jitter = (long) (Math.random() * 1000);     // 0~1s 随机抖动
            Thread.sleep(base + jitter);
        }
    }
    throw new RuntimeException("unreachable");
}

为什么加抖动? 如果 10 个请求同时超时、同时重试,会造成"惊群效应"——重试请求再次撞上服务端限流。加随机抖动让重试错开。

2.3 降级策略:LLM 不可用时的 Plan B

┌─────────────────────────────────────────────┐
│              Agent 请求入口                   │
└───────────────────┬─────────────────────────┘
                    │
                    ▼
            ┌───────────────┐
            │  主模型调用    │ ──→ 成功 → 返回结果
            │  (GPT-4/Claude)│
            └───────┬───────┘
                    │ 失败
                    ▼
            ┌───────────────┐
            │  备用模型调用  │ ──→ 成功 → 返回结果 + 降级标记
            │  (小模型/本地) │
            └───────┬───────┘
                    │ 失败
                    ▼
            ┌───────────────┐
            │  缓存/规则兜底│ ──→ 返回预设答案
            └───────────────┘
public String callWithFallback(String prompt) {
    // 主模型
    try {
        return primaryModel.call(prompt);
    } catch (Exception e) {
        log.warn("主模型调用失败,尝试降级", e);
    }
    
    // 备用模型
    try {
        String result = fallbackModel.call(prompt);
        markAsDegraded(result);  // 标记降级结果
        return result;
    } catch (Exception e) {
        log.error("备用模型也失败了", e);
    }
    
    // 兜底:返回缓存结果或规则引擎输出
    return cacheOrRuleFallback(prompt);
}

2.4 输出格式容错:Schema 校验 + 自动修复

LLM 返回 JSON 不一定合法,需要校验和修复:

public <T> T parseOutput(String raw, Class<T> type) {
    // 1. 尝试直接解析
    try {
        return objectMapper.readValue(raw, type);
    } catch (JsonProcessingException e) {
        log.warn("直接解析失败,尝试修复");
    }
    
    // 2. 提取 JSON 片段(LLM 可能在 JSON 前后加了说明文字)
    String json = extractJsonBlock(raw);
    try {
        return objectMapper.readValue(json, type);
    } catch (JsonProcessingException e) {
        log.warn("JSON 修复失败,触发重试");
    }
    
    // 3. 修复失败,抛出 RetryableException 触发重试
    throw new RetryableException("LLM output format error");
}

三、成本:让 Agent 不烧钱

3.1 Token 成本的真实账单

以一个 Agent 工作流为例(3 步 LLM 调用 + 2 次工具调用):

步骤输入 Token输出 Token单价(GPT-4o)单次成本
规划2,000500$5/$15 /M$0.018
工具调用解析3,000300$5/$15 /M$0.020
结果总结4,000800$5/$15 /M$0.032
合计9,0001,600$0.070

日调用量 1 万次 → 月成本 = 0.07 × 10000 × 30 = $21,000

3.2 降本三板斧

第一斧:缓存相似请求

// 语义缓存:对相似问题复用结果
public String callWithCache(String prompt) {
    // 1. 计算嵌入向量
    float[] embedding = embeddingModel.embed(prompt);
    
    // 2. 在缓存中找最近邻
    CacheEntry hit = semanticCache.findNearest(embedding, threshold = 0.95);
    if (hit != null) {
        log.info("命中语义缓存,相似度={}", hit.similarity);
        return hit.response;
    }
    
    // 3. 缓存未命中,调 LLM
    String response = llm.call(prompt);
    semanticCache.put(embedding, prompt, response);
    return response;
}

语义缓存命中率通常 30%~60%,直接砍掉一半成本。

第二斧:小模型做简单任务

任务分类:
┌──────────────────────────────────────────────────────┐
│  简单任务(70%)         复杂任务(30%)              │
│  - 信息提取              - 多步推理                   │
│  - 格式转换              - 创意写作                   │
│  - 分类打标签            - 复杂代码生成               │
│  → 用 GPT-4o-mini       → 用 GPT-4o/Claude          │
│  成本:$0.15/$0.6 /M    成本:$5/$15 /M              │
└──────────────────────────────────────────────────────┘

综合成本:0.7 × 0.005 + 0.3 × 0.07 = $0.024(降 65%)

第三斧:Prompt 瘦身

// ❌ 每次都带完整上下文(Token 浪费)
String prompt = "你是一个客服助手。以下是历史对话:" + fullHistory + "用户说:" + userMessage;

// ✅ 只带最近 N 轮 + 摘要
String prompt = "你是一个客服助手。历史摘要:" + summarizeHistory(fullHistory) 
    + "最近对话:" + recentN(fullHistory, 3) 
    + "用户说:" + userMessage;
优化手段成本降幅实现难度
语义缓存30%~60%中(需向量库)
小模型分流50%~70%低(路由规则)
Prompt 瘦身20%~40%低(摘要截断)

四、限流:让 Agent 不被打爆

4.1 三层限流架构

┌─────────────────────────────────────────────────────┐
│  第一层:入口限流(用户维度)                         │
│  - 每用户 QPS 限制                                   │
│  - 每用户日调用量限制                                 │
├─────────────────────────────────────────────────────┤
│  第二层:任务限流(Agent 维度)                       │
│  - 并发 Agent 数量限制                                │
│  - 单 Agent 超时熔断                                  │
├─────────────────────────────────────────────────────┤
│  第三层:API 限流(LLM Provider 维度)               │
│  - Token Rate Limit                                  │
│  - RPM / TPM 限制                                    │
│  - 多 Key 轮转                                       │
└─────────────────────────────────────────────────────┘

4.2 实现:令牌桶 + 信号量

// 入口限流:令牌桶
@Component
public class RateLimiter {
    private final ConcurrentHashMap<String, RateLimiter> userLimiters = new ConcurrentHashMap<>();
    
    public boolean tryAcquire(String userId) {
        RateLimiter limiter = userLimiters.computeIfAbsent(userId, 
            id -> RateLimiter.create(10)); // 每用户 10 QPS
        return limiter.tryAcquire();
    }
}

// Agent 并发限流:信号量
@Component
public class AgentPool {
    private final Semaphore semaphore = new Semaphore(50); // 最多 50 个并发 Agent
    
    public <T> T execute(AgentTask<T> task) throws InterruptedException {
        if (!semaphore.tryAcquire(30, TimeUnit.SECONDS)) {
            throw new AgentBusyException("Agent 并发已满,请稍后重试");
        }
        try {
            return task.run();
        } finally {
            semaphore.release();
        }
    }
}

4.3 LLM API 限流:多 Key 轮转

单个 API Key 的 RPM(Requests Per Minute)有限,用多 Key 轮转突破:

@Component
public class MultiKeyLlmClient {
    private final List<LlmKey> keys;
    private final AtomicInteger index = new AtomicInteger(0);
    
    public String call(String prompt) {
        for (int i = 0; i < keys.size(); i++) {
            LlmKey key = keys.get(index.getAndIncrement() % keys.size());
            try {
                return doCall(key, prompt);
            } catch (RateLimitException e) {
                log.warn("Key {} 被限流,尝试下一个", key.id);
                key.markRateLimited(Duration.ofMinutes(1));
            }
        }
        throw new AllKeysRateLimitedException("所有 Key 均被限流");
    }
}

4.4 熔断:连续失败后自动暂停

// 基于 Circuit Breaker 模式
@Component
public class LlmCircuitBreaker {
    private final AtomicInteger failures = new AtomicInteger(0);
    private volatile long lastFailureTime = 0;
    private volatile boolean open = false;
    
    private static final int FAILURE_THRESHOLD = 5;
    private static final long RESET_WINDOW_MS = 60_000; // 1 分钟
    
    public String call(String prompt) {
        if (open) {
            if (System.currentTimeMillis() - lastFailureTime > RESET_WINDOW_MS) {
                open = false;  // 尝试半开
                failures.set(0);
            } else {
                throw new CircuitOpenException("熔断器已打开");
            }
        }
        
        try {
            String result = llm.call(prompt);
            failures.set(0);
            return result;
        } catch (Exception e) {
            if (failures.incrementAndGet() >= FAILURE_THRESHOLD) {
                open = true;
                lastFailureTime = System.currentTimeMillis();
                log.error("LLM 熔断器打开,连续失败 {} 次", failures.get());
            }
            throw e;
        }
    }
}

五、可观测性:生产环境的眼睛

没有监控的生产就是盲飞。Agent 的监控需要三层:

1. 业务指标:任务成功率、平均耗时、用户满意度
2. LLM 指标:Token 用量、每次调用成本、P95 延迟
3. 系统指标:QPS、限流拒绝数、熔断器状态
@Component
public class AgentMetrics {
    private final MeterRegistry registry;
    
    public void recordLlmCall(String model, long inputTokens, long outputTokens, 
                              long latencyMs, boolean success) {
        registry.counter("agent.llm.calls", "model", model, "success", String.valueOf(success)).increment();
        registry.counter("agent.llm.tokens.input", "model", model).increment(inputTokens);
        registry.counter("agent.llm.tokens.output", "model", model).increment(outputTokens);
        registry.timer("agent.llm.latency", "model", model).record(latencyMs, TimeUnit.MILLISECONDS);
    }
    
    public void recordCost(String model, double cost) {
        registry.gauge("agent.llm.cost", Tags.of("model", model), cost);
    }
}

六、生产部署清单

上线前逐项检查:

类别检查项状态
容错重试策略(指数退避 + 抖动)
容错降级方案(备用模型 + 规则兜底)
容错输出 Schema 校验 + 自动修复
容错超时设置(单步 30s / 全局 3min)
成本Token 用量监控告警
成本语义缓存命中率 > 30%
成本简单任务分流到小模型
成本日/月费用上限和熔断
限流用户 QPS 限制
限流Agent 并发数限制
限流LLM API 多 Key 轮转
限流熔断器(连续失败自动暂停)
可观测业务成功率大盘
可观测LLM Token / 成本趋势
可观测限流拒绝 / 熔断告警
安全Prompt 注入防护
安全输出内容过滤(敏感词 / PII)
安全API Key 加密存储

七、成本与稳定性的平衡决策树

                  请求到达
                     │
            ┌────────┴────────┐
            │ 是否命中缓存?   │
            └────────┬────────┘
              命中 ↙     ↘ 未命中
           直接返回      │
                  ┌─────┴─────┐
                  │ 任务复杂度  │
                  └─────┬─────┘
               简单 ↙     ↘ 复杂
            小模型调用    大模型调用
               │             │
          ┌────┴────┐   ┌───┴───┐
          │ 成功?   │   │ 成功? │
          └────┬────┘   └───┬───┘
         是 ↙  ↘ 否     是 ↙  ↘ 否
        返回   重试     返回   重试
          │     │        │     │
          │   ┌─┴─┐      │   ┌─┴──┐
          │   │超限│      │   │超限 │
          │   └─┬─┘      │   └─┬──┘
          │  是 ↙ ↘ 否   │  是 ↙  ↘ 否
          │ 降级   再试   │  降级   再试
          │  │              │  │
          └──┴──────────────┴──┘
                   │
              记录指标
             计算成本

八、系列总结

13 篇走完,从概念到生产,这条路线是:

基础认知(1-4)     →  Agent 是什么、LLM/记忆/工具三大基石
框架实战(5-6)     →  Java 生态框架横评、Spring AI 入门
工程化进阶(7-10)  →  约束管理、Schema、Grill Me、设计模式
高级与落地(11-13) →  Multi-Agent、可观测性、生产部署
阶段核心能力关键词
基础认知理解 Agent 架构ReAct、Memory、Tool
框架实战能跑通第一个 AgentSpring AI、LangChain4j
工程化进阶让 Agent 输出可控Harness、Schema、Grill Me
高级落地让 Agent 上生产Multi-Agent、可观测性、容错限流

最后一句话:Agent 不是玩具,但也不是银弹。把它当做一个需要容错、限流、监控的分布式系统来对待,才能真正上生产。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐