feat: 结题报告初稿助手首版 v1.0.1(draft-closing-report-default)
All checks were successful
技能自动化发布 / release (push) Successful in 6s
All checks were successful
技能自动化发布 / release (push) Successful in 6s
This commit is contained in:
133
development/ADAPTER.md
Normal file
133
development/ADAPTER.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# 适配器标准:真实/仿真 × API/RPA 四档模式
|
||||
|
||||
> 凡是"连接三方系统"(ERP、CRM、SaaS、银行等)的 skill,都应采用本文的 **adapter 四档模式**。同一套业务逻辑可在不同档位间切换;**四档都保留**。
|
||||
|
||||
先遵守 [`AI_RPA.md`](AI_RPA.md) 的授权与技术红线:`real_api` 只连接公开或明确授权的官方接口;不得通过抓包、逆向或猜测私有协议构造“API”。没有授权 API 时使用正常界面 RPA;正常界面无法闭环且只能绕过安全机制时,停止并上报。
|
||||
|
||||
## 为什么要分档
|
||||
|
||||
对接一个外部系统,工程上无非四种方式的组合:
|
||||
|
||||
| | API(有接口) | RPA(操作界面) |
|
||||
|---|---|---|
|
||||
| **真实系统** | `real_api` 真实 API | `real_rpa` 真实界面 |
|
||||
| **仿真/离线** | `mock` 纯内存/fixture | `simulator_rpa` 仿真站点/桌面仿真 |
|
||||
|
||||
### 四档定义
|
||||
|
||||
| 档位 | 含义 | 默认策略 |
|
||||
|------|------|----------|
|
||||
| **`mock`** | 纯内存或 fixture,离线 | **单测 / CI 必过**;mock 不通不要往下走 |
|
||||
| **`simulator_rpa`** | 仿真站点或桌面仿真,可半集成 | 按业务场景选用 |
|
||||
| **`real_api`** | 真实 API | 按业务场景选用(有官方接口时) |
|
||||
| **`real_rpa`** | 真实浏览器/真实系统 | **模板 `.env.example` 默认**;业务自测与交活用此档 |
|
||||
|
||||
- **mock**:纯离线、不联网,给单测 / CI,**保证可重复**。**mock 通 ≠ 开发完成**。
|
||||
- **simulator_rpa**:操作仿真平台(如 `sandbox.jc2009.com`),按场景选用。
|
||||
- **real_api**:有官方接口时按场景选用。
|
||||
- **real_rpa**:真实浏览器路径;模板默认,须真实跑通后再交活。
|
||||
|
||||
> **口径**:业务配置默认 `real_rpa`;单测/CI 用 `mock`。不得只 mock 通就交活;须 release 到匠厂宿主验收(含用户体验)。
|
||||
|
||||
配置读取见 `CONFIG.md`:**bootstrap 之后业务代码只通过 `config.get*()` 读 `OPENCLAW_TEST_TARGET` 等项**(进程 env > 用户 `.env` > `.env.example`)。
|
||||
|
||||
## 目录骨架
|
||||
|
||||
```
|
||||
scripts/service/<domain>_adapter/
|
||||
__init__.py # dispatch:按档位返回对应 adapter 实例
|
||||
base.py # 数据契约(dataclass)+ AdapterBase 接口
|
||||
mock.py # 离线仿真,给 CI/单测
|
||||
real_api.py # 真实系统 API
|
||||
sim_rpa.py # 薄仿真平台 RPA adapter(委托 *_playwright.py)
|
||||
real_rpa.py # 真实系统 RPA(占位,谨慎实现)
|
||||
|
||||
scripts/service/
|
||||
<platform>_playwright.py # RPA 主流程(厚);与薄 sim_rpa.py 并列
|
||||
account_client.py # account-manager subprocess 唯一封装
|
||||
browser_session.py # async persistent context 启动
|
||||
```
|
||||
|
||||
模板示例(均在 `examples/`,**不在** `scripts/`):
|
||||
|
||||
- **仿真浏览器 RPA 完整案例**:`examples/simulator_browser_rpa/` — async mock + simulator_rpa + `simulator_playwright.py` + sandbox 页面 + adapter 分层 + 测试
|
||||
- **真实浏览器 RPA 完整案例**:`examples/real_browser_rpa/` — 登录/验证码/滚动采集 + `account_client.py`
|
||||
- **真实 API / 仿真 API**:`examples/real_api/`、`examples/simulator_api/` — 当前为规划占位,尚未沉淀可复制实现
|
||||
|
||||
复制 adapter 分层时,以修正后的 `examples/simulator_browser_rpa/`(薄 adapter + `simulator_playwright.py`)与 `examples/real_browser_rpa/scripts/service/account_client.py` 为权威参考,在新技能中创建 `scripts/service/<domain>_adapter/` 并按 README copy map 选择性复制。
|
||||
|
||||
> 先判断属于四象限哪一种(`real_browser_rpa` / `real_api` / `simulator_browser_rpa` / `simulator_api`),再读对应 `examples/*/README.md`。示例是**参考架构与边界**,不是业务代码原样复制。
|
||||
|
||||
## 档位 dispatch
|
||||
|
||||
由 `OPENCLAW_TEST_TARGET` 统一决定用哪个 adapter:
|
||||
|
||||
| `OPENCLAW_TEST_TARGET` | adapter | 用途 |
|
||||
|---|---|---|
|
||||
| `unit` / `mock` | `MockAdapter` | 单测 / CI,离线 |
|
||||
| `simulator_rpa` | `SimRpaAdapter` | 开发/演示,操作仿真平台 |
|
||||
| `real_api` | `RealApiAdapter` | 生产,走官方接口 |
|
||||
| `real_rpa` | `RealRpaAdapter` | 生产,操作真实界面 |
|
||||
|
||||
```python
|
||||
# __init__.py
|
||||
from jiangchang_skill_core import config
|
||||
|
||||
def get_adapter():
|
||||
target = (config.get("OPENCLAW_TEST_TARGET") or "real_rpa").lower()
|
||||
if target in ("unit", "mock"):
|
||||
return MockAdapter()
|
||||
if target == "real_api":
|
||||
return RealApiAdapter()
|
||||
if target == "real_rpa":
|
||||
return RealRpaAdapter()
|
||||
return SimRpaAdapter()
|
||||
```
|
||||
|
||||
`get_adapter()` 实现应配合 `tests/adapter_test_utils.py` 的 profile 策略:**默认必跑测试不得误设 `OPENCLAW_TEST_TARGET=real_*`**。
|
||||
|
||||
## contract tests
|
||||
|
||||
每个 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`),**不要**在 `task_service.py`、`task_rpa.py` 等业务流程文件中到处散落 `subprocess.run`。
|
||||
3. **account-manager 账号/租约能力**是例外:可参考 `examples/real_browser_rpa/scripts/service/account_client.py` 与 `examples/simulator_browser_rpa/scripts/service/account_client.py`,封装为**单一** `account_client.py`;允许在该文件内部集中通过 subprocess 调 account-manager CLI。**禁止** import `rpa_helpers` 等 account-manager 内部模块;`simulator_rpa` 与 `real_rpa` 均走同一 `pick_web_account` / `release_lease` 模式。
|
||||
4. **不允许**直接 `import account-manager` 的内部 Python 模块(如 `service/`、`util/`、`db/`)。
|
||||
5. **所有选择路径都必须取得 lease**:包括纯数字 `DEFAULT_ACCOUNT_ID`。`account get <id>` 只能读取账号,不能单独作为运行结果;必须再通过可定位该账号的 pick 流程取得非空 `lease_token`。随后必须 `finally release lease`;进程被 kill 后可能残留 lease,需按 [`ACCOUNT_MANAGER.md`](ACCOUNT_MANAGER.md) 排查。
|
||||
6. **网页 RPA 硬规则**(详见 [`RPA.md`](RPA.md) §0.2):必须先经 `pick_web_account`(底层 **`ensure-web` / `pick-web --ensure`**)拿到 `profile_dir` 再开浏览器;拿不到则失败退出;生产有头;**是否站点登录**按平台约定,但**不等于**可以跳过 account-manager。登记账号 ≠ 站点已登录。
|
||||
7. **账号选择优先级**:纯数字 `DEFAULT_ACCOUNT_ID` → 校验指定账号后带租约 pick;`DEFAULT_LOGIN_ID` / 非数字标识 → `ensure-web --login-id --lease`;皆空 → 按平台 `ensure-web --lease`。人读展示优先 `login_id` / `account_label`,不要只用数字 id。多条匹配或无法确保拿到指定 ID → `ERROR:AMBIGUOUS_ACCOUNT`。
|
||||
8. **运行前校验**:`profile_dir` 和 `lease_token` 均须非空;账号平台必须匹配;缺任一字段立即失败,不打开浏览器。
|
||||
|
||||
```python
|
||||
# 普通兄弟技能 — 走 sibling_bridge
|
||||
from service.sibling_bridge import call_sibling_json
|
||||
|
||||
result = call_sibling_json("account-manager", ["list", "--limit", "10"])
|
||||
```
|
||||
|
||||
```python
|
||||
# account-manager 账号/租约 — 集中在 account_client.py(ensure-web)
|
||||
from service.account_client import pick_web_account, release_lease
|
||||
|
||||
account = pick_web_account(platform="target_platform", login_id=None)
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
release_lease(account.get("lease_token"))
|
||||
```
|
||||
|
||||
## 相关文档
|
||||
|
||||
- `RPA.md` — 三端 RPA 技术选型与拟人/反反爬范式
|
||||
- `ACCOUNT_MANAGER.md` — 账号、Profile、租约、多账号、查看与排障标准
|
||||
- `CONFIG.md` — `.env` 里如何配置运行模式与目标地址
|
||||
- `TESTING.md` — 测试 target 与隔离体系
|
||||
Reference in New Issue
Block a user