BuildingAI 控制台智能体菜单和页面技术架构
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"><UInputv-model="searchForm.keyword":placeholder="$t('ai-agent.backend.search.placeholder')"class="w-80"@change="getLists"/><TagSelect v-model="searchForm.tagIds" @change="getLists" /><USelectv-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><UButtoncolor="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"><divclass="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"><UButtoncolor="primary"variant="ghost"class="w-full"icon="i-lucide-package"size="sm":label="$t('ai-agent.backend.create.fromTemplate')"@click.stop="handleCreateFromTemplate"/><UButtoncolor="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><!-- 智能体卡片 --><AgentCardv-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"><divclass="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"><UButtoncolor="primary"variant="ghost"class="w-full"icon="i-lucide-package"size="sm":label="$t('ai-agent.backend.create.fromTemplate')"@click.stop="handleCreateFromTemplate"/><UButtoncolor="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><!-- 智能体卡片 --><AgentCardv-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"><divclass="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><divv-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" /><UTagInputv-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. 总结
本文档详细介绍了控制台智能体菜单和页面的技术架构,包括系统架构、模块划分、核心功能实现、数据结构与模型、接口设计、性能与安全、部署与维护等方面。通过本文档,开发团队可以清晰地了解控制台智能体菜单和页面的技术实现,为后续的开发和维护提供指导。
