chore: auto release commit (2026-06-08 14:19:45)
All checks were successful
技能自动化发布 / release (push) Successful in 6s

This commit is contained in:
2026-06-08 14:19:46 +08:00
parent 5b555e4313
commit a24f6809da
23 changed files with 701 additions and 184 deletions

View File

@@ -1,6 +1,6 @@
# 适配器标准:真实/仿真 × API/RPA 四档模式
> 凡是"连接三方系统"ERP、CRM、SaaS、银行等的 skill都应采用本文的 **adapter 四档模式**。这样同一套业务逻辑可以在不同档位间切换:开发用仿真、CI 用 mock、上线用真实,互不影响。
> 凡是"连接三方系统"ERP、CRM、SaaS、银行等的 skill都应采用本文的 **adapter 四档模式**。这样同一套业务逻辑可以在不同档位间切换:开发用 mock、半集成用 simulator、上线用真实,互不影响。
## 为什么要分档
@@ -9,104 +9,97 @@
| | API有接口 | RPA操作界面 |
|---|---|---|
| **真实系统** | `real_api` 真实 API | `real_rpa` 真实界面 |
| **仿真系统** | `mock` 离线仿真 | `sim_rpa` 仿真平台界面 |
| **仿真/离线** | `mock` 纯内存/fixture | `simulator_rpa` 仿真站点/桌面仿真 |
### 四档定义
| 档位 | 含义 | 默认策略 |
|------|------|----------|
| **`mock`** | 纯内存或 fixture**默认单测/CI** | 模板 `.env.example` 默认 `OPENCLAW_TEST_TARGET=mock` |
| **`simulator_rpa`** | 仿真站点或桌面仿真,可半集成 | 开发联调可选 |
| **`real_api`** | 真实 API | **必须** `ALLOW_REAL_API=1` |
| **`real_rpa`** | 真实浏览器/真实系统 | **必须** `ALLOW_REAL_RPA=1` |
- **mock**:纯离线、不联网,给单测 / CI / 开发自测,**保证可重复**。
- **sim_rpa**:操作仿真平台(如 `sandbox.jc2009.com`),跑真实端到端流程但不碰生产**默认开发档位**
- **simulator_rpa**:操作仿真平台(如 `sandbox.jc2009.com`),跑端到端流程但不碰生产。
- **real_api**:有官方接口时**首选**(最稳、最快、最易维护)。
- **real_rpa**:没有 API 只能操作生产界面,**风险最高、放最后**。
> 推荐优先级:**real_api > sim_rpa > real_rpa**mock 永远保留做 CI。
> 推荐优先级:**real_api > simulator_rpa > real_rpa**mock 永远保留做 CI。
## 权限开关
未显式授权时**不得**落到真实 API/RPA
| 变量 | 作用 |
|------|------|
| `ALLOW_REAL_API=1` | 允许 `real_api` 访问真实 HTTP |
| `ALLOW_REAL_RPA=1` | 允许 `real_rpa` 驱动真实浏览器/RPA |
| `ALLOW_WRITE_ACTIONS=1` | 在 `real_*` 下允许**写**操作(提交表单、下单等) |
进程环境变量优先于用户 `.env`(见 `CONFIG.md` 三层优先级)。
## 目录骨架
业务逻辑只依赖 `base` 里定义的接口与数据契约,具体档位实现各自一个文件:
```
scripts/service/<domain>_adapter/
__init__.py # dispatch按档位返回对应 adapter 实例
base.py # 数据契约dataclass+ AdapterBase 接口NotImplementedError
base.py # 数据契约dataclass+ AdapterBase 接口
mock.py # 离线仿真,给 CI/单测
real_api.py # 真实系统 API
sim_rpa.py # 仿真平台 RPAPlaywright/pywinauto/uiautomator2
sim_rpa.py # 仿真平台 RPA
real_rpa.py # 真实系统 RPA占位谨慎实现
```
> 参考实现:`disburse-payroll-icbc/scripts/service/disburse_adapter/`(已有 `base.py` / `mock.py` / `icbc_simulator_rpa.py`)。
> 模板示例:`scripts/service/example_adapter/`(复制改名即用)。
### 引用方式
```python
from service.example_adapter import get_adapter
from service.example_adapter.base import ExampleItem
adapter = get_adapter() # 由 OPENCLAW_TEST_TARGET 决定档位
result = adapter.do_batch(
target="https://sandbox.jc2009.com",
items=[ExampleItem(id="1", label="demo")],
)
```
mock 档位纯离线验证:
```bash
set OPENCLAW_TEST_TARGET=mock
python -c "import sys; sys.path.insert(0,'scripts'); from service.example_adapter import get_adapter; from service.example_adapter.base import ExampleItem; r=get_adapter().do_batch('x',[ExampleItem('1','a')]); print(r.ok, r.serial_no)"
```
## base.py 写法(要点)
1. **数据契约用 `@dataclass`**:输入项(如 `PayrollItem`)、批量结果(如 `BatchResult{ok, serial_no, error_msg, artifacts}`)。
2. **接口方法抛 `NotImplementedError`**:所有档位实现同一签名。
3. **以"批"为单位返回**:真实 RPA 往往是整批提交,结果按批次返回,不要假设逐条。
4. **结果里带 `artifacts`**:截图/录屏/日志路径,便于排查。
```python
@dataclass
class XxxResult:
ok: bool
serial_no: Optional[str]
error_msg: Optional[str]
artifacts: Dict[str, Any] = field(default_factory=dict)
class AdapterBase:
name = "base"
def do_batch(self, target: str, items: list) -> XxxResult:
raise NotImplementedError
```
模板示例:`scripts/service/example_adapter/`(复制改名即用)。
## 档位 dispatch
`OPENCLAW_TEST_TARGET` 统一决定用哪个 adapter(与现有测试体系一致)
`OPENCLAW_TEST_TARGET` 统一决定用哪个 adapter
| `OPENCLAW_TEST_TARGET` | adapter | 用途 |
|---|---|---|
| `unit` / `mock` | `MockAdapter` | 单测 / CI离线 |
| `simulator_rpa`(默认) | `SimRpaAdapter` | 开发/演示,操作仿真平台 |
| `simulator_rpa` | `SimRpaAdapter` | 开发/演示,操作仿真平台 |
| `real_api` | `RealApiAdapter` | 生产,走官方接口 |
| `real_rpa` | `RealRpaAdapter` | 生产,操作真实界面(最后手段) |
| `real_rpa` | `RealRpaAdapter` | 生产,操作真实界面 |
```python
# __init__.py
def get_adapter():
target = (os.environ.get("OPENCLAW_TEST_TARGET") or "simulator_rpa").lower()
target = (os.environ.get("OPENCLAW_TEST_TARGET") or "mock").lower()
if target in ("unit", "mock"):
return MockAdapter()
if target == "real_api":
return RealApiAdapter()
if target == "real_rpa":
return RealRpaAdapter()
return SimRpaAdapter() # 默认
return SimRpaAdapter()
```
> 实际可改为读 `.env` 里的运行模式(见 `CONFIG.md``OPENCLAW_TEST_TARGET` 作为进程级覆盖优先
`get_adapter()` 实现应配合 `tests/adapter_test_utils.py` 的 profile 策略:**未授权时不 silently 开启真实网络/RPA**
## 测试要求
## contract tests
- **mock 档必须能纯离线跑通**,进 CI。
- **每个 adapter 单测同一组用例**(契约测试),保证档位间行为一致。
- RPA 档的真实联网测试单独标记,不进默认 CI参考 `tests/desktop/`)。
每个 adapter **必须覆盖同一套契约测试**(复制 `tests/samples/test_service_contract.py.sample`
- 至少覆盖:**timeout**、**unauthorized**、**invalid response**、**empty result**
- **mock 不允许真实网络**
- 使用 `FakeAdapter` 或等价 stub 模拟异常路径
## 兄弟依赖
依赖 account-manager 或其他兄弟技能时:
1.`SKILL.md``metadata.openclaw.dependencies.required` 声明。
2. 调用兄弟技能走统一 **`service.sibling_bridge`**`call_sibling_json`**不要**在 service 层散落 `subprocess.run`
3. **lease 必须 release**account-manager 租约);进程被 kill 后可能残留 lease需在运维文档说明排查方式查 account-manager lease 列表 / 手动释放)。
```python
from service.sibling_bridge import call_sibling_json
result = call_sibling_json("account-manager", ["list", "--limit", "10"])
```
## 相关文档