权限授予与撤销。
授权页是你的用户给智能体划定爆炸半径的地方,而它几乎总是原封不动地从 OAuth 继承来的——一次性的决定,对应一份永久、粗粒度、应用级的范围,是为「不会临场发挥的软件」设计的。由此推出的结论有点出人意料:真正决定人们愿意给你多少访问权的那块界面,根本不是授权页,而是撤销页。当收回访问权这件事不可见时,人们会超额授予;当它看上去不可逆时,人们会吝啬授予。而要抬高智能体实际拿到的访问权,最便宜的办法是把「取消授权」做成一次点击,并且提前把后果摆出来。
你继承来的那块界面,是给另一种行动者设计的。
OAuth 授权页是为「行为在构建期就固定下来」的应用做的。你批准一次 calendar.events,而这个应用拿它干什么,下周二和今天是一样的。那块界面上的每一条惯例都是从这个假设推出来的:永久授权、应用级范围、一串能力名、没有过期。
智能体把这些假设逐条打破了。
- 行为不是固定的。同一份授权每次运行都会被以不同方式行使,因为计划是模型选的。批准能力,不再等于批准行为。
- 范围比任务粗。任务是「把这一场会改个时间」,可选的范围是「你的日历」。用户被要求授权那个更大的东西,只为了拿到那个更小的。
- 动手的那一刻没人在看。在前台会话里批下来的授权,会在凌晨三点被一次定时运行行使。授权时形成的那个心智模型——「它做这件事的时候我在场」——不出一周就是错的。
这不是在主张「多问几次」。逐动作审批有它自己的打法和自己的失效方式——见审批与确认体验,那里的敌人是习惯性麻木。本页说的是压在那些弹窗底下、却没有任何人复核的那份常驻授权。
展示半径,而不是范围名。
一串范围名是一份 API 权限清单,作为风险陈述它是不可读的。drive.readonly 对用户什么也没说;而「可以读取你云盘里全部 12,431 个文件,其中 340 个与公司外的人共享」告诉了他们正在决定什么。在授权那一刻,你几乎总是有数据能把第二句话算出来——一次计数查询而已——而几乎没人愿意花这一下。
- 在提出请求的那一刻,把范围解析成真实对象的计数。数字能把抽象具体化,这是任何措辞上的修改都做不到的。
- 把不可逆的放在最前面。按后果排序,而不是按 API 面排序:会出门的(发送、发布、付款、共享)在改状态的之上,改状态的在读之上。多数授权页按字母排序,那和随机排序是一回事。
- 点名上限,而不只是点名能力。「每天最多可花 50 美元而无需询问」比「可以发起购买」是好得多的披露,而且它本身就兼作那道控制——见成本与配额体验。
- 不要往清单里灌水。你申请的每一项产品里看不出在用的范围,都在训练用户去扫读,而正是这次扫读会把他们带过那条真正要紧的。
授权给任务,让「过期」来替你收窄。
用户预测不了智能体会需要哪些资源,所以要求他们事先勾一个窄范围,得到的要么是一个猜错的答案,要么是一句认命的「全都给」。时间才是他们能推理的那个维度,而且它不需要任何预见:一份只对这次任务、或这一周有效的授权,无需任何人事先枚举资源,就把损害框住了。
- 把时长做成和范围并列的一等选项——本次运行、今天、三十天、始终——并且默认值不要是「始终」。这里的默认值定下了你的整个风险姿态,因为几乎没人会去改它。
- 就地升级,而不是一上来就要大的。先给读;等这次运行真需要写的时候再问,并点名具体那个对象。这时候用户手上有上下文,而这是这个问题唯一答得出来的时刻。
- 按「不用」过期,而不只按钟过期。九十天没被用过的授权,应当带着一条通知自行失效。这会悄悄地清掉绝大部分常驻访问权,而用户一个决定都不用做。
- 让续期既便宜又显眼。如果重新授权是一段五屏的重新引导,用户第一次就会选「始终」,你也就把这套机制丢了。要能从通知里一键延长。
底下那条性质在爆炸半径里讲过:过期无需任何人事先知道任务会碰到什么,就能把损害框住。
撤销才是那块承重的界面,而它通常是做得最差的一块。
这里有一个值得围着它做设计的反转:用户愿意授予多少访问权,主要取决于他们有多确信自己能收回来。当撤销被埋在设置页里、措辞是「断开连接」、并且对「会坏掉什么」只字不提时,理性的做法就是尽量少给,然后绕开你的产品。而当它只需一次点击、能从智能体干活的地方够到、并且在点击之前就把后果摆出来时,人们会给得更多——因为这个决定不再是永久的了。
- 把撤销放在智能体所在的地方,而不只在账户设置里。用户想要它的那一刻,正是智能体刚做了件让人意外的事的那一刻,而那一刻发生在活动视图里。
- 预演后果。「撤销日历访问权将停止 3 条定时规则,并取消 1 次正在进行的运行」,把一个看起来吓人且不可逆的动作,变成了一个知情的动作。
- 在撤销之前先给「暂停」。多数伸手去按那个按钮的人,想要的是让智能体现在停下来,而不是拆掉一周的配置。一个即时且显眼的暂停能吸收掉几乎全部这类流量,并把配置保住。
- 说清数据会怎样。撤销访问权并不等于删掉已经读走的东西;要明确说是哪一种,并把另一种就放在旁边供选择。
- 确认它真的发生了。把令牌显示为已撤销、带上时间戳,并让进行中的运行可见地停下来。一次静默的成功会被读成失败,然后被重复一遍。
如果这一页你只做一件事,那就在智能体自己的界面里做一个撤销控件:它会预演什么将会坏掉,并把「暂停」放在旁边作为相邻选项。这是一小块工作量,而它对上游授权行为的影响,胜过你给授权页做的任何措辞改动。
给用户一份常驻访问权清单,用他们的语言写。
每一个长期使用智能体的用户,最终都会攒下一堆没人记得是什么时候批的授权:试用期间批的一个集成、三月里为某一个任务升上去的一个范围。厂商侧的登记册是运维的事——见智能体清点与登记——但用户需要属于自己的那份视图,而且它讲的应该是后果,不是令牌。
把失败也设计出来:访问权消失时,正在跑的智能体该怎么办。
访问权在运行途中被撤销的频率,比团队计划的要高——被用户、被管理员、被过期的令牌、被上游一次策略变更。而默认行为恰恰是最糟的那一种:工具调用失败,模型把 403 读成一个「需要绕过去」的问题,于是它要么重试,要么发明一条变通路线,要么在一件没做完的任务上报告成功。
- 把权限错误做成终止性的,而不是可重试的。403 不是瞬时故障,绝不能进重试路径。它应当让这次运行停下来并产出一个明确的状态,而不是一段散文式的道歉。
- 绝不让模型用叙述绕过去。把这次丢失做成一等的界面状态——「已暂停:日历访问权被撤销」,旁边就是恢复和重新授权——而不是做成对话记录里一句用户也许永远不会读到的话。
- 把做了一半的工作留住。保存下哪些完成了、哪些没有,这样重新授权是续跑而不是重来。正是这一点让用户敢于去撤销;见撤销与可逆性。
- 不要自动再问一遍。一个在被撤销之后立刻弹出新授权请求的智能体,读起来是有敌意的,而这是让整个集成被卸掉的最快路径。