- agent_runtime: orchestrator、core/memory/schemas 调整 - agent_monitoring API、service、agent_llm_log 模型与 database 注册 - 前端 AgentDashboard、AgentConfig、Agents/MainLayout/路由与 AgentChat - 文档:(红头)项目核心文档汇总、自主AI Agent改造完成情况、AI agent改造计划 Made-with: Cursor
235 lines
8.2 KiB
Python
235 lines
8.2 KiB
Python
"""
|
||
Agent 监控服务 — 提供 Agent 专属统计数据
|
||
"""
|
||
from sqlalchemy.orm import Session
|
||
from sqlalchemy import func, and_
|
||
from datetime import datetime, timedelta
|
||
from typing import Any, Dict, List, Optional
|
||
from app.models.agent_llm_log import AgentLLMLog
|
||
from app.models.agent import Agent
|
||
from app.models.execution import Execution
|
||
import logging
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
class AgentMonitoringService:
|
||
"""Agent 监控服务"""
|
||
|
||
@staticmethod
|
||
def get_overview(db: Session, user_id: Optional[str] = None) -> Dict[str, Any]:
|
||
"""
|
||
获取 Agent 概览统计。
|
||
- 总对话次数(Execution 中 agent_id 不为空的记录数)
|
||
- 总 LLM 调用次数
|
||
- 总 tokens 数(近似)
|
||
- 总工具调用次数
|
||
- 活跃 Agent 数
|
||
"""
|
||
user_filter = Agent.user_id == user_id if user_id else True
|
||
agent_ids_query = db.query(Agent.id).filter(user_filter)
|
||
agent_ids = {row[0] for row in agent_ids_query.all()}
|
||
|
||
# Agent 数量
|
||
agent_count = len(agent_ids)
|
||
|
||
# 对话次数(Execution 表中 agent 执行的记录)
|
||
exec_filter = Execution.agent_id.in_(agent_ids) if agent_ids else False
|
||
chat_count = 0
|
||
if agent_ids:
|
||
chat_count = db.query(func.count(Execution.id)).filter(exec_filter).scalar() or 0
|
||
|
||
# LLM 调用统计
|
||
llm_filter = AgentLLMLog.agent_id.in_(agent_ids) if agent_ids else False
|
||
llm_count = 0
|
||
total_prompt = 0
|
||
total_completion = 0
|
||
if agent_ids:
|
||
stats = db.query(
|
||
func.count(AgentLLMLog.id),
|
||
func.coalesce(func.sum(AgentLLMLog.prompt_tokens), 0),
|
||
func.coalesce(func.sum(AgentLLMLog.completion_tokens), 0),
|
||
).filter(llm_filter).first()
|
||
llm_count = stats[0] or 0
|
||
total_prompt = stats[1] or 0
|
||
total_completion = stats[2] or 0
|
||
|
||
# 工具调用次数(从 ExecutionLog 统计 agent 相关)
|
||
tool_call_count = 0
|
||
if agent_ids:
|
||
tool_call_count = db.query(func.count(AgentLLMLog.id)).filter(
|
||
and_(
|
||
AgentLLMLog.agent_id.in_(agent_ids),
|
||
AgentLLMLog.tool_name.isnot(None),
|
||
)
|
||
).scalar() or 0
|
||
|
||
return {
|
||
"agent_count": agent_count,
|
||
"chat_count": chat_count,
|
||
"llm_call_count": llm_count,
|
||
"total_prompt_tokens": total_prompt,
|
||
"total_completion_tokens": total_completion,
|
||
"total_tokens": total_prompt + total_completion,
|
||
"tool_call_count": tool_call_count,
|
||
}
|
||
|
||
@staticmethod
|
||
def get_llm_calls(
|
||
db: Session,
|
||
user_id: Optional[str] = None,
|
||
days: int = 7,
|
||
limit: int = 50,
|
||
) -> List[Dict[str, Any]]:
|
||
"""获取最近 LLM 调用记录。"""
|
||
end_time = datetime.utcnow()
|
||
start_time = end_time - timedelta(days=days)
|
||
|
||
filters = [AgentLLMLog.created_at >= start_time]
|
||
if user_id:
|
||
filters.append(AgentLLMLog.user_id == user_id)
|
||
|
||
records = db.query(AgentLLMLog).filter(
|
||
and_(*filters)
|
||
).order_by(AgentLLMLog.created_at.desc()).limit(limit).all()
|
||
|
||
return [
|
||
{
|
||
"id": r.id,
|
||
"agent_id": r.agent_id,
|
||
"session_id": r.session_id,
|
||
"model": r.model,
|
||
"provider": r.provider,
|
||
"prompt_tokens": r.prompt_tokens,
|
||
"completion_tokens": r.completion_tokens,
|
||
"total_tokens": r.total_tokens,
|
||
"latency_ms": r.latency_ms,
|
||
"iteration_number": r.iteration_number,
|
||
"step_type": r.step_type,
|
||
"tool_name": r.tool_name,
|
||
"status": r.status,
|
||
"error_message": r.error_message,
|
||
"created_at": r.created_at.isoformat() if r.created_at else None,
|
||
}
|
||
for r in records
|
||
]
|
||
|
||
@staticmethod
|
||
def get_agent_stats(
|
||
db: Session,
|
||
user_id: Optional[str] = None,
|
||
days: int = 7,
|
||
) -> List[Dict[str, Any]]:
|
||
"""获取各 Agent 的用量统计(按 Agent 分组)。"""
|
||
end_time = datetime.utcnow()
|
||
start_time = end_time - timedelta(days=days)
|
||
|
||
filters = [AgentLLMLog.created_at >= start_time]
|
||
if user_id:
|
||
filters.append(AgentLLMLog.user_id == user_id)
|
||
|
||
rows = db.query(
|
||
AgentLLMLog.agent_id,
|
||
Agent.name.label("agent_name"),
|
||
func.count(AgentLLMLog.id).label("call_count"),
|
||
func.coalesce(func.sum(AgentLLMLog.prompt_tokens), 0).label("total_prompt"),
|
||
func.coalesce(func.sum(AgentLLMLog.completion_tokens), 0).label("total_completion"),
|
||
func.coalesce(func.sum(AgentLLMLog.total_tokens), 0).label("total_tokens"),
|
||
func.coalesce(func.avg(AgentLLMLog.latency_ms), 0).label("avg_latency"),
|
||
func.count(AgentLLMLog.tool_name).label("tool_calls"),
|
||
).outerjoin(
|
||
Agent, AgentLLMLog.agent_id == Agent.id
|
||
).filter(
|
||
and_(*filters)
|
||
).group_by(AgentLLMLog.agent_id).order_by(
|
||
func.count(AgentLLMLog.id).desc()
|
||
).all()
|
||
|
||
return [
|
||
{
|
||
"agent_id": r.agent_id or "未知",
|
||
"agent_name": r.agent_name or "未知 Agent",
|
||
"call_count": r.call_count,
|
||
"total_prompt_tokens": r.total_prompt,
|
||
"total_completion_tokens": r.total_completion,
|
||
"total_tokens": r.total_tokens,
|
||
"avg_latency_ms": round(r.avg_latency, 2) if r.avg_latency else 0,
|
||
"tool_call_count": r.tool_calls or 0,
|
||
}
|
||
for r in rows
|
||
]
|
||
|
||
@staticmethod
|
||
def get_tool_usage(
|
||
db: Session,
|
||
user_id: Optional[str] = None,
|
||
days: int = 7,
|
||
) -> List[Dict[str, Any]]:
|
||
"""获取工具调用频次统计。"""
|
||
end_time = datetime.utcnow()
|
||
start_time = end_time - timedelta(days=days)
|
||
|
||
filters = [
|
||
AgentLLMLog.created_at >= start_time,
|
||
AgentLLMLog.tool_name.isnot(None),
|
||
]
|
||
if user_id:
|
||
filters.append(AgentLLMLog.user_id == user_id)
|
||
|
||
rows = db.query(
|
||
AgentLLMLog.tool_name,
|
||
func.count(AgentLLMLog.id).label("call_count"),
|
||
func.coalesce(func.sum(AgentLLMLog.total_tokens), 0).label("total_tokens"),
|
||
func.coalesce(func.avg(AgentLLMLog.latency_ms), 0).label("avg_latency"),
|
||
).filter(
|
||
and_(*filters)
|
||
).group_by(AgentLLMLog.tool_name).order_by(
|
||
func.count(AgentLLMLog.id).desc()
|
||
).all()
|
||
|
||
return [
|
||
{
|
||
"tool_name": r.tool_name or "未知",
|
||
"call_count": r.call_count,
|
||
"total_tokens": r.total_tokens,
|
||
"avg_latency_ms": round(r.avg_latency, 2) if r.avg_latency else 0,
|
||
}
|
||
for r in rows
|
||
]
|
||
|
||
@staticmethod
|
||
def get_daily_trend(
|
||
db: Session,
|
||
user_id: Optional[str] = None,
|
||
days: int = 7,
|
||
) -> List[Dict[str, Any]]:
|
||
"""获取每日 LLM 调用趋势。"""
|
||
end_time = datetime.utcnow()
|
||
start_time = end_time - timedelta(days=days)
|
||
|
||
filters = [AgentLLMLog.created_at >= start_time]
|
||
if user_id:
|
||
filters.append(AgentLLMLog.user_id == user_id)
|
||
|
||
results = []
|
||
for i in range(days):
|
||
day_start = end_time - timedelta(days=days - i)
|
||
day_end = day_start + timedelta(days=1)
|
||
day_filter = and_(
|
||
*filters,
|
||
AgentLLMLog.created_at >= day_start,
|
||
AgentLLMLog.created_at < day_end,
|
||
)
|
||
day_count = db.query(func.count(AgentLLMLog.id)).filter(day_filter).scalar() or 0
|
||
day_tokens = db.query(
|
||
func.coalesce(func.sum(AgentLLMLog.total_tokens), 0)
|
||
).filter(day_filter).scalar() or 0
|
||
|
||
results.append({
|
||
"date": day_start.strftime("%m-%d"),
|
||
"call_count": day_count,
|
||
"total_tokens": day_tokens,
|
||
})
|
||
|
||
return results
|