引言

容量规划是Agent系统运维中最容易被忽视却最关键的环节。一个容量规划不足的系统会在流量高峰时崩溃,而过度规划则会导致巨大的资源浪费。2026年,随着Agent系统规模的扩大,容量规划已从"拍脑袋估算"进化为基于数据驱动的科学决策过程。

容量规划流程

┌──────────────────────────────────────────────────────────┐
│                  容量规划流程                              │
│                                                          │
│  Step 1: 需求预测                                        │
│  ┌────────────────────────────────────────────┐          │
│  │ 历史数据分析 → 增长趋势 → 流量预测           │          │
│  └────────────────┬───────────────────────────┘          │
│                   │                                      │
│  Step 2: 压测验证                                        │
│  ┌────────────────▼───────────────────────────┐          │
│  │ 设计压测场景 → 执行压测 → 收集性能数据       │          │
│  └────────────────┬───────────────────────────┘          │
│                   │                                      │
│  Step 3: 资源建模                                       │
│  ┌────────────────▼───────────────────────────┐          │
│  │ 建立资源消耗模型 → 计算所需资源             │          │
│  └────────────────┬───────────────────────────┘          │
│                   │                                      │
│  Step 4: 容量决策                                       │
│  ┌────────────────▼───────────────────────────┐          │
│  │ 成本分析 → 容量方案 → 采购/扩容决策          │          │
│  └────────────────┬───────────────────────────┘          │
│                   │                                      │
│  Step 5: 持续监控与调整                                  │
│  ┌────────────────▼───────────────────────────┐          │
│  │ 实时监控 → 对比预测 → 调整容量计划           │          │
│  └────────────────────────────────────────────┘          │
└──────────────────────────────────────────────────────────┘

需求预测

import numpy as np
from sklearn.linear_model import LinearRegression

class DemandForecaster:
    """需求预测器"""
    
    def __init__(self, historical_data: list):
        self.data = historical_data  # [{"date": ..., "qps": ..., "sessions": ...}]
    
    def forecast(
        self,
        horizon_days: int = 30,
        confidence_interval: float = 0.95
    ) -> dict:
        """预测未来需求"""
        
        # 准备训练数据
        X = np.array(range(len(self.data))).reshape(-1, 1)
        y_qps = np.array([d["qps"] for d in self.data])
        y_sessions = np.array([d["sessions"] for d in self.data])
        
        # 训练模型
        model_qps = LinearRegression()
        model_qps.fit(X, y_qps)
        
        model_sessions = LinearRegression()
        model_sessions.fit(X, y_sessions)
        
        # 预测
        future_X = np.array(range(
            len(self.data), 
            len(self.data) + horizon_days
        )).reshape(-1, 1)
        
        predicted_qps = model_qps.predict(future_X)
        predicted_sessions = model_sessions.predict(future_X)
        
        # 计算置信区间(简化版)
        residuals_qps = y_qps - model_qps.predict(X)
        std_qps = np.std(residuals_qps)
        
        z_score = 1.96 if confidence_interval == 0.95 else 1.645
        
        forecast = {
            "horizon_days": horizon_days,
            "predicted_qps": predicted_qps.tolist(),
            "predicted_sessions": predicted_sessions.tolist(),
            "confidence_interval": {
                "lower_qps": (predicted_qps - z_score * std_qps).tolist(),
                "upper_qps": (predicted_qps + z_score * std_qps).tolist(),
            },
            "peak_qps": float(np.max(predicted_qps)),
            "peak_sessions": float(np.max(predicted_sessions)),
        }
        
        return forecast
    
    def forecast_with_seasonality(self, horizon_days: int) -> dict:
        """考虑季节性的预测(如工作日vs周末)"""
        
        # 提取季节性模式
        daily_pattern = self._extract_daily_pattern()
        weekly_pattern = self._extract_weekly_pattern()
        
        base_forecast = self.forecast(horizon_days, 0.95)
        
        # 应用季节性调整
        adjusted = []
        for i, qps in enumerate(base_forecast["predicted_qps"]):
            day_of_week = (len(self.data) + i) % 7
            hour_of_day = (len(self.data) + i) % 24
            
            seasonal_factor = (
                daily_pattern[hour_of_day] * 
                weekly_pattern[day_of_week]
            )
            
            adjusted.append(qps * seasonal_factor)
        
        base_forecast["predicted_qps_seasonal"] = adjusted
        return base_forecast

