BuildingAI 控制台智能体菜单和页面技术架构
本文档详细定义了控制台智能体系统的技术架构与实现方案。系统采用前后端分离设计,前端基于Vue 3+Nuxt 3技术栈,后端使用NestJS框架。核心模块包括智能体管理、配置、发布等功能,涵盖智能体全生命周期管理。前端实现智能体列表、详情、创建/编辑等页面,采用无限滚动、卡片展示等交互方式;后端提供智能体管理、配置、发布等API服务,并采用RBAC权限控制。系统支持智能体的搜索过滤、标签管理、DSL
·
1. 文档概述
1.1 文档目的
本文档旨在定义控制台智能体菜单和页面的技术架构,包括系统架构、模块划分、核心功能实现、数据结构与模型、接口设计等,为开发团队提供清晰的技术实现指南。
1.2 适用范围
适用于控制台智能体模块的所有相关页面和功能开发。
1.3 术语定义
- 智能体:具备特定能力和个性的AI实体,可与用户进行对话并执行任务
- 智能体广场:展示和分享智能体的公共平台
- DSL:领域特定语言,用于智能体的配置和导出
- MCP:模型调用协议,用于智能体与外部服务的交互
2. 技术栈
2.1 前端技术栈
- 框架:Vue 3 + TypeScript + Nuxt 3
- UI组件库:@nuxt/ui
- 状态管理:Pinia
- 路由:Nuxt Router
- HTTP客户端:Axios
2.2 后端技术栈
- 框架:NestJS + TypeScript
- ORM:TypeORM
- 数据库:PostgreSQL
- 权限:基于角色的权限控制(RBAC)
3. 系统架构
3.1 整体架构
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ 前端页面层 │ │ 后端服务层 │ │ 数据存储层 │
├─────────────────────────┤ ├─────────────────────────┤ ├─────────────────────────┤
│ 智能体列表页面 │ │ 智能体管理服务 │ │ 智能体实体表 │
│ 智能体创建/编辑页面 │ │ 智能体配置服务 │ │ 智能体配置表 │
│ 智能体详情页面 │ │ 智能体发布服务 │ │ 智能体对话记录表 │
│ 智能体配置页面 │ │ 智能体统计服务 │ │ 智能体发布历史表 │
│ 智能体发布页面 │ │ 智能体标签服务 │ │ 标签表 │
└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘
3.2 前后端交互流程
- 前端页面通过API调用后端服务
- 后端服务处理请求,进行业务逻辑处理
- 后端服务与数据库交互,获取或存储数据
- 后端服务将处理结果返回给前端页面
- 前端页面根据返回结果更新UI
4. 模块划分
4.1 前端模块
4.1.1 页面模块
- 智能体列表页面:展示所有智能体,支持搜索、过滤和批量操作
- 智能体创建/编辑页面:用于创建和编辑智能体的基本信息和配置
- 智能体详情页面:展示智能体的详细信息和配置
- 智能体配置页面:管理智能体的配置
- 智能体发布页面:发布智能体和管理发布历史
4.1.2 组件模块
- 智能体卡片组件:展示智能体的基本信息和操作按钮
- 智能体模态框组件:用于创建和编辑智能体
- 智能体DSL导入组件:用于导入智能体的DSL配置
- 智能体标签组件:用于管理智能体的标签
- 智能体预览聊天组件:用于预览智能体的聊天功能
4.2 后端模块
4.2.1 控制器模块
- 控制台控制器:处理控制台端的API请求
- Web控制器:处理Web端的API请求
4.2.2 服务模块
- 智能体管理服务:处理智能体的创建、编辑、删除等操作
- 智能体配置服务:处理智能体的配置管理
- 智能体发布服务:处理智能体的发布和撤销发布
- 智能体统计服务:处理智能体的统计信息
- 智能体标签服务:处理智能体的标签管理
4.2.3 数据访问模块
- 智能体实体:定义智能体的数据结构
- 智能体配置实体:定义智能体配置的数据结构
- 智能体对话记录实体:定义智能体对话记录的数据结构
- 智能体发布历史实体:定义智能体发布历史的数据结构
5. 核心功能实现
5.1 智能体列表页面
5.1.1 功能说明
展示所有智能体,支持搜索、过滤和批量操作。
5.1.2 技术实现
- 使用Nuxt 3的页面组件实现
- 使用@buildingai/service/consoleapi/ai-agent提供的API获取智能体列表
- 使用BdInfiniteScroll实现无限滚动加载
- 使用AgentCard组件展示智能体卡片
5.1.3 关键代码
<template>
<div class="flex w-full flex-col items-center justify-center">
<div class="bg-background sticky top-0 z-10 flex w-full flex-wrap justify-between gap-4 pb-2">
<div class="flex items-center gap-4">
<UInput
v-model="searchForm.keyword"
:placeholder="$t('ai-agent.backend.search.placeholder')"
class="w-80"
@change="getLists"
/>
<TagSelect v-model="searchForm.tagIds" @change="getLists" />
<USelect
v-model="searchForm.isPublic"
:items="[
{ label: $t('ai-agent.backend.search.allStatus'), value: undefined },
{ label: $t('ai-agent.backend.configuration.public'), value: true },
{ label: $t('ai-agent.backend.configuration.private'), value: false },
]"
:placeholder="$t('ai-agent.backend.search.filterByStatus')"
label-key="label"
value-key="value"
class="w-48"
@change="getLists"
/>
</div>
<div>
<UButton
color="primary"
variant="ghost"
icon="i-lucide-package"
:label="$t('decorate.openDecorateSettings')"
@click.stop="handleAgentDecorate"
/>
</div>
</div>
<BdScrollArea class="h-[calc(100vh-9rem)] min-h-0 w-full">
<BdInfiniteScroll
:loading="loading"
:has-more="hasMore"
:threshold="200"
:loading-text="$t('common.loading')"
:no-more-text="searchForm.page !== 1 ? $t('common.noMoreData') : ' '"
@load-more="loadMore"
>
<div class="grid grid-cols-1 gap-6 pt-2 pb-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div
class="group border-default relative cursor-pointer rounded-lg border border-dashed p-4 transition-all duration-200 hover:shadow-lg"
@click="handleCreate"
>
<div class="group-hover:text-primary text-foreground mb-3 flex items-center gap-3">
<div class="border-default flex size-10 flex-none items-center justify-center rounded-lg border border-dashed">
<UIcon name="i-lucide-plus" class="h-6 w-6" />
</div>
<h3 class="truncate text-sm font-medium">
{{ $t('ai-agent.backend.create.title') }}
</h3>
</div>
<div class="text-muted-foreground mb-6 pr-8 text-xs">
<p class="line-clamp-2 overflow-hidden">
{{ $t('ai-agent.backend.create.desc') }}
</p>
</div>
<div class="flex items-center gap-2">
<UButton
color="primary"
variant="ghost"
class="w-full"
icon="i-lucide-package"
size="sm"
:label="$t('ai-agent.backend.create.fromTemplate')"
@click.stop="handleCreateFromTemplate"
/>
<UButton
color="neutral"
variant="ghost"
class="w-full"
icon="i-lucide-file-up"
size="sm"
:label="$t('ai-agent.backend.dslImport.import')"
@click.stop="handleImportAgent"
/>
</div>
</div>
<!-- 智能体卡片 -->
<AgentCard
v-for="agent in agents"
:key="agent.id"
:agent="agent"
@delete="handleDelete"
@edit="handleEdit"
@export-dsl="handleExportDsl"
@update-tags="handleUpdateTags"
/>
</div>
</BdInfiniteScroll>
</BdScrollArea>
</div>
</template>
5.2 智能体创建/编辑页面
5.2.1 功能说明
用于创建和编辑智能体的基本信息和配置。
5.2.2 技术实现
- 使用Nuxt 3的页面组件实现
- 使用表单组件收集智能体的基本信息和配置
- 使用API调用后端服务创建或编辑智能体
- 支持从模板创建智能体
- 支持DSL配置导入
5.2.3 关键代码
<script lang="ts" setup>
import type { Agent, QueryAgentParams } from "@buildingai/service/consoleapi/ai-agent";
import { apiAddAgentTags, apiDeleteAgent, apiExportAgentDsl, apiGetAgentList, apiRemoveAgentTags } from "@buildingai/service/consoleapi/ai-agent";
// ... 省略部分代码
const handleCreateFromTemplate = async () => {
const drawer = overlay.create(TemplateDrawer);
const instance = drawer.open();
const shouldRefresh = await instance.result;
if (shouldRefresh) {
searchForm.page = 1;
searchForm.pageSize = 15;
await getLists();
}
};
const handleImportAgent = async () => {
const modal = overlay.create(AgentDslImport);
const instance = modal.open();
const shouldRefresh = await instance.result;
if (shouldRefresh) {
searchForm.page = 1;
searchForm.pageSize = 15;
await getLists();
}
};
// ... 省略部分代码
</script>
<template>
<div class="grid grid-cols-1 gap-6 pt-2 pb-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<div
class="group border-default relative cursor-pointer rounded-lg border border-dashed p-4 transition-all duration-200 hover:shadow-lg"
@click="handleCreate"
>
<div class="group-hover:text-primary text-foreground mb-3 flex items-center gap-3">
<div class="border-default flex size-10 flex-none items-center justify-center rounded-lg border border-dashed">
<UIcon name="i-lucide-plus" class="h-6 w-6" />
</div>
<h3 class="truncate text-sm font-medium">
{{ $t('ai-agent.backend.create.title') }}
</h3>
</div>
<div class="text-muted-foreground mb-6 pr-8 text-xs">
<p class="line-clamp-2 overflow-hidden">
{{ $t('ai-agent.backend.create.desc') }}
</p>
</div>
<div class="flex items-center gap-2">
<UButton
color="primary"
variant="ghost"
class="w-full"
icon="i-lucide-package"
size="sm"
:label="$t('ai-agent.backend.create.fromTemplate')"
@click.stop="handleCreateFromTemplate"
/>
<UButton
color="neutral"
variant="ghost"
class="w-full"
icon="i-lucide-file-up"
size="sm"
:label="$t('ai-agent.backend.dslImport.import')"
@click.stop="handleImportAgent"
/>
</div>
</div>
<!-- 智能体卡片 -->
<AgentCard
v-for="agent in agents"
:key="agent.id"
:agent="agent"
@delete="handleDelete"
@edit="handleEdit"
@export-dsl="handleExportDsl"
@update-tags="handleUpdateTags"
/>
</div>
</template>
5.3 智能体详情页面
5.3.1 功能说明
展示智能体的详细信息和配置。
5.3.2 技术实现
- 使用Nuxt 3的页面组件实现
- 使用嵌套路由展示智能体的不同配置页面
- 使用导航菜单切换不同的配置页面
5.3.3 关键代码
<script setup lang="ts">
import { apiGetAgentDetail } from "@buildingai/service/consoleapi/ai-agent";
import type { NavigationMenuItem } from "@nuxt/ui";
const AgentModal = defineAsyncComponent(() => import("./components/agent-modal.vue"));
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const overlay = useOverlay();
const { hasAccessByCodes } = useAccessControl();
const isMobile = useMediaQuery("(max-width: 1380px)");
const collapsed = shallowRef<boolean>(false);
const agentId = computed(() => (route.params as Record<string, string>).id);
const { data: agent, refresh: refreshAgent } = await useAsyncData(
`agent-detail-${agentId.value}`,
() => apiGetAgentDetail(agentId.value as string),
);
provide("agents", agent);
const mountAgentModal = async (id: string) => {
const modal = overlay.create(AgentModal);
const instance = modal.open({ id: id });
const shouldRefresh = await instance.result;
if (shouldRefresh) refreshAgent();
};
const handleEdit = () => {
mountAgentModal(agentId.value as string);
};
const navigationItems = computed(() => {
return [
hasAccessByCodes(["ai-agent:detail"]) ? {
label: t("ai-agent.backend.menu.arrange"),
icon: "i-lucide-radar",
to: useRoutePath("ai-agent:detail", {
id: agentId.value as string,
}),
} : null,
hasAccessByCodes(["ai-agent:publish"]) ? {
label: t("ai-agent.backend.menu.publish"),
icon: "i-lucide-radio-tower",
to: useRoutePath("ai-agent:publish", {
id: agentId.value as string,
}),
} : null,
hasAccessByCodes(["ai-agent-chat-record:list"]) ? {
label: t("ai-agent.backend.menu.chatRecord"),
icon: "i-lucide-file-text",
to: useRoutePath("ai-agent-chat-record:list", {
id: agentId.value as string,
}),
} : null,
hasAccessByCodes(["ai-agent:statistics"]) ? {
label: t("ai-agent.backend.menu.statistics"),
icon: "i-lucide-pie-chart",
to: useRoutePath("ai-agent:statistics", {
id: agentId.value as string,
}),
} : null,
].filter(Boolean) as NavigationMenuItem[];
});
</script>
<template>
<div class="flex h-full min-h-0 w-full">
<div
class="bg-muted flex h-full w-50 flex-none flex-col rounded-tr-xl rounded-br-xl p-2"
:class="{ 'w-18!': collapsed }"
>
<!-- 智能体信息和导航菜单 -->
<!-- ... 省略部分代码 ... -->
</div>
<!-- 内容区域 -->
<NuxtPage />
</div>
</template>
5.4 智能体发布页面
5.4.1 功能说明
发布智能体和管理发布历史。
5.4.2 技术实现
- 使用Nuxt 3的页面组件实现
- 使用表单组件收集发布信息
- 使用API调用后端服务发布智能体
- 支持版本控制
- 支持发布范围设置
5.5 后端服务实现
5.5.1 智能体服务
import { BaseService } from "@buildingai/base";
import { InjectRepository } from "@buildingai/db/@nestjs/typeorm";
import { Agent } from "@buildingai/db/entities/ai-agent.entity";
import { AgentChatRecord } from "@buildingai/db/entities/ai-agent-chat-record.entity";
import { AgentChatMessage } from "@buildingai/db/entities/ai-agent-chat-message.entity";
import { AgentAnnotation } from "@buildingai/db/entities/ai-agent-annotation.entity";
import { Tag } from "@buildingai/db/entities/tag.entity";
import { AiModel } from "@buildingai/db/entities/ai-model.entity";
import { AiProvider } from "@buildingai/db/entities/ai-provider.entity";
import { Repository } from "@buildingai/db/typeorm";
import { HttpErrorFactory } from "@buildingai/errors";
import { Injectable } from "@nestjs/common";
import { randomBytes } from "crypto";
import { CreateAgentDto, PublishAgentDto, QueryAgentDto, QueryAgentStatisticsDto, UpdateAgentConfigDto } from "../dto/agent";
@Injectable()
export class AiAgentService extends BaseService<Agent> {
private readonly defaultAvatar = "/static/images/agent.png";
constructor(
@InjectRepository(Agent) private readonly agentRepository: Repository<Agent>,
@InjectRepository(AgentChatRecord) private readonly chatRecordRepository: Repository<AgentChatRecord>,
@InjectRepository(AgentChatMessage) private readonly chatMessageRepository: Repository<AgentChatMessage>,
@InjectRepository(AgentAnnotation) private readonly annotationRepository: Repository<AgentAnnotation>,
@InjectRepository(Tag) private readonly tagRepository: Repository<Tag>,
@InjectRepository(AiModel) private readonly aiModelRepository: Repository<AiModel>,
@InjectRepository(AiProvider) private readonly aiProviderRepository: Repository<AiProvider>,
) {
super(agentRepository);
}
// 创建新智能体
async createAgent(dto: CreateAgentDto, user: UserPlayground): Promise<Agent> {
const { name, description, avatar, createMode = "direct", thirdPartyIntegration = {}, tagIds } = dto;
await this.checkNameUniqueness(name);
return this.withErrorHandling(async () => {
const agent = await this.create({
name,
description,
avatar: avatar || this.defaultAvatar,
showContext: true,
createMode,
thirdPartyIntegration: thirdPartyIntegration || {},
showReference: true,
enableFeedback: false,
enableWebSearch: false,
userCount: 0,
isPublic: false,
createBy: user.id,
});
if (tagIds && tagIds.length > 0) {
await this.syncAgentTags(agent.id, tagIds);
}
return agent;
});
}
// 发布智能体
async publishAgent(agentId: string, dto: PublishAgentDto): Promise<Agent> {
const { version, releaseNotes, publishScope } = dto;
return this.withErrorHandling(async () => {
const agent = await this.getById(agentId);
if (!agent) {
throw HttpErrorFactory.createNotFoundError("Agent not found");
}
const updatedAgent = await this.updateById(agentId, {
isPublished: true,
publishConfig: { ...agent.publishConfig, version, releaseNotes, publishScope },
});
// 记录发布历史
await this.recordPublishHistory(agentId, version, releaseNotes, publishScope);
return updatedAgent;
});
}
// ... 省略其他方法 ...
}
5.6 智能体配置页面
5.6.1 功能说明
配置智能体的模型、数据集、表单字段等参数。
5.6.2 技术实现
- 使用Nuxt 3的页面组件实现
- 支持多标签页切换配置不同参数
- 使用表单组件收集配置信息
- 使用API调用后端服务保存配置
- 支持实时预览配置效果
5.6.3 关键代码
<script setup lang="ts">
import { apiGetAgentDetail, apiUpdateAgentConfig } from "@buildingai/service/consoleapi/ai-agent";
import type { Agent, ModelConfig, DatasetConfig } from "@buildingai/service/consoleapi/ai-agent";
const route = useRoute();
const { t } = useI18n();
const toast = useToast();
const agentId = computed(() => (route.params as Record<string, string>).id);
const { data: agent, refresh: refreshAgent } = await useAsyncData(
`agent-detail-${agentId.value}`,
() => apiGetAgentDetail(agentId.value as string),
);
const modelConfig = ref<ModelConfig | undefined>(agent.value?.modelConfig);
const datasetConfig = ref<DatasetConfig | undefined>(agent.value?.datasetConfig);
const updateConfig = async () => {
try {
await apiUpdateAgentConfig(agentId.value as string, {
modelConfig: modelConfig.value,
datasetConfig: datasetConfig.value,
});
toast.add({ title: t("common.saveSuccess"), color: "primary" });
await refreshAgent();
} catch (error) {
toast.add({ title: t("common.saveFailed"), color: "negative" });
}
};
</script>
<template>
<div class="space-y-6">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium">{{ t("ai-agent.backend.menu.configuration") }}</h3>
<UButton color="primary" size="sm" :label="t('common.save')" @click="updateConfig" />
</div>
<UTabs v-model="activeTab">
<UTab title="{{ t('ai-agent.backend.configuration.model') }}">
<!-- 模型配置 -->
<ModelConfigPanel v-model="modelConfig" />
</UTab>
<UTab title="{{ t('ai-agent.backend.configuration.dataset') }}">
<!-- 数据集配置 -->
<DatasetConfigPanel v-model="datasetConfig" />
</UTab>
<UTab title="{{ t('ai-agent.backend.configuration.form') }}">
<!-- 表单字段配置 -->
<FormConfigPanel v-model="formConfig" />
</UTab>
<UTab title="{{ t('ai-agent.backend.configuration.thirdParty') }}">
<!-- 第三方集成配置 -->
<ThirdPartyConfigPanel v-model="thirdPartyConfig" />
</UTab>
</UTabs>
</div>
</template>
5.7 智能体日志页面
5.7.1 功能说明
查看智能体的运行日志和调试信息。
5.7.2 技术实现
- 使用Nuxt 3的页面组件实现
- 支持日志过滤和搜索
- 支持分页加载日志
- 支持日志级别筛选
- 使用WebSocket实现实时日志推送
5.7.3 关键代码
<script setup lang="ts">
import { apiGetAgentLogs, apiClearAgentLogs } from "@buildingai/service/consoleapi/ai-agent";
import type { AgentLog, QueryAgentLogsDto } from "@buildingai/service/consoleapi/ai-agent";
const route = useRoute();
const { t } = useI18n();
const toast = useToast();
const agentId = computed(() => (route.params as Record<string, string>).id);
const logs = ref<AgentLog[]>([]);
const total = ref(0);
const loading = ref(false);
const queryParams = reactive<QueryAgentLogsDto>({
page: 1,
pageSize: 50,
level: "all",
keyword: "",
});
const getLogs = async () => {
loading.value = true;
try {
const result = await apiGetAgentLogs(agentId.value as string, queryParams);
logs.value = result.data;
total.value = result.total;
} catch (error) {
toast.add({ title: t("ai-agent.backend.logs.loadFailed"), color: "negative" });
} finally {
loading.value = false;
}
};
const clearLogs = async () => {
try {
await apiClearAgentLogs(agentId.value as string);
toast.add({ title: t("ai-agent.backend.logs.clearSuccess"), color: "primary" });
await getLogs();
} catch (error) {
toast.add({ title: t("ai-agent.backend.logs.clearFailed"), color: "negative" });
}
};
// WebSocket 实时日志推送
const socket = ref<WebSocket | null>(null);
onMounted(() => {
socket.value = new WebSocket(`ws://localhost:3000/agent/${agentId.value}/logs`);
socket.value.onmessage = (event) => {
const log: AgentLog = JSON.parse(event.data);
logs.value.unshift(log);
if (logs.value.length > 100) logs.value.pop();
};
await getLogs();
});
onUnmounted(() => {
if (socket.value) socket.value.close();
});
</script>
<template>
<div class="space-y-6">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium">{{ t("ai-agent.backend.menu.logs") }}</h3>
<UButton color="negative" size="sm" :label="t('ai-agent.backend.logs.clear')" @click="clearLogs" />
</div>
<!-- 日志筛选 -->
<div class="flex flex-wrap gap-4">
<USelect v-model="queryParams.level" :options="logLevels" placeholder="{{ t('ai-agent.backend.logs.level') }}" size="sm" />
<UInput v-model="queryParams.keyword" placeholder="{{ t('ai-agent.backend.logs.search') }}" size="sm" />
<UButton color="primary" size="sm" :label="t('common.search')" @click="getLogs" />
</div>
<!-- 日志列表 -->
<div class="bg-card rounded-lg border p-4 max-h-[600px] overflow-y-auto">
<div v-if="loading" class="flex justify-center py-4">{{ t('common.loading') }}</div>
<div v-else-if="logs.length === 0" class="text-center py-4 text-muted-foreground">
{{ t('ai-agent.backend.logs.empty') }}
</div>
<div v-else>
<div
v-for="log in logs"
:key="log.id"
class="border-l-4 pl-3 py-2 mb-2"
:class="{
'border-red-500': log.level === 'error',
'border-yellow-500': log.level === 'warn',
'border-blue-500': log.level === 'info',
'border-gray-500': log.level === 'debug',
}"
>
<div class="text-xs text-muted-foreground flex items-center gap-2">
<span>{{ log.timestamp }}</span>
<span class="font-medium" :class="{
'text-red-500': log.level === 'error',
'text-yellow-500': log.level === 'warn',
'text-blue-500': log.level === 'info',
'text-gray-500': log.level === 'debug',
}">
{{ log.level.toUpperCase() }}
</span>
</div>
<div class="text-sm whitespace-pre-wrap mt-1">{{ log.message }}</div>
<div v-if="log.stacktrace" class="text-xs text-red-500 mt-1 whitespace-pre-wrap">
{{ log.stacktrace }}
</div>
</div>
</div>
</div>
<!-- 分页 -->
<div v-if="total > 0" class="flex items-center justify-end">
<UPagination v-model="queryParams.page" :total="total" :page-size="queryParams.pageSize" @change="getLogs" />
</div>
</div>
</template>
5.8 智能体统计页面
5.8.1 功能说明
查看智能体的使用统计和性能指标。
5.8.2 技术实现
- 使用Nuxt 3的页面组件实现
- 使用ECharts或其他图表库展示统计数据
- 支持时间范围筛选
- 支持数据导出
- 实时更新统计数据
5.8.3 关键代码
<script setup lang="ts">
import { apiGetAgentStatistics, apiExportAgentStatistics } from "@buildingai/service/consoleapi/ai-agent";
import type { AgentStatistics, QueryAgentStatisticsDto } from "@buildingai/service/consoleapi/ai-agent";
import { createChart } from "@buildingai/echarts";
const route = useRoute();
const { t } = useI18n();
const toast = useToast();
const agentId = computed(() => (route.params as Record<string, string>).id);
const statistics = ref<AgentStatistics | null>(null);
const loading = ref(false);
const queryParams = reactive<QueryAgentStatisticsDto>({
timeRange: "7d",
metrics: ["usage", "responseTime", "successRate"],
});
const chartRef = ref<HTMLElement | null>(null);
let chartInstance: any = null;
const getStatistics = async () => {
loading.value = true;
try {
const result = await apiGetAgentStatistics(agentId.value as string, queryParams);
statistics.value = result;
updateChart();
} catch (error) {
toast.add({ title: t("ai-agent.backend.statistics.loadFailed"), color: "negative" });
} finally {
loading.value = false;
}
};
const updateChart = () => {
if (!chartRef.value || !statistics.value) return;
if (!chartInstance) {
chartInstance = createChart(chartRef.value, {
xAxis: {
type: "category",
data: statistics.value.timeSeries,
},
yAxis: {
type: "value",
},
series: [
{
name: t("ai-agent.backend.statistics.usage"),
type: "line",
data: statistics.value.usage,
smooth: true,
},
{
name: t("ai-agent.backend.statistics.responseTime"),
type: "line",
data: statistics.value.responseTime,
smooth: true,
},
{
name: t("ai-agent.backend.statistics.successRate"),
type: "line",
data: statistics.value.successRate,
smooth: true,
},
],
});
} else {
chartInstance.setOption({
xAxis: {
data: statistics.value.timeSeries,
},
series: [
{
data: statistics.value.usage,
},
{
data: statistics.value.responseTime,
},
{
data: statistics.value.successRate,
},
],
});
}
};
onMounted(() => {
getStatistics();
});
onUnmounted(() => {
if (chartInstance) chartInstance.dispose();
});
</script>
<template>
<div class="space-y-6">
<div class="flex items-center justify-between">
<h3 class="text-lg font-medium">{{ t("ai-agent.backend.menu.statistics") }}</h3>
<UButton color="primary" size="sm" :label="t('ai-agent.backend.statistics.export')" @click="exportStatistics" />
</div>
<!-- 统计筛选 -->
<div class="flex flex-wrap gap-4">
<USelect v-model="queryParams.timeRange" :options="timeRanges" placeholder="{{ t('ai-agent.backend.statistics.timeRange') }}" size="sm" />
<UTagInput
v-model="queryParams.metrics"
:tags="metricsOptions"
placeholder="{{ t('ai-agent.backend.statistics.metrics') }}"
size="sm"
/>
<UButton color="primary" size="sm" :label="t('common.search')" @click="getStatistics" />
</div>
<!-- 统计图表 -->
<div v-if="loading" class="flex justify-center py-8">{{ t('common.loading') }}</div>
<div v-else-if="!statistics" class="text-center py-8 text-muted-foreground">
{{ t('ai-agent.backend.statistics.empty') }}
</div>
<div v-else>
<div ref="chartRef" class="w-full h-[400px] mb-4"></div>
<!-- 统计卡片 -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<UCard variant="outlined">
<div class="text-sm text-muted-foreground">{{ t('ai-agent.backend.statistics.totalUsage') }}</div>
<div class="text-2xl font-bold mt-1">{{ statistics.totalUsage }}</div>
</UCard>
<UCard variant="outlined">
<div class="text-sm text-muted-foreground">{{ t('ai-agent.backend.statistics.averageResponseTime') }}</div>
<div class="text-2xl font-bold mt-1">{{ statistics.averageResponseTime }}ms</div>
</UCard>
<UCard variant="outlined">
<div class="text-sm text-muted-foreground">{{ t('ai-agent.backend.statistics.successRate') }}</div>
<div class="text-2xl font-bold mt-1">{{ (statistics.successRate * 100).toFixed(2) }}%</div>
</UCard>
</div>
</div>
</div>
</template>
6. 数据结构与模型
6.1 智能体实体
interface Agent {
id: string;
name: string;
description?: string;
createMode: string;
avatar?: string;
chatAvatar?: string;
rolePrompt?: string;
showContext: boolean;
showReference: boolean;
enableFeedback: boolean;
enableWebSearch: boolean;
userCount: number;
modelConfig?: ModelConfig;
billingConfig?: ModelBillingConfig;
datasetIds?: string[];
openingStatement?: string;
openingQuestions?: string[];
quickCommands?: QuickCommandConfig[];
autoQuestions?: AutoQuestionsConfig;
formFields?: FormFieldConfig[];
formFieldsInputs?: Record<string, any>;
mcpServerIds?: string[];
isPublished: boolean;
isPublic: boolean;
publishToken?: string;
apiKey?: string;
createBy: string;
publishConfig?: {
allowOrigins?: string[];
rateLimitPerMinute?: number;
showBranding?: boolean;
allowDownloadHistory?: boolean;
};
thirdPartyIntegration?: ThirdPartyIntegrationConfig;
tags?: Tag[];
}
### 6.2 智能体日志实体
```typescript
interface AgentLog {
id: string;
agentId: string;
level: "debug" | "info" | "warn" | "error";
message: string;
stacktrace?: string;
context?: Record<string, any>;
timestamp: string;
createBy: string;
}
6.3 智能体统计实体
interface AgentStatistics {
agentId: string;
timeSeries: string[];
usage: number[];
responseTime: number[];
successRate: number[];
totalUsage: number;
averageResponseTime: number;
totalRequests: number;
successRequests: number;
failureRequests: number;
}
7. 性能与安全
7.1 权限体系
- 基于角色的访问控制(RBAC)
- 支持资源级别的权限控制
- 支持数据级别的权限控制
- 支持动态权限分配
7.2 权限实现
// 权限控制工具函数
export function useAccessControl() {
const user = useUser();
// 检查用户是否有指定权限码
function hasAccessByCodes(codes: string[]): boolean {
if (!user.value || !user.value.roles || user.value.roles.length === 0) return false;
if (user.value.isSuperAdmin) return true;
const userPermissions = new Set<string>();
user.value.roles.forEach((role) => {
role.permissions.forEach((permission) => {
userPermissions.add(permission.code);
});
});
return codes.some((code) => userPermissions.has(code));
}
// 检查用户是否有指定角色
function hasRole(roleCodes: string[]): boolean {
if (!user.value || !user.value.roles || user.value.roles.length === 0) return false;
if (user.value.isSuperAdmin) return true;
const userRoleCodes = user.value.roles.map((role) => role.code);
return roleCodes.some((code) => userRoleCodes.includes(code));
}
return {
hasAccessByCodes,
hasRole,
};
}
// 在页面中使用权限控制
<script setup lang="ts">
const { hasAccessByCodes } = useAccessControl();
const navigationItems = computed(() => {
return [
hasAccessByCodes(["ai-agent:detail"]) ? {
label: t("ai-agent.backend.menu.arrange"),
icon: "i-lucide-radar",
to: useRoutePath("ai-agent:detail", {
id: agentId.value as string,
}),
} : null,
hasAccessByCodes(["ai-agent:publish"]) ? {
label: t("ai-agent.backend.menu.publish"),
icon: "i-lucide-radio-tower",
to: useRoutePath("ai-agent:publish", {
id: agentId.value as string,
}),
} : null,
// ... 其他菜单项
].filter(Boolean);
});
</script>
8. RBAC权限控制
8.1 智能体管理接口
8.1.1 获取智能体列表
GET /api/console/ai/agent/list
请求参数:
interface QueryAgentDto {
page: number;
pageSize: number;
keyword?: string;
tags?: string[];
isPublished?: boolean;
isPublic?: boolean;
}
响应参数:
interface AgentListResponse {
list: Agent[];
total: number;
page: number;
pageSize: number;
}
8.1.2 创建智能体
POST /api/console/ai/agent
请求参数:
interface CreateAgentDto {
name: string;
description?: string;
createMode: string;
avatar?: string;
chatAvatar?: string;
rolePrompt?: string;
showContext: boolean;
showReference: boolean;
enableFeedback: boolean;
enableWebSearch: boolean;
datasetIds?: string[];
openingStatement?: string;
openingQuestions?: string[];
formFields?: FormFieldConfig[];
tagIds?: string[];
}
响应参数:
interface AgentResponse {
agent: Agent;
}
8.1.3 更新智能体配置
PUT /api/console/ai/agent/{id}/config
请求参数:
interface UpdateAgentConfigDto {
modelConfig?: ModelConfig;
datasetConfig?: DatasetConfig;
formConfig?: FormConfig;
thirdPartyConfig?: ThirdPartyIntegrationConfig;
}
响应参数:
interface AgentResponse {
agent: Agent;
}
8.1.4 发布智能体
POST /api/console/ai/agent/{id}/publish
请求参数:
interface PublishAgentDto {
version: string;
releaseNotes?: string;
publishScope?: "private" | "public";
allowOrigins?: string[];
rateLimitPerMinute?: number;
showBranding?: boolean;
}
响应参数:
interface AgentResponse {
agent: Agent;
}
8.2 智能体日志接口
8.2.1 获取智能体日志
GET /api/console/ai/agent/{id}/logs
请求参数:
interface QueryAgentLogsDto {
page: number;
pageSize: number;
level?: "debug" | "info" | "warn" | "error" | "all";
keyword?: string;
startTime?: string;
endTime?: string;
}
响应参数:
interface AgentLogResponse {
list: AgentLog[];
total: number;
page: number;
pageSize: number;
}
8.3 智能体统计接口
8.3.1 获取智能体统计
GET /api/console/ai/agent/{id}/statistics
请求参数:
interface QueryAgentStatisticsDto {
timeRange: "24h" | "7d" | "30d" | "90d" | "custom";
metrics?: ("usage" | "responseTime" | "successRate" | "requestCount")[];
startTime?: string;
endTime?: string;
}
响应参数:
interface AgentStatisticsResponse {
statistics: AgentStatistics;
}
6.2 智能体配置实体
interface AgentConfig {
id: string;
name: string;
type: string;
content: any;
createdAt: Date;
updatedAt: Date;
}
6.3 智能体对话记录实体
interface AgentChatRecord {
id: string;
agentId: string;
userId: string;
sessionId: string;
messages: AgentChatMessage[];
createdAt: Date;
updatedAt: Date;
}
10. 部署与维护
7.1 智能体管理接口
| 接口名称 | 方法 | 路径 | 功能描述 |
|---|---|---|---|
| 智能体列表 | GET | /api/v1/ai/agent | 获取智能体列表 |
| 创建智能体 | POST | /api/v1/ai/agent | 创建新智能体 |
| 智能体详情 | GET | /api/v1/ai/agent/{id} | 获取智能体详情 |
| 更新智能体 | PUT | /api/v1/ai/agent/{id} | 更新智能体信息 |
| 删除智能体 | DELETE | /api/v1/ai/agent/{id} | 删除智能体 |
| 批量删除智能体 | POST | /api/v1/ai/agent/batch-delete | 批量删除智能体 |
7.2 智能体配置接口
| 接口名称 | 方法 | 路径 | 功能描述 |
|---|---|---|---|
| 配置列表 | GET | /api/v1/ai/agent/config | 获取配置列表 |
| 创建配置 | POST | /api/v1/ai/agent/config | 创建新配置 |
| 配置详情 | GET | /api/v1/ai/agent/config/{id} | 获取配置详情 |
| 更新配置 | PUT | /api/v1/ai/agent/config/{id} | 更新配置信息 |
| 删除配置 | DELETE | /api/v1/ai/agent/config/{id} | 删除配置 |
7.3 智能体发布接口
| 接口名称 | 方法 | 路径 | 功能描述 |
|---|---|---|---|
| 发布智能体 | POST | /api/v1/ai/agent/{id}/publish | 发布智能体 |
| 撤销发布 | POST | /api/v1/ai/agent/{id}/unpublish | 撤销智能体发布 |
| 发布历史 | GET | /api/v1/ai/agent/{id}/publish-history | 获取发布历史 |
8. 性能与安全
8.1 性能优化
- 使用无限滚动加载智能体列表,减少初始加载时间
- 使用缓存机制缓存智能体的配置信息
- 使用异步加载组件,提高页面加载速度
- 使用CDN加速静态资源的加载
8.2 安全措施
- 使用JWT进行身份认证
- 使用RBAC进行权限控制
- 使用HTTPS加密数据传输
- 对敏感数据进行加密存储
- 对API请求进行限流和防刷
9. API接口文档
9.1 部署方式
- 使用Docker容器化部署
- 使用Kubernetes进行容器编排
- 使用CI/CD工具自动化部署
9.2 维护措施
- 定期备份数据库
- 定期更新系统和依赖包
- 监控系统的性能和可用性
- 日志记录和分析
- 故障排查和恢复
10. 总结
本文档详细介绍了控制台智能体菜单和页面的技术架构,包括系统架构、模块划分、核心功能实现、数据结构与模型、接口设计、性能与安全、部署与维护等方面。通过本文档,开发团队可以清晰地了解控制台智能体菜单和页面的技术实现,为后续的开发和维护提供指导。
更多推荐



所有评论(0)