引言

Agent SaaS平台在2026年面临的核心挑战之一是多租户架构设计。不同租户的Agent可能使用不同的模型、不同的工具集、不同的Prompt模板,且对性能、安全性和成本的要求差异巨大。如何在共享基础设施上实现高效的资源隔离和公平的成本分摊,是Agent平台架构师必须解决的问题。

多租户隔离模型

三种隔离级别

隔离程度 ──────────────────────────────────▶ 强

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│  共享模式     │  │  混合模式     │  │  独占模式     │
│              │  │              │  │              │
│ 共享所有资源  │  │ 共享计算资源  │  │ 独立资源栈    │
│ 逻辑隔离数据  │  │ 隔离存储资源  │  │ 物理隔离      │
│              │  │              │  │              │
│ 成本最低      │  │ 平衡          │  │ 隔离最强      │
│ 隔离最弱      │  │              │  │ 成本最高      │
└──────────────┘  └──────────────┘  └──────────────┘
from enum import Enum

class IsolationLevel(Enum):
    SHARED = "shared"       # 共享模式:所有租户共享同一Agent实例
    HYBRID = "hybrid"       # 混合模式:共享计算,隔离存储
    DEDICATED = "dedicated" # 独占模式:每个租户独立资源栈

class TenantConfig:
    """租户配置"""
    def __init__(
        self,
        tenant_id: str,
        tier: str,  # free, pro, enterprise
        isolation: IsolationLevel,
        quota: dict,
        custom_config: dict = None
    ):
        self.tenant_id = tenant_id
        self.tier = tier
        self.isolation = isolation
        self.quota = quota
        self.custom_config = custom_config or {}
        
        # 根据tier设置默认配额
        if not quota:
            self.quota = self._default_quota(tier)
    
    @staticmethod
    def _default_quota(tier: str) -> dict:
        defaults = {
            "free": {
                "max_sessions": 10,
                "max_concurrent": 2,
                "max_tokens_per_day": 100000,
                "max_tools": 5,
                "max_memory_mb": 256,
                "rate_limit_rpm": 20,  # 每分钟请求数
            },
            "pro": {
                "max_sessions": 100,
                "max_concurrent": 10,
                "max_tokens_per_day": 2000000,
                "max_tools": 20,
                "max_memory_mb": 2048,
                "rate_limit_rpm": 200,
            },
            "enterprise": {
                "max_sessions": -1,  # 无限
                "max_concurrent": 100,
                "max_tokens_per_day": 50000000,
                "max_tools": -1,
                "max_memory_mb": 32768,
                "rate_limit_rpm": 2000,
            }
        }
        return defaults.get(tier, defaults["free"])

资源隔离实现

计算资源隔离

class TenantResourceManager:
    """租户资源管理器"""
    
    def __init__(self, k8s_client):
        self.k8s = k8s_client
        self.tenant_pools = {}  # tenant_id -> resource pool
    
    async def get_or_create_pool(
        self,
        tenant: TenantConfig
    ) -> str:
        """获取或创建租户资源池"""
        
        if tenant.tenant_id in self.tenant_pools:
            return self.tenant_pools[tenant.tenant_id]
        
        if tenant.isolation == IsolationLevel.DEDICATED:
            # 独占模式:创建独立namespace和资源
            namespace = await self._create_dedicated_namespace(tenant)
            await self._deploy_dedicated_resources(tenant, namespace)
            self.tenant_pools[tenant.tenant_id] = namespace
            
        elif tenant.isolation == IsolationLevel.HYBRID:
            # 混合模式:使用共享namespace但设置ResourceQuota
            await self._apply_resource_quota(tenant)
            self.tenant_pools[tenant.tenant_id] = "shared"
            
        else:
            # 共享模式:仅通过应用层隔离
            self.tenant_pools[tenant.tenant_id] = "shared"
        
        return self.tenant_pools[tenant.tenant_id]
    
    async def _apply_resource_quota(self, tenant: TenantConfig):
        """应用K8s ResourceQuota"""
        quota_yaml = {
            "apiVersion": "v1",
            "kind": "ResourceQuota",
            "metadata": {
                "name": f"quota-{tenant.tenant_id}",
                "namespace": "agent-shared"
            },
            "spec": {
                "hard": {
                    "requests.cpu": f"{tenant.quota['max_cpu']}",
                    "requests.memory": f"{tenant.quota['max_memory_mb']}Mi",
                    "pods": str(tenant.quota["max_pods"]),
                }
            }
        }
        await self.k8s.apply_resource(quota_yaml)