压测方案设计

class CapacityTestPlan:
    """容量测试方案"""
    
    TEST_SCENARIOS = [
        {
            "name": "steady_load",
            "description": "稳态负载测试",
            "qps": 100,
            "duration_minutes": 60,
            "concurrent_users": 500,
        },
        {
            "name": "burst_load",
            "description": "突发负载测试",
            "qps": 500,
            "duration_minutes": 10,
            "concurrent_users": 2000,
        },
        {
            "name": "ramp_up",
            "description": "逐步加压测试",
            "start_qps": 50,
            "end_qps": 1000,
            "step_qps": 50,
            "step_duration_minutes": 5,
        },
        {
            "name": "spike_test",
            "description": "尖峰测试",
            "pattern": "spike",  # 快速上升到峰值然后下降
            "peak_qps": 2000,
            "spike_duration_minutes": 5,
        },
        {
            "name": "soak_test",
            "description": "浸泡测试(长时间运行)",
            "qps": 200,
            "duration_hours": 24,
        },
    ]
    
    async def run_capacity_tests(self) -> dict:
        """执行容量测试套件"""
        results = {}
        
        for scenario in self.TEST_SCENARIOS:
            logger.info(f"Running scenario: {scenario['name']}")
            
            result = await self._execute_test_scenario(scenario)
            results[scenario["name"]] = result
            
            # 如果系统已达极限,停止后续测试
            if result["status"] == "system_overloaded":
                logger.warning(f"System overloaded at {scenario['name']}")
                break
        
        return self._analyze_capacity_results(results)
    
    def _analyze_capacity_results(self, results: dict) -> dict:
        """分析容量测试结果"""
        analysis = {
            "max_sustainable_qps": 0,
            "max_concurrent_users": 0,
            "bottleneck": None,
            "resource_utilization_at_max": {},
            "recommendations": [],
        }
        
        for scenario_name, result in results.items():
            if result["error_rate"] < 0.01:  # 错误率<1%视为可持续
                analysis["max_sustainable_qps"] = max(
                    analysis["max_sustainable_qps"],
                    result["achieved_qps"]
                )
                analysis["max_concurrent_users"] = max(
                    analysis["max_concurrent_users"],
                    result["concurrent_users"]
                )
                
                # 记录资源利用率
                if result["achieved_qps"] > analysis["max_sustainable_qps"] * 0.9:
                    analysis["resource_utilization_at_max"] = result["resource_utilization"]
        
        # 识别瓶颈
        util = analysis["resource_utilization_at_max"]
        if util.get("gpu_utilization", 0) > 0.9:
            analysis["bottleneck"] = "GPU"
            analysis["recommendations"].append("Add more GPU nodes")
        elif util.get("cpu_utilization", 0) > 0.9:
            analysis["bottleneck"] = "CPU"
            analysis["recommendations"].append("Add more CPU nodes or optimize code")
        elif util.get("memory_utilization", 0) > 0.85:
            analysis["bottleneck"] = "Memory"
            analysis["recommendations"].append("Increase memory or optimize memory usage")
        
        return analysis

资源估算模型

