背景
不少技能的脚本要调第三方供应商的 API(天气、翻译、图床、搜索等),运行前提是环境里有对应的 API Key 或账号配置。目前 SKILL.md 没有地方声明这类依赖:
- 用户装完技能就能启用,直到脚本真正跑起来才报错,报错还是脚本自己的原始输出,看不出缺什么、去哪配;
- 模型拿到失败结果后,可能反过来引导用户把密钥直接贴进聊天,既不安全也不好管理;
- 技能作者只能把"请先设置 XXX_API_KEY"写在正文里,靠用户自觉,没有任何界面支撑。
期望行为
- 声明:技能在 SKILL.md frontmatter 里声明脚本必需的环境变量——变量名、供应商名称、用途说明、申请地址、是否可选。未声明的技能行为完全不变。
- 详情页配置:技能详情抽屉新增「运行要求」区块,逐项展示变量状态(未配置 / 已填写 / 使用系统环境变量),支持 masked 填写,也支持一键"从系统环境变量读取"(检测到系统已有该变量时,存引用不落盘值)。
- 可用性标记:必填变量未满足时,技能标记为「暂不可用」——卡片上出现提示徽标,注入模型的技能清单里也带上不可用标注和缺失项;不锁启用开关,不弹窗打断。
- 调用时兜底:会话中模型读取该技能(SkillsManager action=read 或
/技能名 提及)时先做前置检查,不满足则返回结构化的"缺少必要环境变量"结果;聊天界面渲染引导卡片,询问用户「跳转技能详情页填写」或「读取系统环境变量」,配置完成后可直接重试。
- 注入:用户填写的值在该技能启用的会话里注入 shell 工具的执行环境;"系统环境变量"模式下子进程本来就继承,不重复存值。
边界与安全
- 值在界面上始终 mask,错误信息做 redact(参照 stt 模块 secretFields 的做法);
- SkillsManager 的结果只回传"哪些变量缺失/已满足",不回传值本体;
- 返回给模型的文案明确指示:等待用户在界面配置,不要请求用户在聊天中粘贴密钥;
- 声明格式向后兼容,解析器忽略未知字段,其他工具读同一份 SKILL.md 不受影响。
涉及范围(初步)
- Rust 技能服务:frontmatter 解析、list/read 响应扩展、读取前置检查(
crates/agent-gui/src-tauri/src/services/skills/);
- 前端共享层:
SkillSummary 类型、SkillsSettings 存储、buildSkillsSystemPrompt 标注(crates/agent-ui/src/lib/skills/);
- Skills Hub:
InstalledSkillCard 徽标、InstalledSkillPreviewDrawer 运行要求区块;
- 聊天:
skillTools.ts 拦截、工具结果卡片与跳转交互;
- shell 执行:按会话注入技能环境变量;
- 双端:gateway webui 复用同一 Rust 后端(skills.manage 桥接)与共享组件,聊天侧引导卡片需要两端接入。
具体实现方案正在评审,定稿后补充到本 issue。
背景
不少技能的脚本要调第三方供应商的 API(天气、翻译、图床、搜索等),运行前提是环境里有对应的 API Key 或账号配置。目前 SKILL.md 没有地方声明这类依赖:
期望行为
/技能名提及)时先做前置检查,不满足则返回结构化的"缺少必要环境变量"结果;聊天界面渲染引导卡片,询问用户「跳转技能详情页填写」或「读取系统环境变量」,配置完成后可直接重试。边界与安全
涉及范围(初步)
crates/agent-gui/src-tauri/src/services/skills/);SkillSummary类型、SkillsSettings存储、buildSkillsSystemPrompt标注(crates/agent-ui/src/lib/skills/);InstalledSkillCard徽标、InstalledSkillPreviewDrawer运行要求区块;skillTools.ts拦截、工具结果卡片与跳转交互;具体实现方案正在评审,定稿后补充到本 issue。