forked from zhanggangyong/agent_management
272 lines
9.7 KiB
Python
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"
|
|
)
|