Skip to content
文章目录
GitHub Star

Skill 不只是一个 SKILL.md:Agent 如何把研究方法变成可调用的能力 ​

一个 Agent 已经能搜索网页、读取 PDF、调用 MCP、运行 Python,为什么还需要 Skill?

因为拥有工具,不等于掌握完成一类任务的方法。

假设用户问:“比较这两只指数基金的跟踪质量,再看看它们在我的组合里有没有重复暴露。”

搜索工具可以取得基金净值、基准与披露持仓;Python 可以计算主动收益波动和重叠比例。但工具不会替研究者决定:是否先核对份额、币种、分红口径和共同窗口?缺少私人组合授权时,是否应该停止计算个人重叠?两条序列无法对齐时,是否应承认指标不足?

在 ETF Agent 中,我们把这些可以复用的研究方法组织为 Skill,同时坚持一项分工:Skill 定义方法,模型选择并综合方法,Host 管实际能力和来源授权,确定性程序管计算,原 Runtime 管预算、执行和恢复。

这篇文章不仅讨论 Skill 文件结构,还沿一次研究解释:同一个 Skill 为什么在 Entry、Main 和 Child(Subagent)眼里不同;如何从目录变成实际读取、计算与交接;哪些行为可由回执确认,哪些不能靠目录或声明推断。

一、一个 Skill 到底应该包含什么? ​

Agent Skills 规范以 SKILL.md 说明方法,并允许附带 scripts/、references/ 和 assets/ 等资源。其重要价值是渐进加载:先给模型简短名称和描述,需要时再读取正文与支持文件,而不是每轮把所有方法塞进上下文。

不过,SKILL.md 只是入口。需要计算、统一口径和多种可单独调用的方法时,ETF Agent 的内部基金研究包采用这样的结构(为阅读省略部分子目录):

text
fund-research/
├── bundle.json
├── SKILL.md
├── skills/
│   ├── fund-research-planner/
│   ├── index-fund-profile/
│   ├── index-fund-tracking-quality/
│   └── portfolio-overlap/
├── scripts/
├── references/
├── schemas/
└── templates/

截至本文核对的内部版本,Bundle 为 1.3.0,包含一个规划入口和十四种独立研究方法。它们的职责不同:

内容管什么不管什么
SKILL.md研究步骤、前提、证据与适用限制数据授权、模型执行循环
bundle.json包身份、版本、方法和共享资源位置额外调度与执行账本
子方法跟踪质量、组合重叠等明确问题必须创建一个独立 Agent
references/、schemas/参考知识、输入输出约定自动证明来源可信
scripts/对已提供的结构化数据做确定性处理擅自获取私有数据、替代 Host
templates/结果的可复用呈现形式宣称研究已经成功交付

用户只需要跟踪质量时可以直接选择这一方法;完整基金研究可以组合几个方法。子 Skill 不等于子 Agent,Bundle 也不是另一套任务调度器。

二、方法如何进入一次真正的研究? ​

有两条入口路径,但它们不是两份授权体系。

显式选择时,用户可提交已注册的 Skill ID,例如:

text
/index-fund-tracking-quality

原入口解析真实 API 字段或 slash 命令,校验登记身份、版本和内容,然后把选定方法绑定到这次 Research。既然用户已明确选择,就不必再让 Entry 模型猜一次“他是不是想调用 Skill”。显式指引可经受信任的启动上下文进入模型请求,但仍受上层安全约束及原 Run 授权限制。

自然语言选择时,用户只是问“它们跟踪得怎样”。普通 Entry 主要判断 Direct 还是 Research;它不会提前塞进完整 Skill 目录,也不需要凭词表为问题强行绑方法。进入 Research 后,Main 可看到简短目录,根据任务读取方法正文或继续普通研究。按需读取的正文作为普通受控工具结果进入后续上下文,不能因此提升为系统权限。

按起点分组阅读关系与条件;完整图保留在下方。

用户提问
  • → 显式指定 Skill? (判断)
显式指定 Skill? (判断)
  • 是 → 校验注册身份并绑定方法
  • 否 → Entry 判断 Direct / Research
校验注册身份并绑定方法
  • → 原 Run 与 Host Tool Surface
Entry 判断 Direct / Research
  • Research → Main 获得简短目录
  • Direct → 正常直接回答
