diff --git a/internal/controllers/console/agent_run_log_controller.go b/internal/controllers/console/agent_run_log_controller.go index 2fd805a..9280858 100644 --- a/internal/controllers/console/agent_run_log_controller.go +++ b/internal/controllers/console/agent_run_log_controller.go @@ -58,3 +58,22 @@ func (c *AgentRunLogController) GetBy(id int64) *web.JsonResult { } return web.JsonData(builders.BuildAgentRunLog(item)) } + +func (c *AgentRunLogController) AnyGraphSummary() *web.JsonResult { + if _, err := services.AuthService.RequirePermission(c.Ctx, constants.PermissionConversationView); err != nil { + return web.JsonError(err) + } + aiAgentID, _ := params.GetInt64(c.Ctx, "aiAgentId") + summary := services.AgentRunLogService.BuildGraphSummary(aiAgentID) + return web.JsonData(&response.AgentRunGraphSummaryResponse{ + TriageCount: summary.TriageCount, + TriagePrepareTicket: summary.TriagePrepareTicket, + TriagePrepareTicketReady: summary.TriagePrepareTicketReady, + TriageHandoff: summary.TriageHandoff, + TriageContinueAnswering: summary.TriageContinueAnswering, + AnalyzeCount: summary.AnalyzeCount, + PrepareDraftCount: summary.PrepareDraftCount, + CreateTicketCount: summary.CreateTicketCount, + HandoffCount: summary.HandoffCount, + }) +} diff --git a/internal/pkg/dto/response/skill_response.go b/internal/pkg/dto/response/skill_response.go index a46586f..16f4a96 100644 --- a/internal/pkg/dto/response/skill_response.go +++ b/internal/pkg/dto/response/skill_response.go @@ -71,3 +71,15 @@ type AgentRunLogResponse struct { TraceData string `json:"traceData"` CreatedAt string `json:"createdAt"` } + +type AgentRunGraphSummaryResponse struct { + TriageCount int64 `json:"triageCount"` + TriagePrepareTicket int64 `json:"triagePrepareTicket"` + TriagePrepareTicketReady int64 `json:"triagePrepareTicketReady"` + TriageHandoff int64 `json:"triageHandoff"` + TriageContinueAnswering int64 `json:"triageContinueAnswering"` + AnalyzeCount int64 `json:"analyzeCount"` + PrepareDraftCount int64 `json:"prepareDraftCount"` + CreateTicketCount int64 `json:"createTicketCount"` + HandoffCount int64 `json:"handoffCount"` +} diff --git a/internal/services/agent_run_log_service.go b/internal/services/agent_run_log_service.go index fa186b8..793dec8 100644 --- a/internal/services/agent_run_log_service.go +++ b/internal/services/agent_run_log_service.go @@ -2,6 +2,7 @@ package services import ( "cs-agent/internal/models" + "cs-agent/internal/pkg/toolx" "cs-agent/internal/repositories" "strings" @@ -17,6 +18,18 @@ func newAgentRunLogService() *agentRunLogService { type agentRunLogService struct{} +type AgentRunGraphSummary struct { + TriageCount int64 + TriagePrepareTicket int64 + TriagePrepareTicketReady int64 + TriageHandoff int64 + TriageContinueAnswering int64 + AnalyzeCount int64 + PrepareDraftCount int64 + CreateTicketCount int64 + HandoffCount int64 +} + func (s *agentRunLogService) Get(id int64) *models.AgentRunLog { return repositories.AgentRunLogRepository.Get(sqls.DB(), id) } @@ -83,3 +96,37 @@ func (s *agentRunLogService) ApplyHITLStatusFilter(cnd *sqls.Cnd, hitlStatus str } return cnd } + +func (s *agentRunLogService) BuildGraphSummary(aiAgentID int64) *AgentRunGraphSummary { + buildBaseCnd := func() *sqls.Cnd { + cnd := sqls.NewCnd() + if aiAgentID > 0 { + cnd.Eq("ai_agent_id", aiAgentID) + } + return cnd + } + return &AgentRunGraphSummary{ + TriageCount: s.countByGraphTool(buildBaseCnd(), toolx.GraphTriageServiceRequestToolCode), + TriagePrepareTicket: s.countByGraphToolAndPlanReason(buildBaseCnd(), toolx.GraphTriageServiceRequestToolCode, "%prepare_ticket%"), + TriagePrepareTicketReady: s.countByGraphToolAndPlanReason(buildBaseCnd(), toolx.GraphTriageServiceRequestToolCode, "%prepare_ticket with ready ticket draft%"), + TriageHandoff: s.countByGraphToolAndPlanReason(buildBaseCnd(), toolx.GraphTriageServiceRequestToolCode, "%handoff_to_human%"), + TriageContinueAnswering: s.countByGraphToolAndPlanReason(buildBaseCnd(), toolx.GraphTriageServiceRequestToolCode, "%continue_answering%"), + AnalyzeCount: s.countByGraphTool(buildBaseCnd(), toolx.GraphAnalyzeConversationToolCode), + PrepareDraftCount: s.countByGraphTool(buildBaseCnd(), toolx.GraphPrepareTicketDraftToolCode), + CreateTicketCount: s.countByGraphTool(buildBaseCnd(), toolx.GraphCreateTicketConfirmToolCode), + HandoffCount: s.countByGraphTool(buildBaseCnd(), toolx.GraphHandoffConversationToolCode), + } +} + +func (s *agentRunLogService) countByGraphTool(baseCnd *sqls.Cnd, graphToolCode string) int64 { + cnd := baseCnd + cnd.Eq("graph_tool_code", strings.TrimSpace(graphToolCode)) + return s.Count(cnd) +} + +func (s *agentRunLogService) countByGraphToolAndPlanReason(baseCnd *sqls.Cnd, graphToolCode string, planReasonLike string) int64 { + cnd := baseCnd + cnd.Eq("graph_tool_code", strings.TrimSpace(graphToolCode)) + cnd.Where("plan_reason LIKE ?", strings.TrimSpace(planReasonLike)) + return s.Count(cnd) +} diff --git a/web/app/(console)/agent-run-logs/page.tsx b/web/app/(console)/agent-run-logs/page.tsx index 1d8256f..edfc834 100644 --- a/web/app/(console)/agent-run-logs/page.tsx +++ b/web/app/(console)/agent-run-logs/page.tsx @@ -13,6 +13,13 @@ import { ListPagination } from "@/components/list-pagination" import { OptionCombobox } from "@/components/option-combobox" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card" import { Drawer, DrawerContent, @@ -32,8 +39,10 @@ import { } from "@/components/ui/table" import { fetchAgentRunLog, + fetchAgentRunGraphSummary, fetchAgentRunLogs, fetchAIAgentsAll, + type AgentRunGraphSummary, type AgentRunLog, type AIAgent, type PageResult, @@ -119,6 +128,7 @@ export default function DashboardAgentRunLogsPage() { const [page, setPage] = useState(1) const [limit, setLimit] = useState(20) const [loading, setLoading] = useState(true) + const [summaryLoading, setSummaryLoading] = useState(true) const [detailLoading, setDetailLoading] = useState(false) const [detailOpen, setDetailOpen] = useState(false) const [activeLog, setActiveLog] = useState(null) @@ -126,6 +136,17 @@ export default function DashboardAgentRunLogsPage() { results: [], page: { page: 1, limit: 20, total: 0 }, }) + const [graphSummary, setGraphSummary] = useState({ + triageCount: 0, + triagePrepareTicket: 0, + triagePrepareTicketReady: 0, + triageHandoff: 0, + triageContinueAnswering: 0, + analyzeCount: 0, + prepareDraftCount: 0, + createTicketCount: 0, + handoffCount: 0, + }) const [aiAgents, setAiAgents] = useState([]) const activeTraceData = useMemo( () => safeParseJSON(activeLog?.traceData ?? ""), @@ -176,6 +197,23 @@ export default function DashboardAgentRunLogsPage() { void loadData() }, [loadData]) + useEffect(() => { + async function loadGraphSummary() { + setSummaryLoading(true) + try { + const data = await fetchAgentRunGraphSummary({ + aiAgentId: aiAgentId === "all" ? undefined : aiAgentId, + }) + setGraphSummary(data) + } catch (error) { + toast.error(error instanceof Error ? error.message : "加载 Graph 摘要失败") + } finally { + setSummaryLoading(false) + } + } + void loadGraphSummary() + }, [aiAgentId]) + useEffect(() => { async function loadAIAgents() { try { @@ -294,6 +332,57 @@ export default function DashboardAgentRunLogsPage() { +
+ + + + + + + + +
+
@@ -521,6 +610,32 @@ export default function DashboardAgentRunLogsPage() { ) } +function SummaryCard({ + title, + description, + value, + loading, +}: { + title: string + description: string + value: number + loading: boolean +}) { + return ( + + + {title} + {description} + + +
+ {loading ? "--" : value} +
+
+
+ ) +} + function safeParseJSON(value: string) { if (!value.trim()) { return null diff --git a/web/lib/api/admin.ts b/web/lib/api/admin.ts index 40cf148..2b778e3 100644 --- a/web/lib/api/admin.ts +++ b/web/lib/api/admin.ts @@ -416,6 +416,18 @@ export type AgentRunLog = { createdAt: string } +export type AgentRunGraphSummary = { + triageCount: number + triagePrepareTicket: number + triagePrepareTicketReady: number + triageHandoff: number + triageContinueAnswering: number + analyzeCount: number + prepareDraftCount: number + createTicketCount: number + handoffCount: number +} + export type AdminAgentProfile = { id: number userId: number @@ -895,6 +907,12 @@ export function fetchAgentRunLog(id: number) { return request(`/api/console/agent-run-log/${id}`) } +export function fetchAgentRunGraphSummary(params?: { aiAgentId?: string }) { + return request( + `/api/console/agent-run-log/graph/summary${toQueryString(params)}` + ) +} + export function updateSkillDefinitionStatus(id: number, status: number) { return request("/api/console/skill-definition/update_status", { method: "POST",