Codify Skill Action sync/async and task-center gold standards (v1.0.40)
All checks were successful
技能自动化发布 / release (push) Successful in 7s

This commit is contained in:
2026-07-14 07:55:55 +08:00
parent 2e5a8d5eed
commit a7baba7210
13 changed files with 412 additions and 23 deletions

View File

@@ -1,7 +1,7 @@
--- ---
name: 技能开发模板(通用业务版) name: 技能开发模板(通用业务版)
description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。" description: "OpenClaw 通用业务技能开发模板,供复制后定制新业务 skill。定制步骤见 development/DEVELOPMENT.md。"
version: 1.0.39 version: 1.0.40
author: 深圳匠厂科技有限公司 author: 深圳匠厂科技有限公司
metadata: metadata:
openclaw: openclaw:
@@ -20,6 +20,14 @@ allowed-tools:
这是一个**用于复制的新技能模板**,不是业务 skill 本身。复制后请替换占位内容,实现你的真实业务逻辑。 这是一个**用于复制的新技能模板**,不是业务 skill 本身。复制后请替换占位内容,实现你的真实业务逻辑。
## Agent / 宿主编排(复制为业务技能后必改)
- **长耗时 / RPA / 浏览器任务**:必须通过宿主 **Skill Action**`run_skill_action`)调用已声明在 `assets/actions.json` 中的 action`executionProfile` 须为 `"async"`,进度见**任务中心** / Skill Run Card。
- **禁止**对长任务使用 `exec python …/scripts/main.py run``process poll` 干等 CLI 返回。
- 只读查询(`health` / `stats` 等)可用 `executionProfile: "sync"`,立即返回结果。
- 批量处理用参数化 `pickCount` / `--pick N` 顺序消费队列,**不要并发**同一 Profile契约见 `development/SKILL_ACTION_RUNTIME.md``references/ACTIONS.md`
- 模板源仓库本身仅示范 `health` / `version` / `config-path`sync定制 RPA 技能后须自行增加 async action。
## 面向用户问答LLM 规则) ## 面向用户问答LLM 规则)
- 本文(`SKILL.md`)是 LLM / OpenClaw 平台读取的技能入口,**不是**用户市场说明。 - 本文(`SKILL.md`)是 LLM / OpenClaw 平台读取的技能入口,**不是**用户市场说明。
@@ -83,7 +91,7 @@ python {baseDir}/scripts/main.py version
2. 业务技能仓库中**不得**存在 `.openclaw-skill-template`(仅 template 源仓库保留)。 2. 业务技能仓库中**不得**存在 `.openclaw-skill-template`(仅 template 源仓库保留)。
3. 复制目录为你的新 skill 仓库,全局替换 `your-skill-slug` 等占位词。 3. 复制目录为你的新 skill 仓库,全局替换 `your-skill-slug` 等占位词。
4.`development/DEVELOPMENT.md` 完成开发与文档定制。 4.`development/DEVELOPMENT.md` 完成开发与文档定制。
5. 用户市场说明写入根 `README.md`Agent 调用契约见 `references/CLI.md``references/SCHEMA.md``references/ACTIONS.md` 5. 用户市场说明写入根 `README.md`Agent 调用契约见 `references/CLI.md``references/SCHEMA.md``references/ACTIONS.md`;长任务 / 任务中心见 `development/SKILL_ACTION_RUNTIME.md`
6. 同步修改 `scripts/util/constants.py` 中的 `SKILL_SLUG` / `SKILL_VERSION` 6. 同步修改 `scripts/util/constants.py` 中的 `SKILL_SLUG` / `SKILL_VERSION`
7. 本地 SQLite 的中文表名/字段名、标准时间字段与字段顺序规范见 `references/SCHEMA.md`(元数据表 `_jiangchang_*` + `CREATE TABLE` 物理顺序)。 7. 本地 SQLite 的中文表名/字段名、标准时间字段与字段顺序规范见 `references/SCHEMA.md`(元数据表 `_jiangchang_*` + `CREATE TABLE` 物理顺序)。

View File

