feat: add AgentRunGraphSummary and related functionality for graph summary retrieval
This commit is contained in:
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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<AgentRunLog | null>(null)
|
||||
@@ -126,6 +136,17 @@ export default function DashboardAgentRunLogsPage() {
|
||||
results: [],
|
||||
page: { page: 1, limit: 20, total: 0 },
|
||||
})
|
||||
const [graphSummary, setGraphSummary] = useState<AgentRunGraphSummary>({
|
||||
triageCount: 0,
|
||||
triagePrepareTicket: 0,
|
||||
triagePrepareTicketReady: 0,
|
||||
triageHandoff: 0,
|
||||
triageContinueAnswering: 0,
|
||||
analyzeCount: 0,
|
||||
prepareDraftCount: 0,
|
||||
createTicketCount: 0,
|
||||
handoffCount: 0,
|
||||
})
|
||||
const [aiAgents, setAiAgents] = useState<AIAgent[]>([])
|
||||
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() {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<SummaryCard
|
||||
title="统一分流"
|
||||
description="triage_service_request 调用次数"
|
||||
value={graphSummary.triageCount}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="推荐建单"
|
||||
description="triage 推荐 prepare_ticket 次数"
|
||||
value={graphSummary.triagePrepareTicket}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="草稿就绪"
|
||||
description="triage 推荐建单且 ticketDraft 已就绪"
|
||||
value={graphSummary.triagePrepareTicketReady}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="推荐转人工"
|
||||
description="triage 推荐 handoff_to_human 次数"
|
||||
value={graphSummary.triageHandoff}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="继续解答"
|
||||
description="triage 推荐 continue_answering 次数"
|
||||
value={graphSummary.triageContinueAnswering}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="风险分析"
|
||||
description="analyze_conversation 调用次数"
|
||||
value={graphSummary.analyzeCount}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="工单草稿"
|
||||
description="prepare_ticket_draft 调用次数"
|
||||
value={graphSummary.prepareDraftCount}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
<SummaryCard
|
||||
title="最终动作"
|
||||
description={`建单 ${graphSummary.createTicketCount} / 转人工 ${graphSummary.handoffCount}`}
|
||||
value={graphSummary.createTicketCount + graphSummary.handoffCount}
|
||||
loading={summaryLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="overflow-hidden rounded-lg border bg-background">
|
||||
<Table>
|
||||
<TableHeader className="bg-muted/40">
|
||||
@@ -521,6 +610,32 @@ export default function DashboardAgentRunLogsPage() {
|
||||
)
|
||||
}
|
||||
|
||||
function SummaryCard({
|
||||
title,
|
||||
description,
|
||||
value,
|
||||
loading,
|
||||
}: {
|
||||
title: string
|
||||
description: string
|
||||
value: number
|
||||
loading: boolean
|
||||
}) {
|
||||
return (
|
||||
<Card size="sm">
|
||||
<CardHeader>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
<CardDescription>{description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-semibold tracking-tight">
|
||||
{loading ? "--" : value}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
function safeParseJSON(value: string) {
|
||||
if (!value.trim()) {
|
||||
return null
|
||||
|
||||
@@ -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<AgentRunLog>(`/api/console/agent-run-log/${id}`)
|
||||
}
|
||||
|
||||
export function fetchAgentRunGraphSummary(params?: { aiAgentId?: string }) {
|
||||
return request<AgentRunGraphSummary>(
|
||||
`/api/console/agent-run-log/graph/summary${toQueryString(params)}`
|
||||
)
|
||||
}
|
||||
|
||||
export function updateSkillDefinitionStatus(id: number, status: number) {
|
||||
return request<void>("/api/console/skill-definition/update_status", {
|
||||
method: "POST",
|
||||
|
||||
Reference in New Issue
Block a user