class TenantRateLimiter:
    """租户级限流器"""
    
    def __init__(self, redis_client):
        self.redis = redis_client
    
    async def check_and_consume(
        self,
        tenant_id: str,
        resource: str,  # "api_call", "token", "tool_exec"
        amount: int = 1
    ) -> bool:
        """检查配额并消费"""
        
        # 滑动窗口限流
        key = f"quota:{tenant_id}:{resource}:{datetime.now().strftime('%Y%m%d%H%M')}"
        
        pipe = self.redis.pipeline()
        pipe.incr(key, amount)
        pipe.expire(key, 3600)  # 1小时TTL
        results = await pipe.execute()
        
        current_usage = results[0]
        limit = await self._get_limit(tenant_id, resource)
        
        if current_usage > limit:
            # 回滚消费
            await self.redis.decr(key, amount)
            return False
        
        return True

数据隔离

class TenantDataIsolation:
    """租户数据隔离管理"""
    
    def __init__(self, db_client):
        self.db = db_client
    
    async def execute_for_tenant(
        self,
        tenant_id: str,
        query: str,
        params: tuple = None
    ):
        """在租户上下文中执行查询"""
        # 方式1:Row-Level Security (PostgreSQL RLS)
        await self.db.execute(
            f"SET app.current_tenant = '{tenant_id}'"
        )
        try:
            result = await self.db.fetch(query, *(params or ()))
            return result
        finally:
            await self.db.execute("RESET app.current_tenant")
    
    async def get_vector_store_for_tenant(
        self,
        tenant_id: str,
        collection_name: str
    ):
        """获取租户专属的向量存储"""
        # 使用租户ID作为namespace前缀
        namespaced_collection = f"tenant_{tenant_id}_{collection_name}"
        
        return VectorStore(
            collection=namespaced_collection,
            metadata_filter={"tenant_id": tenant_id}  # 双重保障
        )

成本分摊模型

class CostAllocator:
    """成本分摊器——精确追踪每租户的资源消耗"""
    
    # 2026年典型成本基准(美元)
    COST_RATES = {
        "llm_token_input": 0.00001,    # per token
        "llm_token_output": 0.00003,   # per token
        "embedding_token": 0.0000001,  # per token
        "vector_search": 0.0001,       # per 1k queries
        "tool_execution": 0.001,       # per execution
        "memory_storage_gb_month": 0.10,
        "gpu_hour": 2.50,              # per GPU hour
        "cpu_hour": 0.05,
    }
    
    def __init__(self, metrics_store):
        self.metrics = metrics_store
    
    async def record_usage(
        self,
        tenant_id: str,
        resource: str,
        amount: float,
        session_id: str = None
    ):
        """记录资源使用"""
        cost = amount * self.COST_RATES.get(resource, 0)
        
        await self.metrics.insert({
            "tenant_id": tenant_id,
            "resource": resource,
            "amount": amount,
            "cost": cost,
            "session_id": session_id,
            "timestamp": datetime.now()
        })
    
    async def calculate_bill(
        self,
        tenant_id: str,
        period_start: datetime,
        period_end: datetime
    ) -> dict:
        """计算租户账单"""
        usage = await self.metrics.aggregate(
            tenant_id=tenant_id,
            start=period_start,
            end=period_end
        )
        
        bill = {
            "tenant_id": tenant_id,
            "period": f"{period_start.date()} to {period_end.date()}",
            "items": [],
            "total": 0
        }
        
        for resource, amount in usage.items():
            rate = self.COST_RATES.get(resource, 0)
            cost = amount * rate
            bill["items"].append({
                "resource": resource,
                "amount": amount,
                "rate": rate,
                "cost": round(cost, 4)
            })
            bill["total"] += cost
        
        # 应用tier折扣
        tier = await self._get_tenant_tier(tenant_id)
        discount = {"free": 0, "pro": 0.1, "enterprise": 0.25}.get(tier, 0)
        bill["discount"] = round(bill["total"] * discount, 2)
        bill["final_total"] = round(bill["total"] - bill["discount"], 2)
        
        return bill

实时成本监控

