5.1. Skill 是可进化的执行单元而非静态函数

Skill 是可进化的执行单元而非静态函数

2026 年 3 月,我在一个客服 Agent 项目的第 4 次迭代中发现了问题。我们给 Agent 写了 23 个“处理函数”——退款、查单、改地址、发优惠券——每个函数逻辑清晰,单元测试全绿。但上线第二周,运营团队要求新增一个“部分退款”场景。我们修改了退款函数,加了一个 partial_ratio 参数,然后 11 个旧调用方坏了 3 个。那次回滚之后我意识到一个根本性错误:我们一直在用“写死代码”的方式构建一个应该“活”的系统。

Agent 需要的不是一个函数库。它需要的是 Skill:一个能描述自己、感知上下文、接受反馈并自我调整的执行单元。函数是“被调用”的,Skill 是“被激活”的——这两者之间的差异,是所有自进化 Agent 架构的第一道分水岭。


Skill 不是函数:一张对比表格

在 Hermes 的架构中,Skill 的定义是一个 YAML/JSON 文档,包含触发器、参数 schema、前置条件、执行指令和依赖声明。而传统函数是一个代码块,包含输入参数、逻辑体和返回值。以下是两者的系统性对比:

维度 传统函数 Skill(可进化执行单元) 作者的结论
定义方式 代码中的 def / function YAML/JSON 元数据文档 + 可选脚本 Skill 是“声明”出来的,不是“编写”出来的
调用方式 代码显式调用 foo(args) 触发器匹配:关键词、意图、时间或事件 调用方不需要知道 Skill 的存在,Agent 自动路由
状态管理 无状态(或依赖全局变量) 执行上下文沙箱,含工作目录、环境变量、临时资源 Skill 每次激活都是干净的执行环境
参数演化 修改函数签名 → 所有调用方编译/运行时报错 参数 schema 可扩展,旧调用方不受影响 进化不破坏兼容性
适用范围 开发者可见 Agent 自身可见、可调用、可组合 Skill 是 Agent 的“能力原子”,不是程序员的工具
生命周期 写完运行,删掉消失 草稿 → 激活 → 执行 → 评估 → 进化 → 归档 Skill 像生物个体一样有生老病死

这张表格的最大启示在于最后一行:函数没有“退役”这个概念。你删掉它,或者留着它被遗忘。而 Skill 有明确的终态——当 Curator 后台组件判定一个 Skill 连续 30 天未被激活,或者其成功率低于阈值,它会自动进入“待归档”状态,等待人工确认后移入档案库。这是 2026 年 Hermes v0.13 中新增的 archiveprunelist-archived 操作所支撑的能力。


核心洞察:被动 → 主动 → 独立

要理解 Skill 的“可进化”本质,我们可以用一个三级跳跃来梳理:

第一级:被动执行(传统函数)
你定义了 calculate_refund(item_price, quantity),它等在那里。只有当你(程序员)在代码中写入调用语句,它才会执行。函数的主体性为零——它不知道什么时候该出现,不知道调用者的意图,不知道执行结果是否需要反馈。

第二级:主动适应(上下文感知的函数)
你给函数加上了一层薄薄的“意图壳”——比如一个装饰器声明了“当用户表达退款意愿时触发”。Agent 负责将用户输入与这个声明匹配,然后激活函数。这已经是进步了,但函数本体还是死的——参数错了,它报错;场景变了,它沉默。

第三级:独立生存(可进化 Skill)
Skill 不仅声明了“何时触发”,还携带了“执行后如何评估”“失败时如何降级”“从何处读取记忆作为上下文”。当一个“退款”Skill 被激活时,它能从 Memory 系统中拉取用户的历史订单、近期的情绪标记、上一次交互未解决的问题——这些都不是函数参数,而是运行时动态注入的上下文。执行结束后,Skill 的评估钩子将结果写回 Memory,供下一次激活时的“自己”使用。

经验框:Skill 的渐进式披露

Hermes 的 Skill 系统采用了“渐进式披露架构”。一个 Skill 不是一次性暴露所有能力,而是分层展示:触发器匹配后,先向 Agent 展示摘要(名称 + 一句话描述);Agent 确认调用意图后,再展示参数 schema 和执行前条件;执行遇到特定分支时,再展开子流程指令。

这种设计的核心价值在于:Skill 可以很复杂,但在 90% 的激活场景中,Agent 只需看到它的 10%。这让 Skill 像一个有经验的老员工——你不需要告诉它每一步怎么走,它知道什么时候该请示,什么时候该自己决定。


Skill 的元数据与描述模式

一个 Skill 的最小化定义如下(以 Hermes 的 YAML 格式为例):

id: order-refund
name: "订单退款处理"
version: "2.1.0"
triggers:
  - keywords: ["退款", "退钱", "不要了", "取消订单"]
  - intent: "refund_request"
  - event: "order.cancelled.with_payment"
parameters:
  order_id:
    type: string
    required: true
    description: "需要退款的订单号"
  reason:
    type: string
    required: false
    default: "用户主动申请"
    description: "退款原因,用于记录和统计分析"
dependencies:
  - skill: payment-gateway
    version: "^1.3.0"
  - memory: user-order-history
execution:
  entry: scripts/refund_executor.py
  timeout: 30s
  retry: 3
evaluation:
  metric: "refund_success_rate"
  threshold: 0.95
  on_failure: "degrade_to_manual"