Main 获得简短目录
  • → 按需读取方法,或直接研究
按需读取方法,或直接研究
  • → 原 Run 与 Host Tool Surface
原 Run 与 Host Tool Surface
  • → 来源和工具已获准? (判断)
来源和工具已获准? (判断)
  • 是 → 读取证据、必要时计算
  • 否 → 保留未授权或缺失的部分
读取证据、必要时计算
  • → 综合有范围的结论
保留未授权或缺失的部分
  • → 综合有范围的结论
综合有范围的结论
  • → 原发布与交付机制
完整关系图 · 可放大查看细节
自然语言选择时,用户只是问“它们跟踪得怎样”。普通 Entry 主要判断 Direct 还是 Research;它不会提前塞进完整 Skill 目录,也不需要凭词表为问题强行绑方法。进入 Research 后,Main 可看到简短目录,根据任务读取方法正文或继续普通研究。按需读取的正文作为普通受控工具结果进入后续上下文,不能因此提升为系统权限。
Agent architecture diagram 放大查看完整图 / Open full-size SVG

这里必须区分四个动作:发现方法、注入或读取指引、获得工具授权、实际消费方法。前两项让模型知道“可能怎样做”,第三项决定“允许做什么”,第四项才涉及真实工具调用、结果读取和研究综合。没有任何一步能自动证明下一步已经发生。

三、同一个 Skill,为什么在 Entry、Main 和 Subagent 中看到的内容不同? ​

沿开头的问题再走一遍。Main 需要公开数据比较两只基金,同时考虑把跟踪质量检查委派给一个 Child。此时“Skill 已经注册”不能被等同于“Main、Child 都能读、都能算”。

1. Entry、Main、Child 不是共享一份完整提示词 ​

位置实际可看到或取得什么不能据此推出什么
Entry普通入口判断 Direct/Research;显式指定则交给已校验的启动方法普通入口并未预加载整个 Skill 正文
Main经过 Host 投影的短目录:ID、名称、描述;可按需读取目录出现不等于授权、采用或执行
Child自己绑定的专家入口、任务输入,以及按本 Child 有效工具面筛选的 Skill 目录不自动继承 Main 见过的全部 Skill
自定义专家Profile 可带 preferredSkills 和 preloadedSkills偏好与预加载均不扩大工具、来源或预算
恢复后的请求原已封存的指引、内容身份与实际回执;仍受上下文拟合不能把当前最新版偷偷替换为旧请求的内容

具体来说,Child 目录由 Child 当前有效的 Tool Surface 决定。现行实现按下面的判断投影方法摘要:

  • 有 read_skill_resource:可以看到其已绑定目录中可供读取的注册方法摘要。
  • 没有读取工具、但有 run_skill_script:只展示有本地包根的可执行包方法摘要;不把纯远端方法伪装成可读。
  • 两个工具都没有:不投影 Skill 目录。Child 仍可以在获准范围内完成不依赖这些方法的任务。

这套规则过滤的是可发现性,不是新的执行许可。即使列表里有某方法,真正读取时仍要检查有效资源身份、版本、作用域与原权限;真正执行脚本仍要经过原脚本工具的规则。另一方面,普通 Child 自己的专家包入口也有独立的冻结身份:agentId 是专家标识,不是 Skill 文件路径、更不是额外的 Tool grant。

2. 上下文投影决定“这一轮模型实际看到了什么” ​

Main 和符合条件的 Child 会把简短目录投影为带有 host-skill-catalog 标识的模型消息,目录项包含 skillId、名称和描述,必要时附上 omittedCount。这是一份本次请求可用的目录视图,不是把所有 Skill 指引变为系统 Prompt。

为了说明结构,可以将其简化为教学 JSON:

json
{
  "kind": "host-skill-catalog",
  "skills": [
    { "skillId": "index-fund-tracking-quality", "name": "Tracking Quality", "description": "Check comparable tracking data" }
  ],
  "omittedCount": 2
}

此处的两个省略项只是示意数字,不是系统真实目录的统计。实际投影逐项计算输入容量,装不下的摘要应保留确实发生了省略这一事实;如果连必要的目录包络都无法装入,则按原请求容量错误处理,不能假装模型看完了全目录。

