diff --git a/Docs/项目文档/API接口文档_监控功能.md b/Docs/项目文档/API接口文档_监控功能.md new file mode 100644 index 0000000..54b8dab --- /dev/null +++ b/Docs/项目文档/API接口文档_监控功能.md @@ -0,0 +1,723 @@ +# taiji-AI-PAD API 接口文档 - 监控功能 + +**版本**: v2.0 +**创建时间**: 2025年12月23日 +**最后更新**: 2025年12月23日 + +--- + +## 📋 目录 + +1. [概述](#概述) +2. [基础信息](#基础信息) +3. [监控API端点](#监控api端点) +4. [请求/响应示例](#请求响应示例) +5. [错误处理](#错误处理) +6. [集成示例](#集成示例) + +--- + +## 1. 概述 + +本文档描述了 taiji-AI-PAD 平台监控功能的 API 接口。监控功能提供系统健康检查、性能指标、资源使用、服务统计、性能趋势和系统告警等功能。 + +### 1.1 功能特性 + +- ✅ 系统健康检查 +- ✅ 实时性能指标(CPU、内存、磁盘) +- ✅ 服务统计信息(Agent、执行、工具、用户) +- ✅ 性能趋势分析 +- ✅ 系统告警 +- ✅ 监控仪表板(聚合数据) + +### 1.2 监控指标 + +- **系统资源**: CPU使用率、内存使用、磁盘使用 +- **服务指标**: 活跃Agent数、执行次数、成功率、平均响应时间 +- **业务指标**: 日活用户、EU消耗、成本统计 +- **告警信息**: 资源告警、服务告警 + +--- + +## 2. 基础信息 + +### 2.1 基础URL + +``` +http://localhost:8002 +``` + +### 2.2 认证方式 + +当前版本无需认证,未来版本将支持 JWT Token 认证。 + +### 2.3 响应格式 + +所有API响应均为 JSON 格式,使用 UTF-8 编码。 + +### 2.4 HTTP状态码 + +| 状态码 | 说明 | +|--------|------| +| 200 | 请求成功 | +| 400 | 请求参数错误 | +| 500 | 服务器内部错误 | + +--- + +## 3. 监控API端点 + +### 3.1 系统健康检查 + +#### GET /health + +获取系统健康状态。 + +**请求参数**: 无 + +**响应示例**: +```json +{ + "status": "healthy", + "timestamp": "2025-12-23T07:30:00.000000", + "services": { + "database": "healthy", + "redis": "healthy", + "nats": "healthy" + } +} +``` + +**响应字段说明**: +- `status`: 系统整体状态 (`healthy`, `degraded`, `unhealthy`) +- `timestamp`: 检查时间戳 +- `services`: 各服务健康状态 + +--- + +### 3.2 系统性能指标 + +#### GET /api/v1/monitoring/metrics + +获取系统实时性能指标。 + +**请求参数**: 无 + +**响应示例**: +```json +{ + "timestamp": "2025-12-23T07:30:00.000000", + "system": { + "cpu_usage_percent": 15.5, + "memory_usage_percent": 45.2, + "memory_used_mb": 2048.5, + "memory_total_mb": 4096.0, + "disk_usage_percent": 32.1, + "disk_used_gb": 128.5, + "disk_total_gb": 400.0 + }, + "services": { + "active_agents": 10, + "total_executions_24h": 1250, + "success_rate_percent": 98.5, + "avg_execution_time_ms": 125.5, + "daily_active_users": 25 + }, + "billing": { + "total_eu_consumed_24h": 1250.5, + "total_cost_24h": 12.50 + } +} +``` + +**响应字段说明**: +- `system`: 系统资源使用情况 + - `cpu_usage_percent`: CPU使用率(%) + - `memory_usage_percent`: 内存使用率(%) + - `memory_used_mb`: 已使用内存(MB) + - `memory_total_mb`: 总内存(MB) + - `disk_usage_percent`: 磁盘使用率(%) + - `disk_used_gb`: 已使用磁盘(GB) + - `disk_total_gb`: 总磁盘空间(GB) +- `services`: 服务指标(过去24小时) + - `active_agents`: 活跃Agent数量 + - `total_executions_24h`: 总执行次数 + - `success_rate_percent`: 成功率(%) + - `avg_execution_time_ms`: 平均执行时间(毫秒) + - `daily_active_users`: 日活用户数 +- `billing`: 计费统计(过去24小时) + - `total_eu_consumed_24h`: 总EU消耗 + - `total_cost_24h`: 总成本 + +--- + +### 3.3 服务统计信息 + +#### GET /api/v1/monitoring/stats + +获取服务统计信息。 + +**请求参数**: +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| service | string | 否 | 服务类型,可选值: `all`, `agents`, `executions`, `tools`, `users`,默认: `all` | + +**请求示例**: +``` +GET /api/v1/monitoring/stats?service=agents +``` + +**响应示例**: +```json +{ + "timestamp": "2025-12-23T07:30:00.000000", + "stats": { + "agents": { + "total": 50, + "active": 45, + "inactive": 5, + "avg_executions": 125.5, + "avg_success_rate": 98.2 + }, + "executions": { + "total_7d": 8750, + "completed": 8600, + "failed": 100, + "running": 50, + "avg_time_ms": 125.5, + "total_eu": 8750.5 + }, + "tools": { + "total": 20, + "active": 18, + "total_calls": 12500, + "avg_success_rate": 99.5, + "avg_response_time_ms": 50.2 + }, + "users": { + "total": 100, + "active": 95, + "admins": 5 + } + } +} +``` + +**响应字段说明**: +- `agents`: Agent统计 + - `total`: 总Agent数 + - `active`: 活跃Agent数 + - `inactive`: 非活跃Agent数 + - `avg_executions`: 平均执行次数 + - `avg_success_rate`: 平均成功率 +- `executions`: 执行统计(过去7天) + - `total_7d`: 总执行次数 + - `completed`: 成功完成数 + - `failed`: 失败数 + - `running`: 运行中数 + - `avg_time_ms`: 平均执行时间(毫秒) + - `total_eu`: 总EU消耗 +- `tools`: 工具统计 + - `total`: 总工具数 + - `active`: 活跃工具数 + - `total_calls`: 总调用次数 + - `avg_success_rate`: 平均成功率 + - `avg_response_time_ms`: 平均响应时间(毫秒) +- `users`: 用户统计 + - `total`: 总用户数 + - `active`: 活跃用户数 + - `admins`: 管理员数 + +--- + +### 3.4 性能趋势数据 + +#### GET /api/v1/monitoring/trends + +获取性能趋势数据。 + +**请求参数**: +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| metric | string | 否 | 指标类型,可选值: `executions`, `eu_consumption`,默认: `executions` | +| period | string | 否 | 时间范围,可选值: `24h`, `7d`, `30d`,默认: `24h` | +| interval | string | 否 | 时间间隔,可选值: `1h`, `6h`, `1d`,默认: `1h` | + +**请求示例**: +``` +GET /api/v1/monitoring/trends?metric=executions&period=7d&interval=6h +``` + +**响应示例** (metric=executions): +```json +{ + "metric": "executions", + "period": "7d", + "interval": "6h", + "data": [ + { + "timestamp": "2025-12-23T00:00:00", + "count": 125, + "avg_time_ms": 120.5, + "success_rate": 98.5 + }, + { + "timestamp": "2025-12-23T06:00:00", + "count": 150, + "avg_time_ms": 125.2, + "success_rate": 99.0 + } + ] +} +``` + +**响应示例** (metric=eu_consumption): +```json +{ + "metric": "eu_consumption", + "period": "24h", + "interval": "1h", + "data": [ + { + "timestamp": "2025-12-23T00:00:00", + "eu_consumed": 50.5, + "cost": 0.50 + }, + { + "timestamp": "2025-12-23T01:00:00", + "eu_consumed": 52.3, + "cost": 0.52 + } + ] +} +``` + +**响应字段说明**: +- `metric`: 指标类型 +- `period`: 时间范围 +- `interval`: 时间间隔 +- `data`: 趋势数据数组 + - `timestamp`: 时间点 + - `count`: 执行次数(executions指标) + - `avg_time_ms`: 平均执行时间(executions指标) + - `success_rate`: 成功率(executions指标) + - `eu_consumed`: EU消耗(eu_consumption指标) + - `cost`: 成本(eu_consumption指标) + +--- + +### 3.5 系统告警 + +#### GET /api/v1/monitoring/alerts + +获取系统告警信息。 + +**请求参数**: +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| severity | string | 否 | 严重程度过滤,可选值: `warning`, `critical`, `info` | + +**请求示例**: +``` +GET /api/v1/monitoring/alerts?severity=critical +``` + +**响应示例**: +```json +{ + "timestamp": "2025-12-23T07:30:00.000000", + "alerts": [ + { + "severity": "warning", + "type": "high_cpu", + "message": "CPU使用率过高: 85.5%", + "timestamp": "2025-12-23T07:29:00.000000" + }, + { + "severity": "critical", + "type": "low_disk", + "message": "磁盘空间不足: 92.1%", + "timestamp": "2025-12-23T07:25:00.000000" + } + ], + "count": 2 +} +``` + +**响应字段说明**: +- `timestamp`: 查询时间 +- `alerts`: 告警列表 + - `severity`: 严重程度 (`warning`, `critical`, `info`) + - `type`: 告警类型 (`high_cpu`, `high_memory`, `low_disk`, `high_failure_rate`) + - `message`: 告警消息 + - `timestamp`: 告警时间 +- `count`: 告警总数 + +**告警类型说明**: +- `high_cpu`: CPU使用率 > 80% +- `high_memory`: 内存使用率 > 85% +- `low_disk`: 磁盘使用率 > 90% +- `high_failure_rate`: 过去1小时内失败执行 > 10次 + +--- + +### 3.6 监控仪表板 + +#### GET /api/v1/monitoring/dashboard + +获取监控仪表板数据(聚合所有监控信息)。 + +**请求参数**: 无 + +**响应示例**: +```json +{ + "timestamp": "2025-12-23T07:30:00.000000", + "health": { + "status": "healthy", + "timestamp": "2025-12-23T07:30:00.000000", + "uptime_seconds": 86400, + "services": { + "database": "healthy", + "redis": "healthy", + "nats": "healthy" + } + }, + "metrics": { + "timestamp": "2025-12-23T07:30:00.000000", + "system": { + "cpu_usage_percent": 15.5, + "memory_usage_percent": 45.2, + "disk_usage_percent": 32.1 + }, + "services": { + "active_agents": 10, + "total_executions_24h": 1250, + "success_rate_percent": 98.5 + }, + "billing": { + "total_eu_consumed_24h": 1250.5, + "total_cost_24h": 12.50 + } + }, + "stats": { + "agents": { + "total": 50, + "active": 45 + }, + "executions": { + "total_7d": 8750, + "completed": 8600 + }, + "tools": { + "total": 20, + "active": 18 + }, + "users": { + "total": 100, + "active": 95 + } + }, + "alerts": { + "items": [ + { + "severity": "warning", + "type": "high_cpu", + "message": "CPU使用率过高: 85.5%", + "timestamp": "2025-12-23T07:29:00.000000" + } + ], + "count": 1, + "critical_count": 0, + "warning_count": 1 + } +} +``` + +**响应字段说明**: +- `health`: 系统健康状态 +- `metrics`: 系统性能指标 +- `stats`: 服务统计信息 +- `alerts`: 系统告警 + - `items`: 告警列表 + - `count`: 告警总数 + - `critical_count`: 严重告警数 + - `warning_count`: 警告告警数 + +--- + +## 4. 请求/响应示例 + +### 4.1 cURL 示例 + +#### 获取系统性能指标 +```bash +curl -X GET "http://localhost:8002/api/v1/monitoring/metrics" +``` + +#### 获取Agent统计 +```bash +curl -X GET "http://localhost:8002/api/v1/monitoring/stats?service=agents" +``` + +#### 获取执行趋势(7天,6小时间隔) +```bash +curl -X GET "http://localhost:8002/api/v1/monitoring/trends?metric=executions&period=7d&interval=6h" +``` + +#### 获取严重告警 +```bash +curl -X GET "http://localhost:8002/api/v1/monitoring/alerts?severity=critical" +``` + +#### 获取监控仪表板 +```bash +curl -X GET "http://localhost:8002/api/v1/monitoring/dashboard" +``` + +### 4.2 Python 示例 + +```python +import httpx +import asyncio + +async def get_monitoring_data(): + base_url = "http://localhost:8002" + + async with httpx.AsyncClient() as client: + # 获取系统指标 + metrics = await client.get(f"{base_url}/api/v1/monitoring/metrics") + print("系统指标:", metrics.json()) + + # 获取服务统计 + stats = await client.get(f"{base_url}/api/v1/monitoring/stats?service=all") + print("服务统计:", stats.json()) + + # 获取性能趋势 + trends = await client.get( + f"{base_url}/api/v1/monitoring/trends", + params={"metric": "executions", "period": "24h", "interval": "1h"} + ) + print("性能趋势:", trends.json()) + + # 获取告警 + alerts = await client.get(f"{base_url}/api/v1/monitoring/alerts") + print("系统告警:", alerts.json()) + + # 获取监控仪表板 + dashboard = await client.get(f"{base_url}/api/v1/monitoring/dashboard") + print("监控仪表板:", dashboard.json()) + +asyncio.run(get_monitoring_data()) +``` + +### 4.3 JavaScript 示例 + +```javascript +const baseUrl = 'http://localhost:8002'; + +// 获取系统指标 +async function getMetrics() { + const response = await fetch(`${baseUrl}/api/v1/monitoring/metrics`); + const data = await response.json(); + console.log('系统指标:', data); +} + +// 获取服务统计 +async function getStats(service = 'all') { + const response = await fetch(`${baseUrl}/api/v1/monitoring/stats?service=${service}`); + const data = await response.json(); + console.log('服务统计:', data); +} + +// 获取性能趋势 +async function getTrends(metric = 'executions', period = '24h', interval = '1h') { + const url = new URL(`${baseUrl}/api/v1/monitoring/trends`); + url.searchParams.append('metric', metric); + url.searchParams.append('period', period); + url.searchParams.append('interval', interval); + + const response = await fetch(url); + const data = await response.json(); + console.log('性能趋势:', data); +} + +// 获取告警 +async function getAlerts(severity = null) { + let url = `${baseUrl}/api/v1/monitoring/alerts`; + if (severity) { + url += `?severity=${severity}`; + } + + const response = await fetch(url); + const data = await response.json(); + console.log('系统告警:', data); +} + +// 获取监控仪表板 +async function getDashboard() { + const response = await fetch(`${baseUrl}/api/v1/monitoring/dashboard`); + const data = await response.json(); + console.log('监控仪表板:', data); +} + +// 使用示例 +getMetrics(); +getStats('agents'); +getTrends('executions', '7d', '6h'); +getAlerts('critical'); +getDashboard(); +``` + +--- + +## 5. 错误处理 + +### 5.1 错误响应格式 + +```json +{ + "detail": "错误描述信息" +} +``` + +### 5.2 常见错误 + +| HTTP状态码 | 错误类型 | 说明 | +|-----------|---------|------| +| 400 | Bad Request | 请求参数错误 | +| 500 | Internal Server Error | 服务器内部错误 | + +### 5.3 错误处理示例 + +```python +import httpx + +async def get_metrics_safe(): + try: + async with httpx.AsyncClient() as client: + response = await client.get("http://localhost:8002/api/v1/monitoring/metrics") + response.raise_for_status() + return response.json() + except httpx.HTTPStatusError as e: + print(f"HTTP错误: {e.response.status_code}") + print(f"错误信息: {e.response.text}") + except Exception as e: + print(f"其他错误: {e}") +``` + +--- + +## 6. 集成示例 + +### 6.1 实时监控仪表板 + +```python +import asyncio +import httpx +from datetime import datetime + +async def update_dashboard(): + """每30秒更新一次监控仪表板""" + base_url = "http://localhost:8002" + + while True: + try: + async with httpx.AsyncClient() as client: + response = await client.get(f"{base_url}/api/v1/monitoring/dashboard") + data = response.json() + + # 显示关键指标 + print(f"\n[{datetime.now()}] 监控仪表板") + print(f"系统状态: {data['health']['status']}") + print(f"CPU使用率: {data['metrics']['system']['cpu_usage_percent']:.1f}%") + print(f"内存使用率: {data['metrics']['system']['memory_usage_percent']:.1f}%") + print(f"活跃Agent: {data['metrics']['services']['active_agents']}") + print(f"24小时执行次数: {data['metrics']['services']['total_executions_24h']}") + print(f"成功率: {data['metrics']['services']['success_rate_percent']:.2f}%") + print(f"告警数量: {data['alerts']['count']} (严重: {data['alerts']['critical_count']})") + + except Exception as e: + print(f"获取监控数据失败: {e}") + + await asyncio.sleep(30) + +# 运行监控 +asyncio.run(update_dashboard()) +``` + +### 6.2 告警通知 + +```python +import httpx +import asyncio + +async def check_alerts(): + """检查系统告警并发送通知""" + base_url = "http://localhost:8002" + + async with httpx.AsyncClient() as client: + # 获取严重告警 + response = await client.get(f"{base_url}/api/v1/monitoring/alerts?severity=critical") + alerts = response.json() + + if alerts['count'] > 0: + print(f"⚠️ 发现 {alerts['count']} 个严重告警:") + for alert in alerts['alerts']: + print(f" - {alert['message']} ({alert['type']})") + # 这里可以添加通知逻辑(邮件、短信、Slack等) + + # 获取警告告警 + response = await client.get(f"{base_url}/api/v1/monitoring/alerts?severity=warning") + alerts = response.json() + + if alerts['count'] > 0: + print(f"⚠️ 发现 {alerts['count']} 个警告:") + for alert in alerts['alerts']: + print(f" - {alert['message']} ({alert['type']})") + +asyncio.run(check_alerts()) +``` + +--- + +## 7. 最佳实践 + +### 7.1 监控频率建议 + +- **系统指标**: 每30秒-1分钟查询一次 +- **服务统计**: 每5-10分钟查询一次 +- **性能趋势**: 根据需求,建议每1小时查询一次 +- **系统告警**: 每1-5分钟检查一次 + +### 7.2 性能优化 + +- 使用 `/api/v1/monitoring/dashboard` 端点获取聚合数据,减少请求次数 +- 对于趋势数据,合理选择时间范围和间隔,避免查询过大数据集 +- 使用缓存机制,避免频繁查询数据库 + +### 7.3 告警阈值建议 + +- **CPU使用率**: > 80% 警告,> 90% 严重 +- **内存使用率**: > 85% 警告,> 95% 严重 +- **磁盘使用率**: > 85% 警告,> 90% 严重 +- **失败率**: > 5% 警告,> 10% 严重 + +--- + +## 8. 更新日志 + +### v2.0 (2025-12-23) +- ✅ 新增系统性能指标API +- ✅ 新增服务统计信息API +- ✅ 新增性能趋势数据API +- ✅ 新增系统告警API +- ✅ 新增监控仪表板API +- ✅ 优化健康检查API + +--- + +**文档版本**: v2.0 +**最后更新**: 2025年12月23日 +**维护者**: taiji-AI-PAD 开发团队 + diff --git a/services/mcp-server/main.py b/services/mcp-server/main.py index c3dafc4..b78d0fa 100644 --- a/services/mcp-server/main.py +++ b/services/mcp-server/main.py @@ -9,6 +9,7 @@ import logging import os from datetime import datetime from typing import Any, Dict, List, Optional +from fastapi import Query from uuid import UUID import structlog @@ -39,6 +40,7 @@ from schemas import ( from mcp_protocol import MCPProtocolHandler from database import get_db, init_db from config import Settings +from monitoring import system_monitor # 配置日志 structlog.configure( @@ -354,39 +356,11 @@ async def handle_system_event(msg): @app.get("/health", response_model=HealthResponse) async def health_check(): """健康检查端点""" - services = { - "mcp_server": "healthy", - "redis": "unknown", - "nats": "unknown", - "database": "unknown" - } - - # 检查Redis - try: - if redis_client: - await redis_client.ping() - services["redis"] = "healthy" - except Exception: - services["redis"] = "unhealthy" - - # 检查NATS - try: - if nats_client and nats_client.is_connected: - services["nats"] = "healthy" - except Exception: - services["nats"] = "unhealthy" - - # 检查数据库连接 - try: - # 这里应该有数据库连接检查 - services["database"] = "healthy" - except Exception: - services["database"] = "unhealthy" - + health_data = await system_monitor.get_system_health() return HealthResponse( - status="healthy", - timestamp=datetime.utcnow().isoformat(), - services=services + status=health_data["status"], + timestamp=health_data["timestamp"], + services=health_data["services"] ) @app.post("/agents", response_model=AgentCard) @@ -770,6 +744,105 @@ async def get_metrics(): status_code=500 ) +# ========== 监控API端点 ========== + +@app.get("/api/v1/monitoring/metrics") +async def get_system_metrics(): + """获取系统性能指标""" + try: + metrics = await system_monitor.get_system_metrics() + return metrics + except Exception as e: + logger.error(f"获取系统指标失败: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/api/v1/monitoring/stats") +async def get_service_stats(service: str = "all"): + """获取服务统计信息 + + Args: + service: 服务类型 (all, agents, executions, tools, users) + """ + try: + stats = await system_monitor.get_service_stats(service) + return stats + except Exception as e: + logger.error(f"获取服务统计失败: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/api/v1/monitoring/trends") +async def get_performance_trends( + metric: str = "executions", + period: str = "24h", + interval: str = "1h" +): + """获取性能趋势数据 + + Args: + metric: 指标类型 (executions, eu_consumption) + period: 时间范围 (24h, 7d, 30d) + interval: 时间间隔 (1h, 6h, 1d) + """ + try: + trends = await system_monitor.get_performance_trends(metric, period, interval) + return trends + except Exception as e: + logger.error(f"获取性能趋势失败: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/api/v1/monitoring/alerts") +async def get_system_alerts(severity: Optional[str] = None): + """获取系统告警 + + Args: + severity: 严重程度过滤 (warning, critical, info) + """ + try: + alerts = await system_monitor.get_alerts(severity) + return { + "timestamp": datetime.utcnow().isoformat(), + "alerts": alerts, + "count": len(alerts) + } + except Exception as e: + logger.error(f"获取系统告警失败: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + +@app.get("/api/v1/monitoring/dashboard") +async def get_monitoring_dashboard(): + """获取监控仪表板数据(聚合所有监控信息)""" + try: + # 并行获取所有监控数据 + health_task = system_monitor.get_system_health() + metrics_task = system_monitor.get_system_metrics() + stats_task = system_monitor.get_service_stats("all") + alerts_task = system_monitor.get_alerts() + + health, metrics, stats, alerts = await asyncio.gather( + health_task, metrics_task, stats_task, alerts_task + ) + + return { + "timestamp": datetime.utcnow().isoformat(), + "health": health, + "metrics": metrics, + "stats": stats["stats"], + "alerts": { + "items": alerts, + "count": len(alerts), + "critical_count": len([a for a in alerts if a.get("severity") == "critical"]), + "warning_count": len([a for a in alerts if a.get("severity") == "warning"]), + } + } + except Exception as e: + logger.error(f"获取监控仪表板失败: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + if __name__ == "__main__": import uvicorn uvicorn.run( diff --git a/services/mcp-server/monitoring.py b/services/mcp-server/monitoring.py new file mode 100644 index 0000000..397605c --- /dev/null +++ b/services/mcp-server/monitoring.py @@ -0,0 +1,399 @@ +""" +平台监控模块 +提供系统健康检查、性能指标、资源使用等监控功能 +""" + +import asyncio +import json +import logging +import psutil +import time +from datetime import datetime, timedelta +from typing import Dict, List, Optional, Any +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy import text, func +from sqlalchemy.orm import selectinload + +from models import Agent, Execution, User, Tool, Session +from database import AsyncSessionLocal + +logger = logging.getLogger(__name__) + + +class SystemMonitor: + """系统监控类""" + + def __init__(self): + self.start_time = datetime.utcnow() + + async def get_system_health(self) -> Dict[str, Any]: + """获取系统健康状态""" + health = { + "status": "healthy", + "timestamp": datetime.utcnow().isoformat(), + "uptime_seconds": (datetime.utcnow() - self.start_time).total_seconds(), + "services": {} + } + + # 检查数据库 + try: + async with AsyncSessionLocal() as session: + await session.execute(text("SELECT 1")) + health["services"]["database"] = "healthy" + except Exception as e: + health["services"]["database"] = f"unhealthy: {str(e)}" + health["status"] = "degraded" + + return health + + async def get_system_metrics(self) -> Dict[str, Any]: + """获取系统性能指标""" + try: + # 系统资源使用 + cpu_percent = psutil.cpu_percent(interval=1) + memory = psutil.virtual_memory() + disk = psutil.disk_usage('/') + + # 数据库统计 + async with AsyncSessionLocal() as session: + # Agent统计 + agent_result = await session.execute( + text("SELECT COUNT(*) FROM agents WHERE status = 'active'") + ) + active_agents = agent_result.scalar() or 0 + + # 执行统计 + execution_result = await session.execute( + text(""" + SELECT + COUNT(*) as total, + AVG(execution_time) as avg_time, + SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END)::float / NULLIF(COUNT(*), 0) * 100 as success_rate + FROM executions + WHERE started_at > NOW() - INTERVAL '24 hours' + """) + ) + exec_stats = execution_result.fetchone() + total_executions = exec_stats[0] or 0 if exec_stats else 0 + avg_execution_time = float(exec_stats[1] or 0) if exec_stats and exec_stats[1] else 0.0 + success_rate = float(exec_stats[2] or 0) if exec_stats and exec_stats[2] else 0.0 + + # 用户统计 + user_result = await session.execute( + text(""" + SELECT COUNT(DISTINCT user_id) + FROM sessions + WHERE created_at > NOW() - INTERVAL '24 hours' + """) + ) + daily_active_users = user_result.scalar() or 0 + + # EU消耗统计 + eu_result = await session.execute( + text(""" + SELECT + SUM(eu_consumed) as total_eu, + SUM(cost) as total_cost + FROM billing + WHERE created_at > NOW() - INTERVAL '24 hours' + """) + ) + eu_stats = eu_result.fetchone() + total_eu = float(eu_stats[0] or 0) if eu_stats and eu_stats[0] else 0.0 + total_cost = float(eu_stats[1] or 0) if eu_stats and eu_stats[1] else 0.0 + + return { + "timestamp": datetime.utcnow().isoformat(), + "system": { + "cpu_usage_percent": cpu_percent, + "memory_usage_percent": memory.percent, + "memory_used_mb": memory.used / 1024 / 1024, + "memory_total_mb": memory.total / 1024 / 1024, + "disk_usage_percent": disk.percent, + "disk_used_gb": disk.used / 1024 / 1024 / 1024, + "disk_total_gb": disk.total / 1024 / 1024 / 1024, + }, + "services": { + "active_agents": active_agents, + "total_executions_24h": total_executions, + "success_rate_percent": round(success_rate, 2), + "avg_execution_time_ms": round(avg_execution_time, 2), + "daily_active_users": daily_active_users, + }, + "billing": { + "total_eu_consumed_24h": round(total_eu, 4), + "total_cost_24h": round(total_cost, 4), + } + } + except Exception as e: + logger.error(f"获取系统指标失败: {e}") + raise + + async def get_service_stats(self, service: str = "all") -> Dict[str, Any]: + """获取服务统计信息""" + try: + async with AsyncSessionLocal() as session: + stats = {} + + if service == "all" or service == "agents": + # Agent统计 + agent_stats = await session.execute( + text(""" + SELECT + COUNT(*) as total, + COUNT(CASE WHEN status = 'active' THEN 1 END) as active, + COUNT(CASE WHEN status = 'inactive' THEN 1 END) as inactive, + AVG(total_executions) as avg_executions, + AVG(success_rate) as avg_success_rate + FROM agents + """) + ) + row = agent_stats.fetchone() + if row: + stats["agents"] = { + "total": row[0] or 0, + "active": row[1] or 0, + "inactive": row[2] or 0, + "avg_executions": float(row[3] or 0), + "avg_success_rate": float(row[4] or 0), + } + + if service == "all" or service == "executions": + # 执行统计 + exec_stats = await session.execute( + text(""" + SELECT + COUNT(*) as total, + COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed, + COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed, + COUNT(CASE WHEN status = 'running' THEN 1 END) as running, + AVG(execution_time) as avg_time, + SUM(eu_consumed) as total_eu + FROM executions + WHERE started_at > NOW() - INTERVAL '7 days' + """) + ) + row = exec_stats.fetchone() + if row: + stats["executions"] = { + "total_7d": row[0] or 0, + "completed": row[1] or 0, + "failed": row[2] or 0, + "running": row[3] or 0, + "avg_time_ms": float(row[4] or 0), + "total_eu": float(row[5] or 0), + } + + if service == "all" or service == "tools": + # 工具统计 + tool_stats = await session.execute( + text(""" + SELECT + COUNT(*) as total, + COUNT(CASE WHEN is_active = true THEN 1 END) as active, + SUM(total_calls) as total_calls, + AVG(success_rate) as avg_success_rate, + AVG(avg_response_time) as avg_response_time + FROM tools + """) + ) + row = tool_stats.fetchone() + if row: + stats["tools"] = { + "total": row[0] or 0, + "active": row[1] or 0, + "total_calls": row[2] or 0, + "avg_success_rate": float(row[3] or 0), + "avg_response_time_ms": float(row[4] or 0), + } + + if service == "all" or service == "users": + # 用户统计 + user_stats = await session.execute( + text(""" + SELECT + COUNT(*) as total, + COUNT(CASE WHEN is_active = true THEN 1 END) as active, + COUNT(CASE WHEN is_admin = true THEN 1 END) as admins + FROM users + """) + ) + row = user_stats.fetchone() + if row: + stats["users"] = { + "total": row[0] or 0, + "active": row[1] or 0, + "admins": row[2] or 0, + } + + return { + "timestamp": datetime.utcnow().isoformat(), + "stats": stats + } + except Exception as e: + logger.error(f"获取服务统计失败: {e}") + raise + + async def get_performance_trends( + self, + metric: str = "executions", + period: str = "24h", + interval: str = "1h" + ) -> Dict[str, Any]: + """获取性能趋势数据""" + try: + async with AsyncSessionLocal() as session: + # 计算时间范围 + if period == "24h": + hours = 24 + elif period == "7d": + hours = 168 + elif period == "30d": + hours = 720 + else: + hours = 24 + + if interval == "1h": + interval_sql = "1 hour" + elif interval == "6h": + interval_sql = "6 hours" + elif interval == "1d": + interval_sql = "1 day" + else: + interval_sql = "1 hour" + + if metric == "executions": + result = await session.execute( + text(f""" + SELECT + DATE_TRUNC('hour', started_at) as time_bucket, + COUNT(*) as count, + AVG(execution_time) as avg_time, + SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END)::float / NULLIF(COUNT(*), 0) * 100 as success_rate + FROM executions + WHERE started_at > NOW() - INTERVAL '{hours} hours' + GROUP BY time_bucket + ORDER BY time_bucket + """) + ) + rows = result.fetchall() + return { + "metric": metric, + "period": period, + "interval": interval, + "data": [ + { + "timestamp": row[0].isoformat() if row[0] else None, + "count": row[1] or 0, + "avg_time_ms": float(row[2] or 0), + "success_rate": float(row[3] or 0), + } + for row in rows + ] + } + elif metric == "eu_consumption": + result = await session.execute( + text(f""" + SELECT + DATE_TRUNC('hour', created_at) as time_bucket, + SUM(eu_consumed) as total_eu, + SUM(cost) as total_cost + FROM billing + WHERE created_at > NOW() - INTERVAL '{hours} hours' + GROUP BY time_bucket + ORDER BY time_bucket + """) + ) + rows = result.fetchall() + return { + "metric": metric, + "period": period, + "interval": interval, + "data": [ + { + "timestamp": row[0].isoformat() if row[0] else None, + "eu_consumed": float(row[1] or 0), + "cost": float(row[2] or 0), + } + for row in rows + ] + } + else: + return { + "metric": metric, + "period": period, + "interval": interval, + "data": [] + } + except Exception as e: + logger.error(f"获取性能趋势失败: {e}") + raise + + async def get_alerts(self, severity: Optional[str] = None) -> List[Dict[str, Any]]: + """获取系统告警""" + alerts = [] + + try: + # 检查系统资源 + cpu_percent = psutil.cpu_percent(interval=1) + memory = psutil.virtual_memory() + disk = psutil.disk_usage('/') + + if cpu_percent > 80: + alerts.append({ + "severity": "warning", + "type": "high_cpu", + "message": f"CPU使用率过高: {cpu_percent:.1f}%", + "timestamp": datetime.utcnow().isoformat(), + }) + + if memory.percent > 85: + alerts.append({ + "severity": "warning", + "type": "high_memory", + "message": f"内存使用率过高: {memory.percent:.1f}%", + "timestamp": datetime.utcnow().isoformat(), + }) + + if disk.percent > 90: + alerts.append({ + "severity": "critical", + "type": "low_disk", + "message": f"磁盘空间不足: {disk.percent:.1f}%", + "timestamp": datetime.utcnow().isoformat(), + }) + + # 检查服务健康 + async with AsyncSessionLocal() as session: + # 检查失败的执行 + failed_result = await session.execute( + text(""" + SELECT COUNT(*) + FROM executions + WHERE status = 'failed' + AND started_at > NOW() - INTERVAL '1 hour' + """) + ) + failed_count = failed_result.scalar() or 0 + + if failed_count > 10: + alerts.append({ + "severity": "warning", + "type": "high_failure_rate", + "message": f"过去1小时内有 {failed_count} 次执行失败", + "timestamp": datetime.utcnow().isoformat(), + }) + + # 过滤严重程度 + if severity: + alerts = [a for a in alerts if a["severity"] == severity] + + return alerts + except Exception as e: + logger.error(f"获取告警失败: {e}") + return [] + + +# 全局监控实例 +system_monitor = SystemMonitor() + diff --git a/services/mcp-server/requirements.txt b/services/mcp-server/requirements.txt index c6d587a..48dde27 100644 --- a/services/mcp-server/requirements.txt +++ b/services/mcp-server/requirements.txt @@ -35,6 +35,7 @@ bcrypt==4.1.2 prometheus-client==0.19.0 structlog==23.2.0 rich==13.7.0 +psutil==5.9.6 # 配置管理 python-dotenv==1.0.0