这里有三个设计细节值得展开:

  1. 触发器是开放的联合条件keywords 是文本匹配,intent 是 NLU 模型输出的意图标签,event 是系统事件。三者是“或”关系——任意一个命中即激活。这保证了 Skill 的“可达性”不依赖单一信号源。

  2. 参数 schema 支持向前兼容reason 字段不是必填,且有默认值。当未来版本的 Skill 增加 refund_method 参数时,可以不破坏 2.1.0 版本的调用方——Agent 看到缺失参数时会使用默认值或向用户追问,而不是报错中断。

  3. 依赖声明是版本感知的payment-gateway 依赖声明了 ^1.3.0,意味着兼容 1.3.x 但不接受 2.0.0。这让 Skill 在依赖升级时能主动发出兼容性警告,而不是静默失败。

核心建议框:给参数留“呼吸空间”

在设计 Skill 的参数 schema 时,只把必须由调用方提供的字段设为 required: true。其他字段都应该是可选的、有默认值的。这不是为了让 Schema 看起来“宽容”,而是为了给未来版本留出添加新参数的空间——每一次参数扩展都不应该成为一次断裂性变更


执行上下文与环境隔离

当一个 Skill 被激活时,系统不是直接在当前进程中调用一段代码,而是创建一个独立的执行上下文。这个上下文包含:

  • 工作目录:一个临时目录,Skill 可以在其中读写文件,执行结束后可选择保留或清理
  • 环境变量:系统级变量(如 AGENT_SESSION_ID)+ Skill 自定义变量(如 REFUND_MAX_AMOUNT
  • 权限边界:Skill 只能访问其声明的 API 和文件路径,超出范围的请求会被沙箱拦截
  • 时间配额:由 timeout 字段控制,超时后强制终止并记录

这四层隔离带来一个关键能力:同一个 Skill 可以安全地同时被多个对话线程激活。两个用户同时要求退款,会产生两个独立的执行上下文,它们不共享文件、不竞争锁、不污染彼此的状态。这是传统函数调用在并发场景下很难保证的事情——除非你小心翼翼地管理全局状态,而“小心翼翼”本身就是一个架构坏味。


进化事件与版本管理

Skill 的进化不是靠程序员手动修改配置文件来驱动的,而是靠 评估反馈闭环。每一次 Skill 执行完成后,系统会收集以下信号:

  • 执行是否成功(成功率)
  • 执行耗时(性能基线)
  • 用户后续行为(满意/投诉/追问)
  • 是否触发了降级策略

当某个指标连续低于阈值——比如退款 Skill 的成功率从 98% 掉到 91%——Curator 后台组件会生成一个 进化事件。这个事件不等同于“自动修改代码”,而是一个带上下文的提案:包含近期的 10 个失败案例、相关记忆片段、以及可能的改进方向(比如“增加支付网关超时重试次数”“补充对部分退款场景的覆盖”)。

开发者的工作是审阅这个提案,决策是修改定义、增加参数、还是标记为“已知限制”。如果修改了 Skill 定义,版本号会按 SemVer 规则升级:

  • 修订号(1.2.x → 1.2.y):仅修改指令措辞或修復 bug,不影响行为契约
  • 次版本号(1.x → 1.y):新增可选参数或新增触发器,向后兼容
  • 主版本号(x → y):删除必填参数、修改返回值结构、或调整依赖合约

注意框:不要创建“无法退役”的 Skill

v0.13 引入的 archiveprune 操作意味着 Skill 的生命终态已经被架构所确认。但实际工程中,很多团队的 Skill 一旦上线就再也没有清理过——因为“谁也不敢删”。建议从第一天起就给每个 Skill 设定一个观察期(通常是 30 天),观察期内成功率低于 50% 或者零激活记录的 Skill 自动进入待归档状态。一个从不回家的守卫不是警惕,而是迷路了。


适合谁 / 不适合谁

这一章最适合以下场景:

  • 你正在设计一个需要快速迭代“能力单元”的 Agent 系统,每次修改都不应该成为断裂性变更
  • 你的团队中有产品和运营人员参与“技能定义”,而不只是工程师在写代码
  • 你希望 Agent 在运行一段时间后能“自动找出”哪些技能需要优化,而不是靠人工检查日志

这一章可能不适合以下场景:

  • 你只是在做一个简单的 ChatGPT 包装器,所有能力都靠 System Prompt 控制
  • 你的系统调用链路完全固定,没有“根据上下文动态选择能力”的需求
  • 你暂时不需要考虑多技能协作、版本并存、或者长期运行后的技能退化

上一章我们讨论了从记忆到技能的跃迁是如何成为自进化 Agent 的关键路径——记忆提供了原材料,技能是原材料加工后的产物。本章我们深入了这个“产物”的内核:它有自己的元数据、执行上下文、生命周期和进化机制。它不是一段被调用的代码,而是一个在 Agent 生态中独立生存的能力单元。

但理解单个 Skill 的架构只是第一步。一个真正强大的 Agent 从不依赖孤立的技能——下一章,我们将走进 OpenClaw Skills 目录,这是一个拥有 1,800+ 个即装即用技能的社区生态。你会发现,你不需要从零设计每一个 Skill,而是可以像浏览 App Store 一样为 Agent 挑选、安装和组合能力。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~