@@ -1,10 +1,10 @@
# assets # assets
- `actions.json`Skill Action manifest 最小示例(**可选**;不需要宿主入口时可删除)。 - `actions.json`Skill Action manifest 最小示例(**可选**;不需要宿主入口时可删除)。模板要求每个 action 显式声明 `executionProfile``sync` \| `async`)。
- `examples/`CLI 成功输出形状示例(虚构路径与数据)。 - `examples/`CLI 成功输出形状示例(虚构路径与数据)。
- `schemas/`:轻量 JSON Schema`skill-actions.schema.json` 为**新技能严格规范**`task-log-record.schema.json` 等)。 - `schemas/`:轻量 JSON Schema`skill-actions.schema.json` 为**新技能严格规范**`task-log-record.schema.json` 等)。
`skill-actions.schema.json` 使用 `additionalProperties: false` 表达模板推荐契约;宿主运行时可能更宽松以兼容历史 manifest。`row`/`batch` placements 与 `bind`/`concurrency`/`locks` 为预留能力,当前模板示例不得使用。 `skill-actions.schema.json` 使用 `additionalProperties: false` 表达模板推荐契约;宿主运行时可能更宽松以兼容历史 manifest。`row`/`batch` placements 与 `bind`/`concurrency`/`locks` 为预留能力,当前模板示例不得使用。RPA/长耗时 action 必须 `executionProfile: "async"`,见 [`development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。
- 用户市场说明见根目录 [`README.md`](../README.md) - 用户市场说明见根目录 [`README.md`](../README.md)
- Agent 调用/编排参考见 [`references/`](../references/)(含 [`ACTIONS.md`](../references/ACTIONS.md) - Agent 调用/编排参考见 [`references/`](../references/)(含 [`ACTIONS.md`](../references/ACTIONS.md)

View File

@@ -5,8 +5,9 @@
{ {
"id": "health", "id": "health",
"label": "运行检查", "label": "运行检查",
"description": "检查技能运行环境并返回结构化结果。", "description": "检查技能运行环境并返回结构化结果,不启动浏览器。",
"placements": ["skill-detail", "agent"], "placements": ["skill-detail", "agent"],
"executionProfile": "sync",
"entrypoint": { "entrypoint": {
"type": "cli", "type": "cli",
"command": "health", "command": "health",
@@ -18,6 +19,7 @@
"label": "查看版本", "label": "查看版本",
"description": "返回技能版本和机器标识。", "description": "返回技能版本和机器标识。",
"placements": ["skill-detail"], "placements": ["skill-detail"],
"executionProfile": "sync",
"entrypoint": { "entrypoint": {
"type": "cli", "type": "cli",
"command": "version", "command": "version",
@@ -29,6 +31,7 @@
"label": "查看配置位置", "label": "查看配置位置",
"description": "返回技能配置文件位置。", "description": "返回技能配置文件位置。",
"placements": ["skill-detail"], "placements": ["skill-detail"],
"executionProfile": "sync",
"entrypoint": { "entrypoint": {
"type": "cli", "type": "cli",
"command": "config-path", "command": "config-path",

View File

@@ -120,7 +120,12 @@
}, },
"entrypoint": { "$ref": "#/$defs/entrypoint" }, "entrypoint": { "$ref": "#/$defs/entrypoint" },
"inputSchema": { "$ref": "#/$defs/inputSchema" }, "inputSchema": { "$ref": "#/$defs/inputSchema" },
"confirmation": { "$ref": "#/$defs/confirmation" } "confirmation": { "$ref": "#/$defs/confirmation" },
"executionProfile": {
"type": "string",
"enum": ["sync", "async"],
"description": "sync=宿主内联等待结果(约 30sasync=后台 Job 进任务中心。模板要求显式写出RPA/长耗时必须 async。"
}
}, },
"description": "模板 Phase 1 不包含 bind/concurrency/locks 等预留能力字段;宿主类型可能定义这些字段,但新技能不得依赖" "description": "模板 Phase 1 不包含 bind/concurrency/locks 等预留能力字段;宿主类型可能定义这些字段,但新技能不得依赖"
} }

View File

@@ -525,9 +525,11 @@ metadata:
若技能需要出现在宿主**数据管理按钮**、**技能详情**、**定时任务**或 **Agent 直接调用**中,提供 `assets/actions.json`。 若技能需要出现在宿主**数据管理按钮**、**技能详情**、**定时任务**或 **Agent 直接调用**中,提供 `assets/actions.json`。
- 契约说明见 [`references/ACTIONS.md`](../references/ACTIONS.md) - **运行时金标准**见 [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md)sync/async、任务中心、Agent 禁令)
- 字段契约见 [`references/ACTIONS.md`](../references/ACTIONS.md)
- JSON Schema 见 [`assets/schemas/skill-actions.schema.json`](../assets/schemas/skill-actions.schema.json) - JSON Schema 见 [`assets/schemas/skill-actions.schema.json`](../assets/schemas/skill-actions.schema.json)
- 模板最小示例仅暴露 `health` / `version` / `config-path` 三个 CLI 子命令 - 模板最小示例仅暴露 `health` / `version` / `config-path`(均显式 `executionProfile: "sync"`
- 含浏览器 RPA / 长耗时的业务技能:**必须**增加至少一条 `"executionProfile": "async"` 且 `placements` 含 `agent` 的 action`POLICY-SKILL-ACTION-001`
- 没有宿主直接操作需求时,**可以删除** `actions.json`;删除后勿保留失效引用 - 没有宿主直接操作需求时,**可以删除** `actions.json`;删除后勿保留失效引用
- 复制为新技能后scaffold 会自动将 `your-skill-slug` 与 `your_skill_slug` 替换为新 slug后者用于 `LOG_LOGGER_NAME` 等下划线形式action 业务命令由技能作者自行调整 - 复制为新技能后scaffold 会自动将 `your-skill-slug` 与 `your_skill_slug` 替换为新 slug后者用于 `LOG_LOGGER_NAME` 等下划线形式action 业务命令由技能作者自行调整
@@ -539,6 +541,25 @@ python {skillRoot}/scripts/main.py <command> <args...>
CLI 必须提供稳定退出码、结构化成功输出,以及 `ERROR:<CODE>:` / `HINT:` 错误契约(详见 `references/ACTIONS.md`)。 CLI 必须提供稳定退出码、结构化成功输出,以及 `ERROR:<CODE>:` / `HINT:` 错误契约(详见 `references/ACTIONS.md`)。
### 编排范式:单内核 + 多入口
长任务 / RPA 技能推荐拆分(勿把 `job_context` 塞进纯业务内核):
```text
execute_single_<biz>(...) # 鉴权 + 业务 + task_log无 job_context
cmd_run(...) # 单条job_context + execute_single + finish
cmd_run_pick(N, ...) # 批量顺序job_context + for-loop + emit(progress) + finish(partial|success|failed)
cmd_stats() # 只读 syncJSON无 job_context
```
- 批量数量用 **`pickCount` / `--pick N` 参数**(任意正整数),不写死条数。
- **顺序执行、不并发**同一浏览器 Profile / account lease。
- 详情见 [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md) §5§6。
### 队列 + pick可选
有「待处理列表」时:队列表 `pending → processing → success/failed`;数据管理录入或 `import-*` 入库;`run --pick N` 消费;失败用 `reset` 回 `pending`不自动重试。Manifest 上配置 `*-run-pick`asynctoolbar/cron/agent
## 12. 如何接兄弟技能 ## 12. 如何接兄弟技能
如果 skill 要依赖兄弟 skill不要在业务代码里写死绝对路径。 如果 skill 要依赖兄弟 skill不要在业务代码里写死绝对路径。

View File

@@ -96,6 +96,8 @@ OpenClaw 技能常在客户电脑上异步执行,且大量依赖 RPA、浏览
2. 关键步骤 `emit(...)``@rpa_step` / `video.add_step` 2. 关键步骤 `emit(...)``@rpa_step` / `video.add_step`
3. 每个出口 `finish(status=..., message=..., skill=SKILL_SLUG, **fields)` — **不要**手写多行 JSON 结果 3. 每个出口 `finish(status=..., message=..., skill=SKILL_SLUG, **fields)` — **不要**手写多行 JSON 结果
**单内核多入口**`execute_single_*` 不含 `job_context`;仅 `cmd_run` / `cmd_run_pick` 包裹。批量 pick 循环内 `checkpoint()` + `emit(..., progress=...)`;部分失败用 `finish(status="partial")`。完整范式见 [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md)。
RPA 等待用 `interruptible_sleep`**不要**裸 `asyncio.sleep`(否则暂停响应滞后)。 RPA 等待用 `interruptible_sleep`**不要**裸 `asyncio.sleep`(否则暂停响应滞后)。
暂停在**步骤边界**生效(当前 `@rpa_step` / `emit` 执行完后、下一步开始前),与影刀「当前指令完成后暂停」一致。 暂停在**步骤边界**生效(当前 `@rpa_step` / `emit` 执行完后、下一步开始前),与影刀「当前指令完成后暂停」一致。
@@ -306,4 +308,5 @@ def cmd_run(target=None, input_id=None):
| [`CLI.md`](../references/CLI.md) | `logs` / `log-get` 命令与手工排查入口 | | [`CLI.md`](../references/CLI.md) | `logs` / `log-get` 命令与手工排查入口 |
| [`CONFIG.md`](CONFIG.md) | 敏感配置不进日志;`.env` 边界 | | [`CONFIG.md`](CONFIG.md) | 敏感配置不进日志;`.env` 边界 |
| [`DEVELOPMENT.md`](DEVELOPMENT.md) | `service/` 层日志职责与发布前检查清单 | | [`DEVELOPMENT.md`](DEVELOPMENT.md) | `service/` 层日志职责与发布前检查清单 |
| [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md) | 任务中心 / async Job 与 `job_context` 边界 |
| [`POLICY_MATRIX.md`](POLICY_MATRIX.md) | 可自动检测的 logging hard policy 索引 | | [`POLICY_MATRIX.md`](POLICY_MATRIX.md) | 可自动检测的 logging hard policy 索引 |

View File

@@ -28,6 +28,7 @@
| POLICY-CONTROL-002 | `task_service.py` 须使用 `job_context(``finish(` | development/LOGGING.md §2.5、§7 | hard | 扫描 `scripts/service/task_service.py` | `tests/test_development_policy_guard.py::TestPolicyControl002` | | POLICY-CONTROL-002 | `task_service.py` 须使用 `job_context(``finish(` | development/LOGGING.md §2.5、§7 | hard | 扫描 `scripts/service/task_service.py` | `tests/test_development_policy_guard.py::TestPolicyControl002` |
| POLICY-CONTROL-003 | `scripts/service/*.py` 不得裸 `asyncio.sleep`RPA 等待须 `interruptible_sleep` | development/RPA.md §0.1 | hard | 扫描 service 层 Python | `tests/test_development_policy_guard.py::TestPolicyControl003` | | POLICY-CONTROL-003 | `scripts/service/*.py` 不得裸 `asyncio.sleep`RPA 等待须 `interruptible_sleep` | development/RPA.md §0.1 | hard | 扫描 service 层 Python | `tests/test_development_policy_guard.py::TestPolicyControl003` |
| POLICY-DATA-PATH-001 | 技能拥有路径须经 `resolve_data_path` / `get_*_dir``.env.example` 禁止 `./` CWD 相对路径;业务代码禁止 `abspath(config.get(...))` | development/DATA_PATHS.md | hard | 扫描 `.env.example``scripts/**/*.py` | `tests/test_development_policy_guard.py::TestPolicyDataPath001` | | POLICY-DATA-PATH-001 | 技能拥有路径须经 `resolve_data_path` / `get_*_dir``.env.example` 禁止 `./` CWD 相对路径;业务代码禁止 `abspath(config.get(...))` | development/DATA_PATHS.md | hard | 扫描 `.env.example``scripts/**/*.py` | `tests/test_development_policy_guard.py::TestPolicyDataPath001` |
| POLICY-SKILL-ACTION-001 | 存在 `development/SKILL_ACTION_RUNTIME.md`schema 含 `executionProfile`;业务技能(非模板占位 slug若 service 含 `RpaVideoSession``actions.json` 须有 `executionProfile=async` 且 placements 含 `agent` 的 action | development/SKILL_ACTION_RUNTIME.mdreferences/ACTIONS.md | hard | 文档/schema/manifest 检查 | `tests/test_development_policy_guard.py::TestPolicySkillAction001` |
--- ---

View File

@@ -11,9 +11,10 @@
7. [`RPA.md`](RPA.md) — 涉及浏览器 / 桌面 / 手机自动化时 7. [`RPA.md`](RPA.md) — 涉及浏览器 / 桌面 / 手机自动化时
8. [`CONFIG.md`](CONFIG.md) — `.env` 规范与 bootstrap 机制 8. [`CONFIG.md`](CONFIG.md) — `.env` 规范与 bootstrap 机制
9. [`DATA_PATHS.md`](DATA_PATHS.md) — 下载/导入/导出等本地文件路径标准(涉及文件读写时必读) 9. [`DATA_PATHS.md`](DATA_PATHS.md) — 下载/导入/导出等本地文件路径标准(涉及文件读写时必读)
10. [`RUNTIME.md`](RUNTIME.md) — 共享 runtime、数据路径、发布打包与编码约定 10. [`SKILL_ACTION_RUNTIME.md`](SKILL_ACTION_RUNTIME.md) — Skill Action sync/async、任务中心、Agent 禁令、队列 pick涉及 RPA / 长任务 / 数据管理按钮 / Cron / Agent 时必读)
11. [`RUNTIME.md`](RUNTIME.md) — 共享 runtime、数据路径、发布打包与编码约定
脚手架与 Git 防串库:[`../tools/README.md`](../tools/README.md)`scaffold_skill.ps1`)。 脚手架与 Git 防串库:[`../tools/README.md`](../tools/README.md)`scaffold_skill.ps1`)。
Agent 调用契约见 [`../references/CLI.md`](../references/CLI.md)、[`../references/SCHEMA.md`](../references/SCHEMA.md)。 Agent 调用契约见 [`../references/CLI.md`](../references/CLI.md)、[`../references/SCHEMA.md`](../references/SCHEMA.md)、[`../references/ACTIONS.md`](../references/ACTIONS.md)
用户市场说明见根目录 [`README.md`](../README.md),不要写进本目录。 用户市场说明见根目录 [`README.md`](../README.md),不要写进本目录。

View File

@@ -0,0 +1,162 @@
# Skill Action Runtime宿主任务中心契约
本文是匠厂宿主 **Skill Action / 任务中心** 的技能侧金标准。涉及浏览器 RPA、长耗时任务、数据管理按钮、定时任务或 Agent 直接调用时**必读**。
**边界:**
| 文档 | 职责 |
|------|------|
| 本文 | sync/async、任务中心、Agent 禁令、编排范式、队列 pick |
| [`../references/ACTIONS.md`](../references/ACTIONS.md) | `actions.json` 字段、placements、inputSchema |
| [`LOGGING.md`](LOGGING.md) | `job_context` / `emit` / `finish` / Run Journal |
| [`DATA_PATHS.md`](DATA_PATHS.md) | 下载/导入等落盘路径 |
宿主实现细节以匠厂 Host API 为准;技能侧只需保证 manifest 与 CLI 契约稳定。
---
## 1. 核心原则
1. **宿主管调度,技能管业务** — 不自行实现任务中心 / Job 轮询 / 后台守护进程。
2. **长耗时 = async** — 开浏览器、RPA、HTTP 大文件下载等必须 `executionProfile: "async"`,进**任务中心**。
3. **Agent 禁止 `exec` 直跑长任务 CLI** — 必须 `run_skill_action`;禁止 `exec python main.py run` + `process poll` 干等。
4. **进度走 Journal**`emit` / `video.add_step` → Run JournalUI / Skill Run Card / 任务中心展示;**不靠 stdout 流**。
5. **显式声明 `executionProfile`** — 宿主省略时默认 `async`;模板要求每个 action **写明** `sync``async`,避免歧义。
---
## 2. `executionProfile`sync vs async
| 值 | 宿主行为 | 超时 | 任务中心 |
|----|----------|------|----------|
| **`sync`** | 内联等待 CLI 结束,把结果直接返回调用方 | 默认约 **30s**`SKILL_ACTION_SYNC_TIMEOUT_MS` | ❌ 不进 |
| **`async`** | 后台 spawn Job立即返回 `jobId` | Job 本身无默认墙钟上限(可暂停/停止) | ✅ 进入 |
| **省略** | 宿主视为 **async** | 同 async | ✅ |
### 硬规则
| 场景 | 必须 |
|------|------|
| 浏览器 / Playwright / RPA 主路径 | `async` + 建议 `confirmation` |
| 单次可能超过 ~20s 的网络/下载 | `async` |
| `health` / `version` / `config-path` / 纯统计 / 快速导入 | `sync` |
| 数据管理 **toolbar** | **只放 async**(宿主前端对 sync 结果会报错) |
### Agent 路径启发式(宿主行为,技能作者需知)
- 显式写了 `executionProfile` → 以显式为准。
- 未写时:描述含「统计 / 检查 / 不启动浏览器」等只读语义 → Agent 侧可当 **sync**;有 `confirmation` 的副作用 → **async**
- **模板要求一律显式写出**,不要依赖启发式。
---
## 3. 三入口如何感知 sync / async
三个入口统一走 `POST /api/skill-actions/run`,带 `source.kind`
| 入口 | `source.kind` | 推荐 |
|------|---------------|------|
| **Agent** | `agent` | `run_skill_action`;长任务 async秒回 jobId |
| **数据管理** | `data-management` | toolbar 按钮触发 async action |
| **定时任务** | `cron` | Cron 配置 async action + `pickCount` 等参数 |
技能 **不要** 在 Python 里为三个入口写三套分支;宿主读 manifest 分流。
---
## 4. Action 分类与 placements 矩阵
| Action 类型 | `executionProfile` | 典型 placements | `confirmation` |
|-------------|--------------------|-----------------|----------------|
| 运行检查 / 版本 / 配置路径 | `sync` | `skill-detail`, `agent`(只读) | 无 |
| 库 / 队列统计 | `sync` | `skill-detail`, `agent` | 无 |
| 数据导入 / 失败重置 | `sync` | `skill-detail`, `agent` | 可选 |
| **单条**副作用 RPA | `async` | `agent`, `skill-detail` | **必须** |
| **批量顺序** RPApick | `async` | **`toolbar`**, `cron`, `agent` | **必须** |
禁忌:
- 高副作用 async action 不要默认只挂 `toolbar`(应有 confirmation
-`sensitive` 参数的 action 不要挂 `toolbar` / `cron`(除非有明确安全设计)。
- 不要为了「按钮多」暴露所有 CLI 子命令。
---
## 5. 技能内编排范式(单内核 + 多入口)
推荐结构(对齐 create-account / download-video 金标准):
```text
execute_single_<biz>(...) # 纯业务:鉴权 + 核心逻辑 + task_log不含 job_context
cmd_run(...) # 单条job_context + execute_single + finish
cmd_run_pick(N, ...) # 批量job_context + for-loop execute_single + emit(progress) + finish
cmd_stats() # 只读 syncJSON stdout无 job_context
```
要点:
1. **`job_context` 只包编排层**`cmd_run` / `cmd_run_pick`),不包 `execute_single_*`
2. 批量 **顺序、不并发**;数量由参数 `pickCount` / `--pick N` 传入(不写死 100/200
3. pick 循环内:`checkpoint()` + `emit("正在处理 {i}/{total}", progress={...})`
4. 结束:全成功 `finish(success)`;部分失败 `finish(partial)`;全失败 `finish(failed)`
5. 同一浏览器 Profile / account lease**禁止并发**;顺序执行 + lease 即可。
详见 [`LOGGING.md`](LOGGING.md) §2.5 与 `scripts/service/task_service.py` 示范。
---
## 6. 队列 + pick可选Type B 批量)
有「待处理列表」的技能(账号开通、视频下载、帖子发布等)建议:
| 能力 | 约定 |
|------|------|
| 队列表 | `status`: `pending``processing``success` / `failed` |
| 入库 | 数据管理 CRUD`import-*`(相对 `{skill_data}/imports/`,见 [`DATA_PATHS.md`](DATA_PATHS.md) |
| 消费 | `run --pick N`,默认仅 `pending` |
| 重试 | `reset` 或数据管理改 `pending`**failed 不要自动重试** |
| 批次 | 可选 `batch_id`,便于筛选与任务中心对照 |
| Manifest | `*-run-pick``async``placements``toolbar` / `cron` / `agent``inputSchema.pickCount` |
批量有两种合理形态(都是**顺序**
| 形态 | 任务中心条数 | 适用 |
|------|-------------|------|
| 一次 `run-pick` 处理 N 条 | **1** 条 Job | 同类 RPA、同 Profile、定时消费队列 |
| Agent/UI 触发 N 次单条 `run` | **N** 条 Job | 每条独立追踪 / 独立重试 |
默认优先 **一次 pick、一条 Job**(与 create-account `--pick`、download `--pick` 一致)。
---
## 7. Agent / SKILL.md 契约(复制后必改)
业务技能 `SKILL.md` 应明确:
1. 副作用任务必须通过宿主 **Skill Action**`run_skill_action`)调用。
2. **禁止**对长任务使用 `exec` + `process poll`
3. 单条 vs 批量 action id 与参数(如 `url` / `pickCount`)。
4. 长任务进度看**任务中心** / Skill Run Card不等 CLI stdout。
模板源仓库的 `SKILL.md` 含占位段落scaffold 后按业务改写。
---
## 8. Policy
自动检测索引见 [`POLICY_MATRIX.md`](POLICY_MATRIX.md)
- **POLICY-SKILL-ACTION-001**`SKILL_ACTION_RUNTIME.md` 存在schema 含 `executionProfile`;有 RPA 的业务技能必须声明 async agent action。
---
## 9. 与宿主 API参考
| API | 用途 |
|-----|------|
| `GET /api/skill-actions/{skillSlug}` | 读 manifest |
| `POST /api/skill-actions/run` | 执行 actionsync inline / async job |
| `GET /api/skill-actions/jobs` | 任务中心列表 |
| `GET /api/skill-actions/jobs/{jobId}` | 单 Job 详情 / 事件 |
Agent 工具(插件):`list_skill_actions``get_skill_action``run_skill_action`;正常对话**不要**轮询 `get_skill_action_job`(交给 UI / 任务中心)。

View File

@@ -2,6 +2,8 @@
本文件说明 `assets/actions.json``scripts/main.py` CLI 的对应关系,供 Agent 编排与宿主 **Skill Action Runtime** 使用。 本文件说明 `assets/actions.json``scripts/main.py` CLI 的对应关系,供 Agent 编排与宿主 **Skill Action Runtime** 使用。
**权威运行时契约sync/async、任务中心、编排范式见 [`../development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。**
**边界:** 用户市场说明见根目录 [`README.md`](../README.md)CLI 参数与错误码细节见 [`CLI.md`](CLI.md)。 **边界:** 用户市场说明见根目录 [`README.md`](../README.md)CLI 参数与错误码细节见 [`CLI.md`](CLI.md)。
## 模板严格规范 vs 宿主向后兼容 ## 模板严格规范 vs 宿主向后兼容
@@ -21,6 +23,7 @@ Action 是**可选能力**
- 没有宿主直接操作需求的技能,**可以不提供** `actions.json` - 没有宿主直接操作需求的技能,**可以不提供** `actions.json`
- 需要出现在**数据管理按钮**、**技能详情**、**定时任务**或 **Agent 直接调用**中的技能,**必须**提供 `assets/actions.json` - 需要出现在**数据管理按钮**、**技能详情**、**定时任务**或 **Agent 直接调用**中的技能,**必须**提供 `assets/actions.json`
- **含浏览器 RPA / 长耗时任务**的技能:**必须**提供至少一条 `executionProfile: "async"``placements``agent` 的 action`POLICY-SKILL-ACTION-001`)。
## 文件位置 ## 文件位置
@@ -49,11 +52,12 @@ Action 是**可选能力**
| 层级 | 稳定字段 / 能力 | | 层级 | 稳定字段 / 能力 |
|------|----------------| |------|----------------|
| 顶层 | `schemaVersion``skill``actions` | | 顶层 | `schemaVersion``skill``actions` |
| action | `id``label``description``placements``entrypoint`;可选 `inputSchema``confirmation` | | action | `id``label``description``placements``entrypoint`;可选 `inputSchema``confirmation`、**`executionProfile`** |
| entrypoint | `type = cli``command``args`**必填** JSON 数组,项为 `string` / `number` / `boolean` | | entrypoint | `type = cli``command``args`**必填** JSON 数组,项为 `string` / `number` / `boolean` |
| placements | `toolbar``skill-detail``agent``cron` | | placements | `toolbar``skill-detail``agent``cron` |
| inputSchema | 根 `type: object`properties 使用 `string` / `number` / `boolean`;支持 `enum``default``required``sensitive` | | inputSchema | 根 `type: object`properties 使用 `string` / `number` / `boolean`;支持 `enum``default``required``sensitive` |
| confirmation | `{ "message": "..." }` | | confirmation | `{ "message": "..." }` |
| **executionProfile** | `"sync"` \| `"async"`**模板要求显式写出** |
## 预留能力(当前不可当作已实现能力) ## 预留能力(当前不可当作已实现能力)
@@ -82,6 +86,18 @@ python {skill_root}/scripts/main.py <command> <args...>
完整子命令定义见 [`scripts/cli/app.py`](../scripts/cli/app.py)。 完整子命令定义见 [`scripts/cli/app.py`](../scripts/cli/app.py)。
### `executionProfile`sync / async
| 值 | 宿主行为 | 任务中心 |
|----|----------|----------|
| `sync` | 内联等待 CLI默认约 30s | 不进 |
| `async` | 后台 Job立即返回 `jobId` | **进入** |
| 省略 | 宿主默认按 **async** | 进入 |
- **RPA / 浏览器 / 长耗时** → 必须 `"async"`,并建议加 `confirmation`
- **数据管理 toolbar** → 只挂 **async** action。
- 完整规则见 [`../development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md)。
### CLI 输出契约 ### CLI 输出契约
技能 CLI 必须: 技能 CLI 必须:
@@ -108,9 +124,9 @@ HINT: <next step>
| 值 | 含义 | | 值 | 含义 |
|----|------| |----|------|
| `toolbar` | 数据管理第一排技能按钮 | | `toolbar` | 数据管理第一排技能按钮**仅 async** |
| `skill-detail` | 技能详情或技能市场详情页 | | `skill-detail` | 技能详情或技能市场详情页 |
| `agent` | Agent 可直接调用 | | `agent` | Agent 可直接调用`run_skill_action` |
| `cron` | 定时任务可直接调用 | | `cron` | 定时任务可直接调用 |
**预留能力(当前宿主可能尚未完整支持,模板示例请勿使用):** **预留能力(当前宿主可能尚未完整支持,模板示例请勿使用):**
@@ -120,7 +136,15 @@ HINT: <next step>
| `row` | 行内操作(未来扩展) | | `row` | 行内操作(未来扩展) |
| `batch` | 批量操作(未来扩展) | | `batch` | 批量操作(未来扩展) |
可以在 manifest 中作为未来扩展值定义,但不要让技能作者误以为 `row` / `batch` 已经完整可用。 ### Action 类型 × placements 矩阵
| Action 类型 | executionProfile | 典型 placements | confirmation |
|-------------|------------------|-----------------|--------------|
| health / version / config-path | sync | skill-detail, agent | 无 |
| 统计 / 只读查询 | sync | skill-detail, agent | 无 |
| 导入 / 重置 | sync | skill-detail, agent | 可选 |
| 单条副作用 RPA | async | agent, skill-detail | **必须** |
| 批量顺序 pick RPA | async | **toolbar**, cron, agent | **必须** |
## inputSchema 与参数表单 ## inputSchema 与参数表单
@@ -152,6 +176,20 @@ HINT: <next step>
} }
``` ```
批量 pick 参数约定(有队列表时):
```json
{
"pickCount": {
"type": "number",
"title": "处理数量",
"description": "从队列选取 pending 记录条数",
"default": 1,
"minimum": 1
}
}
```
约定: 约定:
- `boolean` 在宿主中渲染为开关 - `boolean` 在宿主中渲染为开关
@@ -166,23 +204,32 @@ HINT: <next step>
## 风险与入口配置 ## 风险与入口配置
- 查询、诊断、版本检查可以放到 `skill-detail``agent` - 查询、诊断、版本检查可以放到 `skill-detail``agent`
- 低风险、常用动作才考虑放 `toolbar` - 低风险、常用动作才考虑放 `toolbar`(且须为 async
- 会修改本地数据、访问外部系统、执行长时间任务的动作必须谨慎配置 - 会修改本地数据、访问外部系统、执行长时间任务的动作必须谨慎配置
- 有副作用的动作应添加 `confirmation.message` - 有副作用的动作应添加 `confirmation.message`
- 高风险动作不应默认出现在 `toolbar` - 高风险动作不应默认出现在 `toolbar`
-`sensitive` 参数的 action 不应放入 `toolbar``cron`,除非有明确安全设计 -`sensitive` 参数的 action 不应放入 `toolbar``cron`,除非有明确安全设计
- 不要为了「按钮多」而暴露所有 CLI 子命令 - 不要为了「按钮多」而暴露所有 CLI 子命令
- 一个 CLI 命令可以拆成多个语义清晰的 action但每个 action 必须有明确语义 - 一个 CLI 命令可以拆成多个语义清晰的 action但每个 action 必须有明确语义
- **Agent 必须用 `run_skill_action`,禁止 `exec` 直跑长任务 CLI**(见 `SKILL_ACTION_RUNTIME.md`
## 模板示例 action 一览 ## 模板示例 action 一览
| id | CLI | placements | 说明 | | id | CLI | executionProfile | placements | 说明 |
|----|-----|------------|------| |----|-----|------------------|------------|------|
| `health` | `health` | skill-detail, agent | 运行环境检查 | | `health` | `health` | sync | skill-detail, agent | 运行环境检查 |
| `version` | `version` | skill-detail | 版本 JSON | | `version` | `version` | sync | skill-detail | 版本 JSON |
| `config-path` | `config-path` | skill-detail | 配置路径 JSON | | `config-path` | `config-path` | sync | skill-detail | 配置路径 JSON |
复制为新技能后,请按业务需要增删 action不需要宿主入口时可删除整个 `actions.json` 业务技能复制后,按需增加例如:
| 模式 | id 示例 | executionProfile | placements |
|------|---------|------------------|------------|
| 单条 RPA | `your-run` | async | agent, skill-detail |
| 队列 pick | `your-run-pick` | async | toolbar, cron, agent |
| 统计 | `your-stats` | sync | skill-detail, agent |
不需要宿主入口时可删除整个 `actions.json`(无 RPA 长任务时)。
## 与宿主 API参考 ## 与宿主 API参考

View File

@@ -15,6 +15,7 @@
| [`CLI.md`](CLI.md) | 命令行入口、参数与调用契约 | | [`CLI.md`](CLI.md) | 命令行入口、参数与调用契约 |
| [`SCHEMA.md`](SCHEMA.md) | 本地 SQLite 与数据管理展示契约 | | [`SCHEMA.md`](SCHEMA.md) | 本地 SQLite 与数据管理展示契约 |
| [`ACTIONS.md`](ACTIONS.md) | Skill Action manifest**可选**能力)与宿主入口契约 | | [`ACTIONS.md`](ACTIONS.md) | Skill Action manifest**可选**能力)与宿主入口契约 |
| [`../development/SKILL_ACTION_RUNTIME.md`](../development/SKILL_ACTION_RUNTIME.md) | sync/async、任务中心、Agent 禁令、编排与队列 pick长任务必读 |
| [`../development/`](../development/) | 开发、测试、技术规范(给开发者 / AI 编程代理) | | [`../development/`](../development/) | 开发、测试、技术规范(给开发者 / AI 编程代理) |
按需阅读:编排任务时优先 `CLI.md`;解析结构化字段时读 `SCHEMA.md`;配置宿主按钮/定时任务/Agent 入口时读 `ACTIONS.md`;运行时环境细节见 `../development/RUNTIME.md` 按需阅读:编排任务时优先 `CLI.md`;解析结构化字段时读 `SCHEMA.md`;配置宿主按钮/定时任务/Agent 入口时读 `ACTIONS.md` + `SKILL_ACTION_RUNTIME.md`;运行时环境细节见 `../development/RUNTIME.md`

View File

@@ -1,6 +1,6 @@
"""技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger""" """技能标识、版本与平台公共库约束(复制后请修改 slug/version/logger"""
SKILL_SLUG = "your-skill-slug" SKILL_SLUG = "your-skill-slug"
SKILL_VERSION = "1.0.39" SKILL_VERSION = "1.0.40"
LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug" LOG_LOGGER_NAME = "openclaw.skill.your_skill_slug"
PLATFORM_KIT_MIN_VERSION = "1.2.0" PLATFORM_KIT_MIN_VERSION = "1.2.0"

View File

@@ -45,6 +45,7 @@ POLICY_IDS = (
"POLICY-CONTROL-002", "POLICY-CONTROL-002",
"POLICY-CONTROL-003", "POLICY-CONTROL-003",
"POLICY-DATA-PATH-001", "POLICY-DATA-PATH-001",
"POLICY-SKILL-ACTION-001",
) )
STRUCTURE_PATHS = ( STRUCTURE_PATHS = (
@@ -734,6 +735,142 @@ class TestPolicyDataPath001(unittest.TestCase):
) )
_TEMPLATE_PLACEHOLDER_SLUG = "your-skill-slug"
_RPA_SESSION_TOKENS = ("RpaVideoSession",)
def _service_has_rpa_video_session(skill_root: str) -> bool:
service_dir = os.path.join(skill_root, "scripts", "service")
if not os.path.isdir(service_dir):
return False
for rel in _walk_files(skill_root, "scripts/service", suffix=".py"):
text = _read_text(os.path.join(skill_root, rel))
if any(token in text for token in _RPA_SESSION_TOKENS):
return True
return False
def _load_actions_json(skill_root: str) -> dict | None:
path = os.path.join(skill_root, "assets", "actions.json")
if not os.path.isfile(path):
return None
import json
with open(path, encoding="utf-8") as f:
return json.load(f)
class TestPolicySkillAction001(unittest.TestCase):
def test_skill_action_runtime_doc_exists(self) -> None:
path = os.path.join(get_skill_root(), "development", "SKILL_ACTION_RUNTIME.md")
self.assertTrue(
os.path.isfile(path),
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"development/SKILL_ACTION_RUNTIME.md",
"file missing",
),
)
text = _read_text(path)
for marker in ("executionProfile", "async", "任务中心", "run_skill_action"):
self.assertIn(
marker,
text,
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"development/SKILL_ACTION_RUNTIME.md",
f"missing marker {marker!r}",
),
)
def test_schema_defines_execution_profile(self) -> None:
import json
path = os.path.join(get_skill_root(), "assets", "schemas", "skill-actions.schema.json")
self.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as f:
schema = json.load(f)
action_props = schema.get("$defs", {}).get("action", {}).get("properties", {})
profile = action_props.get("executionProfile")
self.assertIsInstance(
profile,
dict,
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"assets/schemas/skill-actions.schema.json",
"action.properties.executionProfile missing",
),
)
self.assertEqual(set(profile.get("enum") or []), {"sync", "async"})
def test_actions_md_documents_execution_profile(self) -> None:
path = os.path.join(get_skill_root(), "references", "ACTIONS.md")
text = _read_text(path)
for marker in ("executionProfile", "async", "任务中心", "SKILL_ACTION_RUNTIME"):
self.assertIn(
marker,
text,
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"references/ACTIONS.md",
f"missing marker {marker!r}",
),
)
def test_actions_json_actions_declare_execution_profile(self) -> None:
skill_root = get_skill_root()
manifest = _load_actions_json(skill_root)
if manifest is None:
return
offenders: list[str] = []
for action in manifest.get("actions") or []:
action_id = action.get("id", "?")
profile = action.get("executionProfile")
if profile not in ("sync", "async"):
offenders.append(f"{action_id}: executionProfile={profile!r}")
self.assertEqual(
offenders,
[],
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"assets/actions.json",
"every action must set executionProfile to sync|async:\n" + "\n".join(offenders),
),
)
def test_business_rpa_skill_has_async_agent_action(self) -> None:
from util.constants import SKILL_SLUG
skill_root = get_skill_root()
if SKILL_SLUG == _TEMPLATE_PLACEHOLDER_SLUG:
self.skipTest("template placeholder slug skips business RPA action check")
if not _service_has_rpa_video_session(skill_root):
self.skipTest("no RpaVideoSession in scripts/service")
manifest = _load_actions_json(skill_root)
self.assertIsNotNone(
manifest,
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"assets/actions.json",
"RPA skill must provide assets/actions.json",
),
)
found = False
for action in manifest.get("actions") or []:
placements = set(action.get("placements") or [])
if action.get("executionProfile") == "async" and "agent" in placements:
found = True
break
self.assertTrue(
found,
msg=_policy_msg(
"POLICY-SKILL-ACTION-001",
"assets/actions.json",
"RPA business skill needs at least one action with executionProfile=async and placements containing agent",
),
)
class TestPolicyDocs001(unittest.TestCase): class TestPolicyDocs001(unittest.TestCase):
def test_policy_matrix_exists_and_lists_policy_ids(self) -> None: def test_policy_matrix_exists_and_lists_policy_ids(self) -> None:
skill_root = get_skill_root() skill_root = get_skill_root()