@qing3a/flow-rpa-app 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -13
- package/dist/cli.d.ts +4 -2
- package/dist/cli.js +83 -22
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.js +22 -2
- package/dist/config.js.map +1 -1
- package/dist/gate-key.d.ts +65 -0
- package/dist/gate-key.js +93 -0
- package/dist/gate-key.js.map +1 -0
- package/dist/gate-runtime.d.ts +27 -0
- package/dist/gate-runtime.js +44 -0
- package/dist/gate-runtime.js.map +1 -0
- package/dist/registry.d.ts +33 -2
- package/dist/registry.js +94 -10
- package/dist/registry.js.map +1 -1
- package/dist/runner.d.ts +30 -3
- package/dist/runner.js +89 -14
- package/dist/runner.js.map +1 -1
- package/dist/server.js +29 -11
- package/dist/server.js.map +1 -1
- package/dist/session-rotation.d.ts +58 -0
- package/dist/session-rotation.js +61 -0
- package/dist/session-rotation.js.map +1 -0
- package/dist/status.d.ts +51 -0
- package/dist/status.js +117 -41
- package/dist/status.js.map +1 -1
- package/package.json +2 -2
- package/skill/flow-rpa-engine.md +154 -37
- package/starter/baseline.json +18 -0
- package/starter/liepin_search_resumes/flow.json +78 -54
package/dist/status.js
CHANGED
|
@@ -3,6 +3,9 @@ import { dirname, join } from 'node:path';
|
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { listTargets } from '@qing3a/flow-rpa-engine/src/cdp/client.js';
|
|
5
5
|
import { queueState } from './runner.js';
|
|
6
|
+
import { currentRotation, rotationDetail } from './session-rotation.js';
|
|
7
|
+
/** 兜底率回看窗口(最近 N 次 run;与 ANTI-BAN-PRINCIPLES §四 的「兜底率读法」同一口径) */
|
|
8
|
+
const FALLBACK_WINDOW_RUNS = 20;
|
|
6
9
|
/** HTML 转义(防注入) */
|
|
7
10
|
function esc(s) {
|
|
8
11
|
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
@@ -27,6 +30,65 @@ async function browserProbe(ctx) {
|
|
|
27
30
|
return false;
|
|
28
31
|
}
|
|
29
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* 兜底率聚合(T034-2 / S4):纯函数——给定「窗口内的 run id」与台账原文,算事件数/占比/种类。
|
|
35
|
+
* 台账行 = 一次含兜底的 run:`{at, runId, flowId, hits:[{stepId, kind, selector}]}`(runner 在 run
|
|
36
|
+
* 结束后追加;**0 兜底的 run 不写行**)。窗口外的行、坏行、空 hits 一律不计(旁路数据不污染主聚合)。
|
|
37
|
+
* 抽成纯函数是为了让「0 兜底 → 0%」「有兜底 → 正确计数」可被直测(不经 ctx/fs)。
|
|
38
|
+
*/
|
|
39
|
+
export function fallbackRateFrom(runIds, ledgerRaw) {
|
|
40
|
+
const window = new Set(runIds);
|
|
41
|
+
const withFallback = new Set();
|
|
42
|
+
const kinds = {};
|
|
43
|
+
let events = 0;
|
|
44
|
+
for (const line of ledgerRaw.split('\n')) {
|
|
45
|
+
if (line.trim() === '')
|
|
46
|
+
continue;
|
|
47
|
+
let entry;
|
|
48
|
+
try {
|
|
49
|
+
entry = JSON.parse(line);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
continue; // 坏行跳过(旁路数据不影响主聚合)
|
|
53
|
+
}
|
|
54
|
+
if (typeof entry.runId !== 'string' || !window.has(entry.runId))
|
|
55
|
+
continue;
|
|
56
|
+
const hits = Array.isArray(entry.hits) ? entry.hits : [];
|
|
57
|
+
if (hits.length === 0)
|
|
58
|
+
continue;
|
|
59
|
+
events += hits.length;
|
|
60
|
+
withFallback.add(entry.runId);
|
|
61
|
+
for (const hit of hits) {
|
|
62
|
+
const kind = hit !== null && typeof hit === 'object' && typeof hit.kind === 'string'
|
|
63
|
+
? hit.kind
|
|
64
|
+
: 'unknown';
|
|
65
|
+
kinds[kind] = (kinds[kind] ?? 0) + 1;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const runsWithFallback = withFallback.size;
|
|
69
|
+
const rate = runIds.length > 0 ? runsWithFallback / runIds.length : 0;
|
|
70
|
+
// 「目标 0」是唯一文案源:状态页 HTML / status.json / get_status 三处同文
|
|
71
|
+
const detail = runIds.length === 0
|
|
72
|
+
? '最近 0 次 run(无运行记录):未观测到非拟人兜底——目标 0(B 类捷径,见 docs/ANTI-BAN-PRINCIPLES.md ②)'
|
|
73
|
+
: `最近 ${runIds.length} 次 run 兜底率 ${(rate * 100).toFixed(0)}%(${runsWithFallback}/${runIds.length} 次 run 发生兜底,事件 ${events} 次${events > 0 ? `:${Object.entries(kinds).map(([k, n]) => `${k}×${n}`).join('、')}` : ''})——目标 0(B 类捷径只应收敛、不新增)`;
|
|
74
|
+
return { windowRuns: runIds.length, events, runsWithFallback, rate, kinds, detail };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 兜底率聚合(T034-2 / S4,只读):窗口 = 最近 N 次 run(`data/runs/`),台账 = 旁路
|
|
78
|
+
* `data/behavior/fallback-events.jsonl`。台账缺失/损坏 → 视为 0 兜底(只读聚合,绝不因旁路
|
|
79
|
+
* 数据坏掉而让状态页失败)。
|
|
80
|
+
*/
|
|
81
|
+
function fallbackRate(ctx) {
|
|
82
|
+
const windowIds = ctx.store.listRunIds().reverse().slice(0, FALLBACK_WINDOW_RUNS);
|
|
83
|
+
let raw = '';
|
|
84
|
+
try {
|
|
85
|
+
raw = readFileSync(join(ctx.store.root, 'behavior', 'fallback-events.jsonl'), 'utf8');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
raw = ''; // 无台账 = 从无兜底(不是错误态)
|
|
89
|
+
}
|
|
90
|
+
return fallbackRateFrom(windowIds, raw);
|
|
91
|
+
}
|
|
30
92
|
/** 应用(app 包)版本:读包根 package.json(src/ 或 dist/ → 上一级即包根) */
|
|
31
93
|
function appVersion() {
|
|
32
94
|
try {
|
|
@@ -67,6 +129,10 @@ function lockState(ctx) {
|
|
|
67
129
|
/** 聚合引擎当前状态(自描述;/status.json 与 get_status 共用同一数据源) */
|
|
68
130
|
export async function collectStatus(ctx) {
|
|
69
131
|
const version = appVersion();
|
|
132
|
+
// T033-2(S5):读本轮运行观测(executeRun/doctor 刷新;只读,不改 ctx)
|
|
133
|
+
const rotation = currentRotation();
|
|
134
|
+
// T035-2(S6):流程列表带来源标注(list() 一次拿到,warnings 紧随其后同一轮扫描)
|
|
135
|
+
const flows = ctx.registry.list();
|
|
70
136
|
return {
|
|
71
137
|
version,
|
|
72
138
|
skillVersion: `v${version}`,
|
|
@@ -78,9 +144,12 @@ export async function collectStatus(ctx) {
|
|
|
78
144
|
},
|
|
79
145
|
queue: queueState(),
|
|
80
146
|
lock: lockState(ctx),
|
|
81
|
-
flows:
|
|
147
|
+
flows: flows.map((f) => ({ id: f.id, name: f.name, version: f.version, inputs: f.inputs, origin: f.origin })),
|
|
82
148
|
flowWarnings: ctx.registry.warnings(),
|
|
149
|
+
sessionRotation: { ...rotation, detail: rotationDetail(rotation) },
|
|
83
150
|
runs: { recent: ctx.store.listRunIds().length },
|
|
151
|
+
// T034-2(S4):兜底率只读呈现(读旁路台账,0 兜底 → 0%)
|
|
152
|
+
fallbacks: fallbackRate(ctx),
|
|
84
153
|
};
|
|
85
154
|
}
|
|
86
155
|
/** 生成状态页 HTML(内嵌数据,只读;数据源 = collectStatus) */
|
|
@@ -95,47 +164,54 @@ export async function renderStatusHtml(ctx) {
|
|
|
95
164
|
? `<div class="card" style="color:#b35900"><strong>流程加载警告</strong>(${s.flowWarnings.length})<ul>${s.flowWarnings.map((w) => `<li>${esc(w)}</li>`).join('')}</ul></div>`
|
|
96
165
|
: '';
|
|
97
166
|
const flowItems = s.flows
|
|
98
|
-
.map((f) => `<li>${esc(f.id)}(v${f.version}): ${esc(f.name)}</
|
|
167
|
+
.map((f) => `<li>${esc(f.id)}(v${f.version}): ${esc(f.name)} <span style="color:#888">[来源: ${esc(f.origin)}]</span>${f.origin === 'builtin-modified'
|
|
168
|
+
? ' <span style="color:#b35900">⚠️ 与内置基线不一致(本地已改动)</span>'
|
|
169
|
+
: ''}</li>`)
|
|
99
170
|
.join('\n');
|
|
100
|
-
return `<!DOCTYPE html>
|
|
101
|
-
<html lang="zh">
|
|
102
|
-
<head>
|
|
103
|
-
<meta charset="utf-8">
|
|
104
|
-
<title>flow-rpa 状态页</title>
|
|
105
|
-
<style>
|
|
106
|
-
body { font-family: system-ui, sans-serif; margin: 2rem; color: #222; }
|
|
107
|
-
h1 { border-bottom: 2px solid #eee; padding-bottom: .5rem; }
|
|
108
|
-
.card { border: 1px solid #ddd; border-radius: 8px; padding: 1rem 1.2rem; margin-bottom: 1.2rem; }
|
|
109
|
-
table { border-collapse: collapse; width: 100%; font-size: .9rem; }
|
|
110
|
-
th, td { border: 1px solid #eee; padding: .4rem .6rem; text-align: left; }
|
|
111
|
-
.st-done { color: #1a7f37; } .st-failed { color: #cf222e; } .st-running { color: #9a6700; }
|
|
112
|
-
ul { margin: 0; padding-left: 1.2rem; }
|
|
113
|
-
.badge-up { color: #1a7f37; font-weight: bold; } .badge-down { color: #cf222e; font-weight: bold; }
|
|
114
|
-
</style>
|
|
115
|
-
</head>
|
|
116
|
-
<body>
|
|
117
|
-
<h1>flow-rpa 状态</h1>
|
|
118
|
-
<div class="card">
|
|
119
|
-
<strong>浏览器</strong>:<span class="${browser.connected ? 'badge-up' : 'badge-down'}">${browser.connected ? '运行中' : '未连接'}</span>
|
|
120
|
-
<span style="color:#888">(端口 ${browser.port})</span>
|
|
121
|
-
</div>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
<
|
|
125
|
-
</
|
|
126
|
-
|
|
127
|
-
<div class="card">
|
|
128
|
-
<strong
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
</
|
|
138
|
-
</
|
|
171
|
+
return `<!DOCTYPE html>
|
|
172
|
+
<html lang="zh">
|
|
173
|
+
<head>
|
|
174
|
+
<meta charset="utf-8">
|
|
175
|
+
<title>flow-rpa 状态页</title>
|
|
176
|
+
<style>
|
|
177
|
+
body { font-family: system-ui, sans-serif; margin: 2rem; color: #222; }
|
|
178
|
+
h1 { border-bottom: 2px solid #eee; padding-bottom: .5rem; }
|
|
179
|
+
.card { border: 1px solid #ddd; border-radius: 8px; padding: 1rem 1.2rem; margin-bottom: 1.2rem; }
|
|
180
|
+
table { border-collapse: collapse; width: 100%; font-size: .9rem; }
|
|
181
|
+
th, td { border: 1px solid #eee; padding: .4rem .6rem; text-align: left; }
|
|
182
|
+
.st-done { color: #1a7f37; } .st-failed { color: #cf222e; } .st-running { color: #9a6700; }
|
|
183
|
+
ul { margin: 0; padding-left: 1.2rem; }
|
|
184
|
+
.badge-up { color: #1a7f37; font-weight: bold; } .badge-down { color: #cf222e; font-weight: bold; }
|
|
185
|
+
</style>
|
|
186
|
+
</head>
|
|
187
|
+
<body>
|
|
188
|
+
<h1>flow-rpa 状态</h1>
|
|
189
|
+
<div class="card">
|
|
190
|
+
<strong>浏览器</strong>:<span class="${browser.connected ? 'badge-up' : 'badge-down'}">${browser.connected ? '运行中' : '未连接'}</span>
|
|
191
|
+
<span style="color:#888">(端口 ${browser.port})</span>
|
|
192
|
+
<div style="margin-top:.4rem"><strong>会话轮换</strong>:<span class="${s.sessionRotation.enabled ? 'badge-up' : 'badge-down'}">${s.sessionRotation.enabled ? '已启用' : '未启用'}</span> <span style="color:#888">${esc(s.sessionRotation.detail)}</span></div>
|
|
193
|
+
</div>
|
|
194
|
+
<div class="card">
|
|
195
|
+
<strong>兜底率</strong>:<span class="${s.fallbacks.rate === 0 ? 'badge-up' : 'badge-down'}">${(s.fallbacks.rate * 100).toFixed(0)}%</span>
|
|
196
|
+
<span style="color:#888">${esc(s.fallbacks.detail)}</span>
|
|
197
|
+
</div>
|
|
198
|
+
<div class="card">
|
|
199
|
+
<strong>流程列表</strong>(${s.flows.length})
|
|
200
|
+
<ul>${flowItems || '<li>(无流程)</li>'}</ul>
|
|
201
|
+
</div>
|
|
202
|
+
${warnBlock}
|
|
203
|
+
<div class="card">
|
|
204
|
+
<strong>最近运行</strong>(${runs.length})
|
|
205
|
+
<table>
|
|
206
|
+
<tr><th>runId</th><th>flowId</th><th>status</th><th>startedAt</th><th>error</th></tr>
|
|
207
|
+
${rows || '<tr><td colspan="5">(无运行记录)</td></tr>'}
|
|
208
|
+
</table>
|
|
209
|
+
</div>
|
|
210
|
+
<div class="card" style="color:#888;font-size:.8rem">
|
|
211
|
+
只读状态页:无编辑/执行/管理动作。数据来源 data/(runs/、flows/)。
|
|
212
|
+
</div>
|
|
213
|
+
</body>
|
|
214
|
+
</html>
|
|
139
215
|
`;
|
|
140
216
|
}
|
|
141
217
|
//# sourceMappingURL=status.js.map
|
package/dist/status.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qing3a/flow-rpa-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"engines": { "node": ">=20" },
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "flow-rpa 应用壳(原 app crate):MCP server + CLI",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"typecheck": "tsc --noEmit"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@qing3a/flow-rpa-engine": "^0.
|
|
29
|
+
"@qing3a/flow-rpa-engine": "^0.7.0",
|
|
30
30
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
31
31
|
"zod": "^4.4.3"
|
|
32
32
|
}
|
package/skill/flow-rpa-engine.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
<!-- 由 docs/SKILL-ENGINE.md 构建生成(M11c D-K1
|
|
1
|
+
<!-- 由 docs/SKILL-ENGINE.md 构建生成(M11c D-K1 + P1 剥离来源注记),勿手改;改 docs 真源后重跑 pnpm build -->
|
|
2
2
|
# 引擎级 skill:flow-rpa 本地 RPA 引擎(契约版)
|
|
3
3
|
|
|
4
|
-
>
|
|
5
|
-
> 用途:挂载在 npm 包内作为「引擎级 skill
|
|
4
|
+
> 状态:本文件与 @qing3a/flow-rpa-app 包版本绑定(0.6.x),是引擎级 skill 的唯一真源;构建时同步为包内 `skill/flow-rpa-engine.md`。
|
|
5
|
+
> 用途:挂载在 npm 包内作为「引擎级 skill」的唯一真源,安装时同步/链接到 Agent 平台的 skills 目录。
|
|
6
6
|
> 分工:本 skill 只讲「怎么调引擎」;业务编排(何时调、调哪个流程)在业务级 skill(外部/平台分发)。
|
|
7
7
|
|
|
8
8
|
---
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
### 0.1 检测是否已安装
|
|
15
15
|
|
|
16
16
|
- 先试 `flow-app doctor`(或 MCP `get_status`):**命令不存在(command not found)或连不上 → 引擎未安装/未启动**
|
|
17
|
-
- `flow-app doctor`
|
|
18
|
-
- ⚠️
|
|
17
|
+
- `flow-app doctor` 体检(目录/流程解析/Edge/锁),**三级口径**:✅ 通过 / ⚠️ 可忽略 / ❌ 阻断(仅 ❌ 退出码非 0)
|
|
18
|
+
- ⚠️ 可忽略项:空流程(正常初始态,先获取流程包)、Edge 未显式配置(外部 9222 可复用)
|
|
19
19
|
- ❌ 阻断项:目录缺失、锁被另一存活实例持有(先退出该实例;死锁用 `flow-app unlock` 清理)
|
|
20
20
|
|
|
21
21
|
### 0.2 安装序列(依次执行;Windows PowerShell 环境默认,命令用 PowerShell 写法)
|
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
- **装完先验证**:`flow-app --version` 应输出当前版本(若 PATH 有多套 Node/npm 运行时,可能装到与读取不同的目录——以 `--version` 实际输出为准)
|
|
26
26
|
2. `flow-app init`(创建数据/流程目录 + 4 步引导)
|
|
27
27
|
3. `flow-app doctor --edge "<Edge 路径>"`(体检;可先单独验 Edge 项)
|
|
28
|
-
- **必须通过(无 ❌)**:flows/data
|
|
28
|
+
- **必须通过(无 ❌)**:flows/data 目录、单实例锁
|
|
29
|
+
- **Edge 三态**(doctor 与真实运行配置同源,0.6.6 起):`✅ 显式配置 --edge` / `✅ 自动探测到(打印探测路径)` / `⚠️ 未配置且未探测到`;只有第三态才需要处理,且它会明写「本次 run 不会做会话轮换」
|
|
30
|
+
- **会话轮换**项随 Edge 三态给出:`✅ 已启用(…)` 或 `⚠️ 未启用(原因)`——未启用时本次 run **不会**在 run 前重启浏览器会话(缺 Edge 可执行文件时无法轮换)
|
|
31
|
+
- Edge 的 ⚠️ 不阻断(外部已开 9222 调试实例时仍可跑;代价是没有会话轮换)
|
|
29
32
|
4. 启动常驻服务(端口可改;`--edge` 必填:从零环境无 9222 调试实例时引擎靠它拉起专用浏览器)
|
|
30
33
|
- PowerShell 写法(空格/括号路径不被拆坏):
|
|
31
34
|
```powershell
|
|
@@ -44,19 +47,21 @@
|
|
|
44
47
|
→ 浏览器弹出(不跑流程)→ 登录目标平台 → **Ctrl-C 关命令(释放单实例锁;浏览器保持运行)** → 再启动引擎跑流程
|
|
45
48
|
→ ⚠️ **锁 UX**:open-browser 与引擎服务互斥(同一把锁)——用 open-browser 时**先 Ctrl-C 退出再启动服务**;
|
|
46
49
|
若服务已在跑(持锁),open-browser 会报「另一个实例运行」——先停服务
|
|
47
|
-
- **方案 B(懒加载弹窗)**:直接跑流程——首次 `run_flow` 需要开页面时才弹出浏览器并导航到目标站 → 在弹窗里登录一次 → **流程进入 paused
|
|
50
|
+
- **方案 B(懒加载弹窗)**:直接跑流程——首次 `run_flow` 需要开页面时才弹出浏览器并导航到目标站 → 在弹窗里登录一次 → **流程进入 paused(人工接管),登录完成后调 `resume_run(runId)` 从暂停点继续**(不重跑已完成步骤)
|
|
48
51
|
- **用户日常浏览器登录 ≠ 引擎已登录**(profile 隔离);未登录时引擎报「未登录:引擎已暂停…resume_run」(不再误报误点),登录后 resume_run 继续即可
|
|
49
52
|
- ⚠️ **杀进程注意**:强杀引擎服务不会带走其拉起的 Edge(detached)——残留 9222 实例会占 profile 锁,下次启动前可 `flow-app unlock` 或先清 9222 Edge
|
|
50
53
|
|
|
51
|
-
### 0.4
|
|
54
|
+
### 0.4 会话轮换与冷却(防封节奏)
|
|
52
55
|
|
|
53
|
-
-
|
|
56
|
+
- **会话轮换(引擎自动,每次 run 前轮换)**:每次 run 开始前引擎重启浏览器会话(同 profile,登录态保留)——首 run 不复用 open-browser/上次旧会话;实证新会话点击恢复 3-5s
|
|
57
|
+
- **前提:Edge 可执行文件可用**(显式 `--edge` 或系统默认路径探测到)。没有 Edge → 会话轮换**不会发生**(run 日志会打「会话轮换:未启用(原因:…)」、doctor 与 `get_status.sessionRotation` 同样可见)——此时点击慢/被节流时不要反复重试,先补齐 Edge 或按冷却等待
|
|
54
58
|
- **同站冷却 ≥2h 降级为辅助**:会话轮换后点击正常(3-5s),冷却主要用于平台关注度管理
|
|
55
59
|
- 冷却不足时点击动作 60-500s/次、run 时长显著拉长——**属平台节奏,非引擎故障**;不要重试、不要绕过同站闸门
|
|
60
|
+
- **翻页护栏(0.6.1 起,引擎强制)**:翻页步骤(`next_page`)页间引擎固定 15~25 秒随机节拍(流程 `paceMs` 只能调高);单步上限 20 页、单次 run 上限 30 页,超限直接失败(提示拆分步骤);翻页 ≥3 页的 run 结束后记**同站 150~300 秒随机深度翻页冷却**,下一个关键词的 run 会被闸门挡回「还需等待」。**翻页多 = run 慢 = 正常**,不要为提速改流程参数(低于下限会被抬回,改也没用)
|
|
56
61
|
|
|
57
62
|
### 0.5 自举完成(正常初始态)
|
|
58
63
|
|
|
59
|
-
- 用 `get_status`
|
|
64
|
+
- 用 `get_status` 预检(浏览器连接/队列/锁)→ 进入下文「典型调用序列」
|
|
60
65
|
- **正常初始态**:未跑流程前 `browser.connected=false`、`flows=[]`(或仅流程包内的)均为正常——
|
|
61
66
|
浏览器懒加载(首次 run_flow 才弹出),流程需放入 flows/(starter 内置 1 个;更多流程包由 owner 分发)
|
|
62
67
|
- **非 MCP 环境(脚本/CI/人工排查)**:CLI 直跑与 MCP 同源——`flow-app run <flowId> --vars '<JSON>'`(同步,退出码 done/paused=0、failed=1)、`flow-app resume <runId>`、`flow-app get <runId>`;闸门/队列/单实例锁语义与 MCP 完全一致,不可用于绕过防封节奏
|
|
@@ -65,31 +70,31 @@
|
|
|
65
70
|
|
|
66
71
|
## 一、这是什么
|
|
67
72
|
|
|
68
|
-
flow-rpa 是本地 RPA 执行引擎:它解释执行**流程定义**(JSON 数据),拟人化操作用户本地的浏览器(CDP
|
|
73
|
+
flow-rpa 是本地 RPA 执行引擎:它解释执行**流程定义**(JSON 数据),拟人化操作用户本地的浏览器(CDP),产生结构化执行记录并本地导出脱敏统计/观察包(观察包为本地工件,可经运营层传递)
|
|
69
74
|
|
|
70
75
|
- **流程是数据**:执行什么由 flow.json 决定,引擎不含业务逻辑。
|
|
71
76
|
- **接口是 MCP**:本引擎通过 MCP 工具被 Agent 调用(stdio 传输,进程由 Agent 拉起)。
|
|
72
77
|
- **流程级工具面**:只提供「跑流程/查结果/管建议」等流程级工具,**不提供**页面原子操作(点击/输入/截图等),也不提供业务工具。
|
|
73
78
|
|
|
74
|
-
## 二、工具面(12
|
|
79
|
+
## 二、工具面(12 个,签名冻结)
|
|
75
80
|
|
|
76
81
|
### 核心执行
|
|
77
82
|
|
|
78
83
|
| 工具 | 参数 | 返回 | 说明 |
|
|
79
84
|
|---|---|---|---|
|
|
80
|
-
| `get_status` | 无 | 引擎状态 JSON(版本/skill
|
|
81
|
-
| `list_flows` | 无 | 流程列表(id/name/version
|
|
82
|
-
| `run_flow` | `flowId`(必填)、`vars`(可选,如 `{"keyword":"算法工程师"}`)、`wait`(可选,默认 false)、`owner
|
|
83
|
-
| `get_run` | `runId`、`owner
|
|
84
|
-
| `cancel_run` | `runId` | 取消结果文案 |
|
|
85
|
-
| `resume_run` | `runId` | `{runId, status:'running'\|'done'\|'failed'\|'paused', error?, pauseGuidance?, precheckRejected?}` |
|
|
85
|
+
| `get_status` | 无 | 引擎状态 JSON(版本/skill 版本/浏览器连接/会话轮换状态/队列/锁/流程列表含来源标注/**兜底率**) | **调用流程前预检环境**:浏览器是否连接、队列是否忙、锁是否被占;`sessionRotation`(**只读字段,0.6.6 起**)说明本次是否会做 run 前会话轮换及依据的 Edge 路径;`flows[].origin`(**只读字段,0.6.7 起**)标注流程来源(见下);`fallbacks`(**只读字段,0.7.0 起**)为最近 20 次 run 的兜底率(`rate`/`events`/`kinds`,目标 0) |
|
|
86
|
+
| `list_flows` | 无 | 流程列表(id/name/version/inputs + **来源标注**) | **调用任何流程前先查这里**,不要硬编码流程 id(流程包会更新)。**0.6.7 起**每行带 `[来源: builtin \| builtin-modified \| local]` |
|
|
87
|
+
| `run_flow` | `flowId`(必填)、`vars`(可选,如 `{"keyword":"算法工程师"}`)、`wait`(可选,默认 false)、`owner`(可选,发起者标识,缺省 local) | `{runId, status:'running'\|'done'\|'failed'\|'paused', error?, origin?, gateKey?, gateKeySource?}` | **异步**:wait=false 立即返回 runId,用 `get_run` 轮询;流程含随机 15~70 秒同站间隔,同步等待会较久。**status=paused = 人工接管**(登录/验证码/确认弹窗命中),处理完调 `resume_run`。**0.6.7 起**附带 `origin`(流程来源)与 `gateKey`/`gateKeySource`(本次闸门锚点:`site:<host>` 站点维度 / `flow:<id>` 流程维度兜底)——只进工具返回,不落 run.json |
|
|
88
|
+
| `get_run` | `runId`、`owner`(可选,发起者过滤) | run.json 全量(每步 verify/耗时/失败类别/暂停信息) | 轮询直到 status 为 done/failed;**步骤级**看哪步失败;paused 时含 pausedAt/pauseReason/resumeStepIndex |
|
|
89
|
+
| `cancel_run` | `runId` | 取消结果文案 | 取消排队中的任务(状态置 cancelled);执行中任务暂不支持取消(页面层 abort 留待后续) |
|
|
90
|
+
| `resume_run` | `runId` | `{runId, status:'running'\|'done'\|'failed'\|'paused', error?, pauseGuidance?, precheckRejected?}` | 从暂停点继续(人工接管后)——不重跑已完成步骤;执行中/非 paused 拒绝;暂停超时(缺省 5 分钟)转 failed。login 暂停 resume 前会预检登录态——仍在登录区 → 拒绝(`precheckRejected:true`,run 保持 paused)并给 `pauseGuidance` 指引,不消耗执行轮次;预检不可判定(浏览器不可达)→ 放行走原路径 |
|
|
86
91
|
| `validate_flow` | `json` 或 `path` | `{valid, flowId?, error?}` | 校验流程定义 |
|
|
87
92
|
|
|
88
93
|
### 学习与建议
|
|
89
94
|
|
|
90
95
|
| 工具 | 参数 | 返回 | 说明 |
|
|
91
96
|
|---|---|---|---|
|
|
92
|
-
| `list_suggestions` | `flowId?` | 建议列表(来源 local
|
|
97
|
+
| `list_suggestions` | `flowId?` | 建议列表(来源 local、状态 pending/mature、样本数) | L2 学习产物;**未成熟建议必须人工确认后才能应用** |
|
|
93
98
|
| `apply_suggestion` | `suggestionId`、`confirm`(布尔) | 应用结果 | 结构改动铁律:pending 建议必须 confirm=true;mature 建议可自动 |
|
|
94
99
|
| `dismiss_suggestion` | `suggestionId` | 忽略结果 | 人工处置,不再展示 |
|
|
95
100
|
|
|
@@ -100,14 +105,14 @@ flow-rpa 是本地 RPA 执行引擎:它解释执行**流程定义**(JSON 数
|
|
|
100
105
|
| `export_observation` | 把一次执行导出为观察报告包(本地工件:outbox/ 下,可人工查看或经运营层传递) |
|
|
101
106
|
| `get_perf` | 端到端性能基准(质量门数据) |
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
**工具面角色裁剪**:启动参数 `--role hunter` 时工具面裁剪为 6 个
|
|
104
109
|
(`run_flow` / `get_run` / `list_suggestions` / `apply_suggestion` / `dismiss_suggestion` / `resume_run`)——流程执行与建议处置 +
|
|
105
|
-
人工接管恢复(登录暂停后必须能 resume);不暴露 get_status/list_flows(预检与流程发现)与运维工具;缺省 owner 角色暴露全部
|
|
110
|
+
人工接管恢复(登录暂停后必须能 resume);不暴露 get_status/list_flows(预检与流程发现)与运维工具;缺省 owner 角色暴露全部 12 个。
|
|
106
111
|
Agent 按自己被赋予的角色调用,超出裁剪集的工具会报「工具不存在」。
|
|
107
112
|
|
|
108
113
|
## 三、典型调用序列
|
|
109
114
|
```
|
|
110
|
-
0. get_status(可选) →
|
|
115
|
+
0. get_status(可选) → 预检环境:浏览器是否连接、队列是否忙、锁是否被占
|
|
111
116
|
(浏览器未连接 / 锁被其他实例持有 → 先处理环境再调流程,见「失败处理·环境类」)
|
|
112
117
|
1. list_flows → 确认流程 id + **inputs 参数声明**(每个流程要什么变量)
|
|
113
118
|
2. run_flow(flowId, vars) → {runId, status:"running"}
|
|
@@ -121,11 +126,15 @@ Agent 按自己被赋予的角色调用,超出裁剪集的工具会报「工
|
|
|
121
126
|
|
|
122
127
|
## 四、调用规则
|
|
123
128
|
|
|
124
|
-
|
|
129
|
+
**前置条件(独立 profile 优先)**
|
|
125
130
|
- **引擎默认使用专用浏览器(独立 profile)**:首次使用需人工登录一次(引擎会拉起自己的浏览器实例,登录目标平台后即可)——登录态与用户日常浏览器**隔离**,用户日常登录 ≠ 引擎已登录
|
|
126
131
|
- 可选能力:若用户主动以调试模式(9222)启动了浏览器,引擎可复用其登录态(不鼓励给日常浏览器开调试模式——同 profile 双进程有锁冲突风险)
|
|
127
|
-
- 同站节流:同一站点两次执行间隔**随机 15~70
|
|
128
|
-
-
|
|
132
|
+
- 同站节流:同一站点两次执行间隔**随机 15~70 秒**(引擎强制);间隔不足会被拒(返回需等待时间)——**不要自行绕过或频繁重试**
|
|
133
|
+
- **闸门锚点是「实际访问的站点」(0.6.7 / S2)**:节流 key 按 ① 运行时实际 host(该流程最近一次 run 起点所在页)→ ② 流程声明的**全部** `navigate` host 里**最后一个** → ③ `flow:<flowId>` 兜底 三级解析,**没有 `navigate` 步骤的流程照样受闸门约束**。自建流程若「手工导航后直接操作当前页」,可能第一次跑完就被挡(上一版这里完全不受节流)——**这是设计目标,不是故障**:等够间隔或换站点即可。被拒文案会点名闸门维度(`site:h.liepin.com:443(站点维度)` 或 `flow:<id>(流程维度兜底)`)
|
|
134
|
+
- **流程来源标注(0.6.7 / S6)**:`list_flows` / `get_status.flows[].origin` / `flow-app list` / `doctor` 会标注 `builtin`(内置原样)/ `builtin-modified`(**内置流程被本地改动过**)/ `local`(自建)。**看到 `builtin-modified` 只是告警不是阻断**——引擎照你的版本执行;自建流程不会因此受影响(没有基线就不校验)。想回到内置原版:从流程包重新取一份(`flow-app init` 对已存在的目录**不覆盖**,需手动替换或删除该目录后重跑 init)
|
|
135
|
+
- **深度翻页冷却**:含翻页步骤的 run(翻页 ≥3 页)结束后,同站下一次执行会被闸门再挡 **150~300 秒随机**(关键词之间的冷却);被拒文案会写明「其中深度翻页冷却…」。**这是防封护栏,不是故障**——等够时间或换下一个关键词
|
|
136
|
+
- **冷却节奏**:目标站点对自动化访问有**交互响应节流**——同站冷却不足(<2h)时点击动作 60-500s/次(run 时长显著拉长);冷却 ≥5h 后点击恢复 3-6s。跑真实流程前**同站冷却 ≥2h 优先**(点击正常且反封安全);冷却不足时预计 run 时长大幅增加,属平台节奏而非引擎故障
|
|
137
|
+
- **动作最小间隔(0.7.0 / S3,引擎强制)**:同一个 run 内两个**交互动作**(click / input / extract / next_page)之间至少间隔**随机 3~8 秒**(每次动作重新取值,不是固定停顿)。**流程只能放慢**:流程里显式写的 `wait` 计入已耗时间——写得更长就以流程为准,写得更短或不写由引擎补足到下限;**没有任何流程参数能把它调快**。日志会打一行 `[flow-rpa] 动作间隔: X.Xs`。`navigate` 之后的第一个动作、`finalize` 这类机械步骤不受约束(不会拖慢导航→动作的正常衔接)。**注意**:不要用固定停顿去「调慢」流程(固定值本身是机器指纹,见 `docs/ANTI-BAN-PRINCIPLES.md` §一);需要更慢请用 `wait coolMs` 区间。
|
|
129
138
|
|
|
130
139
|
**参数组装**
|
|
131
140
|
- `vars` 按 `list_flows` 返回的 `inputs` 声明组装(`{name, required, description}`);值为字符串
|
|
@@ -148,17 +157,44 @@ Agent 按自己被赋予的角色调用,超出裁剪集的工具会报「工
|
|
|
148
157
|
- extract 步骤:`field`(字段标签)、`count`(命中数)、`saved`(是否落盘本地明细)
|
|
149
158
|
- `selectorHit`:实际生效的选择器(页面改版排查用)
|
|
150
159
|
- paused 记录额外字段:`pausedAt`(暂停时刻)、`pauseReason`(login/env_check/paused)、`resumeStepIndex`(resume 起点)
|
|
151
|
-
-
|
|
152
|
-
-
|
|
160
|
+
- **薪资加密**:BOSS 薪资为字体加密字符(PUA 私用区 `\uE000-\uF8FF`,如 `\uE02E\uE02F-\uE02E\uF9K` 渲染为 "13-16K")——提取内容**保留原始加密字符**(不再删除),步骤记录 `salaryEncrypted: true` 标注「薪资加密」;**不要当作乱码/提取失败**。
|
|
161
|
+
- **消费口径**:引擎**不解码** PUA 字体(解码映射在防封红线内不做);需要真实薪资时,**在引擎浏览器中实时读取**——用引擎浏览器当前页面的薪资元素查看真实数值,或引导用户人工查看登录态下的薪资显示(get_run 之外,引擎不提供解码)。
|
|
153
162
|
- **报告口径**:报告/表格中薪资统一标注「薪资加密 · 登录后可见」,**不填数值**(不做解码、不填乱码猜测值)。
|
|
154
|
-
-
|
|
155
|
-
- **React
|
|
156
|
-
-
|
|
163
|
+
- **标题过滤**:click 步骤可用 `matchText: "{keyword}"`(region 寻址时跳过标题不含关键词的卡片——BOSS 搜索混入推荐位场景);表达式引擎内部为 `region:<标签> >> [match:<文本>] >> <n>`,index 作用于过滤后集合,凑够 limit 或列表耗尽自然终止
|
|
164
|
+
- **React 合成事件兜底 `reactFallback`(默认禁用,0.7.0 / S4)**:click 步骤可配 `reactFallback: true`——CDP 拟人点击(首选)后,若目标元素(或所属组件祖先,≤5 层)存在 React `onClick` 事件属性(`__reactProps$` / `__reactEventHandlers$` / `__reactInternalInstance$` / `__reactFiber$` 等多版本名),引擎构造合成事件对象调用之。**这是 B 类非拟人捷径(不是拟人回退)**:产生人类不可能产生的信号,**默认禁用**——只有流程里**显式**写 `"reactFallback": true`(**布尔** true;字符串 `"true"` / 数字 1 等真值形态一律不开)才启用,且**每次命中都会计入兜底率**(见下条)。开启前必须在流程里/交付说明里写明理由,能不用就不用。适用:部分形态按钮(猎聘 hash 路由搜索按钮等)对 CDP 合成鼠标事件不响应 React 事件委托;zhipin/antd 场景 CDP 点击即可触发(无需此参数)。**兜底非首选**:拟人 CDP 点击始终优先。安全语义:只调用目标元素自身链的 `onClick`,不触发其它元素事件(不点危险区)。**历史(已消除)**:同族的「input 合成补派发」已于 0.6.7 删除(真实浏览器复验:`Input.insertText` 本就原生派发 `isTrusted:true` 的 `input`,合成补派发无条件多余)。
|
|
165
|
+
- **兜底率(0.7.0 / S4)**:`get_status.fallbacks`(`/status.json` 与只读状态页同一数据源)给出最近 20 次 run 的 `rate` / `events` / `runsWithFallback` / `kinds`,文案含「**目标 0**」。数据源是旁路台账 `data/behavior/fallback-events.jsonl`(每个含兜底的 run 一行;**不写 run.json**,运行记录格式不变)。**非拟人兜底 = 0 是目标**:>0 必须能解释(哪个流程哪一步为什么必须要),能量则消。
|
|
166
|
+
- **猎聘简历详情模式 = 列表逐行提取(v4)**:liepin_search_resumes v4 起不再逐份点开详情——猎聘找简历新版点卡片=沟通弹窗,简历详情唯一入口 `showresumedetail` 为新标签打开,与引擎「新标签误点检测」冲突(后者是防误点外链安全语义,无开关)。v4 采用 **loop 逐页提取**:`loop(maxIterations="{pages}", maxCandidates=30)` 包住 `extract_rows(tr[data-tlg-scm], field=resumeRows)` + `extract_ids(tr[data-tlg-scm] input[name=res_id_encode], attr=value, field=resIdList)` + `next_page(max=1, stopAtEnd=true)`;nav URL 为 `h.liepin.com/search/getConditionItem`。**新标签误点检测 = 已知边界**:站点详情若强制新标签打开,走流程层适配(列表/详情 URL 提取),不给引擎开单站开关。**v4 移除 `limit` 输入**——loop 上限取 `min(maxIterations, maxCandidates, vars.limit)`,留着 limit 会把页数悄悄截掉;页数只由 `pages` 控。
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
### 猎聘批量采集(v4,2026-09-10)
|
|
169
|
+
|
|
170
|
+
**批量 = 每关键词一次 `run_flow`**(不要在一次 run 里塞多关键词)。每个关键词独立 run,run 之间引擎自动记账同站冷却。
|
|
171
|
+
|
|
172
|
+
**页数由 `pages` 显式控制;`pages` 缺省 = 1(只取首页)**:**要批量就显式传** `vars.pages`(如 `{"keyword":"产品经理","pages":"6"}`)。缺省 1 是防封默认——一次 `{keyword}` 的 run 默认只取首页,不会静默翻 30 页;上限仍由引擎护栏兜(单步 ≤20 页、单 run ≤30 页)。
|
|
173
|
+
(历史纠正:0.6.4 的 flow.json/SKILL 曾声明「缺省 1」,但当时引擎**没有** inputs 默认值机制——缺失变量插值为空串 → `maxIterations` 解析失败回落 50 → 被 `maxCandidates=30` 截到 30,实际缺省翻 30 页。0.6.5 补齐 `inputs[].default` 后声明与行为一致,见 RELEASE-0.6.5。)
|
|
174
|
+
|
|
175
|
+
**`pages=N` 会多访问第 N+1 页**(已知行为):loop 每轮 body 末尾都会翻一次页,故 `pages=1` 时也会**访问**第 2 页——**只访问、不提取**(不产生落盘产物)。消费方按落盘份数计页,别用「访问过几页」计。
|
|
176
|
+
|
|
177
|
+
**引擎护栏自动生效**(无需流程额外配置):
|
|
178
|
+
- 页间拟人节拍:15~25 秒/页随机(`next_page` 引擎强制下限,流程 `paceMs` 只能调高)
|
|
179
|
+
- **动作最小间隔**:同 run 内交互动作之间 3~8 秒随机(`click` / `input` / `extract` / `next_page`;流程 `wait` 只能放慢,详见「四、调用规则」)
|
|
180
|
+
- 单步翻页上限:≤20 页(`next_page.max` 超限 fail-loud)
|
|
181
|
+
- 单 run 翻页总量上限:≤30 页(跨步骤累计,超限 fail-loud)
|
|
182
|
+
- 翻页 ≥3 页的 run 结束后:同站深度翻页冷却 150~300 秒随机(下一个关键词的 run 至少等这么久)
|
|
183
|
+
|
|
184
|
+
**预期时长**(设计目标,不是卡顿;0.7.0 起含动作间隔):
|
|
185
|
+
- 30 页约 13~19 分钟(含页间节拍 15~25s/页 + 每页 3 个交互动作的 3~8s 间隔)
|
|
186
|
+
- 6 个关键词约 75~120 分钟(含轮间冷却)
|
|
187
|
+
|
|
188
|
+
**逐页产物文件名规则**:`extractSink` 按调用序号落盘,第 k 页 = `extract_rows_00k.json` + `extract_ids_00k.json`(同 stepId 多次提取不覆盖)。行级数据 `resumeRows` 与 `resIdList` **按行号一一对应**(同为 `tr[data-tlg-scm]` 文档序),消费方按行号配对。
|
|
189
|
+
|
|
190
|
+
**列表页无薪资**:猎聘列表页不显示薪资,需详情页另取(本流程不含详情页逐份打开——新标签冲突为已知边界)。
|
|
191
|
+
|
|
192
|
+
**click_search 的 reactFallback**:v4 保持 `true`(**去留未决**——本项按 T038 远程真机复验结论决定,owner 已定「暂不与远程用户交互」[其账号在风控观察期],故 0.7.0 **不擅自移除**;复验通过则从内置流程移除,回归纯拟人。登记位置:`docs/RELEASE-0.7.0.md` + `docs/ANTI-BAN-PRINCIPLES.md` §二)。
|
|
193
|
+
|
|
194
|
+
**任务背景理解与结果提示**(文档/惯例级,引擎零改动)
|
|
159
195
|
- **run 前:把任务背景转化为可执行输入**(转化与判断均在 **Agent 决策层**,引擎零 LLM 分层;本小节只约定惯例)
|
|
160
196
|
- 收集任务背景:本地 JD 文本 / 线上收藏职位链接 / 用户口述要求(如「找珠海产品经理,运营岗不要」)
|
|
161
|
-
- 转化为 `vars`:`keyword
|
|
197
|
+
- 转化为 `vars`:`keyword`(标题关键词);城市(`city`——仅 boss 流程使用,liepin v4 **未使用**城市筛选,走平台默认);页数(`pages`——liepin v4,缺省 1);数量上限(`limit`——boss 流程;liepin v4 已移除)
|
|
162
198
|
- 派生过滤:`matchText` 过滤词 = 从背景提炼的「必含」词(如「产品经理」);**排除词** = 背景里「不要」的方向(如「运营」「外包」)——用排除词前先确认当前流程支持(matchText 为包含式,排除用否定词需业务侧同意)并写明理由
|
|
163
199
|
- **转化依据留痕**:把「背景句 → 转化出的 input/过滤词」的对应关系写进交付说明(如「收藏 JD 标题含『产品经理』→ keyword=产品经理、matchText=产品经理」),便于复核
|
|
164
200
|
- **run 后:读 `get_run` 输出显性提示**(固定模板,每次交付都带):
|
|
@@ -172,15 +208,19 @@ Agent 按自己被赋予的角色调用,超出裁剪集的工具会报「工
|
|
|
172
208
|
- ② 的 X 只给量级(如「约 3-5 分钟」),不给精确值(平台冷却节奏会使实际时长波动)
|
|
173
209
|
- **每次都要问「是否重跑」**——把决策权交回用户/业务侧,不自行反复跑
|
|
174
210
|
- **交付报告惯例**:任何 run 的汇报固定含**两行**——「质量信号」(提取数 / matchText 命中 / 耗时 / 异常)与「建议下一步」(排除词 or 增加 limit or 直接交付)
|
|
175
|
-
- **边界**:引擎代码零改动;不新增 JD 提取流程;不动
|
|
211
|
+
- **边界**:引擎代码零改动;不新增 JD 提取流程;不动 12 工具签名;转化/判断均在 Agent 决策层(不违反引擎零 LLM 分层)
|
|
176
212
|
|
|
177
213
|
**失败处理**
|
|
178
214
|
- `索引越界`:循环内列表耗尽,**正常终止不是错误**,流程仍为 done
|
|
179
|
-
- 验证码 /
|
|
215
|
+
- 验证码 / 环境验证 / **风险页**:**立即暂停(status=paused),告知用户人工处理**,绝不自动重试(执行树级风险事件;处理完 resume_run 继续)
|
|
216
|
+
- **检测是引擎不变量(0.6.6 起)**:引擎内置**风险页基线清单**(含 `captcha`、`verify`、`security-check`、`/safe/`、`risk-control`、`puzzle`、`id-verify` 等路径形态,只匹配 URL 路径、不匹配正文/查询串),**流程不写 `envCheck` 也一样生效**;流程的 `envCheck.verifyUrls`/`dialogs` 只是**追加**(只能加严)
|
|
217
|
+
- **导航后即查**:`navigate` 之后(含复用已打开标签)立刻复查一次——「只导航不动作」的流程同样会被拦住
|
|
218
|
+
- **用户/Agent 侧动作**:**立即停手**——不要刷新、不要重试、不要让脚本自动过验证码(反复重试是最糟的动作);引导用户在引擎浏览器里人工完成验证,再决定是否 `resume_run`
|
|
219
|
+
- run 的暂停文案会指出命中来源(基线条目名 / 流程 verifyUrl / 风险弹窗选择器),据此判断是该停手等待还是该改流程
|
|
180
220
|
- **环境类失败(首次使用必踩,重点)**:
|
|
181
221
|
- `浏览器未就绪` / 9222 连不上 → 引擎浏览器没启动或调试端口不可用 → **告知用户检查/启动引擎浏览器**,不要重试流程(调用前可用 `get_status` 预检 `browser.connected`)
|
|
182
222
|
- 登录页 / 未登录 → **引擎已暂停(status=paused)** → **告知用户在引擎浏览器里登录目标平台,登录完成后调 `resume_run(runId)` 从暂停点继续**(不重跑已完成步骤;返回含 `pauseGuidance` 结构化指引)
|
|
183
|
-
-
|
|
223
|
+
- login 暂停的 resume 前引擎会预检登录态——仍在登录区 → `precheckRejected:true` 拒绝本次 resume(不消耗执行轮次),把 `pauseGuidance` 转告用户即可;**绝不反复尝试 resume,绝不自动重试**
|
|
184
224
|
- 引擎浏览器与用户日常浏览器是隔离的 profile——用户日常登录 ≠ 引擎已登录
|
|
185
225
|
- 其他失败:看失败步骤与类别;重试前先确认前置条件(登录态/节流),**重试最多一次**
|
|
186
226
|
|
|
@@ -190,7 +230,7 @@ Agent 按自己被赋予的角色调用,超出裁剪集的工具会报「工
|
|
|
190
230
|
- ❌ 不自动应用 pending 建议(人工确认铁律;除非用户明确同意,不得传 confirm:true)
|
|
191
231
|
- ❌ 不把业务判断写成引擎工具调用链的循环(业务编排在业务级 skill)
|
|
192
232
|
- ❌ 不使用运维工具(export_observation 等)除非你是 owner 且明确需要
|
|
193
|
-
- ❌ 不要求/不尝试让引擎附着用户日常浏览器或其活动 tab
|
|
233
|
+
- ❌ 不要求/不尝试让引擎附着用户日常浏览器或其活动 tab(**执行隔离不变量**:流程只在引擎专用浏览器实例内执行;用户日常登录 ≠ 引擎已登录)
|
|
194
234
|
|
|
195
235
|
## 五、与业务级 skill 的关系
|
|
196
236
|
|
|
@@ -198,7 +238,84 @@ Agent 按自己被赋予的角色调用,超出裁剪集的工具会报「工
|
|
|
198
238
|
- 业务级 skill(如「猎头工作流」):何时调、调哪个流程、业务规则(外部维护或平台分发)
|
|
199
239
|
- 冲突时:业务级 skill 管「做什么」,本 skill 管「怎么调」
|
|
200
240
|
|
|
201
|
-
##
|
|
241
|
+
## 六、自研 CDP / 调试:复用引擎注入的 __rpaDom
|
|
242
|
+
|
|
243
|
+
> 适用:在**引擎浏览器页面**上自研 CDP(绕过引擎动作通道、直接 evaluate 裸页面)的 agent / 调试者。
|
|
244
|
+
> 引擎动作通道(MCP 流程)仍是首选;本节只保证「自研时不要重造引擎已解决的轮子」。
|
|
245
|
+
|
|
246
|
+
### 6.1 事实:页面已注入 window.__rpaDom(幂等,随引擎会话存在)
|
|
247
|
+
|
|
248
|
+
- 引擎每个活动页面**已注入** `window.__rpaDom`(DOM 辅助,由引擎注入,幂等——页面已有则跳过,且版本感知升级);
|
|
249
|
+
- 因此**在引擎浏览器页面上自研 CDP 时,`window.__rpaDom` 直接可用**(同一页面上下文,无需再自行注入);
|
|
250
|
+
- **铁律:自研/调试时优先复用 `__rpaDom` 现成函数,禁止自行发明 React 交互**——自行发明 = 重踩引擎已解决的坑(实测单坑技术探索 15-20 分钟)。
|
|
251
|
+
|
|
252
|
+
### 6.2 可复用函数清单(签名与对应场景)
|
|
253
|
+
|
|
254
|
+
| 函数 | 签名(selector=CSS 或 text:xxx) | 解决场景 |
|
|
255
|
+
|---|---|---|
|
|
256
|
+
| `reactClick(sel, idx)` | `(selector, index?) → {ok:boolean, reason?/tag?/cls?}` | **React onClick 不触发**——CDP 合成鼠标事件对 React 事件委托无效(如猎聘 hash 路由搜索按钮);直接调用目标元素(或所属组件祖先,≤5 层)的 React `onClick`,成功 `{ok:true,...}`,失败带 `reason` |
|
|
257
|
+
| `focusInput(sel, idx)` | `(selector, index?) → boolean` | **React 受控输入**——聚焦输入框(select+focus);受控组件还需「输入 + 补派发 input 事件」让 React onChange 同步 state(见 6.3 示例②) |
|
|
258
|
+
| `clickableRects(sel)` | `(selector) → 矩形数组` | **下拉/遮挡/危险子元素识别**——目标元素的可点击矩形,用于判断遮挡、识别会误跳转的危险子元素 |
|
|
259
|
+
| `findDialogClose(sel, idx)` | `(selector, index?)` | 对话框/弹窗关闭按钮定位 |
|
|
260
|
+
| `safePointIn(sel, idx)` | `(selector, index?) → {x,y} \| null` | **安全点击点**——大容器卡片中心常落在公司/职位链接上(直接点会误跳转);返回避开交互子元素(a/button/[onclick] 等)的网格采样点 |
|
|
261
|
+
| `pageText()` | `() → string` | 页面文本(含 input/textarea 值),调试与断言用 |
|
|
262
|
+
|
|
263
|
+
(另有 `getBySels`(选择器定位,CSS 或 `text:xxx`)、`scrollIntoView` / `scrollIntoViewRhythm`(拟人滚动节奏)、`textOf`、`numberedElements`、`describeHit`、`hitTestAt`、`identifyRegion`、`setRegionRules` 等,均可用。)
|
|
264
|
+
|
|
265
|
+
### 6.3 最小复用示例(自研 CDP evaluate)
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
// 自研 CDP:在引擎页面上下文直接 evaluate(示例形态,非引擎 API)
|
|
269
|
+
// ① React onClick 不触发 → 直接调 __rpaDom.reactClick(成功 {ok:true,...},失败 {ok:false, reason})
|
|
270
|
+
const r = await client.send('Runtime.evaluate', {
|
|
271
|
+
expression: `window.__rpaDom.reactClick('.search-btn', 0)`,
|
|
272
|
+
returnByValue: true,
|
|
273
|
+
})
|
|
274
|
+
|
|
275
|
+
// ② React 受控输入:聚焦 + 真实键盘输入 + 补派发 input 事件(React onChange 靠 input 事件同步 state)
|
|
276
|
+
await client.send('Runtime.evaluate', {
|
|
277
|
+
expression: `window.__rpaDom.focusInput('.search-input', 0)`,
|
|
278
|
+
})
|
|
279
|
+
// …键盘输入后,对当前焦点元素补派发一次 input 事件:
|
|
280
|
+
await client.send('Runtime.evaluate', {
|
|
281
|
+
expression: `(() => { const el = document.activeElement; if (el && typeof el.dispatchEvent === 'function') el.dispatchEvent(new Event('input', { bubbles: true })); })()`,
|
|
282
|
+
})
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 6.4 非契约声明(重要)
|
|
286
|
+
|
|
287
|
+
- `window.__rpaDom` 是**引擎内部实现**:函数签名/存在性**可能随引擎版本变化**(当前 `_domVersion` 可探测,注入为版本感知);
|
|
288
|
+
- **优先走引擎流程动作**(MCP 流程 / 引擎动作通道)——`__rpaDom` **仅自研/调试兜底**;
|
|
289
|
+
- 本节**不构成对外契约**:不承诺 `__rpaDom` 跨版本稳定,不承诺引擎为自研用法适配;
|
|
290
|
+
- 自研中探索出的**新交互应回写流程定义**(走流程包/建议通道),**不长期维护并行自研代码**。
|
|
291
|
+
- **本节的自研用法不在引擎护栏内**——护栏的生效边界见下节,动手前先读。
|
|
292
|
+
|
|
293
|
+
## 七、引擎护栏的生效边界(重要:这些保护只在「走引擎流程」时存在)
|
|
294
|
+
|
|
295
|
+
**先把边界说清:下面这些保护——拟人动作、同站节流、翻页节奏、验证码中止、掩码落盘、运行记录——全部长在引擎进程里,只有「引擎执行流程」这条路径才享有。**
|
|
296
|
+
|
|
297
|
+
### 7.1 引擎外自研脚本:零保护,出事自负
|
|
298
|
+
|
|
299
|
+
- 自己写脚本(Node / Python / 任何语言)连上引擎用的那个浏览器调试端口去操作页面,**引擎对此既无感知也无法约束**:节奏由脚本常量决定、没有 runId、没有验证码检测、没有落盘记录、没有同站冷却。
|
|
300
|
+
- **这不是配置问题,调参数补不回来**——要么把要做的事写成引擎流程(受全部护栏约束),要么由你自行承担平台风控后果。
|
|
301
|
+
- **本案复盘(真实事故,2026-09-10)**:远程用户以自写脚本直连引擎浏览器的调试端口操作猎聘,脚本常量写死「每页间隔 8~15 秒 + 每 5 页停 12 秒 + **关键词之间零冷却**」,连续跑 6 个关键词 ≈ **180 页 / 40 分钟不间断**,随后命中平台验证码页。
|
|
302
|
+
- **读法纠偏**:当时引擎日志里的「第 21 页」**不是页数阈值**,而是累积访问量到了——这类脚本没有冷却、没有节律扰动,**出事只是时间问题**,换个小页数阈值也躲不掉。
|
|
303
|
+
- 平台看到的是「固定周期 + 固定值 + 无冷却」三件事同时成立,这本身就是机器指纹(引擎侧对应纪律见 `docs/ANTI-BAN-PRINCIPLES.md` §一)。
|
|
304
|
+
|
|
305
|
+
### 7.2 不要与引擎共用同一个 9222 端口 / 同一个 profile 并行操作
|
|
306
|
+
|
|
307
|
+
- 引擎**每次 run 前会重启浏览器会话**(会话轮换,同 profile 保留登录态;重启后为新页面)。脚本与引擎并行 → **互相打断**:脚本刚打开的页面被引擎重启掉、脚本占着 profile 又让引擎连不上或连到错误页面。
|
|
308
|
+
- 要自己调试:**单独开一个浏览器实例**(另一个调试端口 + 另一个 profile 目录),或**只在引擎完全闲置时**临时用一下。**任何时候都不要两边同时操作同一个账号**(同账号并发本身就是风险信号)。
|
|
309
|
+
|
|
310
|
+
### 7.3 遇阻先查引擎能力,不要用「合成事件」替代拟人动作
|
|
311
|
+
|
|
312
|
+
- 页面交互失败时,先查本文档的「调用规则 / 失败处理」——**很多「点不动」问题引擎已解决**(典型:输入框联想下拉遮挡导致真实点击落空,引擎会先关下拉再点,不需要你另想办法)。
|
|
313
|
+
- **节奏也交给引擎**:同站节流、页间节拍(15~25s/页)、**交互动作最小间隔(3~8s/动作,0.7.0 起)**都是引擎强制的不变量,**不要在流程里再叠加固定停顿**去「保险」——固定值本身就是机器指纹(`docs/ANTI-BAN-PRINCIPLES.md` §一),要更慢用 `wait coolMs` 区间,引擎会取两者中更慢的那个。
|
|
314
|
+
- **禁止**以合成事件替代拟人动作:直调页面组件的 `onClick`、`element.click()`、手工派发 `input` / `change`。这些动作平台侧能看出不是人做的(无鼠标轨迹、事件对象残缺),等于自己给账号加风险指纹。流程里唯一的例外开关 `reactFallback` **默认禁用**(见「四、调用规则」),开启要写明理由,且会被计入兜底率(目标 0)。
|
|
315
|
+
- 引擎确实不支持的能力:**回写流程 / 提需求**,不要用「另写脚本绕过引擎」来解。
|
|
316
|
+
|
|
317
|
+
## 八、版本与同步
|
|
202
318
|
|
|
203
319
|
- 本 skill 与引擎版本绑定:npm 包升级 → 本文件更新 → 安装/启动时同步到 Agent skills 目录(带版本标注)
|
|
204
320
|
- 若 Agent 平台已有一份旧拷贝:以「引擎版本 + skill 内容哈希」判断是否需要覆盖
|
|
321
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"generatedAt": "2026-09-10T17:46:55.601Z",
|
|
4
|
+
"flows": {
|
|
5
|
+
"boss_search_jobs": {
|
|
6
|
+
"files": {
|
|
7
|
+
"behavior.rules.json": "b2482701a8e287914f28d0c1e3b685b1ae955b6607be38243a00e2ab0323446a",
|
|
8
|
+
"flow.json": "e1538b874a7db71c1d71f80af6f8300d5d98a8421c298917a7f82864891e60a1"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"liepin_search_resumes": {
|
|
12
|
+
"files": {
|
|
13
|
+
"behavior.rules.json": "8d61b6fbb7aef8fc1ece62bb877ac5d2954a4e345fb865f8afa1589ebed3b1d0",
|
|
14
|
+
"flow.json": "769c101a30bc86a28a4c922565115c413f76e300688b026cfe4e0e3d8dc91113"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|