|
|
@@ -18,14 +18,6 @@
|
|
|
<span class="ai-plugin__tab-count">{{ tools.length }}</span>
|
|
|
</template>
|
|
|
|
|
|
- <div class="ai-plugin__hint">
|
|
|
- 这里展示 agent 在对话里可调用的全部工具(共 <b>{{ tools.length }}</b> 个), 由服务端启动时<b
|
|
|
- >反射扫描 @Tool 方法</b
|
|
|
- >并对照运行时装配后写入 <b>agent_tool</b> 表,页面只读。 新会话默认只装备「{{
|
|
|
- defaultGroupLabel || '人员画像'
|
|
|
- }}」组,其余组由模型在对话中按需装备; <b>基础工具</b>不属于任何域,会话里始终可用。点任意卡片看说明。
|
|
|
- </div>
|
|
|
-
|
|
|
<div class="ai-plugin__ops">
|
|
|
<a-select
|
|
|
v-model:value="groupFilter"
|
|
|
@@ -55,7 +47,7 @@
|
|
|
<a-spin :spinning="loading">
|
|
|
<div v-if="filteredTools.length" class="ai-plugin__grid">
|
|
|
<article
|
|
|
- v-for="tool in pagedTools"
|
|
|
+ v-for="tool in filteredTools"
|
|
|
:key="tool.toolName"
|
|
|
class="ai-plugin__card is-clickable"
|
|
|
:title="tool.toolName"
|
|
|
@@ -79,18 +71,6 @@
|
|
|
tools.length ? '没有匹配的工具' : '还没有同步到工具清单(服务端启动时会自动同步,需先建 agent_tool 表)'
|
|
|
}}
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 卡片量大,分页兜住首屏;翻页不改变筛选结果 -->
|
|
|
- <div v-if="filteredTools.length > PAGE_SIZE" class="ai-plugin__pager">
|
|
|
- <a-pagination
|
|
|
- v-model:current="toolPage"
|
|
|
- size="small"
|
|
|
- :page-size="PAGE_SIZE"
|
|
|
- :total="filteredTools.length"
|
|
|
- :show-size-changer="false"
|
|
|
- :show-total="(t: number) => `共 ${t} 个`"
|
|
|
- />
|
|
|
- </div>
|
|
|
</a-spin>
|
|
|
</a-tab-pane>
|
|
|
|
|
|
@@ -101,13 +81,6 @@
|
|
|
<span class="ai-plugin__tab-count">{{ skills.length }}</span>
|
|
|
</template>
|
|
|
|
|
|
- <div class="ai-plugin__hint">
|
|
|
- 技能是放在服务端本机的 <b>SKILL.md</b> 包,按<b>用户</b>隔离、跨案件共享,不写数据库。 装着目录:<b>{{
|
|
|
- skillRootHint
|
|
|
- }}</b
|
|
|
- >。停用后<b>下一轮对话立即生效</b>(不用重建会话), 卸载会直接删除该技能目录。
|
|
|
- </div>
|
|
|
-
|
|
|
<div class="ai-plugin__ops">
|
|
|
<a-button size="small" type="primary" :disabled="installing" :loading="installing" @click="pickZip">
|
|
|
<template #icon><Icon icon="mdi:upload" :size="14" /></template>
|
|
|
@@ -152,73 +125,6 @@
|
|
|
</div>
|
|
|
</a-spin>
|
|
|
</a-tab-pane>
|
|
|
-
|
|
|
- <!-- ── ③ MCP 接入 ──────────────────────────────────────── -->
|
|
|
- <a-tab-pane key="mcp">
|
|
|
- <template #tab>
|
|
|
- <span>MCP 接入</span>
|
|
|
- </template>
|
|
|
-
|
|
|
- <div class="ai-plugin__hint">
|
|
|
- 本服务同时是一个 <b>MCP Server</b>,<b>只给外部客户端</b>(Claude Desktop / Cursor 等)使用,
|
|
|
- <b>不参与 agent 会话</b>:它在独立的一份工具容器里,把同一批业务工具与基础工具、外加 4 个
|
|
|
- 案件管理工具全量暴露出去。外部模型接入后要先绑定案件才能查数据。
|
|
|
- <b>当前端点未做鉴权</b>,只应在本机或内网使用。
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="ai-plugin__panel">
|
|
|
- <div class="ai-plugin__panel-title">
|
|
|
- <Icon icon="mdi:connection" :size="15" />
|
|
|
- 接入信息
|
|
|
- <span v-if="mcp?.enabled" class="ai-plugin__chip is-success">已启用</span>
|
|
|
- <span v-else class="ai-plugin__chip is-error">未启用</span>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="ai-plugin__kv">
|
|
|
- <span class="ai-plugin__mono">端点</span>
|
|
|
- <b class="ai-plugin__mono ai-plugin__ellipsis" :title="mcp?.endpoint || ''">{{ mcp?.endpoint || '—' }}</b>
|
|
|
-
|
|
|
- <span class="ai-plugin__mono">传输协议</span>
|
|
|
- <b class="ai-plugin__mono">{{ mcp?.protocol || '—' }}</b>
|
|
|
-
|
|
|
- <span class="ai-plugin__mono">服务名</span>
|
|
|
- <b class="ai-plugin__mono">{{ mcp?.name || '—' }} {{ mcp?.version ? `v${mcp.version}` : '' }}</b>
|
|
|
-
|
|
|
- <span class="ai-plugin__mono">已暴露工具</span>
|
|
|
- <b class="ai-plugin__mono"
|
|
|
- >{{ mcp?.toolCount ?? '—' }} 个(含 {{ mcp?.caseTools?.length || 0 }} 个案件管理工具)</b
|
|
|
- >
|
|
|
-
|
|
|
- <span class="ai-plugin__mono">SSE 端点</span>
|
|
|
- <b class="ai-plugin__mono ai-plugin__ellipsis" :title="mcp?.sseEndpoint || ''">
|
|
|
- {{ mcp?.sseEndpoint || '当前协议下未注册' }}
|
|
|
- </b>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <div class="ai-plugin__panel">
|
|
|
- <div class="ai-plugin__panel-title">
|
|
|
- <Icon icon="mdi:format-list-numbered" :size="15" />
|
|
|
- 外部客户端调用顺序
|
|
|
- </div>
|
|
|
- <ol class="ai-plugin__steps">
|
|
|
- <li>连接上面的端点(Streamable HTTP)。</li>
|
|
|
- <li>
|
|
|
- 先调 <span class="ai-plugin__mono">list_cases</span> 拿案件 ID —— MCP 调用没有登录态,
|
|
|
- 不绑定案件时业务工具都会报「缺少案件上下文」。
|
|
|
- </li>
|
|
|
- <li>
|
|
|
- 再调 <span class="ai-plugin__mono">open_case</span> 绑定案件;换案件重复调用即可,
|
|
|
- 多个客户端各自绑定互不影响。
|
|
|
- </li>
|
|
|
- <li>
|
|
|
- 之后即可调用业务工具(通话 / 交易 / 轨迹 / 画像 / 图谱 / SQL);需要时用
|
|
|
- <span class="ai-plugin__mono">close_case</span> 释放案件数据源。
|
|
|
- </li>
|
|
|
- </ol>
|
|
|
- <p class="ai-plugin__note">{{ mcp?.note }}</p>
|
|
|
- </div>
|
|
|
- </a-tab-pane>
|
|
|
</a-tabs>
|
|
|
|
|
|
<!-- 弹框:工具详情(说明 + 所属工具组说明) -->
|
|
|
@@ -277,32 +183,30 @@
|
|
|
/**
|
|
|
* 插件与技能(服务端 agent 版)
|
|
|
*
|
|
|
- * 三个页签全部来自服务端 `module/agent`:
|
|
|
+ * 两个页签全部来自服务端 `module/agent`:
|
|
|
* ① 工具 —— 服务端启动时反射扫描 `@Tool` 后落库的 `agent_tool` 表(只读)。
|
|
|
* 平铺展示全部工具,支持按分类筛选 + 工具名搜索,点行弹框看详情与参数。
|
|
|
* ② 技能 —— 服务端本机用户级技能目录(`.agentscope/users/{用户}/skills`)的 SKILL.md 包,
|
|
|
* 支持上传 zip 安装 / 卸载 / 启停
|
|
|
- * ③ MCP 接入 —— 本服务对外的 MCP 端点与已暴露工具数(只读)
|
|
|
*
|
|
|
* 旧版这里走的是桌面端 Codex 的 IPC(codexApi),Codex 已废弃,故整页重写为服务端驱动;
|
|
|
* 也因此浏览器环境同样可用(不再需要「仅桌面客户端可用」的降级提示)。
|
|
|
+ * 原「MCP 接入」页签已按需求移除(服务端 MCP Server 能力本身保留,仅不再在页面展示)。
|
|
|
*/
|
|
|
- import { computed, onMounted, ref, watch } from 'vue';
|
|
|
- import { Button, Drawer, Input, Modal, Pagination, Select, Spin, Switch, Tabs } from 'ant-design-vue';
|
|
|
+ import { computed, onMounted, ref } from 'vue';
|
|
|
+ import { Button, Drawer, Input, Modal, Select, Spin, Switch, Tabs } from 'ant-design-vue';
|
|
|
import Icon from '@/core/components/Icon/src/Icon.vue';
|
|
|
import { useMessage } from '@/core/hooks/web/useMessage';
|
|
|
- import { useAccountStore } from '@/core/store/modules/account';
|
|
|
import { useUserStore } from '@/core/store/modules/case';
|
|
|
import {
|
|
|
deleteSkill,
|
|
|
installSkillZip,
|
|
|
listSkills,
|
|
|
listTools,
|
|
|
- mcpServerInfo,
|
|
|
readSkill,
|
|
|
toggleSkill,
|
|
|
} from '../../api/capabilityApi';
|
|
|
- import type { AgentTool, McpServerInfo, Skill, SkillDetail } from '../../api/types';
|
|
|
+ import type { AgentTool, Skill, SkillDetail } from '../../api/types';
|
|
|
|
|
|
const ATabs = Tabs;
|
|
|
const ATabPane = Tabs.TabPane;
|
|
|
@@ -311,18 +215,13 @@
|
|
|
const AInput = Input;
|
|
|
const AModal = Modal;
|
|
|
const ASelect = Select;
|
|
|
- const APagination = Pagination;
|
|
|
const ASpin = Spin;
|
|
|
const ASwitch = Switch;
|
|
|
|
|
|
- /** 卡片列表每页个数 */
|
|
|
- const PAGE_SIZE = 24;
|
|
|
-
|
|
|
const { showMessage } = useMessage();
|
|
|
const userStore = useUserStore();
|
|
|
- const accountStore = useAccountStore();
|
|
|
|
|
|
- type PluginTab = 'tool' | 'skill' | 'mcp';
|
|
|
+ type PluginTab = 'tool' | 'skill';
|
|
|
const activeTab = ref<PluginTab>('tool');
|
|
|
|
|
|
const loading = ref(false);
|
|
|
@@ -330,15 +229,12 @@
|
|
|
/** agent_tool 表的扁平行(只读) */
|
|
|
const tools = ref<AgentTool[]>([]);
|
|
|
const skills = ref<Skill[]>([]);
|
|
|
- const mcp = ref<McpServerInfo | null>(null);
|
|
|
|
|
|
const toolOpen = ref(false);
|
|
|
const selectedTool = ref<AgentTool | null>(null);
|
|
|
const toolKeyword = ref('');
|
|
|
/** 分类筛选(空 = 全部分类) */
|
|
|
const groupFilter = ref('');
|
|
|
- /** 卡片列表当前页 */
|
|
|
- const toolPage = ref(1);
|
|
|
|
|
|
const skillOpen = ref(false);
|
|
|
const skillDetail = ref<SkillDetail | null>(null);
|
|
|
@@ -348,12 +244,6 @@
|
|
|
/** 当前案件ID:只用于附带展示「该案件 agent 配置的只读技能仓库」,技能本身与案件无关 */
|
|
|
const caseId = computed(() => (userStore.getCaseInfo?.id ? String(userStore.getCaseInfo.id) : null));
|
|
|
|
|
|
- /** 技能目录提示:服务端按登录用户隔离,这里把口径写清楚,方便运维对照磁盘 */
|
|
|
- const skillRootHint = computed(() => {
|
|
|
- const userId = accountStore.getUserInfo?.id;
|
|
|
- return userId ? `.agentscope/users/${userId}/skills` : '.agentscope/users/{当前用户}/skills';
|
|
|
- });
|
|
|
-
|
|
|
const summary = computed(() => `${tools.value.length} 个工具 · ${skills.value.length} 个技能`);
|
|
|
|
|
|
/**
|
|
|
@@ -383,14 +273,6 @@
|
|
|
];
|
|
|
});
|
|
|
|
|
|
- /** 默认装备的组名(提示文案里用,不写死「人员画像」) */
|
|
|
- const defaultGroupLabel = computed(() => tools.value.find((t) => t.defaultActive)?.groupLabel || '');
|
|
|
-
|
|
|
- // 筛选/搜索变化后回到第一页,否则会停在越界页码上看到空列表
|
|
|
- watch([groupFilter, toolKeyword], () => {
|
|
|
- toolPage.value = 1;
|
|
|
- });
|
|
|
-
|
|
|
/** 列表筛选:分类 + 工具名关键字(工具名搜索,与需求一致) */
|
|
|
const filteredTools = computed(() => {
|
|
|
const keyword = toolKeyword.value.trim().toLowerCase();
|
|
|
@@ -401,24 +283,17 @@
|
|
|
});
|
|
|
});
|
|
|
|
|
|
- /** 卡片分页:全部工具一屏放不下,翻页只在筛选结果内切,不改筛选条件 */
|
|
|
- const pagedTools = computed(() =>
|
|
|
- filteredTools.value.slice((toolPage.value - 1) * PAGE_SIZE, toolPage.value * PAGE_SIZE),
|
|
|
- );
|
|
|
-
|
|
|
onMounted(reload);
|
|
|
|
|
|
async function reload() {
|
|
|
loading.value = true;
|
|
|
try {
|
|
|
- const [toolList, skillList, mcpInfo] = await Promise.all([
|
|
|
+ const [toolList, skillList] = await Promise.all([
|
|
|
listTools().catch(() => [] as AgentTool[]),
|
|
|
listSkills(caseId.value).catch(() => [] as Skill[]),
|
|
|
- mcpServerInfo().catch(() => null),
|
|
|
]);
|
|
|
tools.value = toolList || [];
|
|
|
skills.value = skillList || [];
|
|
|
- mcp.value = mcpInfo;
|
|
|
} finally {
|
|
|
loading.value = false;
|
|
|
}
|
|
|
@@ -573,21 +448,6 @@
|
|
|
border-radius: 999px;
|
|
|
}
|
|
|
|
|
|
- &__hint {
|
|
|
- margin-bottom: var(--ai-sp-2);
|
|
|
- padding: var(--ai-sp-3);
|
|
|
- font-size: 12.5px;
|
|
|
- line-height: 1.75;
|
|
|
- color: var(--ai-text-2);
|
|
|
- background: var(--ai-primary-softer);
|
|
|
- border-left: 3px solid fade(@primary-color, 45%);
|
|
|
- border-radius: 0 var(--ai-radius-sm) var(--ai-radius-sm) 0;
|
|
|
-
|
|
|
- b {
|
|
|
- color: var(--ai-text);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
&__ops {
|
|
|
display: flex;
|
|
|
gap: var(--ai-sp-2);
|
|
|
@@ -725,12 +585,6 @@
|
|
|
border: 1px solid var(--ai-border);
|
|
|
border-radius: 999px;
|
|
|
|
|
|
- &.is-error {
|
|
|
- color: @error-color;
|
|
|
- background-color: fade(@error-color, 10%);
|
|
|
- border-color: fade(@error-color, 26%);
|
|
|
- }
|
|
|
-
|
|
|
&.is-success {
|
|
|
color: @success-color;
|
|
|
background-color: fade(@success-color, 10%);
|
|
|
@@ -757,57 +611,6 @@
|
|
|
color: var(--ai-text-2);
|
|
|
}
|
|
|
|
|
|
- /* 分块面板:与卡片同档(8px 圆角 / 12px 内边距) */
|
|
|
- &__panel {
|
|
|
- margin-bottom: var(--ai-sp-2);
|
|
|
- padding: var(--ai-sp-3);
|
|
|
- background: var(--ai-bg);
|
|
|
- border: 1px solid var(--ai-border);
|
|
|
- border-radius: var(--ai-radius);
|
|
|
- }
|
|
|
-
|
|
|
- &__panel-title {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: var(--ai-sp-2);
|
|
|
- margin-bottom: var(--ai-sp-3);
|
|
|
- font-size: 13px;
|
|
|
- font-weight: 600;
|
|
|
- color: var(--ai-text);
|
|
|
- }
|
|
|
-
|
|
|
- /* 键值表:左列定宽,右列占满并可省略 */
|
|
|
- &__kv {
|
|
|
- display: grid;
|
|
|
- grid-template-columns: 92px minmax(0, 1fr);
|
|
|
- gap: var(--ai-sp-2) var(--ai-sp-3);
|
|
|
- align-items: baseline;
|
|
|
-
|
|
|
- b {
|
|
|
- font-weight: 600;
|
|
|
- color: var(--ai-text);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &__steps {
|
|
|
- margin: 0;
|
|
|
- padding-left: 20px;
|
|
|
- font-size: 12.5px;
|
|
|
- line-height: 1.9;
|
|
|
- color: var(--ai-text-2);
|
|
|
-
|
|
|
- b {
|
|
|
- color: var(--ai-text);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- &__note {
|
|
|
- margin: var(--ai-sp-2) 0 0;
|
|
|
- font-size: 12px;
|
|
|
- line-height: 1.7;
|
|
|
- color: var(--ai-text-2);
|
|
|
- }
|
|
|
-
|
|
|
/* 筛选栏:分类下拉 + 工具名搜索 + 计数 */
|
|
|
&__select {
|
|
|
min-width: 180px;
|
|
|
@@ -827,13 +630,6 @@
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
- /* 卡片分页条:居中,与卡片网格留出间距 */
|
|
|
- &__pager {
|
|
|
- display: flex;
|
|
|
- justify-content: center;
|
|
|
- margin-top: var(--ai-sp-3);
|
|
|
- }
|
|
|
-
|
|
|
&__tool-name {
|
|
|
min-width: 0;
|
|
|
font-size: 12.5px;
|