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))
|
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"`
|
TraceData string `json:"traceData"`
|
||||||
CreatedAt string `json:"createdAt"`
|
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 (
|
import (
|
||||||
"cs-agent/internal/models"
|
"cs-agent/internal/models"
|
||||||
|
"cs-agent/internal/pkg/toolx"
|
||||||
"cs-agent/internal/repositories"
|
"cs-agent/internal/repositories"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -17,6 +18,18 @@ func newAgentRunLogService() *agentRunLogService {
|
|||||||
|
|
||||||
type agentRunLogService struct{}
|
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 {
|
func (s *agentRunLogService) Get(id int64) *models.AgentRunLog {
|
||||||
return repositories.AgentRunLogRepository.Get(sqls.DB(), id)
|
return repositories.AgentRunLogRepository.Get(sqls.DB(), id)
|
||||||
}
|
}
|
||||||
@@ -83,3 +96,37 @@ func (s *agentRunLogService) ApplyHITLStatusFilter(cnd *sqls.Cnd, hitlStatus str
|
|||||||
}
|
}
|
||||||
return cnd
|
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 { OptionCombobox } from "@/components/option-combobox"
|
||||||
import { Badge } from "@/components/ui/badge"
|
import { Badge } from "@/components/ui/badge"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card"
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerContent,
|
DrawerContent,
|
||||||
@@ -32,8 +39,10 @@ import {
|
|||||||
} from "@/components/ui/table"
|
} from "@/components/ui/table"
|
||||||
import {
|
import {
|
||||||
fetchAgentRunLog,
|
fetchAgentRunLog,
|
||||||
|
fetchAgentRunGraphSummary,
|
||||||
fetchAgentRunLogs,
|
fetchAgentRunLogs,
|
||||||
fetchAIAgentsAll,
|
fetchAIAgentsAll,
|
||||||
|
type AgentRunGraphSummary,
|
||||||
type AgentRunLog,
|
type AgentRunLog,
|
||||||
type AIAgent,
|
type AIAgent,
|
||||||
type PageResult,
|
type PageResult,
|
||||||
@@ -119,6 +128,7 @@ export default function DashboardAgentRunLogsPage() {
|
|||||||
const [page, setPage] = useState(1)
|
const [page, setPage] = useState(1)
|
||||||
const [limit, setLimit] = useState(20)
|
const [limit, setLimit] = useState(20)
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [summaryLoading, setSummaryLoading] = useState(true)
|
||||||
const [detailLoading, setDetailLoading] = useState(false)
|
const [detailLoading, setDetailLoading] = useState(false)
|
||||||
const [detailOpen, setDetailOpen] = useState(false)
|
const [detailOpen, setDetailOpen] = useState(false)
|
||||||
const [activeLog, setActiveLog] = useState<AgentRunLog | null>(null)
|
const [activeLog, setActiveLog] = useState<AgentRunLog | null>(null)
|
||||||
@@ -126,6 +136,17 @@ export default function DashboardAgentRunLogsPage() {
|
|||||||
results: [],
|
results: [],
|
||||||
page: { page: 1, limit: 20, total: 0 },
|
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 [aiAgents, setAiAgents] = useState<AIAgent[]>([])
|
||||||
const activeTraceData = useMemo(
|
const activeTraceData = useMemo(
|
||||||
() => safeParseJSON(activeLog?.traceData ?? ""),
|
() => safeParseJSON(activeLog?.traceData ?? ""),
|
||||||
@@ -176,6 +197,23 @@ export default function DashboardAgentRunLogsPage() {
|
|||||||
void loadData()
|
void loadData()
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
async function loadAIAgents() {
|
async function loadAIAgents() {
|
||||||
try {
|
try {
|
||||||
@@ -294,6 +332,57 @@ export default function DashboardAgentRunLogsPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</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">
|
<div className="overflow-hidden rounded-lg border bg-background">
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader className="bg-muted/40">
|
<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) {
|
function safeParseJSON(value: string) {
|
||||||
if (!value.trim()) {
|
if (!value.trim()) {
|
||||||
return null
|
return null
|
||||||
|
|||||||
@@ -416,6 +416,18 @@ export type AgentRunLog = {
|
|||||||
createdAt: string
|
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 = {
|
export type AdminAgentProfile = {
|
||||||
id: number
|
id: number
|
||||||
userId: number
|
userId: number
|
||||||
@@ -895,6 +907,12 @@ export function fetchAgentRunLog(id: number) {
|
|||||||
return request<AgentRunLog>(`/api/console/agent-run-log/${id}`)
|
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) {
|
export function updateSkillDefinitionStatus(id: number, status: number) {
|
||||||
return request<void>("/api/console/skill-definition/update_status", {
|
return request<void>("/api/console/skill-definition/update_status", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|||||||
Reference in New Issue
Block a user