安全模式¶
PHONE_AGENT_SAFETY_MODE 控制执行类动作(tap / long_press / type_text / launch_app)的门控。
四档¶
| 档位 | 行为 | 适用场景 |
|---|---|---|
wary(默认) |
风险调用不执行、不中断:工具返回预警(世界事实 + 选项),模型带 confirm_irreversible=true 重发才执行 |
有人看管的日常使用 |
hard |
风险调用中断,等待人工 approve / reject | 无人值守运行 |
reviewer |
wary + 第二模型对软候选做可逆性精排;精排故障时按预警处理 | 需要压低误报的场景 |
off |
执行动作不门控 | 受控环境跑批 |
任何档位下,ask_user 与 take_over 都会中断并等待人工输入。
wary 的预警不是人工付款门:它只把世界事实与选项交回模型,不阻塞、不召唤人工、不新增模型决策;
真正需要人工的只有 ask_user / take_over,以及 hard 档的审批中断。reviewer 档的精排模型不可用
时按 fail-closed 处理(当作风险预警),不会静默放行。
风险判定¶
安全门只覆盖四个执行工具:tap / long_press / type_text / launch_app。scroll、swipe、back、home、wait 与读屏类工具不参与判定。每个调用被判定为一个层级:none、recall(软候选)、reviewer(交由复核模型精排)、hard。launch_app 与 type_text 走各自的专属分支,其余工具的判定基于目标文本:
| 条件 | 层级 |
|---|---|
模型自申报 sensitive=true(或 dangerous=true;四个执行工具的公开 schema 只暴露 sensitive,该键当前没有公开路径可达) |
hard |
type_text 目标确为密码输入框(只看显式的 target_mark_id) |
hard |
type_text 内容命中凭据/验证码域(手机号/邮箱/订单号/密钥形态,或密码/验证码/登录/账户词表) |
hard |
launch_app 目标疑似支付/银行类敏感应用(词表含 bank / alipay / wallet / pay / 银行 / 支付宝 / 钱包 / 支付 / 转账等) |
软候选(可逆) |
| 承诺动词 + 不可逆对象共现(如“确认支付”“提交订单”“确认删除”;裸“删除”“支付方式”是软候选) | hard |
目标命中凭据/验证码敏感域(经 tap/long_press 目标) |
hard |
| 其余命中策略词表的软候选 | 软候选 |
软候选与预警不是一回事:wary / hard 档不对软候选预警,只有 hard 信号升级;软候选只在 reviewer 档由复核模型精排。launch_app 的敏感应用命中只算软候选——启动应用可逆;例外是模型自申报 sensitive=true,它在 launch_app 专属分支之前就先升级为 hard。
预警文案¶
预警回执(status 为 error)由世界事实与选项空间组成,形如:
⚠️ 已拦截(未执行):tap → 「确认支付」
世界事实:该操作疑似『不可逆提交』(如支付/转账/下单/删除等确认动作)。
选项:
1) 确认执行:带 confirm_irreversible=true 重新调用同一工具(其余参数不变)。
2) 放弃:改做其它操作或重新观测。
3) 交人工:调用 ask_user 询问,或 take_over 请求人工接管。
confirm_irreversible 参数存在于 tap / long_press / type_text / launch_app 四个执行工具上(swipe / scroll / back / home / wait 没有)。确认标记只在 wary / reviewer 的预警监听里先于分类检查:带 confirm_irreversible=true 重发的调用直接放行,不重新分类,因此自申报敏感 + 确认标记的组合也会执行。hard 档的审批中断直接分类、不读该标记,带确认重发仍会中断等待人工 approve / reject。预警目标文本的加工方式:type_text 与 target_description 先脱敏再截断(32 / 24 字),launch_app 目标只截断到 24 字;wary / reviewer 档拦截时 stdout 打一行非阻塞提示 [safety] <tool>: <reason> — 已拦截,等待模型确认。复核模型判为不可逆时,回执里的世界事实写「复核模型判定该操作『不可逆』」。
复核模型(reviewer 档)¶
reviewer 档只对软候选征询第二个模型:复核器只看工具名与被脱敏的目标文本摘要(最多 120 字),不读完整对话。判定可逆则放行,判为不可逆则按预警处理。本节的「复核模型 / 复核器」指这道安全精排;finish 两段式返回的「复核包」是另一件事——那是 v2/review.py 生成的世界镜像(见finish 验收)。
- 复核模型的来源依次为
PHONE_AGENT_SAFETY_REVIEWER_MODEL、models.json的roles.safety_reviewer.model、PHONE_AGENT_VERIFIER_MODEL;三者皆空时不构建复核器,该档对软候选转为 fail-closed 预警(原因reviewer_unavailable_failclosed),不回落到 actor 模型; - 复核器构建先沿角色链试一跳回退(
safety_reviewer的下一跳是verifier或 actor 主模型)并记model_fallback审计;回退也失败才跳过精排并按预警处理; - 精排调用异常记原因
reviewer_error_failclosed并按预警处理;答复无法解析时同样按不可逆处理(fail-closed); - 精排调用作为
reviewer角色计入同一份 token 用量台账,因此消耗 run 的 token 预算。
人工中断¶
ask_user 与 take_over 在任何档位都中断并等待人工输入,与安全门控相互独立。hard 档的审批中断经 langgraph 的 HITL 中断实现:审批通过则执行原调用,拒绝则返回 error 回执且不执行;被拒绝的 take_over 同样只回 error 回执,run 继续。自由文本答复只在 allowed_decisions 为 approve / reject 的中断(hard 审批与 take_over)里做映射:肯定词(approve / yes / y / 同意 / 确认 / ok)映射为批准,其余映射为拒绝;ask_user 的答复恒为 respond,原样交回模型,不走该映射。
wary 流程¶
flowchart TD
CALL["模型发起执行调用"] --> GATE{"安全门判定"}
GATE -- "无风险" --> EXEC["执行并回传新观测"]
GATE -- "风险" --> WARN["短路:返回预警回执<br/>(世界事实 + 选项空间)"]
WARN --> CHOICE{"模型选择"}
CHOICE -- "confirm_irreversible=true 重发" --> EXEC
CHOICE -- "放弃该动作" --> NEXT["继续任务"]
CHOICE -- "ask_user / take_over" --> HUMAN["中断,等待人工"]
finish 验收¶
与安全门控独立的两道完成检查(机制细节见架构):
- 两段式 finish:首次
finish返回复核包(世界事实、路线状态、疑点、选项四节);模型confirm=true再次调用才定稿。 被接受的 finish 立即终局:同轮的后续 sibling 工具调用收到 error-status skipped 回执,且不再采样模型; 首次复核、陈旧 confirm 与验收器拒绝在预算允许时仍可继续。被接受的take_over同样终局。 - 独立验收器(
PHONE_AGENT_FINISH_VERIFY,默认auto):auto档在高风险目标或硬矛盾时触发,always档每次 confirm 都触发,off退化为单段落定。验收器只能看到目标、证据路线与尾部截图,看不到 actor 的 完整对话;连续两次驳回转take_over(终局原因finish 反复被验收驳回,需人工确认)。验收器 setup/调用 故障时放行(Verdictapprove=True,fail-open)并把审计状态记为skipped,未触发与off档的 run 同样 记skipped——不等于验收通过;验收答复无法解析时也按 APPROVE 放行(statuspass)。
若 token 达到预算时已有有效复核包,系统最多保留一次模型真实确认机会,避免仅因预算跨线而无法
完成第二段。它绑定成功观测的屏幕序号及关闭的任务板、原始目标;这些事实变化后不能使用。模型仍可
不确认或请求人工介入,系统不代写 confirm=true。该额外响应中的普通工具操作不执行,重复 finish
不能续期;验收器拒绝后也不再获得额外模型轮次。确认仍经过原有证据、任务板、seq 与独立验收器检查;
人工中断、MAX_STEPS 和已经接受的终局保持优先。完整规则见预算配置。
普通工具一旦在复核后被委托执行,旧复核就不再有续办资格;这也覆盖命令可能已派发、结果未知且屏幕
序号尚未更新的错误。后续成功的新复核可重新证明当前状态,但仍不能刷新已经使用的一次续办额度。