乐于分享
好东西不私藏

04-OpenClaw 测试团队实践(五)

04-OpenClaw 测试团队实践(五)

上一篇,记忆解决了”记住什么”——Agent 关掉会话再开,还记得你是谁、边界在哪、你纠正过它什么。

但它真能干活吗?


一、会干活≠什么都会干

装齐了人格、安全、记忆的 Agent,我让它帮我拆测试点。它给了我一份目录:功能正向、功能反向、异常场景、性能、兼容性、UI、可观测性……结构齐整、覆盖面广,但每一项都停在”该测什么”的层面,没一项能往下到”怎么测”。它能告诉我”异常场景要测”,但不能告诉我”按我们规范,异常场景要拆到规范中的哪一层、用什么黑盒方法、关联哪条历史漏测”。它知道方向,不知道路径。

这是没有 skill 的 Agent 的第一个失败模式:通用回答覆盖不到专业场景

刚开始使用的时候,我把规范、拆分原则、历史案例都塞在提示里面,单次会话是没有问题的,但是无法工程化落地,一旦关闭会话或者重开话题,这些规范、拆分原则、历史案例就需要再提供一次,并且这些信息严重占用了上下文空间,时不时就报 “run out of context”。这就是第二个失败模式:什么都往上下文塞,token 爆炸,该用哪个原则 Agent 反而不知道了

而 Skill 就可以解决这个问题。


二、Skill 是什么:从”知道”到”会做”

先给个一句话定义:Skill 不是知识,是可调用的能力单元

知识是”测试点要按五层拆”,能力是”给一份需求拆解结果,按规范输出一份结构化的测试点 YAML”。前者 Agent 知道就能答,后者要装上 skill 才会做。

skill 和前四篇的关系,我用一张表总结一下:

维度
解决什么
人格
该不该做
安全
能不能动手
记忆
记住什么
skill
会做什么

前三件管”判定”,skill 管”动手”。判定的再准,不会动手,Agent 还是停在”会答”那一步。

skill 的载体是一个 SKILL.md 文件。拿我常用的测试点生成 skill 来看它的结构:

---name: test-point-generatordescription: 根据拆解后的需求,按五层拆分法(功能入口/功能使用/ 功能交互/特殊场景/非功能质量属性)生成结构化测试点,并识别横向 适配矩阵差异。---# 测试点生成 Skill(含修订模式)## 用途基于需求拆解结果,按规范 V2.2 的**五层拆分法**生成结构化测试点...- **模式 A(mode=full,首次)**:从需求拆解直接产出测试点 v1- **模式 B(mode=full,全量重做)**:推翻上一版,基于评审重新生成(慎用)- **模式 C(mode=scoped,局部修订)**:只动 scope_filter 内的测试点...## 输入### 通过任务卡 (task card) 接收由 orchestrator 主 Agent 下发,字段参见...## 输出格式[test_points YAML schema]

短短一个 SKILL.md,干了几件事。

  • frontmatter 的 description 是触发入口——Agent 看到这个 description 才知道”现在该调这个 skill”。description 写得模糊,Agent 永远不会触发它。

  • 用途 / 输入 / 输出 / 工作流是渐进式披露的载体——这些段落不是给读者看的,是给 Agent 看的:什么时候调、收什么、产出什么、按什么顺序。

  • 模式 A/B/C 是同一 skill 的复用——一个 skill 不是只能干一件事,它可以根据任务卡里的 mode 字段走不同路径。这点对 token 效率很关键,后面展开。

skill 不是知识,是 Agent 拿到一份”操作手册”后能照着干的能力。


三、渐进式披露:不要一次把牌全亮出来

skill 装上之后,下一个问题是:Agent 怎么读它?

最笨的做法是把所有 skill 的 SKILL.md 全文塞进上下文——我装了几十个 skill,每个 SKILL.md 几百到上千行,全塞进去 token 直接爆。更糟的是 Agent 迷失——上下文里同时有”测试点生成””测试用例生成””需求分析””评审”四个 skill 的全文,Agent 接到任务时,该用哪个?它会在四个 skill 之间反复横跳,输出一份四不像。

openclaw 从设计之初就很好的规避了这个问题,使用的策略就是渐进式披露——不用不读,要用了才读,读了也只读当下要用的那一层。

