plan-sess_fbc85caf-ba9c-45c6-b871-268c8ad99fcb.md 8.3 KB

「专家页面」设计与实施计划 v2(含技能、工具配置)

一、已探明的关键事实

  • agent 表实体有 skills_allow_json / tools_allow_json / skills_deny_json 字段但全部无人消费——正是为本需求预留的;缺的只有 user_id(隔离)与 is_builtin(内置标记)。
  • 智能体没有任何 CRUD 接口,照抄 /models 模板(AgentModelController + Result 包装 + 前端 modelApi)。
  • 会话绑定天然满足:agent_chat_session.agent_id 创建时写入(兜底默认 row_id=1),后端无任何修改通道 →「绑定后不能更换」。
  • 技能机制(关键):技能=磁盘上含 SKILL.md 的目录,装在 .agentscope/users/{userId}/skills(天然按用户隔离);运行时模型只见「名称+描述」目录、正文用 load_skill_through_path 按需加载;b.skillFilter(SkillFilter.only(名…)) 就是 builder 级按名白名单,与现有「用户停用技能」overlay 正确叠加。候选技能列表接口 GET /capability/skills 已有(前端 capabilityApi.listSkills ✓)。
  • 工具机制:AgentToolRegistry.registerBusinessTools(toolkit, activeGroups) 只控制初始装备,6 个组全部注册、模型可用 reset_equipped_tools 随时换组 → 不构成限制。要做真限制需「按组注册」:只注册专家启用的组(组工具不注册=模型不可见、切不过去)。基础设施工具(execute_sql/render_chart/render_graph 等)始终可用,不纳入配置(非目标)。
  • 组元数据已有:GROUP_LABELS(groupLabel)、组常量 6 个(person/call/trans/track/graph/file,otg 未注册)、GET /capability/tools 每条带 groupCode/groupLabel/groupDescription/defaultActive → 前端工具组勾选无需新接口。
  • ⚠️ AgentService 实例池无失效机制:任何专家配置变更必须 evictAgentPool(rowId)。
  • 表 DDL 不在仓库(无 Flyway):新列用手写幂等 SQL 放 sql/,用 jshell+postgres 驱动执行。

默认决策(沿用上轮推荐):内置专家不可编辑不可删;智能体只从专家页选择,输入框只读显示。

二、数据与后端

SQL sql/agent_expert.sql(幂等)

ALTER TABLE agent ADD COLUMN IF NOT EXISTS user_id BIGINT;
ALTER TABLE agent ADD COLUMN IF NOT EXISTS is_builtin SMALLINT NOT NULL DEFAULT 0;
CREATE INDEX IF NOT EXISTS idx_agent_user_id ON agent(user_id);
UPDATE agent SET is_builtin = 1, user_id = COALESCE(user_id, 1) WHERE user_id IS NULL;  -- 存量→内置

(skills_allow_json/tools_allow_json 列按实体已存在,脚本里附注释核对:ALTER TABLE agent ADD COLUMN IF NOT EXISTS skills_allow_json TEXT; 同 tools_allow_json 兜底)

接口 AgentExpertController(/agents,Result 包装,仿 /models)

方法 语义
GET /agents 列表:is_builtin=1 OR user_id=me;VO 带 builtin/mine + skillNames[] + toolGroups[]
POST /agents 新建(user_id=me, is_builtin=0);body=SaveExpertDTO
PUT /agents/{rowId} 编辑;内置拒绝(400「内置专家不可编辑」);校验归属
DELETE /agents/{rowId} 删除;内置拒绝(400「内置专家不可删除」);仅物理删自建
  • SaveExpertDTO:name(@NotBlank ≤100)、description(≤500)、sysPrompt(@NotBlank)、skillNames: List<String>、toolGroups: List<String>(校验 toolGroups ⊆ 6 个已注册组名;skillNames 校验存在于用户技能目录,复用 SkillStore.resolveDir)。
  • userId 由 controller 取 StpUtil 后传 service(便于单测)。

AgentService 改动

  1. buildAgent:读 entry.getToolsAllowJson() → 新增 registerBusinessTools(toolkit, enabledGroups, activeGroups) 重载(只注册 enabledGroups 的工具 + createGroup,active=enabled;enabled=null 走旧全注册路径,InsightAgentFactory/MCP/能力枚举零改动);读 entry.getSkillsAllowJson() 非空 → b.skillFilter(SkillFilter.only(名…))。
  2. 新增 evictAgentPool(Long agentRowId):agentPool.keySet().removeIf(k -> k.contains("-a" + rowId + "-"));create/update/delete 专家后调用。
  3. getOrCreateAgent 加 agent==null 兜底 → 404「智能体不存在或已删除」(被删专家的旧会话再对话得到明确报错)。

三、前端(src/ai)

文件 内容
新 api/agentApi.ts listExperts/createExpert/updateExpert/deleteExpert(Result 族,仿 modelApi;雪花 ID 一律字符串)
api/types.ts +AiExpert(含 builtin/mine/skillNames/toolGroups)、SaveExpertParams
新 views/aiExpert/index.vue 卡片网格页(照 aiModel 骨架:ai-page 壳 + grid + a-spin + 空态 + Modal.confirm 删除);两个分组:内置专家(只读卡片:去对话)/我的专家(去对话、编辑、删除);删除确认提示「绑定它的会话将无法继续对话」
新 components/AgentFormModal.vue 新建/编辑弹窗(照 ModelFormModal):名称(必填)、描述、系统提示词(必填 textarea)、工具(6 业务组勾选:从 capabilityApi.listTools() 按 groupCode 聚合,显示中文名/描述/数量;默认勾 person)、技能(从 capabilityApi.listSkills() 勾选,默认全选=不限制;空数组=不限制语义)
views/aiWorkbench/index.vue WorkbenchView +'agent';RAIL_ITEMS +「专家」;分支(plugin 的裸 v-else 改 v-else-if);专家页 @chat → view='chat' + store.setPendingExpert(expert)
store/chatStream.ts +pendingExpertId/setPendingExpert;createSession 透传 agentId(会话就此绑定);绑定后清 pending
composables/useAiChatPage.ts +experts 加载、expertName 计算(已绑定会话→绑定的;否则 pending;兜底「默认专家」)
components/ChatComposer.vue + ChatPanel.vue 输入框加只读专家 chip「专家:xxx」(会话已绑定=固定,不可点)——体现绑定后不可更换

「去对话」:同工作台内不走路由,emit → view='chat' + pendingExpert(首条消息创建会话时带上 agentId);不动当前会话。

四、交互口径(成文)

  1. 卡片操作:去对话(预选该专家,下一条消息创建绑定它的新会话)/ 编辑 / 删除(后两者仅自建)。
  2. 会话创建即绑定智能体,UI 无更换入口;后端本就没有改 agent_id 的接口。
  3. 内置专家:全局可见、只有「去对话」,不可编辑/删除(后端双重校验,不靠前端隐藏)。
  4. 删除自建专家:确认提示绑定它的会话将无法继续对话;之后这些会话再对话 → 404「智能体不存在或已删除」。
  5. 工具配置=组级启用(选中的组注册并初始装备,未选的组模型不可见也切不出去);技能配置=名称白名单(不勾=不限制)。编辑保存后 evict 池,新对话立即生效。

五、实施步骤

  1. SQL + AgentEntity 加列 → jshell 执行 DDL 并核对既有两列。
  2. 后端:DTO/VO/Controller + AgentService(CRUD/隔离/内置校验/evict/两处 buildAgent 配置消费)+ registerBusinessTools 三参重载 → 编译。
  3. 后端单测:AgentExpertTest(隔离过滤:内置+自己的;内置拒改拒删;归属校验;toolGroups/skillNames 校验;evict 生效;被删专家 getOrCreateAgent 兜底)——userId 走参数不依赖 sa-token。
  4. 前端:agentApi/types → AgentFormModal(含工具组+技能勾选)→ aiExpert 卡片页 → 工作台接入 + composer 只读 chip → tsc/eslint。
  5. E2E(浏览器):新建专家(勾 call 组 + 2 个技能)→ 卡片在「我的」分组 → 去对话 → 提问 → 会话绑定该专家(chip 固置)→ 编辑提示词 → 新会话验证 evict 生效 → 内置专家无编辑/删除 → 删除自建专家 → 旧会话对话报「智能体不存在或已删除」。

六、验收

  • 内置:全局可见、仅去对话;自建:仅本人可见(后端过滤)可编辑可删。
  • 会话绑定后无任何更换入口;被删专家的会话报错明确。
  • 专家配置的工具组/技能在新对话真实生效(未启用组模型不可见;技能目录只见白名单),保存后立即生效(evict)。
  • 卡片列表 + 内置/我的分组展示;tsc/eslint/后端测试全绿(存量 8 个失败除外)。