随后模型可通过 read_skill_resource 读取合法方法正文。读取结果是可回查的工具结果,仍不代表已执行其中的计算,也不能从“下一轮对话提及 Skill 名称”反推曾成功读取。

3. 自定义专家的偏好、预加载和授权是三回事 ​

一个自定义基金专家可以配置:

json
{
  "preferredSkills": ["index-fund-tracking-quality"],
  "preloadedSkills": ["index-fund-tracking-quality"]
}

preferredSkills 只是对模型的软偏好,不会自动把正文塞入 Child,也不会隐藏未偏好的合法能力。而 preloadedSkills 明确请求在专家启动时读取并投影指引,但必须使用原注册资源、真实 Child grant、内容校验与上下文容量。失败就局部报告 unavailable,不能把未知路径拿来直接读,也不能假装加载成功。

若完整指引超出容量,当前投影以完整项为单位保留省略,避免截断一半再让模型误以为拿到了可用方法。拟合发生在 Child 的实际请求装配阶段;Main 在提交 spawn_subagent 时,不应该提前声称“Child 已成功预加载”。

自定义 Profile 可以改善专家的研究倾向,但不能靠“偏好 Skill”给专家追加私有持仓、外部数据服务或执行预算。

4. Child 用方法做了什么,最终仍须交回真实结果 ​

同一问题可以按这样的路径委派,以下是机制示意,不是一次真实基金交易或模型运行记录:

按起点分组阅读关系与条件;完整图保留在下方。

用户:基金跟踪与组合重叠
  • → Entry:Research
Entry:Research
  • → Main:方法目录与公开证据
Main:方法目录与公开证据
  • → 委派 Child:限定任务和原授权
委派 Child:限定任务和原授权
  • → Child 有读取工具? (判断)
Child 有读取工具? (判断)
  • 是 → 按需读取对应 Skill 指引
  • 否 → 不宣称已读;按现有能力研究
按需读取对应 Skill 指引
  • → 所需数据与脚本可用? (判断)
不宣称已读;按现有能力研究
  • → 交回总结、引用和未解缺口
所需数据与脚本可用? (判断)
  • 是 → 实际工具调用并检查结果
  • 否 → 说明缺口,保留可用发现
实际工具调用并检查结果
  • → 交回总结、引用和未解缺口
说明缺口,保留可用发现
  • → 交回总结、引用和未解缺口
交回总结、引用和未解缺口
  • → Main 判断是否采纳与综合
Main 判断是否采纳与综合
  • → 原发布机制交付
完整关系图 · 可放大查看细节
同一问题可以按这样的路径委派,以下是机制示意,不是一次真实基金交易或模型运行记录:
Agent architecture diagram 放大查看完整图 / Open full-size SVG

Child 提交 submit_subagent_result 时可以带摘要、证据引用、产物引用和缺口,但**“Child 交回了结果”不等于“Main 已在正文里真正使用它”**。Main 是否采纳哪一项发现,要对照其后续实际模型输入、引用和最终回答;任务关系提示、专家名称、声明使用了某方法,都不能替代这个事实。

这也是 Skill 与 Subagent 的合理组合:Skill 提供可复用的研究步骤,Child 提供独立上下文和模型工作段,Main 负责把已取得的研究贡献整合到用户问题。一个 Child 可用多个获准 Skill,一个 Skill 也可以由 Main 直接使用;不必制造“一个 Skill 对应一个 Agent”的固定拓扑。Subagent 自身的生命周期与多专家协作另有专题,这里只讲方法交接。

四、Skill 可以提出数据需求,但不能授予数据权限 ​

基金跟踪质量通常需要基金净值和可比的基准总收益序列;个人组合重叠还需要实际配置权重及对应时点的底层持仓。

方法可以声明 research-scopes: [fund] 或 research-scopes: [portfolio_details],但它们表达研究领域依赖,不是用户授权。部分受审包还可声明 allowed-mcp-servers,表示方法可依赖的专用 MCP 服务上限;实际能否调用,仍由当前 Run 的授权、Host 政策、服务配置、只读限制、阶段与资源预算决定。