但这件事能成立,前提是 skill 本身的格式得配合。回头看上一节 tpg 的 SKILL.md 结构——frontmatter 的 description 是一段话,用途/输入/输出/工作流是分段的。这个结构不是随手写的,它是渐进式披露能成立的前提:frontmatter 是独立的,框架能只读 frontmatter 不读正文;正文是分段的,框架能按需只读某一节,不用整篇加载。如果 SKILL.md 是一大段流水文字,框架再想渐进式披露也做不到——没有结构可分。

openclaw 的加载机制按三层走:

谁来读
读什么
何时读
触发层
主 Agent
各 skill 的 frontmatter description
启动时扫描一遍,决定要不要调
调用层
子 Agent
被调 skill 的用途 + 输入 + 输出格式
任务卡下发后
详情层
子 Agent
被调 skill 的完整工作流 + 模板 + 例子
真正执行时按需打开

对照看 token :

维度
全加载
渐进式披露
启动时读
十几个 SKILL.md 全文,几万 token
十几个 description,几百 token
执行时读
已经全在上下文里
按需打开当前 skill 的详情
Agent 决策
在多个 skill 间横跳
任务卡指定一个,专注执行

第三层”skill 链路编排”是更进一步的渐进式——多个 skill 之间怎么协作。我用的方式是用一个主调度 skill(orchestrator)按状态机依次调用 5 个从 skill,下一节展开。

渐进式披露的核心就一句话:框架启动时只读索引,要用了才读详情,读详情也只读当下要用的那一层——而这一切的前提是 skill 本身写得有结构。


四、真实案例:1 主 5 从的测试设计工作流

我现在的测试设计工作流是 1 个主调度 skill + 5 个从 skill:

orchestrator(主调度)├── requirement-analyzer(需求拆解)├── test-point-generator(测试点生成)├── test-point-reviewer(测试点评审)├── test-case-generator(用例生成)└── test-case-reviewer(用例评审)

主调度干的事是:

  1. 把用户需求落地成 run_id 和 state.json
  2. 按状态机依次给 5 个从 skill 下发任务卡
  3. 收到工件后做 schema/scope/diff 校验
  4. 维护 state.json,中断可恢复

核心约束:主 Agent 不存内容,只存路径。所有业务内容落盘到 artifacts 目录,主 Agent 上下文只保留任务卡、路径表和摘要。

这一条约束是渐进式披露的极致应用——主 Agent 连从 skill 的输出都不进上下文,只看路径和摘要。子 Agent 各自读自己 skill 的详情,干完活把工件写盘,把摘要回报给主 Agent。

流程如下:

flowchart TD U[用户需求] --> O[orchestrator 主调度] O -->|任务卡1| RA[requirement-analyzer] RA -->|需求拆解 YAML| O O -->|任务卡2| TPG[test-point-generator] TPG -->|测试点 YAML| O O -->|任务卡3| TPR[test-point-reviewer] TPR -->|评审报告| O O -->|任务卡4| TCG[test-case-generator] TCG -->|用例 YAML| O O -->|任务卡5| TCR[test-case-reviewer] TCR -->|用例评审报告| O O --> R[端到端测试设计完成]

这张图里没有一个 skill 读全文。主调度只读各从 skill 的 description(触发层),从 skill 各自读自己的输入输出格式(调用层),真正执行时按需打开工作流详情(详情层)。

对照裸 Agent 干这件事:

维度
裸 Agent
orchestrator + 5 从 skill
任务拆解
全靠提示词现场指挥
状态机固化,依次下发
token 去向
一份长提示塞所有规范
各 skill 各读各的,主 Agent 只存路径
中断恢复
上下文丢了从头来
state.json 持久化,续跑
局部修订
全量重跑
scoped 模式只动指定 function × layer
输出规范
靠 Agent 自觉
schema 校验,违规重做

裸 Agent 做的事情是把token 花在”每次都把规范讲一遍”上;装上工作流后,token 花在”按规范干活”上。既然是工作流,那么就是一个流水线,那是不是所有的任务都要按照流水线来一步步执行呢?这就是前面提到的测试点生成工作流中 mode A/B/C 的作用。它就是来编排每个任务执行哪个流水线,全部按完整的流水线走,对 token是极大的浪费,而根据需要调整一部分流水线,那对 token 效率很关键。

实际跑测试设计时,绝大多数场景不是”从零生成”,是”评审发现某几个测试点要改、某个功能要补”。这种情况裸 Agent 只能全量重跑——把整份测试点重新生成一遍,token 花在没改的部分上。

