
现在随便一个Agent系统装几十上百个Skill是常态。
比如前两天介绍过开发项目用的51 个 Agent 和 35 个技能,还不包括之前陆续介绍过各类好用的技能:给 AI 装上真实浏览器:camofox-browser 实战等等。
所以,北大的这篇论文就是:如何结构化表达 Skill 让 AI 更好的选择用对技能:
把Skill文档从「人读的自然语言」变成「机器能查的三层JSON图」,技能检索效果提升12%,安全风险评估提升24%。
曾经见到有人说他装了300多个技能,一是上下文会塞进去更多的内容,二是上下文长了之后,大模型容易不好选中要执行哪个技能来完成任务。
先看一个来自论文附录B的实例:Writing Refiner。
它的SKILL.md描述大致是:「根据用户提供的草稿文本和写作上下文,加载风格指南,解析写作意图,选择适用的编辑规则,对文本进行润色,返回修订后的文本和应用的编辑原则列表。」
这段描述里混杂了四类信息:
技能能干什么:润色文本。
怎么调用:输入草稿文本、写作上下文,输出修订文本、应用的原则列表。
执行步骤:先验证输入、再解析上下文、再加载指南、再选择规则、最后应用修订。
安全边界:需要文件系统读取权限、依赖文本处理能力。
一个Agent系统想用这个Skill的时候,它需要同时回答三个问题:
第一,这个Skill和我当前任务匹配吗?要知道意图签名、输入输出契约。
第二,这个Skill的执行流程是什么?要知道分几个阶段、阶段间怎么跳转。
第三,这个Skill安全吗?有什么资源访问、会不会删数据、有没有网络调用。
这三个问题的答案在SKILL.md里全都有,但混在一起。
每次要做判断,都需要从头解析一遍全文。
论文管这个叫「表示瓶颈」:语义上不同的属性被压缩到单一的文本表面。
论文在引言和背景里花了大量篇幅分析为什么纯文本表示不够用,从两个方向展开。
从集合中选择技能,本质上是将用户请求与一个或多个可执行能力匹配。
这个设置表面上像查询与文档检索,但关键区别在于:每个候选者不是普通段落,而是一个可执行能力。
神经检索研究已经表明,表示质量是查询与候选匹配的核心。
工具和技能检索工作进一步发现,能力匹配依赖的信号分散在接口字段、指令文本、示例、实现细节和结构依赖中。
通用检索器并不总能可靠地迁移到工具或技能选择。
如果检索器只看一小段元数据描述,可能漏掉关键线索。
如果检索器通读几千字的SKILL.md全文,真正有用的调用信号又被噪音淹没了。
关于工具文档压缩和增强的工作也指出:冗长的工具描述通常需要预处理、重组才能有效检索。
但所有这些工作还有一个前提假设没有解决:候选表示已经以合适的形态存在。
实际上,每个技能在检索前到底该怎么表示,才能让匹配过程利用显式的接口、结构和操作线索,而不是只靠原始文档文本,这个问题一直没被认真对待。
工具使用Agent的安全风险通常出现在自然语言指令与外部能力的边界。
间接提示注入研究表明,检索或工具返回的文本可能模糊数据与指令的区别。
Agent安全基准将这一关切扩展到更复杂的多步设置:涉及工具调用、内存读写、不可信的外部观察以及有害的用户目标。
在这些场景下,攻击者不需要直接修改代码,只需要在Skill的文本描述里嵌入一段看似无害的指令,就可能绕过审查。
提示流完整性、强制访问控制、最小权限执行这些安全工作将安全框定为权限边界和资源访问问题。
也就是:谁能碰什么,在什么条件下能碰,碰完之后有什么后果。
技能特定的安全研究更进一步指出:可复用技能本身就构成一个独立的攻击面。
因为技能包含自然语言指令、可执行代码片段、隐式信任关系,而且通过分发和复用在不同Agent间传播。
这些副作用依赖纯文本阅读来发现基本不可能。
你需要知道每个动作的类型是什么、资源范围在哪、有没有数据流向外部的线索,而这些信息分散在长篇自然语言里。
所以安全分析的真正挑战是:让风险相关的证据在技能被调用之前就变得可检查。
这些证据包括动作类型、资源范围、依赖关系和数据流线索。
例如这篇可利用的漏洞就是从Skill的攻击开始:89.2%攻击成功率!腾讯、字节研究发现 OpenClaw Agent 存在可利用结构性漏洞
SSL核心思想:把上述三类信息拆到三个不同的层里,每一层只干一件事。
调度层 → 这个Skill能做什么、怎么调用
结构层 → 这个Skill的执行流程分几步
逻辑层 → 每一步具体做了什么操作、碰了什么资源论文借用了认知语言学的三个经典理论来设计这三层。
这三个类比是SSL的骨架,不是贴上去的标签。
调度层类比「记忆组织包」。
这是Schank 1980年提出的概念。
人在记忆里组织重复经验时,不是按关键词存,而是把经验打包成「目标+情境」的单元。
比如「去餐厅吃饭」不是存成一个「吃」的标签,而是存成一个有入场、点单、结账节奏的整体。
SSL的调度层就是这个作用:把Skill打包成「意图签名+输入输出契约+依赖项」的能力单元,让系统在不展开细节的情况下就能判断:这个Skill能干什么、适不适合当前任务。
结构层类比「脚本理论」。
来自Schank和Abelson 1977年的经典工作。
核心观察是:人的刻板活动不是线性意识流,而是由一系列可预期的场景组成的。
去餐厅吃饭这个活动,由「进门→就座→点餐→用餐→结账→离开」这几个场景构成,每个场景有自己进入条件和退出规则。
SSL的结构层同理:把Skill拆成PREPARE、ACQUIRE、REASON、ACT、VERIFY、RECOVER、FINALIZE这些类型化场景,每个场景标注输入输出、进入条件、退出规则、下一场景跳转逻辑。
逻辑层类比「概念依赖」。
Schank 1972年提出的。
他认为语言意义的底层可以被分解为有限种类的原始动作结构:ATRANS(抽象转移)、PTRANS(物理转移)、MOVE(移动)、MBUILD(心智构建)等。
SSL的逻辑层就受此启发,定义了12种原子动作类型:READ、SELECT、COMPARE、VALIDATE、INFER、WRITE、UPDATE_STATE、CALL_TOOL、REQUEST、TRANSFER、NOTIFY、TERMINATE。
每个原子动作从封闭清单中选择act_type,并将参数、效果和资源边界记录为类型化证据。
关键一点:原子性是表示的属性,与运行时细节无关。逻辑步骤就是源工件支持的最小操作单元,不发明缺失的实现细节。
论文给出了SSL的形式化表示。
设d为一个技能工件,比如SKILL.md文件。SSL将d映射为类型化的表示:
G_d = (r_sch, G_str, G_log, R_cont, R_entry)其中r_sch是调度层,捕获调用信号的技能级接口记录。
G_str是结构层,捕获执行阶段及其转换的场景级图。
G_log是逻辑层,捕获原子动作和资源使用证据的逻辑步骤图。
R_cont记录跨层的包含关系。R_entry记录入口指针。
在操作上,r_sch被实现为单个技能级记录,其余两层被实现为记录图:
场景记录将G_str实例化为执行阶段上的有向图,逻辑步骤记录将G_log实例化为这些阶段内原子动作上的有向图。
两个辅助关系指定如何组装:R_cont将场景分配给技能、将逻辑步骤分配给场景,R_entry标识遍历开始的位置。
SSL遵循三个严格的设计目标:
紧凑。
只保留技能管理和使用所需的证据,主动排除开放式或主观属性。
不评估技能质量好坏,不描述用户画像,不推断开发者意图,不推测隐藏行为。
类型化。
使用受限词汇表,规范化输出在技能间可比较。
比如scene_type只能是PREPARE/ACQUIRE/REASON/ACT/VERIFY/RECOVER/FINALIZE这七种。
act_type只能是READ/SELECT/COMPARE等十二种。不能写自由文本。
接地。
字段严格总结源工件中存在的证据,不尝试推断隐藏行为。
如果SKILL.md里没提某个操作,对应的字段就空着,不能从上下文编造。
调度层不把技能仅仅视为指令文档,而是将工件作为调用级能力单元来对待。
它在更深层次检查之前暴露支持的意图、输入输出契约以及粗粒度依赖和控制流属性。
这为每个技能提供了一个稳定的能力记录,可以在不展开完整场景或逻辑步骤结构的情况下,跨仓库进行比较。
一个仓库里有500个Skill,你不需要挨个展开内部结构,只看调度层就能做初筛。
结构层的节点是场景,边表示这些场景之间的阶段级转换。
受脚本式事件结构启发,它将低级操作分组为连贯的阶段:PREPARE(准备)、ACQUIRE(获取)、REASON(推理)、ACT(行动)、VERIFY(验证)、RECOVER(恢复)。
这使得技能的阶段组织在读者检查单个逻辑步骤之前就可见。
看到结构层你就能快速理解:这个Skill的执行分几个阶段、每个阶段的进入条件和退出条件是什么、阶段之间怎么跳转、有没有分支和回退路径。
逻辑层的节点是逻辑步骤,边表示基于源代码的原子动作之间的微级转换。
每个原子动作从封闭的原始清单中选择act_type,将参数、效果和资源边界记录为类型化证据。
比如一个步骤标注了act_type = CALL_TOOL、resource_scope = NETWORK,你立刻知道这是外部调用。
标注了resource_scope = CREDENTIALS,立刻知道涉及凭证访问。
因为原子性是表示的属性,逻辑步骤就是源工件支持的最小操作单元。
这层回答的是:这个技能在每一步具体做了什么、碰了什么东西、会产生什么效果。
怎么把一个SKILL.md变成SSL JSON对象?
用DeepSeek-V3.2规范化器,四个步骤:
第一,提取技能级调度记录。
第二,将文档分解为场景。
第三,将每个场景扩展为基于源代码的逻辑步骤。
第四,验证生成的图。
规范化器严格作为语义提取器运行,避免开放式摘要。
其提示明确指定模式、允许的词汇表和接地策略,每个填充的字段必须由源工件支持。
验证环节检查六项硬约束:结构良好性、标识符一致性、允许的枚举值、包含链接无环、入口指针指向有效记录、转换目标引用现有记录或终止值。
解析失败或验证失败的输出会被重试。
在6,500个候选技能上,两遍规范化后6,184个产生了有效的SSL对象,成功率95.1%。
第一遍用DeepSeek-V3.2的Thinking模式,温度0.1。失败或格式错误的重试用Non-Thinking模式,同样温度0.1。
论文还做了人工审计:对100个标注技能检查接地质量,83%的SSL输出被判定为由对应源工件支持。
以下是论文附录B中Writing Refiner经过规范化后的完整SSL对象:
{
"skill": {
"skill_id": "SKILL_WRITING_REFINER",
"skill_name": "Writing Refiner",
"skill_goal": "Revise user-provided text for clarity and concision.",
"top_pattern": "GUIDE_AND_APPLY",
"expected_inputs": [
{"name": "draft_text", "type": "str"},
{"name": "writing_context", "type": "str"}
],
"expected_outputs": [
{"name": "revised_text", "type": "str"},
{"name": "applied_principles", "type": "list"}
],
"dependencies": [
{"type": "permission", "value": "filesystem.read"},
{"type": "capability", "value": "text_processing"}
],
"tags": ["writing", "editing", "documentation"],
"intent_signature": ["improve this text", "edit for concision"],
"control_flow_features": {
"has_branch": true,
"has_loop": false,
"has_tool_call": true,
"touches_sensitive_resources": false
},
"entry_scene_id": "S_PREPARE",
"subscenes": ["S_PREPARE", "S_ACQUIRE", "S_REVISE"]
},
"scenes": [
{
"scene_id": "S_PREPARE",
"scene_name": "Prepare request",
"scene_type": "PREPARE",
"scene_goal": "Validate the request and infer the editing intent.",
"input": ["$draft_text", "$writing_context"],
"output": ["$parsed_intent", "$target_principles"],
"entry_conditions": ["skill_dispatched"],
"exit_conditions": ["writing_task_clarified"],
"entry_logic_step_id": "L_VALIDATE_INPUT",
"contained_logic_steps": ["L_VALIDATE_INPUT", "L_PARSE_CONTEXT"],
"next_scene_rules": [
{"condition": "success", "target": "S_ACQUIRE"},
{"condition": "default", "target": "END_FAIL"}
]
},
{
"scene_id": "S_ACQUIRE",
"scene_name": "Acquire style guidance",
"scene_type": "ACQUIRE",
"scene_goal": "Load the style guidance needed for the task.",
"input": ["$writing_context", "$target_principles"],
"output": ["$loaded_guidelines"],
"entry_conditions": ["writing_task_clarified"],
"exit_conditions": ["guidelines_loaded"],
"entry_logic_step_id": "L_SELECT_GUIDE",
"contained_logic_steps": ["L_SELECT_GUIDE", "L_READ_GUIDE"],
"next_scene_rules": [
{"condition": "success", "target": "S_REVISE"},
{"condition": "default", "target": "END_FAIL"}
]
},
{
"scene_id": "S_REVISE",
"scene_name": "Revise draft",
"scene_type": "REASON",
"scene_goal": "Apply the selected rules and return the revision.",
"input": ["$draft_text", "$loaded_guidelines", "$parsed_intent"],
"output": ["$revised_text", "$applied_principles"],
"entry_conditions": ["guidelines_loaded"],
"exit_conditions": ["text_revised", "summary_generated"],
"entry_logic_step_id": "L_PARSE_GUIDE",
"contained_logic_steps": ["L_PARSE_GUIDE", "L_SELECT_RULES", "L_APPLY_EDITING"],
"next_scene_rules": [
{"condition": "success", "target": "END_SUCCESS"},
{"condition": "default", "target": "END_FAIL"}
]
}
],
"logic_steps": [
{
"logic_step_id": "L_VALIDATE_INPUT",
"act_type": "VALIDATE",
"input_args": ["$draft_text", "$writing_context"],
"output_binding": "$input_valid",
"actor": "skill",
"object": "user_input",
"instrument": null,
"preconditions": ["skill_dispatched"],
"effects": ["$input_valid == true"],
"resource_scope": "MEMORY",
"resource_target": "working_memory.user_request",
"next_step_rules": [
{"condition": "$input_valid == true", "target": "L_PARSE_CONTEXT"},
{"condition": "default", "target": "YIELD_FAIL"}
]
}
]
}把这个JSON和前文的自然语言描述放在一起看,区别一目了然。
自然语言描述里需要AI判断:「这技能通不通网络?有没有文件操作?」
SSL结构化就可以直接看resource_scope字段。
自然语言描述里需要AI判断:「这技能执行分几个阶段?失败了怎么办?」
SSL结构化就可以直接看scenes数组和next_scene_rules。
论文构建了6,184个公开技能的检索候选池。
测试集包含431个意图级请求,覆盖功能性、基于约束、组合式、面向安全和场景式五种类型。
每个查询只有一个正确答案即它的源技能。
十种检索输入在统一的嵌入和排序管道下比较。
所有稠密向量由Qwen3-Embedding-0.6B生成,FAISS内积索引做排序。
十种输入分为三组:
第一组是纯文本基线:Desc_only只用短元数据描述、Full SKILL.md用完整源文档、Source Outline用源文档压缩大纲、Desc+Source Outline组合两者。
第二组是SSL增强变体,在相同源上下文基础上叠加三个层级的结构化表示:
SSL-Shallow:只加浅层规范化字段。就是调度层的基本接口信息:intent_signature、expected_inputs、expected_outputs、dependencies。抽掉冗长的自然语言描述,只留结构化信号。
SSL-Sched:在Shallow基础上加上紧凑的调度视图。包含control_flow_features和顶层执行模式(top_pattern),提供了比Shallow多一层的控制流信息,但还没有展开完整的场景和逻辑步骤。
SSL-Rich:最丰富的视图。在Sched基础上完整展开场景级信号(场景图、阶段类型、进入与退出条件)和接口级信号(输入输出契约、依赖项)。这是三层全部展开的完整SSL。
第三组是Full SKILL.md与各层SSL的叠加:测试在完整源文档上额外附加SSL是否还有增益。
核心指标是MRR@50:正确答案在前50个返回结果中的平均倒数排名。
分组 | 方法 | MRR@50 | NDCG@5 | NDCG@10 | Recall@10 |
|---|---|---|---|---|---|
基线 | Desc_only | 0.588 | 0.608 | 0.626 | 0.761 |
Full SKILL.md | 0.645 | 0.655 | 0.682 | 0.821 | |
Source Outline | 0.592 | 0.615 | 0.634 | 0.787 | |
Desc + Source Outline | 0.649 | 0.670 | 0.689 | 0.833 | |
SSL增强 | Desc + SSL-Shallow | 0.716 | 0.730 | 0.752 | 0.879 |
Desc + SSL-Sched | 0.694 | 0.716 | 0.733 | 0.868 | |
Desc + SSL-Rich | 0.729 | 0.748 | 0.770 | 0.905 | |
Full SKILL.md + SSL-Shallow | 0.664 | 0.688 | 0.706 | 0.847 | |
Full SKILL.md + SSL-Sched | 0.676 | 0.694 | 0.715 | 0.849 | |
Full SKILL.md + SSL-Rich | 0.681 | 0.705 | 0.720 | 0.856 |
最强非SSL基线Desc+Source Outline的MRR@50是0.649。
Desc+SSL-Rich是0.729,提升12.3%。
而且它超过了完整SKILL.md全文的0.645。
说明增益不是简单地靠塞更多文本堆出来的。
消融实验的观察:
SSL-Shallow(仅浅层规范化)已经比原始描述提供了显著增益,MRR@50从0.588跳到0.716。
SSL-Sched(加调度视图)反而不如Shallow,MRR@50回退到0.694。这可能因为调度视图本身比较紧凑,但缺少了场景级的组织信息,对检索信号贡献反而变小。
SSL-Rich(三层全开)效果最好,MRR@50 = 0.729。因为它加满了场景级和接口级信号,这些正是意图级技能搜索最需要的匹配线索。
Recall@10的变化更直观:Desc_only是0.761,Desc+SSL-Rich是0.905。多出14个百分点的用户在前十名里找到了正确技能。
Full SKILL.md与SSL叠加(第三组)整体弱于Desc+SSL的组合。这说明如果你已经有简洁的结构化表示,再叠全文原文反而稀释信号。
风险评估基准从6,184个技能里分层抽样252个。
六个风险维度:数据泄露、破坏性行为、权限升级、隐蔽执行、资源滥用、凭证访问。
黄金标签由Gemini-3.1-pro-preview、Claude-Sonnet-4.5、GPT-5三模型投票产生,分歧维度做二次审查加抽样人工审计。
每个模型同时接收完整的SKILL.md和完整的SSL表示。
评判器固定为DeepSeek-V3.2,只改变提供给评判器的表示,隔离SSL本身对证据暴露的影响。
五种输入:Desc Only(原始注册表名称和描述)、Full SKILL.md(完整源文档)、SSL-Shallow(规范化SSL接口字段)、Full SSL(完整结构化表示)、Full SKILL.md+SSL(源文档加完整结构化)。
威胁维度 | Desc | Full MD | SSL-Sh. | Full SSL | MD + SSL |
|---|---|---|---|---|---|
数据泄露 | 0.280 | 0.511 | 0.272 | 0.651 | 0.699 |
破坏性行为 | 0.162 | 0.371 | 0.147 | 0.403 | 0.439 |
权限升级 | 0.364 | 0.381 | 0.381 | 0.348 | 0.455 |
隐蔽执行 | 0.083 | 0.222 | 0.042 | 0.083 | 0.264 |
资源滥用 | 0.132 | 0.271 | 0.133 | 0.323 | 0.419 |
凭证访问 | 0.391 | 0.695 | 0.286 | 0.722 | 0.780 |
宏F1 | 0.235 | 0.409 | 0.210 | 0.422 | 0.509 |
输入 | 宏准确率 | 宏精确率 | 宏召回率 | 宏F1 |
|---|---|---|---|---|
Desc Only | 0.714 | 0.823 | 0.148 | 0.235 |
Full MD | 0.765 | 0.828 | 0.283 | 0.409 |
SSL-Shallow | 0.709 | 0.783 | 0.129 | 0.210 |
Full SSL | 0.778 | 0.841 | 0.315 | 0.422 |
MD + SSL | 0.801 | 0.884 | 0.382 | 0.509 |
最佳方案是Full SKILL.md+SSL,宏F1从纯文本的0.409提升到0.509。
每个维度的结果显示:组合视图在所有六个维度上都是最强。
提升最大的三个维度:
凭证访问:0.695到0.780。因为SSL的逻辑层直接标记了哪些步骤访问CREDENTIALS资源范围,不用从自然语言里猜是否存在凭证操作。
数据泄露:0.511到0.699。SSL暴露了NETWORK和LOCAL_FS资源访问,这些是数据泄露的核心静态信号。
资源滥用:0.271到0.419。SSL的结构层显示了执行阶段和执行次数,可以判断是否存在资源消耗循环。
重要发现:Full SSL单独使用时宏F1是0.422,只比纯文本的0.409提了一点。但Full SKILL.md+SSL直接跳到0.509。
召回率增益尤其关键:组合视图恢复了纯文本输入经常遗漏的弱但具体的风险信号,同时精确保持平甚至提升。
这说明SSL不能替代源文档。
它应该作为源文档旁边的「结构化证据层」来使用。
SSL负责暴露动作类型和资源范围,源文档负责解释为什么需要这些操作、在什么条件下调用、有没有限制条件。
如果你或者团队用着上百个Skill,可以从三个方向借鉴SSL的思路:
第一,给Skill加一层结构化清单
不一定非要搞三层JSON图。
最简单的起点:让每个Skill的元数据里包含intent_signature字段,用一两个自然语言短句描述这个Skill能响应什么请求。
这比全文向量检索已经强很多。论文的消融实验里,仅加SSL-Shallow就能把MRR@50从0.649拉到0.716。
Shallow层只包含调度层的基本字段:intent_signature、expected_inputs/outputs、dependencies、tags。
这些字段在任何一个技能文档里都能直接提取,不需要复杂的场景分解和逻辑步骤展开。
第二,在安全审查时用结构化证据替代全文阅读
现在大部分Agent系统的安全检查还是靠人读SKILL.md全文。SSL的逻辑层提供了另一种思路:让审查者直接看到每个步骤的act_type和resource_scope。
一个Skill如果所有逻辑步骤的resource_scope都是MEMORY,基本可以判定为低风险。如果出现NETWORK、CREDENTIALS、LOCAL_FS的组合,就需要重点审查。
论文在附录A的表5定义了完整的受限词汇表。act_type只有12种:READ、SELECT、COMPARE、VALIDATE、INFER、WRITE、UPDATE_STATE、CALL_TOOL、REQUEST、TRANSFER、NOTIFY、TERMINATE。resource_scope只有8种:MEMORY、LOCAL_FS、CODEBASE、PROCESS、USER_DATA、CREDENTIALS、NETWORK、OTHER。
这些枚举值的设计意义在于:它把安全检查从「读一段话猜风险」变成了「查字段做判断」。每个审查者面对的不是自由文本,而是有限类型组成的证据签名。
第三,把结构化和源文档配对使用,不要二选一
这是论文最重要的实践教训。SSL最佳表现永远出现在「源文档+SSL」的组合里,而不是SSL单独使用。
为什么?实验中SSL单独使用的宏F1(0.422)虽然略高于纯文本(0.409),但幅度很小。真正的跳升来自组合(0.509)。
因为自然语言里有结构化字段表达不出来的东西:设计意图、边界条件、使用场景的限制说明。
SSL是索引和线索,源文档是上下文和解释。两者配合才完整。
论文链接:https://arxiv.org/abs/2604.24026 代码仓库:https://github.com/COOLPKU/SSL 数据集:Hugging Face (SSL-SkillDiscovery / SSL-RiskAssessment)