权限只分允许和拒绝,手机智能体就没有可降级的中间档

一、两档制在三个位置失效
看懂屏幕、模拟点击、跨应用把一件事做完,这三项能力早就不是问题。问题出在把它们交给用户时的表述方式:一个开关,两档,允许或者拒绝。这类产品的讨论点已经从“能不能做到”挪到了“该以什么形状把选择权交出去”。
两档制的第一处失效发生在装机那一刻。用户面对的是“允许这个助手读取你的屏幕并代替你操作”,一句话里包含了两件风险完全不同的事。任何一个对手机里存了什么有基本感觉的人,第一反应都是先不给。于是一个能力被整块废掉,不是因为它做得不好,而是因为它的定价方式让人没法只买一部分。
第二处失效发生在给出去之后。一次允许,覆盖的是这台机器上所有能被显示出来的内容:聊天列表、账单页、相册、邮件签名、验证码短信弹出的那一瞬。授权的作用域和产品实际需要的作用域差了十几个数量级,用户给的时候以为自己在给“帮忙订个车”,系统记下来的是“可以看见一切”。
第三处失效发生在出事之后。用户说助手在某个页面做了不该做的事,工程侧要回答的是“当时读到了什么、由哪一次读取决定了这一次点击、这一跳是谁发起的”。两档制下这三个问题都没有答案,因为授权模型里根本没有把这几层分开记录的概念,日志只能证明“当时是允许状态”。
三处失效的共同根因是同一个:把两类能力打包成了一个开关。
二、把开关拆成四档,读与写各占一轴
要有的不是更多档位,而是两条独立的轴。读是被动的信息获取,点了就点了,回滚的代价是把已看到的内容收不回;写是主动改变外部状态,一次点击会真的把钱转出去、把消息发出去、把订单取消。可回滚性完全不同的两件事,不能共用一个布尔值。
下面的表是我们给端侧产品做权限设计时的写法,四个档位是建议的产品形态,不是任何系统里现成的权限项名称:
| 档位 | 允许读什么 | 允许写什么 | 典型用途 | 主要代价 |
|---|---|---|---|---|
| 禁止 | 无 | 无 | 用户不信任某类场景时的默认落点 | 功能整块不可用 |
| 只读当前屏 | 当前这一屏的可见内容,切屏即断 | 无 | 念一遍页面上的信息、解释这一屏在讲什么 | 跨屏任务无法推进 |
| 只读白名单内应用 | 白名单里指定应用的前台内容,其余一律不可见 | 无 | 常驻比价、行程跟踪类任务 | 白名单要维护,漏配就失效 |
| 允许指定控件类型注入 | 与只读白名单档相同的读范围 | 只对指定控件类型(如输入框、列表项)注入,不含确认类控件 | 填表、选择、滚动 | 控件类型识别错误会让功能退化 |
这张表里“读什么”和“写什么”两列是可以任意组合的,产品要给的应该是这两列的组合选择,而不是一个总开关。最常被忽略的一格是“只读当前屏、不写”——它恰好覆盖了大量真实需求:把这一屏的信息读出来讲清楚,本来就不需要点任何东西。
写轴还要再切一刀:注入的目标控件类型比注入本身更能决定风险。把一段文本填进搜索框,和按下“确认支付”,在实现上都是模拟一次点击,在后果上不是同一件事。按控件类型收窄,是让“允许写”这一档不至于变成“允许一切”的唯一办法。
三、跨应用任务链上,授权到底是谁给的
一个任务在 A 应用里发起,在 B 应用里完成,中间还过了浏览器。这条链上最容易出事的问题是:B 里那次操作,用户到底有没有同意过。
按两档制的逻辑,B 的授权算“用户已经给了助手”。但用户的心智根本不是这样分配的——他记得的是“我让它帮我查一下明天的票”,不记得自己同意过在这个应用里点按钮。授权的作用域和用户的同意范围之间出现了一道缝,这道缝是所有端侧智能体纠纷的现场。
可执行的做法是把任务链拆成跳,每一跳单独记账:这一跳在哪个应用、读了什么屏、准备写什么控件、这个动作是不是可逆。对不可逆动作(提交、支付、删除、发送)设置发起时确认,确认页面上要显示“接下来会在某某应用里按下某某按钮”,而不是只弹一个通用授权提示。链上每一跳是否都要人确认,可以按可逆性分档而不是全确认——全部要求确认的产品的下场是被用户整个关掉,这比不设中间档更糟。
工程侧要留的一条硬结构是:任务链标识(同一次任务的多跳共用一个标识)加上跳序号,加上发起方应用标识。没有这三样,事后无法把散在几个应用里的操作还原成“用户原本的那一次委托”。
四、中间档具体降级什么,以及两张清单
“降级运行”不是一个态度,是一组具体动作。中间档要降的通常是三件事:
- 关掉写操作,只输出建议:助手仍然读屏、仍然规划,但不注入,把“我帮你点了”改成“你点这里”。
- 自动执行改成高亮待确认:动作照旧生成,落地前停在界面上等一次人工确认,风险从“错了不知道”变成“错了当屏可见、可打断”。
- 后台常驻改成前台一次性:只在应用处于前台且任务在进行中时可读,任务结束即断,取消“随时可以看见我的屏幕”这种状态。
第二张清单是敏感页面硬拦截,这一张不随用户授权档位变化,任何档位下都不允许读也不允许写:支付确认页、系统权限弹窗本身、密码与验证码输入框、系统设置页。这四类的共同点是“在这一屏上的任何动作都会改变授权或资产状态”,而权限弹窗尤其危险——如果助手能读甚至能点权限弹窗,它就能替自己扩权限。这不是理论风险,它是任何“允许一切”的模型自带的漏洞,所以这一条要写死在拦截层。
这张清单必须支持远程更新,不能编进安装包。理由是新的敏感页面会不断出现,而一个需要发版才能补一条拦截项的安全机制,实际生效日期是下一次发版日。远程更新要配三样东西:签名校验、拉取失败时的旧清单继续生效、每次命中拦截都记录清单版本号——否则“我们更新了清单”这句话没法证明。
白名单本身有三种粒度,成本递增,精度也递增:按签名(同厂商所有应用一并放行,维护最省事,一次签名字段写错就会整批失配);按包名(单应用放行,安装与卸载时状态会变);按包名加页面路径(细到某个页面才放行,精度最好,代价是页面改版就会失效,需要有人跟着改)。三档不是越细越好,页面路径粒度适合放在敏感任务的窄路径上,全量使用会把维护成本变成安全债。
五、回显、撤销与三条隐私问答
授权状态不能只记在日志里,要在界面上看得见。日志是给用户之外的人看的,回显是给用户本人看的,两者不可互相替代。该回显的字段最少有四个:这一屏正在被读取(或者这一屏在前一次交互里被读走)、这一次写入落在哪个控件、这一跳任务由哪个应用发起、当前所处档位。回显的强度可以分级——读取用一条持续可见的状态标记,写入用一次性浮层提示,不可逆动作用拦截确认。
撤回要给用户三条路径,一条都不能少:一键撤销全部授权并终止所有在途任务;按应用撤销,撤掉某个应用后与之相关的白名单和任务链一并失效;撤销之后的补偿问题要单独回答——已经注入出去的点击撤不回来,所以撤销流程必须显式提示“此前在某某应用里有次操作已经完成,需要你去那里撤销”,并且在可逆范围内自动尝试一次回退。把“一键撤销”做成一个只清标记的按钮,是这类功能最常见的空头承诺。
端侧模型的隐私边界有三个必须能查到答案的问题,答案不该靠用户猜:截图在设备本地处理还是会上传;上传的内容包含什么(整屏像素、结构化文本、还是只提取到的控件描述);留存多久、留存目的是什么。这三个问题在产品的隐私说明里应该有一段专门的位置,而且要以能力为单位写,不以公司为单位写——“我们重视隐私”回答不了“我刚才那屏去哪儿了”。
六、交付时我们把住的三件事
这件事落到洹拓的交付上,能守住的不是模型能力,是权限模型的结构。三件具体的事:
- 评审任何端侧智能体功能时,先问“读轴和写轴分开没有”。两轴共用一个开关的设计,无论文案写得多谨慎,中间档都不存在,后面所有降级能力都无从挂上去。
- 敏感页面拦截清单当配置对待,不当代码对待:远程可更新、版本可回查、命中有记录,三项缺一就不算做完。
- 授权回显当功能验收项,不当视觉细节:演示时能否一眼看出这一屏是被读还是被写、这一跳是谁发起的,是这类产品能不能被非专业用户长期保留的决定因素。
多档位还会带来一个必须提前定的产品约束:档位组合数量多了以后,用户会不知道自己选的是哪一档。解决办法不是把所有组合都开放,而是把常见任务预置成几种命名组合(只看不说、看了提醒、允许代填、允许代办),把细粒度开关收进高级页。权限模型的可用性,最后是靠命名而不是靠组合数支撑的。