Files
agent_management/web_service/app.py
T
2026-01-05 12:44:28 +00:00

272 lines
9.7 KiB
Python

"""
Agent Manager Web Service
提供RESTful API来管理AKS上的AI Agent服务
"""
from fastapi import FastAPI, HTTPException, status
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any, List
import logging
import sys
import os
# 添加父目录到路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from agent_manager import AKSAgentManager
from agent_manager.exceptions import (
AgentNotFoundError,
AgentDeploymentError,
QuotaExceededError,
ConcurrencyLimitError,
CircuitBreakerOpenError
)
from web_service.config import config
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# 创建FastAPI应用
app = FastAPI(
title="Agent Manager API",
description="用于管理AKS上AI Agent服务的RESTful API",
version="1.0.0"
)
# 全局管理器实例
manager: Optional[AKSAgentManager] = None
# ==================== 请求/响应模型 ====================
class AgentCreateRequest(BaseModel):
"""创建Agent请求"""
name: str = Field(..., description="Agent名称")
template: str = Field(..., description="模板类型 (basic_agent, mcp_agent, echo_agent, task_worker)")
config: Dict[str, Any] = Field(..., description="Agent配置")
namespace: Optional[str] = Field(None, description="K8s命名空间")
user_id: Optional[str] = Field("default", description="用户ID")
timeout_seconds: Optional[int] = Field(3600, description="超时时间(秒)")
auto_cleanup: bool = Field(True, description="是否自动清理")
class HealthResponse(BaseModel):
"""健康检查响应"""
status: str
aks_connected: bool
cluster_name: str
# ==================== 启动/关闭事件 ====================
@app.on_event("startup")
async def startup_event():
"""应用启动事件"""
global manager
try:
logger.info("正在启动Agent Manager Web Service...")
# 验证配置
config.validate()
if config.aks_config.use_local_kubeconfig:
logger.info("使用本地 kubeconfig 连接到 Kubernetes 集群")
else:
logger.info(f"配置验证通过: AKS集群={config.aks_config.cluster_name}")
# 初始化管理器
manager = AKSAgentManager(
subscription_id=config.aks_config.subscription_id,
resource_group=config.aks_config.resource_group,
cluster_name=config.aks_config.cluster_name,
default_namespace=config.aks_config.default_namespace,
auto_connect=True,
enable_quota_management=config.enable_quota_management,
enable_lifecycle_management=config.enable_lifecycle_management,
enable_retry_mechanism=config.enable_retry_mechanism,
enable_metering=config.enable_metering,
cleanup_interval=config.cleanup_interval,
use_local_kubeconfig=config.aks_config.use_local_kubeconfig
)
logger.info("Agent Manager Web Service 启动成功")
except Exception as e:
logger.error(f"启动失败: {str(e)}")
raise
@app.on_event("shutdown")
async def shutdown_event():
"""应用关闭事件"""
global manager
logger.info("正在关闭Agent Manager Web Service...")
if manager and manager.lifecycle_manager:
manager.lifecycle_manager.stop_cleanup_worker()
logger.info("Agent Manager Web Service 已关闭")
# ==================== 健康检查 ====================
@app.get("/health", response_model=HealthResponse, tags=["健康检查"])
async def health_check():
"""健康检查接口"""
try:
cluster_info = manager.aks_client.get_cluster_info()
cluster_name = config.aks_config.cluster_name or cluster_info.get("cluster_name", "unknown")
return HealthResponse(
status="healthy",
aks_connected=True,
cluster_name=cluster_name
)
except Exception as e:
cluster_name = config.aks_config.cluster_name or "unknown"
return HealthResponse(
status="unhealthy",
aks_connected=False,
cluster_name=cluster_name
)
# ==================== Agent管理 ====================
@app.post("/agents", status_code=status.HTTP_201_CREATED, tags=["Agent管理"])
async def create_agent(request: AgentCreateRequest):
"""
创建新的AI Agent,返回详细信息包括Pod ID用于归属确认
"""
try:
result = manager.create_agent(
name=request.name,
template=request.template,
config=request.config,
namespace=request.namespace,
user_id=request.user_id,
timeout_seconds=request.timeout_seconds,
auto_cleanup=request.auto_cleanup
)
# 获取详细信息
namespace = request.namespace or config.aks_config.default_namespace
# 获取 Pod 信息
try:
pods = manager.aks_client.core_v1_api.list_namespaced_pod(
namespace=namespace,
label_selector=f"app={request.name}"
)
pod_info = [{
"pod_id": pod.metadata.uid,
"pod_name": pod.metadata.name,
"status": pod.status.phase,
"node_name": pod.spec.node_name,
"pod_ip": pod.status.pod_ip,
"host_ip": pod.status.host_ip,
"creation_timestamp": pod.metadata.creation_timestamp.isoformat() if pod.metadata.creation_timestamp else None,
"labels": pod.metadata.labels,
"owner": {
"user_id": pod.metadata.labels.get("user-id", request.user_id) if pod.metadata.labels else request.user_id,
"agent_name": request.name,
"namespace": namespace
}
} for pod in pods.items]
except Exception as e:
logger.warning(f"获取 Pod 信息失败: {str(e)}")
pod_info = []
# 获取 Deployment 信息
try:
deployment = manager.aks_client.apps_v1_api.read_namespaced_deployment(
name=request.name,
namespace=namespace
)
deployment_info = {
"deployment_id": deployment.metadata.uid,
"deployment_name": deployment.metadata.name,
"replicas": {
"desired": deployment.spec.replicas,
"ready": deployment.status.ready_replicas or 0,
"available": deployment.status.available_replicas or 0
},
"labels": deployment.metadata.labels,
"creation_timestamp": deployment.metadata.creation_timestamp.isoformat() if deployment.metadata.creation_timestamp else None
}
except Exception as e:
logger.warning(f"获取 Deployment 信息失败: {str(e)}")
deployment_info = None
return {
"message": f"Agent {request.name} 创建成功",
"agent": {
"name": request.name,
"namespace": namespace,
"template": request.template,
"user_id": request.user_id,
"timeout_seconds": request.timeout_seconds,
"auto_cleanup": request.auto_cleanup
},
"deployment": deployment_info,
"pods": pod_info,
"summary": {
"total_pods": len(pod_info),
"running_pods": len([p for p in pod_info if p["status"] == "Running"]),
"owner_user_id": request.user_id
}
}
except QuotaExceededError as e:
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(e))
except ConcurrencyLimitError as e:
raise HTTPException(status_code=status.HTTP_429_TOO_MANY_REQUESTS, detail=str(e))
except CircuitBreakerOpenError as e:
raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=str(e))
except AgentDeploymentError as e:
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
except Exception as e:
logger.error(f"创建Agent失败: {str(e)}")
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@app.get("/agents", tags=["Agent管理"])
async def list_agents(namespace: Optional[str] = None, label_selector: Optional[str] = None):
"""列出所有AI Agent"""
try:
agents = manager.list_agents(namespace=namespace, label_selector=label_selector)
return {"count": len(agents), "agents": agents}
except Exception as e:
logger.error(f"列出Agents失败: {str(e)}")
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
@app.delete("/agents/{agent_name}", tags=["Agent管理"])
async def delete_agent(agent_name: str, namespace: Optional[str] = None, user_id: Optional[str] = None):
"""删除指定的Agent"""
try:
manager.delete_agent(name=agent_name, namespace=namespace, user_id=user_id)
return {"message": f"Agent {agent_name} 删除成功"}
except AgentNotFoundError as e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
except Exception as e:
logger.error(f"删除Agent失败: {str(e)}")
raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"app:app",
host=config.host,
port=config.port,
workers=config.workers,
log_level="info"
)