class ResourceEstimator:
    """资源估算器"""
    
    # 基于压测数据的资源消耗基准
    BENCHMARKS = {
        "requests_per_gpu": 50,      # 每块GPU每秒处理的请求数
        "requests_per_cpu": 10,      # 每vCPU每秒处理的请求数(非LLM部分)
        "memory_per_session_mb": 10, # 每会话内存消耗
        "storage_per_user_mb": 100,  # 每用户存储消耗
    }
    
    def estimate_resources(
        self,
        predicted_qps: float,
        predicted_sessions: int,
        growth_margin: float = 0.3  # 30%增长余量
    ) -> dict:
        """估算所需资源"""
        
        # 1. GPU资源(LLM推理)
        required_qps_with_margin = predicted_qps * (1 + growth_margin)
        gpu_count = int(np.ceil(
            required_qps_with_margin / self.BENCHMARKS["requests_per_gpu"]
        ))
        
        # 2. CPU资源(路由、工具执行等)
        cpu_vcpus = int(np.ceil(
            required_qps_with_margin / self.BENCHMARKS["requests_per_cpu"]
        ))
        
        # 3. 内存资源
        memory_gb = int(np.ceil(
            (predicted_sessions * self.BENCHMARKS["memory_per_session_mb"]) / 1024
        )) + 8  # +8GB系统开销
        
        # 4. 存储资源
        storage_tb = (predicted_sessions * self.BENCHMARKS["storage_per_user_mb"]) / (1024 * 1024)
        
        estimate = {
            "compute": {
                "gpu": {
                    "type": "A100-80GB",
                    "count": gpu_count,
                    "utilization_target": 0.75,  # 目标利用率75%
                },
                "cpu": {
                    "vcpus": cpu_vcpus,
                    "type": "8vCPU-16GB",
                    "nodes": int(np.ceil(cpu_vcpus / 8)),
                }
            },
            "memory": {
                "total_gb": memory_gb,
                "per_node_gb": 64,
                "nodes": int(np.ceil(memory_gb / 64)),
            },
            "storage": {
                "total_tb": storage_tb * 1.5,  # 1.5x用于复制和增长
                "type": "SSD",
            },
            "network": {
                "bandwidth_gbps": 10,
                "load_balancers": int(np.ceil(gpu_count / 8)),
            }
        }
        
        return estimate
    
    def estimate_cost(
        self,
        resources: dict,
        cloud_provider: str = "aws"
    ) -> dict:
        """估算成本"""
        
        PRICING = {
            "aws": {
                "a100_gpu_hour": 4.10,      # A100每小时
                "ec2_8vcpu_hour": 0.40,     # 8vCPU实例
                "memory_gb_month": 0.005,   # $/GB/月
                "storage_tb_month": 50,      # $/TB/月
                "network_gb": 0.09,         # $/GB流量
            },
            "azure": {
                "a100_gpu_hour": 3.80,
                "vm_8vcpu_hour": 0.35,
                "memory_gb_month": 0.004,
                "storage_tb_month": 45,
                "network_gb": 0.08,
            }
        }
        
        pricing = PRICING[cloud_provider]
        
        # 计算月成本
        gpu_cost = resources["compute"]["gpu"]["count"] * pricing["a100_gpu_hour"] * 24 * 30
        cpu_cost = resources["compute"]["cpu"]["nodes"] * pricing["ec2_8vcpu_hour"] * 24 * 30
        memory_cost = resources["memory"]["total_gb"] * pricing["memory_gb_month"] * 30
        storage_cost = resources["storage"]["total_tb"] * pricing["storage_tb_month"]
        
        total_monthly = gpu_cost + cpu_cost + memory_cost + storage_cost
        
        return {
            "cloud_provider": cloud_provider,
            "resources": resources,
            "cost_breakdown": {
                "gpu": gpu_cost,
                "cpu": cpu_cost,
                "memory": memory_cost,
                "storage": storage_cost,
            },
            "total_monthly_usd": total_monthly,
            "total_annual_usd": total_monthly * 12,
            "cost_per_request_usd": total_monthly / (resources["compute"]["gpu"]["count"] * self.BENCHMARKS["requests_per_gpu"] * 24 * 30),
        }

容量规划报告

