Cindy 复用浏览器登录态跑流程,需要先回答的问题通常是「Cindy 能不能跑完」,但真正要先定下来的是「这条流程里有几个动作一旦发生就收不回来」。判断边界建议先看动作是否可逆,不要先看步骤多少或页面复杂度。可行的顺序是:把任务里会碰到的动作逐条列出来,按只读、写入、不可逆三档打标,把不可逆动作改成需要人确认的停点,最后用一条低风险任务验证停点确实拦得住动作。在跑测确认之前,这条流程建议仍按半自动处理,由人在关键一步接手。
适用场景是 Agent 带浏览器登录态跑后台或业务系统流程。操作动作是先列动作清单,再按可逆性分只读、写入、不可逆三档,不可逆档统一改成人工停点。验证方式是用一条低风险任务完整跑一遍,看停点处写请求是否真的没发出、任务状态是否停在待确认。风险边界是:读取类页面可以放手,写入类需要留痕或可回滚,提交、发布、付款、删除、授权这类动作必须人工确认后才继续。
把一个任务里会碰到的动作逐条列出来
动作清单建议按四列写全:步骤、页面地址、目标控件、触发前后的条件。写得含糊的地方通常就是容易出问题的地方,尤其是流程中途弹出的二次确认框:不少系统的发布按钮点下去之后还会再弹一层确认,Agent 如果把这层弹窗当成普通对话框自动点掉,停点就等于没有。
下面是一条内容后台发布流程的清单示例,页面路径按实际系统替换,控件描述写成能被脚本稳定定位的形式,不要只写「保存按钮」。
| 步骤 | 页面地址 | 控件 / 动作 | 触发前条件 | 触发后可见变化 |
|---|---|---|---|---|
| 1 打开文章列表 | GET /admin/articles | 无,直接访问 | 登录态有效 | 列表表格渲染完成 |
| 2 新建草稿 | 点击后跳 /admin/articles/new | 新建按钮 | 列表已加载 | URL 变化,出现编辑表单 |
| 3 填写标题与正文 | /admin/articles/new | 标题输入框、正文编辑器 | 表单为空 | 输入框有值,草稿未落库 |
| 4 保存草稿 | POST 保存接口 | 保存草稿按钮 | 标题非空 | 出现「已保存」提示,列表多一条草稿 |
| 5 预览 | /admin/articles/{id}/preview | 预览链接 | 草稿已存在 | 打开预览页,内容只对自己可见 |
| 6 发布 | POST 发布接口 | 发布按钮 + 二次确认弹窗 | 文章状态为草稿 | 状态变为已发布,对外可见 |
清单落到执行侧可以用一份步骤配置承载,把风险标签写在步骤上,方便后面统一拦截:
task: publish_article
steps:
- id: open_list
url: https://<host>/admin/articles
action: goto
expect: css=table.article-list
risk: read_only
- id: save_draft
action: click
target: css=button[data-action=save-draft]
expect: text=已保存
risk: write
- id: publish
action: click
target: css=button[data-action=publish]
expect: text=已发布
risk: irreversible
gate: human_confirm
按只读、写入、不可逆三档给动作打标
打标用三个问题就够:这次动作会不会改变服务端状态;改了能不能撤回;影响会不会外溢到外部可见、涉及金额或涉及权限。三问都是否,归只读;改变状态但能改回来,归写入;改变状态且撤不回,或者一发生就对外可见,归不可逆。
- 只读:翻页、筛选、打开详情、生成预览、导出到自己手里。确认方式是自动执行,保留页面快照或接口响应摘要;一旦跳到登录页、权限不足页,就停下等人处理登录态。
- 写入:保存草稿、改备注、改状态字段、上传附件。确认方式是自动执行但必须留痕:操作前后各取一次关键字段(列表行数、目标字段值),失败重试设上限,重试仍失败就停。
- 不可逆:发布、提交审核、付款、删除、授权、发送通知给外部。确认方式是硬停点,Cindy 只能准备到触发前一步,点击由人完成或由人明确放行。
对照例子最容易被混淆:读取订单列表并发起导出,属于只读,导出文件落在本地,不改变对方系统状态;提交一张表单、把订单备注从 A 改成 B,属于写入,改完还能再改回去;点发布、点确认付款、点删除按钮,属于不可逆。同一个 POST 请求既可能是保存草稿也可能是发布,所以风险标签建议写在步骤配置里,不要只靠 URL 正则批量匹配。
risk_rules:
read_only:
methods: [GET, HEAD]
patterns: ["*/list", "*/detail", "*/preview"]
write:
methods: [POST, PUT, PATCH]
patterns: ["*/draft", "*/save", "*/update"]
irreversible:
methods: [POST, DELETE]
patterns: ["*/publish", "*/submit", "*/pay", "*/delete", "*/grant"]
fallback: block
把不可逆动作改成需要人确认的停点
停点放在不可逆动作的最后一次点击之前,不是点击之后。Cindy 到这一步只做三件事:整理待确认信息(目标对象、即将执行的动作、会影响什么、当前页面 URL 和关键字段)、把任务状态置为待确认、停止推进。没有人确认,Cindy 不点那个按钮。
确认之后如何继续:确认信号要和 task_id 绑定,恢复时只执行停点之后的动作,不重放前面的步骤,否则会出现重复保存、多条草稿的情况。确认方式建议由人在页面上真实点一次,或者在确认渠道回一条带任务 ID 的指令;不建议让 Cindy 从「继续」这类自由文本里自己推断是否已获授权。
长时间没人确认时的处理:设超时,默认挂起而不是自动放行。超时后保留登录态、不主动关闭浏览器会话,写一条 pending_human 记录,任务由人接管继续或直接作废。
gate:
step_id: publish
mode: block
timeout: 15m
on_timeout: hold # hold 挂起 / abort 中止,不建议 auto_pass
require: explicit_confirm
给 Cindy 的约束可以直接写进任务提示词,重点是禁止绕路:
执行到 risk=irreversible 的步骤时,先停止点击并输出:
1) 即将执行的动作和目标对象
2) 该动作会改变什么、能否撤回
3) 当前页面 URL 和关键字段值
然后等待确认。未收到确认前,不要重试,不要换其他入口或换一个按钮完成同一个动作。
跑一条低风险任务验证停点是否真的生效
验证要选一条结果可清理的任务,例如针对一篇测试文章或测试订单走完整流程,不要拿真实对外内容做首次跑测。观察点集中在页面行为上:停点处的页面是否停在原 URL、有没有出现成功提示、列表里的状态有没有变化、浏览器网络记录里有没有发出 POST 或 DELETE 请求。
预期表现是:页面不跳转、无成功提示、目标对象状态不变、写请求没有发出、任务状态停在待确认;超时后也没有任何一步被自动补上。日志里应能看到一条明确的 gate 记录,例如:
ts=<time> task_id=<id> step=publish risk=irreversible gate=blocked
url_before=/admin/articles/123
url_after=/admin/articles/123
write_request=none
status=pending_human
不符合预期时,重点记录这几项,便于定位是分级错了还是停点位置错了:task_id、step_id、risk 标签及其来源(配置还是规则匹配)、gate 决策与决策时间、触发前后 URL、是否发出 POST/DELETE 及其目标路径、页面提示文本、重试次数、对应日志行。典型失效有两种:日志里出现 write_request=POST 而 url_after 没变,说明点击已经发生只是页面没刷新;或者二次确认弹窗被自动点掉,停点看似存在实际没拦。这两种情况都要回到动作清单,把停点往前提一步,然后重跑同一条低风险任务确认拦住为止。