Skip to content

Support loading bundled Skill resource files - #19031

Merged
88250 merged 3 commits into
siyuan-note:devfrom
frostime:feat/skill-multifile-resources
Aug 31, 2026
Merged

Support loading bundled Skill resource files#19031
88250 merged 3 commits into
siyuan-note:devfrom
frostime:feat/skill-multifile-resources

Conversation

@frostime

@frostime frostime commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Feat/skill multifile resources | 支持 Skill 导入资源文件

问题与需求

SiYuan 能够安装多文件 Skill,但 skill.load 只返回 SKILL.md,Agent 无法从激活结果中发现和读取随 Skill 安装的资源。

本次变更按照 Agent Skills 客户端实现指南Agent Skills 规范补充按需加载资源的能力。

方案

激活 Skill 时,将资源清单写入返回给 Agent 的 context;需要资源内容时,仍调用现有的 skill 工具。

name 参数增加资源路径语义:

load("<skill-name>")
load("<skill-name>/<resource-path>")

第一种形式加载 SKILL.md 并列出资源,第二种形式读取指定资源。所有 Skill 都支持这种用法。

对于 SiYuan 工作空间内且能通过现有 file 工具访问的 Skill,激活结果还会提供工作空间相对路径,Agent 可以选择使用 file.readfile.listfile.grepfile.find,完成分段读取、目录浏览和内容搜索。用户级 Skill 不提供该路径,继续使用 skill.load(name/path)

此方案的优势在于简单,不引入任何额外的新工具,仅靠提示词引导,拓展已有行为的边界

具体变更

  • Skill 加载逻辑支持 locator、资源清单和指定资源读取,并沿用现有的 Skill 优先级、启用状态和别名解析
  • skill 工具返回 <skill_resources>;工作空间路径通过现有 file 校验后,再返回 <skill_location>
  • 资源路径限制在 Skill 根目录内;资源清单和单次读取均有数量及大小限制
  • 增加工作空间 Skill、用户级 Skill、路径边界、符号链接、输出格式和限制条件测试

效果示例与对比

场景 变更前 变更后
激活 Skill 只返回 SKILL.md 返回 SKILL.md 和资源清单
读取工作空间 Skill 资源 依赖正文写死路径 可以使用 skill.load(name/path),也可以选择 file.readfile.listfile.grepfile.find
读取用户级 Skill 资源 没有可用入口 使用 skill.load(name/path)

AGENT 可能的使用效果如下:

导入工作空间 Skill:

{
  "action": "load",
  "name": "pdf-processing"
}
<skill_content name="pdf-processing">

# PDF Processing

处理 PDF 文件的相关指令。

<skill_resources>
  <file>references/spec.md</file>
  <file>scripts/extract.py</file>
</skill_resources>

<skill_location path="data/storage/ai/agent/skills/pdf-processing">
  For this skill directory, use the `file` tool's read-only actions: `read`, `list`, `grep`, and `find`.
</skill_location>

</skill_content>

其中 skill_resourcesskill_location 均为新增。此格式参考 Agent Skills 客户端实现指南

此时只读取 SKILL.md。需要参考资料时,再读取对应资源:

{
  "action": "load",
  "name": "pdf-processing/references/spec.md"
}
<skill_resource skill="pdf-processing" path="references/spec.md">

资源文件内容

</skill_resource>

skill_location 和内部的指令仅仅作为附加产品,在 AGENT 可能希望做 chunk 读取而非无脑全量的时候,给他提供一些工具。其启发灵感来源于 PI 的设计,在 PI Coding Agent 中,harness 不提供 skill read 工具,而是直接暴露 SKILL 所在路径,让模型自己调用 read 或者 bash 工具根据需求读取。

特别说明

本次变更中,SKILL.md 正文保留现有的非敏感变量渲染;resource 部分刻意保留原文,不进行变量渲染。

考虑如下:

  • 思源内部的 SKILL 变量渲染机制误杀风险较高($NAME${NAME} 都是非常常见的模式)。
  • 我个人对“在 SKILL 内部放置文字变量并按需更改”这一需求持怀疑态度(这与 SECRET Key 是两回事)。引入变量的灵活性是否确为实际需求,以及它可能带来的误杀风险,权衡之下是否值得,存疑。
  • PR 不宜扩大范围,因此排除了“单独将 $NAME${NAME} 排除在外、另立渲染机制”的做法。

若开发者对本 PR 的选择有异议,可以提出辩驳,也可以按自己的意愿修改,或关闭 PR。

变更类型

  • 缺陷修复
  • 代码重构
  • 新功能
  • 文案或语言更新

验证

go test ./util ./mcp/tools ./agent
go vet ./util ./mcp/tools

效果对比

变更前

图片

变更后

图片

检查清单

  • 已完成代码自查
  • 我拥有所提交代码的完整权利,并同意以 AGPL-3.0 许可证授权
  • PR 提交到 dev 分支且不存在合并冲突

@88250 88250 added this to the 3.8.3 milestone Aug 29, 2026
@88250

88250 commented Aug 30, 2026

Copy link
Copy Markdown
Member

建议补充 UTF-8 校验后再合并。

当前 readSkillResource 会将任意字节直接转换为字符串,测试中也使用 GBK 字节验证了内部读取结果。但工具内容随后会在 kernel/mcp/server.go::convertContentItem 中经过 json.Marshal;Go 的 JSON 编码会把无效 UTF-8 替换为 U+FFFD,因此该测试虽然能通过,实际 MCP 返回内容仍会变成乱码。

建议:

  • 将该接口明确限定为 UTF-8 文本,读取后使用 utf8.Valid(data) 校验,不合法时返回明确错误
  • 增加经过 convertContentItem 的传输层测试,避免只验证 util.LoadSkill 的中间结果
  • 如果后续需要支持图片等二进制资源,再考虑 MIME 类型和 Base64 返回机制

@frostime

Copy link
Copy Markdown
Contributor Author

已修改

@88250 88250 changed the title Feat/skill multifile resources | 支持 Skill 导入资源文件 Support loading bundled Skill resource files Aug 31, 2026
@88250
88250 merged commit 13e7d1a into siyuan-note:dev Aug 31, 2026
2 checks passed
@88250

88250 commented Aug 31, 2026

Copy link
Copy Markdown
Member

感谢你的贡献,思源有你更精彩!
Thank you for your contribution. SiYuan will be more wonderful with you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants