feat(dashboard): 数据总览智能体图表中将以 slug 展示的智能体ID替换为名称
- 后端 AgentAnalytics 和 TimeSeriesStats 响应新增 agent_names 映射字段 - 智体统计图表(对话数/工具调用)x 轴标签替换为智能体名称 - 调用分析堆叠柱状图系列名和图例替换为智能体名称 - 前端新增 resolveAgentName 统一根据映射取名称,不存在则回退到 slug Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
7f387fd6e7
commit
5a9827b862
@ -89,6 +89,7 @@ class AgentAnalytics(BaseModel):
|
||||
agent_satisfaction_rates: list[dict]
|
||||
agent_tool_usage: list[dict]
|
||||
top_performing_agents: list[dict]
|
||||
agent_names: dict[str, str] = {} # agent_id -> agent_name 映射
|
||||
|
||||
|
||||
class ConversationListItem(BaseModel):
|
||||
@ -486,7 +487,7 @@ async def get_agent_analytics(
|
||||
):
|
||||
"""获取智能体分析(管理员权限)"""
|
||||
try:
|
||||
from yuxi.storage.postgres.models_business import Conversation, Message, MessageFeedback, ToolCall
|
||||
from yuxi.storage.postgres.models_business import Agent, Conversation, Message, MessageFeedback, ToolCall
|
||||
|
||||
# 获取所有智能体
|
||||
agents_result = await db.execute(
|
||||
@ -557,12 +558,19 @@ async def get_agent_analytics(
|
||||
top_performing_agents.sort(key=lambda x: x["conversation_count"], reverse=True)
|
||||
top_performing_agents = top_performing_agents[:5]
|
||||
|
||||
agent_slugs = [agent_id for agent_id, _ in agents if agent_id]
|
||||
agent_names = {}
|
||||
if agent_slugs:
|
||||
agent_names_result = await db.execute(select(Agent.slug, Agent.name).where(Agent.slug.in_(agent_slugs)))
|
||||
agent_names = {slug: name for slug, name in agent_names_result.all()}
|
||||
|
||||
return AgentAnalytics(
|
||||
total_agents=total_agents,
|
||||
agent_conversation_counts=agent_conversation_counts,
|
||||
agent_satisfaction_rates=agent_satisfaction,
|
||||
agent_tool_usage=agent_tool_usage,
|
||||
top_performing_agents=top_performing_agents,
|
||||
agent_names=agent_names,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
@ -718,6 +726,7 @@ class TimeSeriesStats(BaseModel):
|
||||
average_count: float
|
||||
peak_count: int
|
||||
peak_date: str
|
||||
agent_names: dict[str, str] | None = None # agent_id -> agent_name 映射(仅 type=agents)
|
||||
|
||||
|
||||
@dashboard.get("/stats/calls/timeseries", response_model=TimeSeriesStats)
|
||||
@ -729,7 +738,7 @@ async def get_call_timeseries_stats(
|
||||
):
|
||||
"""获取调用分析时间序列统计(管理员权限)"""
|
||||
try:
|
||||
from yuxi.storage.postgres.models_business import Conversation, Message, ToolCall
|
||||
from yuxi.storage.postgres.models_business import Agent, Conversation, Message, ToolCall
|
||||
|
||||
# 计算时间范围(使用北京时间 UTC+8)
|
||||
now = utc_now()
|
||||
@ -887,6 +896,13 @@ async def get_call_timeseries_stats(
|
||||
|
||||
categories = sorted(list(categories))
|
||||
|
||||
agent_names = None
|
||||
if type == "agents" and categories:
|
||||
agent_slugs = [c for c in categories if c]
|
||||
if agent_slugs:
|
||||
result = await db.execute(select(Agent.slug, Agent.name).where(Agent.slug.in_(agent_slugs)))
|
||||
agent_names = {slug: name for slug, name in result.all()}
|
||||
|
||||
# 重新组织数据:按时间点分组每个类别的数据
|
||||
time_data = {}
|
||||
|
||||
@ -961,6 +977,7 @@ async def get_call_timeseries_stats(
|
||||
average_count=average_count,
|
||||
peak_count=peak_data["total"],
|
||||
peak_date=peak_data["date"],
|
||||
agent_names=agent_names,
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
|
||||
@ -63,7 +63,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="column.key === 'agent_id'">
|
||||
<a-tag color="blue">{{ record.agent_id }}</a-tag>
|
||||
<a-tag color="blue">{{ resolveAgentName(record.agent_id) }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'satisfaction_rate'">
|
||||
<a-statistic
|
||||
@ -128,7 +128,7 @@ const performerColumns = [
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: '智能体ID',
|
||||
title: '智能体',
|
||||
key: 'agent_id',
|
||||
width: '30%'
|
||||
},
|
||||
@ -161,6 +161,10 @@ const topPerformers = computed(() => {
|
||||
return props.agentStats?.top_performing_agents || []
|
||||
})
|
||||
|
||||
const agentNames = computed(() => props.agentStats?.agent_names || {})
|
||||
|
||||
const resolveAgentName = (agentId) => agentNames.value[agentId] || agentId
|
||||
|
||||
// 初始化对话数和工具调用数合并图表
|
||||
const initConversationToolChart = () => {
|
||||
if (
|
||||
@ -235,7 +239,7 @@ const initConversationToolChart = () => {
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: topAgentIds,
|
||||
data: topAgentIds.map(resolveAgentName),
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: getCSSVariable('--gray-200')
|
||||
|
||||
@ -169,8 +169,15 @@ const renderCallStatsChart = () => {
|
||||
}
|
||||
})
|
||||
|
||||
const agentNames = callStatsData.value.agent_names || {}
|
||||
|
||||
const resolveCategoryLabel = (cat) => {
|
||||
if (cat === 'None') return '未知模型'
|
||||
return agentNames[cat] || cat
|
||||
}
|
||||
|
||||
const series = categories.map((category, index) => ({
|
||||
name: category === 'None' ? '未知模型' : category,
|
||||
name: resolveCategoryLabel(category),
|
||||
type: 'bar',
|
||||
stack: 'total',
|
||||
emphasis: { focus: 'series' },
|
||||
@ -234,7 +241,7 @@ const renderCallStatsChart = () => {
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: categories.map((cat) => (cat === 'None' ? '未知模型' : cat)),
|
||||
data: categories.map(resolveCategoryLabel),
|
||||
bottom: 5 /* 调整图例位置,从0改为5 */,
|
||||
textStyle: { color: getCSSVariable('--gray-500'), fontSize: 12 },
|
||||
itemWidth: 14,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user