class RealtimeCostMonitor:
    """实时成本监控与告警"""
    
    async def monitor_tenant(self, tenant_id: str):
        """监控租户实时成本"""
        while True:
            daily_cost = await self._get_daily_cost(tenant_id)
            budget = await self._get_budget(tenant_id)
            
            utilization = daily_cost / budget if budget > 0 else 0
            
            if utilization > 0.9:
                await self._alert(
                    tenant_id=tenant_id,
                    level="critical",
                    message=f"Budget at {utilization:.0%}: ${daily_cost:.2f}/${budget:.2f}"
                )
                # 触发降级或限流
                if utilization > 1.0:
                    await self._throttle_tenant(tenant_id)
            
            elif utilization > 0.7:
                await self._alert(
                    tenant_id=tenant_id,
                    level="warning",
                    message=f"Budget at {utilization:.0%}"
                )
            
            await asyncio.sleep(60)  # 每分钟检查

租户级配置管理

class TenantConfigManager:
    """租户配置管理器"""
    
    async def get_agent_config(self, tenant_id: str) -> dict:
        """获取租户专属的Agent配置"""
        base_config = {
            "model": "gpt-4o-mini",
            "temperature": 0.7,
            "max_tokens": 4096,
            "tools": ["search", "calculator"],
            "system_prompt": "You are a helpful assistant.",
            "safety_level": "standard"
        }
        
        # 合并租户自定义配置
        tenant_overrides = await self._load_tenant_overrides(tenant_id)
        config = {**base_config, **tenant_overrides}
        
        # 应用tier限制
        tier = await self._get_tier(tenant_id)
        if tier == "free":
            config["model"] = "gpt-4o-mini"  # 限制免费用户使用小模型
            config["max_tokens"] = min(config["max_tokens"], 2048)
        
        return config
    
    async def validate_config_change(
        self,
        tenant_id: str,
        new_config: dict
    ) -> dict:
        """验证租户配置变更"""
        tier = await self._get_tier(tenant_id)
        tier_limits = self.TIER_LIMITS[tier]
        
        errors = []
        
        # 检查模型权限
        if new_config.get("model") not in tier_limits["allowed_models"]:
            errors.append(f"Model {new_config['model']} not available for {tier} tier")
        
        # 检查工具数量
        if len(new_config.get("tools", [])) > tier_limits["max_tools"]:
            errors.append(f"Too many tools for {tier} tier")
        
        # 检查安全级别
        if new_config.get("safety_level") == "none" and tier != "enterprise":
            errors.append("Safety level 'none' requires enterprise tier")
        
        return {"valid": len(errors) == 0, "errors": errors}

安全边界设计

class TenantSecurityBoundary:
    """租户安全边界"""
    
    async def enforce_boundary(self, tenant_id: str, request: dict):
        """执行安全边界检查"""
        
        # 1. 防止跨租户数据访问
        if request.get("target_tenant") and request["target_tenant"] != tenant_id:
            raise SecurityViolation("Cross-tenant access denied")
        
        # 2. 工具白名单检查
        allowed_tools = await self._get_allowed_tools(tenant_id)
        for tool in request.get("tools", []):
            if tool not in allowed_tools:
                raise SecurityViolation(f"Tool '{tool}' not allowed for tenant")
        
        # 3. 出站请求域名白名单
        if request.get("api_endpoints"):
            allowed_domains = await self._get_allowed_domains(tenant_id)
            for endpoint in request["api_endpoints"]:
                domain = urllib.parse.urlparse(endpoint).hostname
                if domain not in allowed_domains:
                    raise SecurityViolation(f"Domain '{domain}' not allowed")
        
        # 4. 敏感操作审计
        if request.get("action") in ["file_write", "code_exec", "network_access"]:
            await self._audit_log(tenant_id, request)

总结

Agent多租户架构的核心是在资源共享与租户隔离之间找到平衡点。共享模式成本最低但隔离最弱,独占模式隔离最强但成本最高,混合模式是大多数SaaS平台的最佳选择。无论选择哪种模式,都必须建立完善的配额管理、成本分摊和安全边界机制。

核心原则:多租户系统的成功不仅取决于技术架构,更取决于成本透明度和安全可信度。租户需要清楚知道自己的资源消耗和费用,平台需要确保租户间的数据安全和行为隔离。

加入讨论

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

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