FastGPT ChatAgent 辅助生成接入 Agent Skill:子 Skill 元数据存储与运行态对齐方案解析
【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT
导读
本文围绕 FastGPT 开源仓库中的设计文档 skill-assist-generation-integration.md,完整解析 ChatAgent 的“辅助生成”(HelperBot TopAgent)如何接入 Agent Skill 能力:从权限查询、资源列表与 Prompt 约束、生成结果回填、sandbox 自动联动,到发布阶段结构化存储包内子 Skill 元数据(runtimeSkills/currentRuntimeSkills),再到运行态 Skill 应用名与子 Skill 的两层信息对齐。读者读完可以掌握该方案的完整数据流、Schema 变更点、校验规则与兼容策略,并能在当前仓库中找到对应的真实实现位置。
说明:设计文档中的
topAgentParamsSchema/TopAgentFormDataSchema在仓库实际实现中对应 auxiliaryGeneration/type.ts 中的ChatAgentHelperMetadataSchema与ChatAgentConfigFormDataSchema,文中以当前仓库实际代码为准。
背景:辅助生成链路缺少 Skill 维度
ChatAgent 是 FastGPT 中面向 LLM 的轻量 Agent 应用形态。用户在 ChatAgent 编辑页可切换“辅助生成”(Helper Bot)与“对话调试”两个标签页(见 ChatTest.tsx)。辅助生成的作用是:把应用已有的系统提示词、工具、知识库、文件上传状态、虚拟机(sandbox)状态传入一条独立的 LLM 链路,让模型基于这些预设资源为用户生成一整套 ChatAgent 配置并回填到表单。
设计文档指出该链路存在一个明显缺口:辅助生成只认识工具和知识库,不认识 Agent Skill。具体现状如下:
- 前端构建
topAgentMetadata时没有携带appForm.selectedAgentSkills。 - 辅助生成的入参/出参 Schema 中没有 Skill 字段。
generateResourceList()只生成工具与知识库资源列表,Prompt 也只描述这两类资源。- Skill 创建/发布时,Mongo 只保存平台 Skill 主表信息和版本包指针(
storageKey),没有结构化保存包内多个SKILL.md的name/description。
因此辅助生成既无法基于当前用户可访问的 Skill 做规划,也无法把生成结果回填到应用的 Skill 关联中。
当前数据模型
平台 Skill 主表(MongoAgentSkills)
主表保存的是平台 Skill 应用层面的元信息:
{ parentId, // 所属文件夹,支持继承权限 type, // skill 或文件夹 inheritPermission, // 是否继承上级权限 source, // personal / system(store)等来源 name, // 平台 Skill 应用名 description, // 平台 Skill 应用描述 avatar, teamId, tmbId, // 归属团队与成员 category, createTime, updateTime, deleteTime, currentVersionId, // 当前生效版本指针 creationStatus, creationError, creationPayload // 异步创建状态 }其中name与description是 Skill 应用层面的名称和描述,不等同于包内子 Skill 的名称与描述。
Skill 版本表(MongoAgentSkillsVersion)
版本表保存每次发布/导入产生的版本记录,真实包内容存放在对象存储,Mongo 仅保存storageKey:
{ skillId, // 所属 Skill tmbId, // 创建者 versionName, storageKey, // 版本包在对象存储的 key importSource, // 导入来源(仅导入场景) createdAt }在仓库实现中,该 Schema 已增加runtimeSkills字段(见 version/schema.ts),用于保存该版本包内实际包含的子 Skill 元数据,即本文第二阶段的核心落点。
应用关联结构
应用表单中关联 Skill 的结构为:
{ skillId: string; name: string; description: string; avatar?: string; isDeleted: boolean; }该结构对应仓库中的SelectedAgentSkillItemTypeSchema(见 formEdit/type.ts),保存的是平台 Skill 应用信息,不包含子 Skill 信息。另有StoredSelectedAgentSkillItemTypeSchema只保留skillId,用于持久化存储场景。
目标与非目标
分两阶段落地:
- 第一阶段:让辅助生成可以使用当前用户可访问的 Skill 应用——读取、展示、规划选择、回填
selectedAgentSkills,并在选择 Skill 后自动保持useAgentSandbox = true。 - 第二阶段:在 Skill 创建/导入/保存发布时解析包内
skills/**/SKILL.md的 frontmatter,把子 Skill 信息结构化写入 Mongo,让辅助生成展示更准确的子 Skill 能力。
明确的非目标(边界约束):
- 不把每个子 Skill 拆成独立权限资源,应用仍然关联平台 Skill 应用。
- 不在辅助生成请求时临时下载对象存储 zip 或解包读取
SKILL.md(元数据前置落库)。 - 不为了平台 Skill
description接一层 LLM 摘要。 - 不在第一阶段修改 Skill 发布/打包链路。
第一阶段:Skill 接入辅助生成
数据来源与权限查询
第一阶段只读取MongoAgentSkills的平台字段{ skillId, name, description, avatar },不读取版本包、不解析SKILL.md。
关键约束是:不要在服务端辅助生成中直接调用/core/ai/skill/listAPI,而应把列表 API 中“当前成员可访问 Skill”的权限查询抽成 service/helper 复用。仓库中该列表接口位于 pages/api/core/ai/skill/list.ts,其核心逻辑委托给listReadableAgentSkills()(见 manage/list.ts)。从实现看,该查询具备以下权限语义:
- 通过
authUserPer校验用户读权限,父文件夹存在时再用authSkill校验; - 通过
getResourcePermissionsByTeam汇总用户、组织、用户组权限(PerResourceTypeEnum.agentSkill); - 过滤
deleteTime: null(见 manage/list.ts); - 支持
source: personal/source: system的来源区分(见 manage/list.ts),设计建议第一版只接入source: personal,与现有应用选择器保持一致; - 默认只返回
AgentSkillTypeEnum.skill,不把文件夹作为可选资源暴露给辅助生成。
设计文档建议新增的 service 签名:
type AccessibleSkillResource = { skillId: string; name: string; description: string; avatar?: string; }; async function getAccessibleSkillResources({ teamId, tmbId, isRoot }: { teamId: string; tmbId: string; isRoot: boolean; }): Promise<AccessibleSkillResource[]>;metadata 与 Schema 变更
前端topAgentMetadata增加当前应用已选 Skill:selectedAgentSkills: appForm.selectedAgentSkills || []。仓库中该 metadata 的构建位于 ChatTest.tsx,chatAgentHelperMetadata已包含selectedAgentSkills、selectedTools、selectedDatasets、fileUpload、enableSandbox等字段。
对应 Schema 在 auxiliaryGeneration/type.ts 中落地:
- 入参
ChatAgentHelperMetadataSchema增加selectedAgentSkills: z.array(SelectedAgentSkillItemTypeSchema).nullish()(第 23 行); - 出参
ChatAgentConfigFormDataSchema增加selectedAgentSkills: z.array(SelectedAgentSkillItemTypeSchema).optional().default([])(第 61 行)。
这些预设 Skill 在 Prompt 中作为高优先级“已有配置”提示,不代表固定约束。
资源列表:新增 Skill 分区
generateResourceList()从“可用工具与知识库”扩展为“可用工具、知识库与 Skill”:
## 可用工具、知识库与 Skill ### 工具 ... ### 知识库 ... ### Skill - **skillId** [Skill]: name - description没有可访问 Skill 时展示:
暂未配置 SkillPrompt 约束
Prompt 需要让 TopAgent 明确知道:
- Skill 是可选资源,适合表达可复用操作经验、项目规范、流程约束和领域方法;
- 选择 Skill 时返回平台 Skill 的
skillId; - 不要求用户提供 Skill ID,TopAgent 应从资源列表中自行选择;
- 如果资源列表里没有合适 Skill,不要强行选择;
- 选择 Skill 后需要启用虚拟机,因为 Agent Skill 运行依赖 sandbox。
预设信息区增加:
**预设 Skill**: 搭建者已预先选择了以下 Skill ID: ...生成结果 Schema 与权限过滤
辅助生成的计划资源提取从{ tools, knowledges }扩展为{ tools, knowledges, skills },并根据skills过滤出真实可访问 Skill,形成selectedAgentSkills。
过滤逻辑必须按skillId校验当前用户仍有读权限,不能完全信任 LLM 输出——这是安全底线:即使 LLM 输出了无权限 Skill ID,也不能进入selectedAgentSkills。
前端回填与 sandbox 联动
onApply(formData)增加:
selectedAgentSkills: formData.selectedAgentSkills并且:
aiSettings.useAgentSandbox = enableSandboxEnabled || formData.selectedAgentSkills.length > 0仓库实现中该逻辑位于 ChatTest.tsx:onApply会把formData.selectedAgentSkills写入setAppForm,同时计算useAgentSandbox: enableSandboxEnabled || (formData.selectedAgentSkills?.length || 0) > 0。
如果当前套餐或系统配置不支持 sandbox,沿用现有checkAgentSkillSandboxUnavailable的提示与阻断逻辑。该函数位于 ChatAgent/utils.ts:当已选 Skill 且未开启 sandbox、且系统未展示 sandbox 或套餐不可用时,判定为“历史遗留的 Skill + 虚拟机不可用状态”,允许保存草稿但阻断发布/运行。
两条 Skill 选择路径
手动选择路径(已存在):
SkillSelectModal -> onAddAgentSkill(skill) -> appForm.selectedAgentSkills -> useAgentSkillSelect 自动保持 sandbox 开启仓库中useAgentSkillSelect(见 useAgentSkillSelect.ts)负责管理 ChatAgent 表单中的 Skill 选择与 sandbox 开关联动:选择 Skill 时自动打开 sandbox;系统未配置或套餐不可用时不允许开启 sandbox,但保留关闭入口,避免历史配置无法自助修复。第一阶段不新增“辅助生成专用 Skill 选择器”,手动选择仍沿用该链路,辅助生成只需读取其结果作为预设信息。
辅助生成路径(第一阶段新增):
HelperBot TopAgent -> generateResourceList() 提供可访问 Skill 资源 -> LLM 规划并返回 selectedAgentSkills -> topAgentConfig SSE -> HelperBot onApply(formData) -> ChatAgent ChatTest.onApply -> setAppForm 写入 appForm.selectedAgentSkills与工具回填的关键区别在于:Skill 回填不能只写 ID。前端应用表单需要完整保存:
{ skillId, name, description, avatar, isDeleted: false }因此服务端生成TopAgentFormData时,必须把 LLM 输出的 Skill ID 重新映射成可访问 Skill 列表中的完整对象,前端不再额外请求 Skill 详情。这与工具的loadGeneratedTools()按工具 ID 补齐模板配置不同——Skill 不需要加载节点模板,权限必须在服务端生成阶段校验,且回填时需要同步开启useAgentSandbox。
运行态 Skill 应用名与子 Skill 对齐
第一阶段还需解决一个关键的提示词对齐问题:
- 辅助生成和调试预览阶段展示、回填的是平台 Skill 应用;
- 真正运行 Agent 时,sandbox 中可读取和执行的是该 Skill 应用包内展开后的一个或多个
skills/**/SKILL.md子 Skill。
如果运行态只把子 Skill 的name/description提供给模型,而不提供平台 Skill 应用名,就会出现错位:
系统提示词或辅助生成结果提到:数据分析助手 运行态可用技能列表只有:data-cleaning、chart-reporting 模型无法稳定判断><skill> <app_id>平台 Skill 应用 ID</app_id> <app_name>平台 Skill 应用名</app_name> <app_description>平台 Skill 应用描述</app_description> <name>子 Skill 名</name> <description>子 Skill 描述</description> <directory>子 Skill 目录</directory> <path>子 Skill 的 SKILL.md 路径</path> </skill>并明确告诉模型:
- 匹配用户任务、系统提示词和应用配置时,应同时参考
app_name/app_description与子 Skill 的name/description; - 如果用户任务或系统提示词提到某个平台 Skill 应用名,应在该应用下选择最匹配的子 Skill;
- 执行时不能只凭平台 Skill 应用描述推断完整流程,仍必须读取最终选中的子 Skill
SKILL.md; app_name/app_description只用于对齐应用层语义和辅助生成回填结果,实际执行入口仍然是子 Skill 的path。
实现上,普通运行态应在注入 Skill 包后,把selectedAgentSkills中的平台应用信息合并到已部署版本信息,再传给getAgentSkillInfos();扫描子 Skill 时把匹配到的应用信息附加到每个DeployedSkillInfo,最后由buildAgentSkillsPrompt()输出上述字段和匹配规则。
仓库中该机制已有对应实现:getAgentSkillInfos(见 runtime/skill/core.ts)在 sandbox 中执行 find 命令扫描SKILL.md、解析 frontmatter,并在命中deployedVersion时把appId、appName、appDescription附加到每个 skill info(第 116-121 行);injectAgentSkillFilesToSandbox负责把已发布 Skill 包注入 sandbox 实例(含目录清理、authSkillByTmbId读权限校验与deleteTime: null过滤)。运行态入口统一从 runtime/skill/index.ts 导出getAgentSkillInfos、injectAgentSkillFilesToSandbox、syncBuiltinSkillsToSandbox、runAgentSkillVersionEntrypoints。
这个方案属于第一阶段的运行态 prompt 对齐,不要求提前解析并落库子 Skill 元数据,也不要求辅助生成资源列表展示子 Skill 详情——后者放在第二阶段。
第一阶段验收清单
- 辅助生成资源列表包含当前用户可访问 Skill;
- 用户要求适合某个 Skill 的场景时,生成结果能自动关联该 Skill;
- 已选 Skill 会作为预设信息进入下一轮辅助生成;
- 无权限 Skill 即使被 LLM 输出,也不会进入
selectedAgentSkills; - 选择 Skill 后应用配置中自动开启 sandbox;
- 手动选择的 Skill 会进入辅助生成预设信息;
- 辅助生成选择的 Skill 会直接显示在 ChatAgent 编辑表单的 Skill 列表中,和手动选择效果一致;
- 运行态 skill prompt 包含平台 Skill 应用
app_name/app_description和子 Skillname/description; - 当系统提示词或用户输入提到平台 Skill 应用名时,模型能在该应用下选择匹配的子 Skill,并读取对应
SKILL.md。
第二阶段:发布时保存子 Skill 元数据
目标
在创建、导入、保存发布 Skill 包时,解析包内所有skills/**/SKILL.md的 frontmatter,把子 Skill 信息结构化写入 Mongo。辅助生成后续直接从 Mongo 读取子 Skillname/description,不再需要临时解包。
子 Skill 元数据结构
新增公共类型:
type RuntimeSkillMetadata = { name: string; // 子 Skill 名(frontmatter name) description: string; // 子 Skill 描述 path: string; // 包内相对路径,如 skills/data-cleaning/SKILL.md };示例:
runtimeSkills: [ { name: 'data-cleaning', description: '清洗表格中的缺失值、异常值和格式问题', path: 'skills/data-cleaning/SKILL.md' }, { name: 'chart-reporting', description: '根据数据生成图表和分析报告', path: 'skills/chart-reporting/SKILL.md' } ]仓库中该类型已落地为RuntimeSkillMetadataSchema(见 global/core/ai/skill/type.ts),并被版本表 Schema 的runtimeSkills字段引用。
两层存储设计
MongoAgentSkillsVersion.runtimeSkills(必须存):表示该版本包里实际包含的子 Skill,不同版本可以不同。仓库 Schema 已实现(见 version/schema.ts)。MongoAgentSkills.currentRuntimeSkills(建议存):缓存currentVersionId指向版本的子 Skill 列表,辅助生成和列表查询直接读主表,避免每次 join 当前版本表。
关键原则:“最新版本信息”以当前生效版本为准,而不是按createdAt最大的版本为准。现有版本模型中的相关事实:
MongoAgentSkills.currentVersionId是当前生效版本指针;getCurrentVersion(skillId)先读主表currentVersionId,再查询对应版本(见 version/query.ts);- 保存发布新版本时,
saveDeploySkillFromSandbox()通过updateCurrentVersion(skillId, versionId)切换当前版本; - 版本列表可按
createdAt倒序展示历史版本,但用户也可通过版本切换把历史版本重新设为当前版本。
仓库中updateCurrentVersion(见 manage/update.ts)在一次updateOne内同时写currentVersionId、currentRuntimeSkills,并把creationStatus置为ready、清理creationError/creationPayload。
因此第二阶段必须保证:新建版本时把解析出的子 Skill 元数据写入版本表runtimeSkills;当前版本变化时把目标版本的runtimeSkills同步写入主表currentRuntimeSkills;版本切换必须同步刷新currentRuntimeSkills,否则会出现currentVersionId已切换但辅助生成仍展示旧子 Skill 的不一致。
平台 Skill name/description 不被覆盖
- 平台 Skill
name不被子 Skill 覆盖,仍然是 Skill 应用名; - 平台 Skill
description不由子 Skill 描述自动填充或覆盖:导入时用户填写什么就写入什么,未填写时保持空字符串。
原因是平台描述属于Skill 应用级元信息,子 Skill 描述属于运行时能力元信息。第二阶段只把子 Skill 信息写入runtimeSkills/currentRuntimeSkills,不反向改写平台 Skill 主表字段。
解析时机:覆盖所有产生版本包的入口
- AI 创建初始包:
completePendingSkillCreation(); - 导入 Skill 包;
- 复制 Skill;
- 从编辑态 sandbox 保存发布:
saveDeploySkillFromSandbox()。
解析应在上传对象存储前完成,确保包内容和入库 metadata 来自同一份内容。
仓库中已实现的解析器为extractRuntimeSkillsFromPackage(见 skill/package/runtimeMetadata.ts),其行为与设计文档高度一致:
- 先
validateZipSafety校验 zip 安全(空包、危险路径、symlink、解压大小限制,见 skill/package/zipBuilder.ts); - 标准 workspace 读取
skills/<skillDir>/SKILL.md;历史单 skill 包允许从根目录或一级目录SKILL.md兜底解析,并把 path 规范化为运行态可识别的skills/<name>/SKILL.md; - 初始空白 workspace 可通过
allowEmpty跳过空包校验(对应 AI 创建初始包场景)。
各发布入口已接入:completePendingSkillCreation(见 manage/creation/index.ts)在解析后调用updateCurrentVersion({ ... runtimeSkills });saveDeploySkillFromSandbox(见 sandbox/application/skillEdit/deploy.ts)同样在打包后解析并写入 runtimeSkills。
校验规则
发布包解析后需要校验:
- 至少存在一个
skills/**/SKILL.md; - 每个
SKILL.md必须有 frontmattername; description建议必填;若为兼容旧包允许为空,辅助生成展示时用空字符串;- 同一个包内子 Skill
name不能重复; path必须在skills/下,不能接受../等越界路径。
重复 name 不应静默覆盖,应发布失败并给出明确错误。仓库实现中均以抛错方式执行(如Duplicate runtime skill name: ${name}、${path}: frontmatter name is required、Skill package must contain at least one skills/<name>/SKILL.md)。
辅助生成展示升级
第一阶段资源列表:
- **skillId** [Skill]: 平台 Skill 名 - 平台描述第二阶段升级为展示当前版本全部子 Skill:
- **skillId** [Skill]: 平台 Skill 名 - 平台描述 - **data-cleaning**: 清洗表格中的缺失值、异常值和格式问题 - **chart-reporting**: 根据数据生成图表和分析报告模型选择时仍然只返回平台skillId,不返回子 Skill path。
Prompt 长度控制策略
第二阶段资源列表完整展示当前版本里的所有子 Skill:
- 不限制单个 Skill 展示的子 Skill 数量;
- 不截断子 Skill 描述;
- 不追加“还有 N 个子 Skill”这类摘要提示。
如果后续出现 prompt 过长问题,应先基于真实包规模和模型上下文窗口做数据评估,再单独设计压缩策略;第二阶段不提前加入展示限制。
数据迁移与兼容
第二阶段上线后,旧版本记录没有runtimeSkills。兼容策略:
- 旧数据的
runtimeSkills缺失时,辅助生成退回使用平台 Skillname/description; - 不强制后台批量解包历史对象存储;
- 用户下一次保存发布后,自动写入当前版本的
runtimeSkills和主表缓存。
测试计划
第一阶段测试
ChatAgentHelperMetadataSchema(对应文档中的topAgentParamsSchema)支持selectedAgentSkills;ChatAgentConfigFormDataSchema(对应TopAgentFormDataSchema)支持selectedAgentSkills默认值;generateResourceList()能输出 Skill 分区;无 Skill 时输出空提示;- 权限过滤只返回当前用户可读 Skill;
- LLM 输出不存在或无权限 Skill ID 时被过滤;
- 前端
onApply能回填selectedAgentSkills;回填 Skill 时自动开启 sandbox; - 运行态
buildAgentSkillsPrompt()输出app_id、app_name、app_description; - 运行态 prompt 明确要求用平台 Skill 应用信息匹配任务,再读取匹配子 Skill 的
SKILL.md。
第二阶段测试
- 单个
SKILL.md解析出一个 runtime skill;多个SKILL.md解析出多个 runtime skills; - 重复
name发布失败;缺少name发布失败;无SKILL.md发布失败; - 导入 Skill 时不使用子 Skill 描述自动填充平台
description; - 保存发布新版本后:
MongoAgentSkillsVersion.runtimeSkills写入、MongoAgentSkills.currentRuntimeSkills更新、currentVersionId正确切换; - 旧版本无
runtimeSkills时辅助生成仍可使用平台描述。
实现状态对照与后续演进
对照设计文档与当前仓库代码可以发现,该方案的大部分骨架已在代码库中落地,可以作为继续开发与验证的基线:
| 设计要点 | 仓库落地位置 |
|---|---|
| metadata 携带已选 Skill | ChatTest.tsx |
| 入参/出参 Schema 增加 Skill 字段 | auxiliaryGeneration/type.ts 与 auxiliaryGeneration/type.ts |
| 前端回填 Skill 并联动 sandbox | ChatTest.tsx |
| sandbox 不可用判定 | ChatAgent/utils.ts |
| 可访问 Skill 权限查询 | pages/api/core/ai/skill/list.ts 与 manage/list.ts |
版本表runtimeSkills+ 主表currentRuntimeSkills | version/schema.ts、manage/update.ts、global/core/ai/skill/type.ts |
| 包内子 Skill 元数据解析与校验 | skill/package/runtimeMetadata.ts 与 skill/package/zipBuilder.ts |
| 发布入口写入元数据 | manage/creation/index.ts、sandbox/application/skillEdit/deploy.ts |
| 运行态两层信息(app 层 + 子 Skill 层) | runtime/skill/core.ts |
设计文档中列出的后续 TODO(如把可访问 Skill 查询进一步抽成独立 service 供generateResourceList()复用、扩展计划资源提取支持skill类型、运行态 prompt 的匹配规则补充、以及各发布入口/版本切换的测试覆盖)可作为该功能继续完善的方向。对运行态与辅助生成链路有更深入需求的读者,可以从上述源码文件继续追踪调用链。
【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考