桌面助手 Ankita 跑技能的方式很朴素:一个文件夹,里面放一份 SKILL.md,开头一小段 frontmatter,后面是模型照着走的 markdown 步骤。这套东西一直能用,直到作者被一个不太舒服的问题问住——一个技能,到底被允许做什么?
诚实的答案是:模型决定做什么就做什么,只受工具审批约束。技能本来有个"建议工具"的提示,而技能工具自己也写明,建议工具只是提示。于是在 v2.7.0 里,作者给技能加了一份权限清单,并且是刻意把它做成"建议"而不是"强制"。这篇文字讲的就是这个选择,以及他为什么到现在仍不确定它是对的。
问题出在:一个看起来像边界的提示
技能是指令,不是代码。一个加载了代码审查技能的模型,转头去调 shell 工具,没有任何东西能拦住它,因为技能文本从来没有地方声明"我只需要这几个工具"。模型看到的目录行只列了描述和建议工具,没有范围声明——读者分不清一个只读的导览技能,和一个会碰网络和 shell 的技能。
失效模式是安静的越界:一个只读技能慢慢多出一个写操作、一次网络调用、一条命令,因为每一步看上去都合理。
做出来的东西:两个 frontmatter 字段
清单就放在同一个 frontmatter 块里,由 src/core/skills.mjs 中的 parseSkillFile 解析。两个字段:allowed-tools 和 touches。示例里,一个 commit-review 技能声明 allowed-tools 为 git、read_file,touches 为 files、network。
解析在便宜且诚实的地方做得很严:每个字段上限 200 字符(PERMISSION_CHARS),这个界限是为了让安装提示保持可读;工具名必须匹配 ^[a-z][a-z0-9_-]*$,范围必须匹配 ^[a-z][a-z0-9_.-]*$;条目通过 Set 去重,touches 的值转小写,字段非法则整个技能被拒——loadSkills 记下原因然后跳过。格式错误的技能不会拖垮启动,它只是不加载。
模型看到的内容来自 skillPromptLines:在技能描述后面追加一行 Permissions: allowed-tools: git, read_file; touches: files, network。每条提示还带着一句常驻指令:"技能只是指令。建议工具是提示,不是要求。当 allowed-tools 存在时,优先使用这些工具,使用其他工具前先询问。"
注意措辞——是"优先"和"询问",不是"你只能使用";而且清单只是被展示出来,从不进入任何过滤器。
刻意留下的缺口:强制机制在别处,但不在这里
这是最该被戳的地方。强制机制其实已经存在,只是没和技能连起来:agent 接受一个 allowedTools 集合,tool-host.mjs 用它过滤 specs,子 agent 预设也跑在解析好的工具集上。十月的信任安全工作甚至删掉了一个没被使用的 isToolAllowedForSkill 辅助函数,免得代码暗示出一种并不存在的强制。发布说明也写得很直白:工具审批仍然是强制边界。
所以现状是:清单声明意图,审批拦住单次危险调用,中间没有任何东西阻止一个技能的模型去够一个未列出的工具,然后凭它自己的理由被批准。
为什么不直接接上?强制有它自己的失效模式。一个任务中途确实需要未列出工具的技能,要么直接坏掉,要么反复弹窗向用户要例外,而每一次例外提示都在训练用户点掉下一次。建议式清单从不弄坏任务,它只让范围可见;强制式清单让范围成真,但也让每一个作者的失误变成一条断掉的工作流。
还有一个采用率的事实,足以压掉任何宏大说法:没有任何内置技能声明 allowed-tools 或 touches。解析器、校验、目录行、提示指令都在 v2.7.0 里发了,而这个格式在代码树里零用户。一个没人写权限的权限系统是文档,不是边界。信任安全批次记录的验证(11 个信任安全测试、10 个技能测试通过)覆盖的是解析器和目录渲染,不是一个真实在用的清单。
会改什么,以及不会声称什么
清单即便没人用,也买到了一样东西:词汇。下一个技能作者可以在标准位置声明范围,而不是把它埋进散文里;工具名打错现在会在加载时带着清晰信息失败,而不是悄悄等于什么都没说。
它没买到的是安全。如果你在 v2.7.0 的说明里读到"技能权限"就脑补出一个沙箱,那幅图是错的:permissionSummary 渲染的是文本,提示只是请模型优先,真正拦住的是审批闸门,一次一个调用。
作者把决定写得尽量可证伪:建议式清单加逐次调用审批是诚实的设计,因为提示层的提示永远不可能成为安全边界,假装它可以比承认它不行更糟。强制应该等一个路由层,能按技能上下文拒绝调用,又不把每个边缘情况变成审批弹窗。
他把话递出来了——告诉我我错在哪,它最先会在哪里断?如果明天就把 skill.allowedTools 接进 agent 的工具过滤器,第一个被弄坏的合法技能任务是什么,这个代价值不值得为一个模型说不通的边界去付?Ankita 是开源的,代码在 src/core/skills.mjs,他说比起继续发布"打扮成权限的建议",更想听到那个反例。
热门跟贴