# 技能文件规范 > 落盘日期:2026-09-16 > 状态:生效中 > 作用:定义“每个技能在前端代码中应该以什么文件形式存在” > 关联文档:`AR05_工作台架构约定.md` --- ## 1. 结论 从本规范生效开始: **技能不再以独立执行页面的形式存在。** 每个技能必须被建模为: `定义文件 + 执行器文件 + 结果组件 + 可选扩展组件` 而不是: `一个完整的 xxxPage.vue` --- ## 2. 标准目录 所有技能统一存放在: `frontend/src/skills/` 目录结构固定为: ```text src/skills/ registry/ eai.ts custom.ts index.ts shared/ types.ts schema.ts runtime.ts eai/ smart-assistant/ index.ts executor.ts ResultCard.vue document-translate/ index.ts executor.ts ResultCard.vue copy-proofreading/ index.ts executor.ts ResultCard.vue contract-review/ index.ts executor.ts ResultCard.vue audio-transcribe/ index.ts executor.ts ResultCard.vue batch-extract/ index.ts executor.ts ResultCard.vue custom/ adapters/ normalize.ts executorFactory.ts ``` 强约束: 1. `eai/` 只放 EAI 官方内置技能 2. `custom/` 不放每个用户技能的源码页面 3. 用户自定义技能的实例数据应存后端,由前端运行时归一化 4. 前端 repo 里只允许保留 `custom/adapters/*` 这类运行时适配代码 --- ## 3. 技能来源必须分开 从本规范生效开始,技能必须强制区分来源: - `source = 'eai'` - `source = 'custom'` ### 3.1 EAI 技能 定义: - 平台内置 - 由 EAI 官方提供 - 随代码仓库交付 - 放在 `src/skills/eai/` ### 3.2 用户自定义技能 定义: - 由租户 / 用户 / 管理员创建 - 不属于平台内置源码 - 应以数据形式存储在后端 - 由前端在运行时转换成统一 `SkillDefinition` ### 3.3 不允许混放 禁止以下做法: 1. 把用户技能和 EAI 技能放在同一层目录 2. 给用户技能也手工创建一个 `src/skills/eai/*` 目录 3. 把用户技能伪装成内置技能编号 4. 让前端页面层自己判断“这是官方技能还是用户技能” 来源判断必须统一由 registry 和 definition 字段承担。 --- ## 4. 每个工具允许的文件 每个 EAI 工具目录下只允许这 4 类文件: ### 4.1 `index.ts` 这是工具主定义文件,必须存在。 作用: - 定义工具身份 - 定义显示名称 - 定义设置 schema - 定义开场提示 - 绑定执行器 - 绑定结果渲染组件 ### 4.2 `executor.ts` 这是工具执行器,必须存在。 作用: - 组装请求参数 - 调用后端接口 - 归一化响应结果 约束: - 不允许依赖页面组件 - 不允许写 DOM 逻辑 - 不允许直接操作路由 ### 4.3 `ResultCard.vue` 这是工具结果渲染组件,必须存在。 作用: - 渲染该工具的结果卡片 - 展示结构化结果 - 展示摘要、状态、附加信息 约束: - 这是局部结果组件,不是页面 - 不允许包含整个聊天布局 - 不允许自己持有输入框 ### 4.4 `SettingsPanel.vue` 这是可选文件,只在 schema 驱动不够时允许出现。 作用: - 处理复杂设置 UI 约束: - 默认不创建 - 只有通用 schema 无法表达时才允许新增 --- ## 5. 编号与命名规范 每个工具必须同时拥有以下 4 个身份字段: 1. `id` 2. `code` 3. `key` 4. `label` 四者职责不同,禁止混用。 ### 5.1 `id` `id` 是机器标识,必须全局唯一,且不可变。 格式固定为: - EAI 技能:`eai.` - 用户技能:`custom..` 示例: - `eai.document-translate` - `eai.audio-transcribe` - `custom.acme.document-translate-pro` 规则: 1. 全小写 2. 使用 `.` 做命名空间分隔 3. 最后一段必须和 `skill-key` 一致 4. 一经创建不得修改 ### 5.2 `code` `code` 是展示编号,给人看,用于列表、配置页、运营页、审计页。 格式固定为: - EAI 技能:`EAI-T-001` - 用户技能:`CUS-T-001` 规则: 1. `EAI-T-xxx` 只保留给官方内置技能 2. `CUS-T-xxx` 只保留给用户自定义技能 3. `xxx` 为三位流水号,从 `001` 开始 4. 用户技能的 `code` 允许在“当前租户 / 当前工作区”范围内顺序编号 5. 全局唯一性由 `id` 保证,不由 `code` 保证 ### 5.3 `key` `key` 是前端目录名和查找键,必须使用 kebab-case。 示例: - `document-translate` - `contract-review` - `batch-extract` 规则: 1. 只能包含小写字母、数字、中划线 2. 不允许空格 3. 不允许中文 4. 必须与目录名一致 ### 5.4 `label` `label` 是用户可见名称,必须是中文产品名。 示例: - `文档翻译` - `合同审查` - `批量提取` 规则: 1. 优先使用中文 2. 不带技术实现词 3. 不在名称里拼接编号 4. 不在名称里拼接来源前缀 错误示例: - `EAI-T-001 文档翻译` - `custom_contract_review` - `合同审查工具V2` --- ## 6. 每个技能必须导出的定义结构 每个技能的 `index.ts` 必须默认导出一个标准对象。 字段固定如下: ```ts export interface SkillDefinition { id: string code: string source: 'eai' | 'custom' key: string label: string kind: 'skill' icon: string description: string settings: SkillSettingSchema[] starterPrompts: string[] executor: SkillExecutor resultCard: Component } ``` 强约束: 1. `id` 必须全局唯一 2. `code` 必须符合来源对应的编号前缀 3. `source` 只能是 `eai` 或 `custom` 4. `key` 必须与目录名一致 5. `kind` 固定为 `'skill'` 6. `settings` 必须是 schema 数据,不允许把设置 UI 直接写进定义文件 7. `executor` 必须指向当前技能自己的 `executor.ts` 8. `resultCard` 必须指向当前技能自己的 `ResultCard.vue` --- ## 7. 文件职责边界 ### 7.1 `index.ts` 负责什么 只负责: - 元数据 - schema - 组件与执行器装配 不负责: - 发请求 - 维护输入状态 - 渲染完整聊天界面 ### 7.2 `executor.ts` 负责什么 只负责: - 把任务上下文和设置值转换成请求 - 调用后端 - 返回标准结果对象 不负责: - 决定页面跳转 - 直接操作 store 里的 UI 状态 - 渲染文本 ### 7.3 `ResultCard.vue` 负责什么 只负责: - 接收标准结果数据 - 渲染结果 不负责: - 调接口 - 打开任务 - 切换工具 --- ## 8. 严格禁止的文件形式 从本规范生效开始,以下文件形式禁止继续新增: 1. `src/views/tools/*Page.vue` 作为工具执行页 2. 每个工具单独复制一份 `ChatLayout` 3. 每个工具单独复制一份 `ChatInputBar` 4. 每个工具单独维护一份本地 `messages` 5. 每个工具单独维护一份本地 `taskItems` 6. 每个工具单独维护一份右栏面板 7. 每个工具用独立路由页面表达“当前正在使用” 一句话: **技能可以有自己的定义文件和局部组件,但不能再有自己的执行页面。** --- ## 9. 统一注册方式 技能注册必须按来源拆分: - `src/skills/registry/eai.ts` - `src/skills/registry/custom.ts` - `src/skills/registry/index.ts` 注册形式固定为: ```ts // registry/eai.ts import smartAssistant from '../eai/smart-assistant' import documentTranslate from '../eai/document-translate' export const eaiSkillRegistry = [ smartAssistant, documentTranslate, ] // registry/custom.ts export async function loadCustomSkillRegistry() { return fetchCustomSkillsFromBackend() } // registry/index.ts export async function loadSkillRegistry() { const customSkills = await loadCustomSkillRegistry() return [...eaiSkillRegistry, ...customSkills] } ``` 约束: 1. EAI 工具只能从 `registry/eai.ts` 注册 2. 用户工具只能从 `registry/custom.ts` 装载 3. `registry/index.ts` 只做合并,不写业务逻辑 4. 页面层不允许自己 import 某个工具目录 5. 工作台只能通过统一 registry 查工具 6. 页面层不允许自己判断来源并做分支 7. 工具排序优先使用 `code` 和显式排序字段,不允许靠文件名碰运气 --- ## 10. 工作台与工具的关系 Workbench 是唯一执行面。 因此: - 输入框属于 Workbench - 当前对象 chip 属于 Workbench - 设置条属于 Workbench - 工作流 / 产物右栏属于 Workbench - 任务列表属于 Workbench 工具只提供: - 自己是谁 - 自己能做什么 - 自己有哪些设置 - 自己如何执行 - 自己如何渲染结果 --- ## 11. 文件名规范 ### 11.1 工具目录名 必须使用 kebab-case: - `document-translate` - `audio-transcribe` - `batch-extract` ### 11.2 文件名 固定如下: - `index.ts` - `executor.ts` - `ResultCard.vue` - `SettingsPanel.vue` 禁止: - `DocumentTranslatePage.vue` - `AudioToolPage.vue` - `TranslateWorkbench.vue` ### 11.3 组件名 工具局部组件必须使用 PascalCase: - `ResultCard.vue` - `SettingsPanel.vue` - `ArtifactPreview.vue` ### 11.4 目录与 key 的关系 工具目录名必须与 `key` 一致: - 目录:`document-translate` - key:`document-translate` 不允许目录名和 key 各写各的。 --- ## 12. 迁移结果 `src/views/tools/*.vue` 旧执行页已经完成退出,当前状态如下: ### 已完成 - 旧技能执行路由已删除 - `src/views/tools/*.vue` 中的执行页源码已删除 - 技能配置与执行逻辑已开始收敛到 `src/skills/*` - `/home` 已成为唯一工作台执行入口 ### 仍需继续推进 虽然旧页面已经删除,但下面几类资产还要继续补齐: - 更完整的 `executor` 接线 - 更统一的 `ResultCard` / `SettingsPanel` 规范 - 用户自定义技能的后端配置接入 - 对象实例化后的运行记录与产物呈现 --- ## 13. 特殊情况处理 ### 13.1 设置特别复杂的技能 如果某个技能的设置无法由通用 schema 表达,允许新增: - `SettingsPanel.vue` 但仍然禁止: - 为此恢复一个完整技能页 ### 13.2 结果展示特别复杂的技能 如果 `ResultCard.vue` 不够,可以继续拆分局部组件,例如: - `ResultSummary.vue` - `ArtifactPreview.vue` 但这些文件仍然必须放在该技能目录下,且都属于局部组件,不是页面。 --- ## 14. 最终硬规范 从现在开始,前端对“技能”的唯一合法表达方式是: EAI 技能: `src/skills/eai//index.ts` 用户技能: 不以源码页面形式存在,统一由后端配置记录 + `src/skills/custom/adapters/*` 运行时适配。 这是技能的主入口。 配套合法文件只有: - `executor.ts` - `ResultCard.vue` - `SettingsPanel.vue`(可选) 除此之外,不再接受任何“每个工具一个执行页”的实现方式,也不接受把用户工具和 EAI 工具混放在同一来源目录里。