如果用户只允许公开基金资料,没有授权私人组合明细,Agent 可以研究公开费率、基准和已取得的收益序列;但无法计算这些基金在用户真实持仓中的重叠比例。它不能从历史闲聊、旧 RAG 摘要或想象的配置权重里补齐,也不能把未知重叠写成零。

用户要求“只用上传文件”时,同样不能因 Skill 声明了外部 MCP 就擅自联网。方法声明不能扩大 grant,grant 也不要求模型强制使用某方法。

五、从说明书到计算:Skill 如何真正产生结果? ​

金融研究中,部分判断必须由模型理解,部分计算则应交给可复算的程序。以指数基金为例:

  • 跟踪差(Tracking Difference):某可比期间基金收益与基准收益之差。
  • 跟踪误差(Tracking Error):逐期主动收益的波动,通常需要根据实际频率年化。
  • 相对财富变化:基金与基准累计财富路径的相对变化。

三者不能相互替代,更不能把“跟踪误差”当成某一天基金收益率减去指数收益率。

基金包提供的受审脚本可先回答“方法需要哪些输入”:

json
{
  "path": "scripts/run_module.py",
  "args": ["--describe", "--module", "index-fund-tracking-quality"]
}

这只读取输入约定,不自动下载行情、读取个人组合或执行跟踪质量计算。真正取得合规结构化数据后,脚本才对输入做确定性处理。较大数据可通过 stdinFromToolCallId 指向本 Run 已完成且对模型实际可读的工具结果:

json
{
  "path": "scripts/run_module.py",
  "args": ["--module", "index-fund-tracking-quality"],
  "stdinFromToolCallId": "call_example"
}

这里 call_example 仅是字段形态占位符,不是有效调用 ID。Host 按真实回执解析当前 Run 的已完成调用,把实际投影给模型的结果序列化后送入脚本 stdin,拒绝跨 Run、未完成、歧义或已被省略的引用。Host 私有原始捕获不会因此暴露。

包内脚本路径必须相对已批准的包并受路径约束;调用继续遵守原有期限、取消、资源和执行回执。受信任包的脚本运行器,不等于可运行任意不可信 Python 的安全沙箱。受管理 Child 专家包如果使用隔离 Analysis Sandbox,也不能因此推导所有 Skill 脚本都拥有相同隔离保障。

六、为什么计算必须带上方法、参数和来源身份? ​

假设输出只有 {"trackingError": 0.012}。下一轮无法判断是哪只基金、哪个份额、什么日期窗口、对应哪个基准版本,以及用日频还是月频和什么年化参数。

因此方法结果应保留对象、份额、时期(requested、used、as-of)、输入引用、来源、参数身份和实际结果状态。基金研究包用这组上下文关联方法计算;外部 Host 仍独立确认真实来源和准入,包内声明不能伪造合法引用。

例如基金份额或基准版本变更,不能复用旧计算;参数从每年 252 期改成 12 期,也不能只改结果标签。方法内容更新时,只有名称相同更不足以说明是同一种执行:必须绑定注册版本、内容哈希以及已经实际采用的指引。

恢复时也一样。旧 Run 已封存的方法不能静默替换成当前最新版。另一方面,方法内容身份与 Run 的授权身份是两条独立约束:重新加载方法不会自动重授权限;Skill 被停用也不意味着已合法发布的历史正文必须随之抹除。

七、一个方法失败,整份研究是否必须失败? ​

继续最初的基金比较,可以遇到三种不同结果。

基金身份、费用等公开资料已经核对,但缺少可比的基准总收益序列。这时可以交付产品事实,却不能用价格指数代替总收益序列补出正式跟踪误差。

用户未授权组合明细,个人重叠率只能标记不可用,不能报零。脚本输入格式错误也必须保留真实失败,不能把空输出当成零收益或成功结果。

以下是教学化结果结构,并非实际生产接口:

json
{
  "research": "两只指定指数基金的比较",
  "findings": {
    "fundProfile": { "status": "available" },
    "trackingQuality": {
      "status": "insufficient",
      "missing": ["可比的基准总收益序列"]
    },
    "portfolioOverlap": {
      "status": "unavailable",
      "reason": "未获私人组合明细授权"
    }
  }
}