class CapacityReport:
    """容量规划报告生成器"""
    
    def generate(
        self,
        forecast: dict,
        capacity_test: dict,
        resource_estimate: dict,
        cost_estimate: dict
    ) -> str:
        """生成容量规划报告"""
        
        report = f"""
# Agent系统容量规划报告

**生成时间**: {datetime.now().strftime('%Y-%m-%d %H:%M')}

---

## 1. 需求预测

### 未来{forecast["horizon_days"]}天预测

- **峰值QPS**: {forecast["peak_qps"]:.1f}
- **峰值并发会话**: {forecast["peak_sessions"]:.0f}

### 预测曲线

QPS ^ | * | * | * | * | * |*_______ +————————> 时间 (天) 0 {forecast[“horizon_days”]//4} {forecast[“horizon_days”]//2} {forecast[“horizon_days”]*3//4} {forecast[“horizon_days”]}


**置信区间**: 95%置信水平下,QPS预测范围为 
[{forecast["confidence_interval"]["lower_qps"][-1]:.1f}, 
{forecast["confidence_interval"]["upper_qps"][-1]:.1f}]

---

## 2. 容量测试结果

### 最大可持续负载

- **最大QPS**: {capacity_test["max_sustainable_qps"]}
- **最大并发用户**: {capacity_test["max_concurrent_users"]}
- **系统瓶颈**: {capacity_test["bottleneck"]}

### 资源利用率(最大负载时)

- GPU利用率: {capacity_test["resource_utilization_at_max"].get("gpu_utilization", 0):.1%}
- CPU利用率: {capacity_test["resource_utilization_at_max"].get("cpu_utilization", 0):.1%}
- 内存利用率: {capacity_test["resource_utilization_at_max"].get("memory_utilization", 0):.1%}

---

## 3. 资源需求估算

### 计算资源

- **GPU**: {resource_estimate["compute"]["gpu"]["count"]} x {resource_estimate["compute"]["gpu"]["type"]}
- **CPU**: {resource_estimate["compute"]["cpu"]["nodes"]} 节点 x {resource_estimate["compute"]["cpu"]["type"]}

### 存储资源

- **总存储**: {resource_estimate["storage"]["total_tb"]:.1f} TB
- **类型**: {resource_estimate["storage"]["type"]}

---

## 4. 成本估算

### 月度成本明细

| 项目 | 成本 (USD) | 占比 |
|------|-----------|------|
| GPU   | ${cost_estimate["cost_breakdown"]["gpu"]:,.2f} | {cost_estimate["cost_breakdown"]["gpu"]/cost_estimate["total_monthly_usd"]*100:.1f}% |
| CPU   | ${cost_estimate["cost_breakdown"]["cpu"]:,.2f} | {cost_estimate["cost_breakdown"]["cpu"]/cost_estimate["total_monthly_usd"]*100:.1f}% |
| 内存   | ${cost_estimate["cost_breakdown"]["memory"]:,.2f} | {cost_estimate["cost_breakdown"]["memory"]/cost_estimate["total_monthly_usd"]*100:.1f}% |
| 存储   | ${cost_estimate["cost_breakdown"]["storage"]:,.2f} | {cost_estimate["cost_breakdown"]["storage"]/cost_estimate["total_monthly_usd"]*100:.1f}% |
| **合计** | **${cost_estimate["total_monthly_usd"]:,.2f}** | **100%** |

### 年度成本

- **年度总成本**: ${cost_estimate["total_annual_usd"]:,.2f}
- **单请求成本**: ${cost_estimate["cost_per_request_usd"]:.4f}

---

## 5. 建议

{chr(10).join(f"- {rec}" for rec in capacity_test["recommendations"])}

### 扩容时间表

| 时间 | 行动 | 预期成本增加 |
|------|------|-------------|
| 立即 | {capacity_test["recommendations"][0] if capacity_test["recommendations"] else "无"} | TBD |
| 3个月后 | 根据增长情况评估 | TBD |
| 6个月后 | 全面容量review | TBD |

---

## 附录:测试详情

### 压测环境

- **测试时间**: {capacity_test.get("test_time", "N/A")}
- **测试工具**: {capacity_test.get("test_tool", "N/A")}
- **监控工具**: Prometheus + Grafana

### 数据来源

- 历史数据: {forecast.get("data_source", "N/A")}
- 预测模型: {forecast.get("model_type", "N/A")}
"""
        return report

总结

Agent系统的容量规划是一个数据驱动的决策过程。需求预测基于历史数据和增长趋势,压测验证通过实际负载测试确认系统极限,资源估算模型将业务需求转化为具体的硬件需求,成本估算则帮助在性能和成本之间找到平衡点。

核心原则:容量规划不是一次性的工作,而是持续的过程。随着业务增长和技术演进,容量规划应该每月review、每季度更新,确保系统始终"刚好够用"——既不会因容量不足导致服务中断,也不会因过度规划造成资源浪费。


结语

本文完成的20篇技术博客系统覆盖了Agent架构设计与生产工程的核心主题。从微服务架构、消息总线、状态管理,到可扩展性、工作流引擎、灰度发布,再到多租户、循环检测、限流熔断,以及监控告警、日志架构、链路追踪、成本优化、自动化运维、故障排查、性能测试、回放测试、CI/CD和容量规划——这20篇文章构成了一个完整的Agent系统生产工程知识体系。

对于正在构建或运营Agent系统的团队,这些文章可以作为技术选型和架构设计的参考指南。每篇文章都力求在理论深度和实践指导之间取得平衡,既有架构原理的阐述,也有可直接参考的代码实现。

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。