测试点生成 skill 的 mode C(scoped 局部修订)是另一种做法:任务卡里带一个 scope_filter,指定只动哪个 function × 哪一层,其他测试点全部冻结。子 Agent 读上一版测试点 + 评审报告,只对 scope 内的重新生成,scope 外的 ID 原样保留,输出里带一份 diff——加了哪些、改了哪些、删了哪些。

场景
裸 Agent
mode C
改 1 个功能的测试点
全量重跑,token × 1
只动 scope 内,token 省一截
改 1 个功能的 1 层
还是全量重跑
只动那 1 层,其他冻结
输出
一份新的全量
一份 diff + 未变 ID 清单

一个 skill 三种 mode,干的是同一件事的三个粒度。mode A 首次、mode B 全量重做、mode C 局部修订——按场景挑,token 只花在真正要动的地方。skill 不是让 Agent 变聪明,是让它的能力固化下来、可复用、可校验。


五、团队治理:skill 不能随便装

03 的 SAFETY.md 里第六条是”skill 安装审查”,为什么要有这一条呢?

skill 是 Agent 的能力单元,但 skill 也是 Agent 能不能进团队的最后一道门槛——因为 skill 是会动手操作命令的。

我搜了一下,最近一年 skill 安全出过几件事。

一家安全研究机构做了一个假的 AI agent skill,推到一个流行的 skill 市场和社交媒体广告上,触达了大约 26000 个 agent,其中包括一些企业账号。这个假 skill 的 payload 是无害的——只收集用户邮箱——但它证明了:现有的 skill 安全扫描器,没有一个能拦住它。

为什么拦不住?因为扫描器只扫描你提交的包(SKILL.md 和随附文件),但 skill 可以指向一个外部 URL,让 Agent 去读外部文档并按文档操作。扫描时这个外部链接指向真实文档,扫描通过后,攻击者可以把这个链接指向的内容换成恶意指令。扫描是一次性的,但链接指向的内容可以随时改写。

还有更隐蔽的——Unicode 标签字符注入。攻击者用 Unicode 里的不可见标签字符把恶意指令嵌在 skill 文件的合法段落里,肉眼看 SKILL.md 全是正常文字,但 LLM 能读到隐藏指令。已经有研究演示在一份看起来正常的”安全最佳实践”skill 文件里嵌入执行任意 shell 命令和 curl 调用的隐藏指令,成功攻破多个主流编码 Agent。

这不是理论威胁。skill 是会动手的——它能跑 shell、能改文件、能发网络请求。一份恶意 skill 进了团队,它不是污染一个 Agent 的输出,是污染整个团队的工作流。

所以 skill 治理要三层兜底:

审什么
怎么审
来源审查
skill 从哪来、作者是谁、是否有团队背书
不装来路不明的 skill,团队 skill 必须从受控仓库拉取
内容审查
SKILL.md 写了什么、引用了什么外部链接、脚本做了什么
静态扫描 + 人工 review,重点查外部 URL 和可执行脚本
运行时审查
skill 实际执行时做了什么
沿用 03 的操作分级 + 自动备份 + 变更日志,skill 动手前一样要过 SAFETY

三层兜底的核心逻辑:skill 的可信度不能只靠安装时扫描,要在来源、内容、运行时三个环节各卡一道

个人用 skill,来源审查松一点、内容审查自己看一眼、运行时靠 SAFETY 兜底,也能跑。团队用 skill,三层都不能松——一份恶意 skill 进了团队共享仓库,下一秒所有人都在跑。


六、个人能用,不代表团队能用

如果只是个人电脑上自己用,skill 装得乱一点、来源审查松一点、出事自己兜底,影响面就一台机器。但放到团队、放到公司,这条线立刻就紧了。多个同事用同一套 skill 仓库,来源不卡死,谁装的、什么时候装的、装完有没有人 review,全都说不清;出了事,翻不出”这个 skill 是谁引入的、它当时引用了哪个外部链接”的记录,问责无从谈起。

个人用 skill,治理是锦上添花;团队用 skill,治理是开工前提。


skill 装上那一刻,Agent 才真正从”会答”走到了”会干”。前面四篇——人格、安全、记忆——管的是 Agent 怎么”判定”。skill 管的是 Agent 怎么”动手”。判定的再准,不会动手,Agent 还是停在屏幕上跟你聊天的那一步。但 skill 也是 Agent 能不能进团队的最后一道门槛。判定的错,影响一次输出;skill 的错,影响的是整个工作流,一个恶意的 skill,污染的不是一次对话,是团队所有人接下来要用的东西。

Agent 从”会答”到”会干”,靠 skill;从”会干”到”敢让团队用”,靠 skill 治理。