一项方法失败,不应抹掉邻近有效发现。Child 可交回“已确认事实 + 未解决缺口”,Main 再决定哪些值得放进报告。方法读取完成、脚本执行成功、研究结论有证据、Main 实际采纳和文件交付成功是不同环节;它们复用原有回执,不另外制造“所有子 Skill 必须成功”的发布门。

八、Skill 版本治理,管的不是 Markdown 文件名 ​

个人脚本中修改一份 Markdown 可能很简单,但在存在包安装、可恢复任务与私有来源的系统里,修改方法前提、脚本、允许的依赖或输出解释,都会影响后续执行。

ETF Agent 区分 draft、active、disabled、superseded 等生命周期状态,注册、受审激活与实际执行分开处理。即使名称和版本号未变,内容哈希改变也不能沿用旧内容的批准结论。

行为它确认的事实它不证明什么
注册一份方法以哪个身份、版本存在已激活或已授权
激活某候选经过现有审查可供合法选用用户数据可读
投影或读取某次请求实际看到目录或正文模型已执行方法
执行与交接实际工具结果与 Child 回执可定位Main 一定采纳、用户一定收到

禁用主要阻止不合格的新使用;历史回读与尚未执行动作的恢复,则由原版本、权限、回执和发布规则分别约束。治理的目标是使方法变化可识别,而不是新增第二套 Runtime。

九、公开实现能复现到哪一步? ​

前述基金 Bundle、私人组合准入与受审 Python 链属于内部系统,不等于公开的 Open ETF Agent 已具备相同功能。

不过,在公开 v0.2 的一个固定源码版本里,compare-guides Skill 指导 Agent 比较两份合成软件指南:读取双方真实版本,分清免费/企业条件,核对数值与单位,再保存可回读的来源报告。可以阅读其 固定 SKILL.md 和 目录实现。

按该固定版本的实际引擎约束,使用 Node.js 26.11.1(或兼容的 26.x):

bash
git clone https://github.com/hpoenixf/open-etf-agent.git
cd open-etf-agent
git checkout a79e89bf709d4a2245b5a8652882a591ba467519
npm ci --ignore-scripts --no-audit --no-fund
npm run demo -- --case skill-mcp

这是离线合成示例,不需要真实基金数据或模型密钥。它可以帮助观察“方法发现 → 正文读取 → Host 来源获取 → 报告 → 运行记录”的交接,不证明前面介绍的私有 Bundle、Child 预加载或基金计算已在公开版发布。

十、什么时候用 Skill,什么时候写普通代码? ​

固定输入、确定公式、稳定结果的任务,直接编写普通函数往往更简单。若任务形式多变、需要领域判断,但存在可复用的检查步骤和证据前提,Skill 才真正有价值。

对基金研究,这包括先核产品份额、基准和期间,区分跟踪差/误差,处理未知穿透暴露,或者在证据不足时给出正确范围的结论。对专家协作,这意味着Child 不是方法的所有者,方法也不是执行权限:专家可以拥有独立上下文,同时按自己的可用工具面发现和组合方法,交回结果由 Main 实际消费。

可以把判断收束为四句话:

  • 需要模型理解、选择和综合的步骤,放在 Skill。
  • 确定性计算放在受控代码;公开数据、私人来源和外部动作仍经原工具。
  • 工具授权、版本绑定、预算、恢复与发布留给原 Host/Runtime。
  • 目录、指引、工具调用、Child 回执和最终采纳分别可观察,不互相冒充。

Skill 合同可检查,不代表模型总能选对方法;脚本可复算,不代表输入金融资料必然可靠。这两项仍要单独评估。

结语 ​

Agent 已经有工具,为什么仍需要 Skill?

工具回答“能调用什么”,Skill 帮模型回答“面对这类问题,应当怎样组织研究”。只有进一步分清谁看到了方法、谁获准执行、谁实际读了正文、谁产出结果、谁在最终研究中采纳,它才不只是高级 Prompt 包装。

ETF Agent 想建立的不是拥有隐式权限的 Skill 系统,而是一套可以持续增加研究方法、却不重复建设执行权威的能力体系。新方法只定义前提、步骤、输入和结果;授权、计算、回执和交付仍由原来真正负责它们的机制承担。


延伸阅读

与个人博客并行:这里按问题连续阅读,博客保留时间线。 · 联系邮箱:hpoenixf@foxmail.com · 微信:hpoenixf0510