
长跑 Agent 的权限边界审批卡、意图检查与凭证隔离原文Claude Duet 的安全、隐私与凭证隔离设计https://claude.com/blog/claude-duet-safety-privacy-and-security一个关掉电脑还在跑的 Agent麻烦在哪常跑的 Agent 有个分水岭它是不是在你关掉窗口之后还在干活。传统的对话式助手每个动作都在你眼皮底下发生出错最多是一句胡说。而一个自主 Agent用云端计算机替你读收件箱、起草回复、下单、跑例行任务它在时间上和你错开了——你不在场的那几个小时就是它自由发挥的窗口。这时候防幻觉解决不了问题真正要防的是三件更具体的事它会不会替你把不该发的东西发出去、会不会在你没同意的情况下动钱、会不会被人从网页里塞一句话就把你的密码交出去。Anthropic 在 Claude Duet 的发布文里把设计拆成了三条原则我觉得这三条几乎是通用模板跟具体哪家模型无关你的信息要有分寸地处理发出的消息先过一道检查你保留对它替你做什么的控制权Agent 永远看不见你的密码、浏览器 cookie、登录令牌。下面按这三条往下拆重点看它们各自落了什么机制而不是记结论。一、审批卡把要不要做变成一个人能回答的问题让 Agent 停下来问说起来容易做起来有两个坑问得太少出事问得太多人会麻木地连点。Anthropic 的做法是在发送可能敏感的消息、付款、删除任何东西之前弹一张审批卡卡上必须同时给出三样东西动作是什么、目的地是哪里、内容是什么——内容里包括每一个收件人。然后等用户点。这里有个细节值得抄审批卡本身在模型外面渲染旁边还并行跑一个自动检查判断这个动作是否匹配你原本的意图。这个检查基于 Claude Code 的 auto mode 分类器扩展而来关键在于它不在干活的模型里跑——所以页面里藏着的指令影响不了判定结果。想一下攻击面就明白了如果要不要批准也由同一个模型判断那么一段注入文本既能驱动动作又能说服审查者等于自己给自己发通行证。另外两条规则很实用可以直接搬到自己的 Agent 上类别规则例子绝对禁区即使你批准也不做证券交易、账户间转账、访问别人的账号不认别再问我批过多少次每次都重新问付款/转账、删除订单、创建账号第二类是最容易做错的地方。很多 Agent 框架都提供本次会话内允许此类操作的记忆图省事把付款也塞了进去结果一次心软等于永久授权。官方也承认审批频率是个需要长期调的参数理由是审批疲劳——请求太多人会不看内容直接点过去。这句话对做产品的人比对做安全的人更有用审批卡的数量本身就是一种安全预算给多了等于没有。二、敏感信息两级不是能不能发而是发之前先分级Agent 替你发消息最难的一步是决定哪些信息不该出现在这条消息里。你去让它给团队发周报它应该只写大家本来就有权限看到的内容而不该把绩效面谈的细节带上。Duet 为此单独训练了一个分类器和主模型分开跑在每条外发消息发出前检查是否含敏感信息并把信息分成两级绝不分享登录凭证、完整卡号与银行账号、政府证件号。这类东西即使你亲自要求Agent 也被指示不要发出。需要同意才分享健康、财务状况等私人信息。你明确批准才能外发而且你自己的请求本身就算同意——给我牙医发条消息说明牙疼这种不需要再弹一次卡。被拦下来之后Agent 还有一个动作可以做重写这条消息把敏感部分去掉再拿给你确认。这个改写而不是失败的设计比直接报错友好得多也让拦截从阻断变成了协作。官方在文中也明确说了局限还在研究预览阶段这些防护偶尔会漏掉一些东西也会把无害的消息标出来。这话得当真——分类器是概率件它的错误率决定了你能不能只靠它。所以审批机制不能省两层要叠着用。三、凭证隔离让它被钓鱼变成一个不成立的问题这一节是整篇里最工程化的部分也是最值得抄的。原则很硬Agent 永远看不到密码、cookie、登录令牌也不能把它们写进记忆。这些东西只经过模型访问不到的独立机器审批和安全检查也在那里跑。密码。站点要密码时你是在 Claude 应用内的表单里输入的不是在网页里。这个表单用一把单次密钥加密密码密钥只存在于那台安全云浏览器机器上、只用这一次密文原样传到浏览器由浏览器替你敲进站点。模型既看不到明文也没有办法保存或复述。这个设计直接改了钓鱼的形态Agent 不持有你的凭证所以没法被钓出密码也没有任何隐藏指令能让它泄露一个它根本没有的秘密。但反向的风险也出现了——登录表单本身成了靶子任何东西都能伪装成请输入密码。所以 Duet 的登录表单只在你主动发起的登录过程中、在应用内部出现永远不会通过链接、短信、邮件或 claude.ai 之外的网站发给你。登录令牌。连接受托的第三方服务时Agent 需要一个令牌。做法沿用 Claude Managed Agents 的思路真实凭证永不进入 Agent 干活的那台机器进去的是占位符由机器外的一个代理在实际请求发出的瞬间把真值填进去。浏览器。浏览器跑在自己专属的云端虚拟机里和模型、和 Agent 的工作机都分开。Agent 只能通过一组固定命令驱动它打开这个页面、点这里、输入这个。站点执行的代码留在浏览器机上站点代码和 cookie 到不了 Agent 的记忆和你已连接的应用Agent 的任何命令也读不出 cookie。回来的只有页面文本和下载文件——因为 Agent 得读它们才能干活这也正是为什么其他检查必须存在以及为什么要叠上浏览器提示注入的防护。通过浏览器上传的文件走和外发消息同一套敏感信息检查。还有一条容易被忽略Agent 要主动表明自己是 Agent。现在它浏览器发出的请求带一个可识别的 User-Agent出口 IP 段是公开的站点可以据此决定怎么对待它上线前他们还联系了几家大型网站给对方禁止 Duet 访问的选项有些站选了。四、一个最小的审批网关骨架上面这些机制落到代码上第一层其实很薄一个纯粹的规则判定。下面这段是我按原文规则重写的示意实现不是官方代码也没有对应任何 SDK。fromdataclassesimportdataclass,fieldfromenumimportEnumclassDecision(Enum):ALLOWallow# 规则层放行直接执行NEED_APPROVALapproval# 渲染审批卡等人点DENYdeny# 硬禁区批准也不放行HARD_DENY{trade_security,move_money,access_other_account}NO_REMEMBER{pay,delete_order,create_account}dataclassclassAction:kind:str# 动作类型target:str# 目的地商户域名 / 账号 / URLpayload:str# 正文或金额recipients:list[str]field(default_factorylist)defgate(action:Action,remembered:set[str],approved:bool)-Decision:ifaction.kindinHARD_DENY:returnDecision.DENYifaction.kindinNO_REMEMBER:returnDecision.NEED_APPROVAL# 批过多少次都重新问ifaction.kindinrememberedandapproved:returnDecision.ALLOWreturnDecision.NEED_APPROVAL参数逐个说action是一次待执行的动作target和recipients是审批卡要展示的目的地信息缺了它们卡片就没法让人做判断remembered是这个会话里用户勾过别再问我的动作类型集合approved是本次审批卡的结果。返回值是三态而不是布尔因为需要人确认和绝不允许是两种完全不同的处理路径混成 False 会让上层分不清该弹卡还是该报错。调用链是这样Agent 产出Action→ 进gate得到三态 → 若是NEED_APPROVAL就渲染卡片必须带上 target、recipients、payload→ 用户批准后再交给一个在模型外跑的意图检查 → 都通过才进执行器。注意这个函数只做规则层语义上的这个动作是否匹配用户意图它答不了那需要另一个独立跑的判断单元。付款那条还要再加一层原文用的是钱包如 Stripe 的 Link出的一次性卡限商户、限额你真卡号不给 Agent 也不给商户。这层在代码里体现为执行器的约束不在gate里。五、怎么验证自己的实现没白做官方文里提了他们的测试方法我觉得这套可以直接拿来当验收清单评测任务按日常任务形状构造每个任务出三个变体正常版、带注入指令版、诱导它做超出授权范围的版除了自己构造的用例还并行跑真实使用的影子流量对整个系统建威胁模型把所有仓库和信任边界都圈进去每个新的凭证相关功能先在隔离的台架上做红队通过后才发外部渗透测试公司拿着源码测云浏览器和登录表单是后续单独一轮。不用五条全做但三变体和新凭证功能先隔离红队这两条成本低、收益最大。尤其是第一变体之外的第三变体——诱导越权绝大多数自测都缺这一档。收尾这篇的价值不在Anthropic 又发了个 Agent而在它把一件通常被含糊处理的事拆成了可实现的四层规则层决定哪些动作永远不做、审批层决定哪些必须问、分类层决定哪些信息不能出去、隔离层决定凭证根本不进模型。四层各管一件事任何一层失效都还有下一层兜着。要记的就一句安全性不来自把提示词写得更严而来自让模型没有能力做那件事——它拿不到凭证、读不到 cookie、看不见审批卡的判定逻辑能力边界画在模型外面。