第 34 章 · Agent Wallet 是什么
把钱包交给 AI 之前,必须先设定什么?
- 练习的能力
- AI LiteracySystem ThinkingOnchain Literacy
- 动手
- 为一个假想 Agent 写一份权限表:单笔上限、日上限、允许的合约、需要人工审批的条件。
- AI Lab
- 让 AI 尝试绕过你写的权限表,把它找到的每一个漏洞补进规则里。
一个现实问题
你决定给你的软件一个自己的钱包。你很谨慎,先写了一份规则:
1. 单笔不超过 100
2. 只能和这三个合约交互
3. 超过 100 的操作,弹给我审批这份规则看起来相当严格。你把它交给一个熟悉链上安全的朋友看,请他挑毛病。他看了三分钟,列出了下面这些:
第一,第 1 条只管单笔。 一百笔 99 元的操作,每一笔都合规,总共九千九百元。这份规则里没有任何一个地方限制总量。
第二,「和合约交互」和「花钱」不是一回事。 你的软件可以调用白名单里的合约,给另一个地址授予一个没有上限的额度。这一步本身没有转走任何钱,所以它连一百元的限额都用不上。但授权之后,拿到额度的那一方可以在任何时候把钱全部取走。
第三,白名单里的合约可能能替你调用别的合约。 很多合约带有转发或者批量执行的能力。你允许了合约 A,而合约 A 可以代你去调用合约 Z——你的白名单在第二跳之后就失效了。
第四,第 3 条那个审批,多久内有效? 如果它弹出来没人处理,是一直等着,还是超时放行?如果是超时放行,那么你的软件只要在凌晨三点提交请求,第 3 条就不存在。
第五,「100」是什么单位? 一百个稳定币和一百个别的代币,价值可能差几个数量级。规则里没有说清楚以什么计价,也没说价格从哪里来——而价格从哪里来这件事,第 32 章已经演示过它出错时会发生什么。
第六,谁来执行这三条规则? 如果它们只写在一份文档里,而软件的私钥可以签任何交易,那么这三条规则的实际效力等于零。一份没有被强制执行的规则不是防线,是一个愿望。
你朋友最后说了一句:「你写的不是权限,是期望。」
所以本章的问题:把钱包交给 AI 之前,必须先设定什么?
这一章的答案不会是「要小心」。它是一份具体的、可以照着写的清单,以及一套检验这份清单有没有漏洞的方法。核心的那句话很短:Agent Wallet 的核心不是签名能力,而是花钱的策略与上限。 让一个软件能签名是几行代码的事;让它只能签那些你真正允许的交易,才是这一章的全部内容。
思想实验
把上面那个过程变成一个练习。
三个人,同一份权限表,四十八小时。 规则只有一条:不许攻破私钥,不许利用任何代码漏洞,只能在你写的规则之内,想办法把钱弄出去。
权限表就是上面那三条。
第一个人花了四分钟。 他发起了一百零一笔操作,每笔 99。第一百笔完成时,钱已经基本没了。他用的是这份规则允许的全部行为,没有任何一步违规。
第二个人花了二十分钟。 他不转账,他调用白名单里的一个合约,给自己的另一个地址授予了一个不设上限的额度。这一步在权限表里完全找不到对应条款——因为授权不是支付。第二天他从那个地址把钱取走了。
第三个人花了三十六个小时,用的方法最让人不舒服。 他每隔几分钟提交一个金额略超 100 的请求,每一个都触发审批。第一天,你认真看了前十几个。第二天上午,你开始只看金额。第二天下午,他提交了一个金额相同但目标不同的请求,你点了确认。
这三个人代表三类完全不同的失效方式,而它们都不需要任何技术攻击:
| 用的是什么 | 你的规则为什么没拦住 | 补法 | |
|---|---|---|---|
| 第一个人 | 拆单 | 规则只管单笔,不管累计 | 加时间窗累计上限和频率上限 |
| 第二个人 | 授权不是支付 | 规则只定义了「转多少钱」,没定义「授予多大权力」 | 把授权额度纳入限额,禁止无上限授权 |
| 第三个人 | 审批疲劳 | 规则假设了「人会认真看每一次」 | 给审批本身设预算,超出就一律拒绝 |
第三个人的方法最值得停下来想。他没有绕过任何规则,他攻击的是执行规则的那个人。
而且这件事不需要有人恶意实施。一个完全善意的软件,在一个设计不当的系统里,会自然产生同样的效果——它并不知道你已经疲劳了,它只是按自己的逻辑不断提交请求。AI 在 Crypto OS 中的位置里那句话在这里得到了最具体的印证:一个每天弹出两百次审批的系统,等于没有审批。
把这个实验收成一句话:
一份权限表的强度,等于它最弱的那一条;而最弱的那一条,通常不在表里,在表的假设里。
你来决定
你要让这个软件真的能花钱。钥匙和权限怎么安排?
观察结果
四个选项的差别,可以归结成一个问题:这条规则由谁执行?
| 规则写在哪 | 谁执行 | 模型误解时有效吗 | 有人注入指令时有效吗 | 强度 |
|---|---|---|---|---|
| 提示词里 | 模型自己 | 无效 | 无效 | 接近零 |
| 一份文档里 | 靠软件自觉 | 无效 | 无效 | 接近零 |
| 调用前的代码检查里 | 确定性程序 | 有效 | 有效 | 中 |
| 钥匙的权限里 | 签名系统 | 有效 | 有效 | 高 |
| 钱包的余额里 | 物理上限 | 有效 | 有效 | 最高,但最粗 |
这张表是这一章的骨架。 它说明一件反直觉的事:把规则写得更详细、更周全,并不会提高它的强度。 一条写在提示词里的规则,无论写得多好,强度都接近零;一条「这个钱包里只有这么多钱」的物理限制,虽然粗糙,但无法被任何方式绕过。
从这张表可以直接得到三条设计原则:
第一,规则要尽可能往下移。 同一条限制,能放进钥匙权限里就不要放进代码检查,能放进代码检查就不要放进提示词。
第二,越往下的规则越粗糙,所以需要分层。 钱包余额这一层最强但只能限制总量;钥匙权限这一层可以限制目标和函数;代码检查这一层可以做复杂判断(比如价格偏离)。三层配合,而不是选一层。
第三,最上面那一层不算防线,只算提效。 在提示词里写「不要超过 100」是有用的——它能让软件大部分时候做对的事,减少被拦截的次数。但它不是安全措施,不能被计入你的风险评估。
把这一节收成一句话:
Agent Wallet 的核心不是签名能力,而是花钱的策略与上限。
而策略和上限只有在模型够不到的地方执行时,才真的存在。
建立模型
一、完整的链路:钱包只是最后一格
先把这一章放回整个第六阶段的框架里。一个 Agent 的资金操作要走完这十层:
这一章处理的是第五层(Policy)和第八层(Execution)。 前四层在 T32 里有完整的工程实现,这里只需要记住一件事:Policy 层必须是确定性的,它不能包含任何模型。
对照着看那个绝对不能出现的结构:
课程里禁止出现的架构
- Prompt
- LLM
- Private Key
- Send Transaction
模型直接拿到私钥并发出交易,中间没有任何校验。任何作业出现这个结构都判不通过。
它和上面十层的区别不是少了几个检查。是模型直接持有了私钥——一旦如此,前面那张强度表里的每一行都同时失效,因为所有的规则都变成了「希望它遵守」。
二、权限表的八个字段
一份真正可执行的权限表,至少要回答八件事。少一个字段,就多一个上一节那样的洞:
| 字段 | 要回答什么 | 漏掉会怎样 |
|---|---|---|
| 1 身份 | 这是哪个 Agent,用哪个地址 | 出事时分不清是谁做的 |
| 2 单笔上限 | 一次最多动多少,以什么计价 | 一次就被掏空 |
| 3 累计上限 | 一小时、一天、一周分别最多多少 | 拆单(第一个人的方法) |
| 4 频率上限 | 单位时间内最多几次同类操作 | 高频小额累积,且异常难以察觉 |
| 5 目标白名单 | 哪些地址、哪些合约、哪些函数 | 钱被送到任意地方 |
| 6 授权上限 | 可以授予别人多大额度,能不能无上限 | 授权不是支付(第二个人的方法) |
| 7 审批条件 | 什么情况必须人来批,批不到怎么办 | 审批疲劳与超时放行(第三个人的方法) |
| 8 有效期与撤销 | 这份权限什么时候失效,怎么紧急停掉 | 一份忘了回收的权限会一直有效 |
第六个字段是链上特有的,也是最容易被漏掉的一个。 在传统支付里没有「授权」这个概念——你要么付钱,要么不付。链上不同:你可以在不转移任何资产的情况下,授予另一个地址动用你资产的权力,而且这个权力可以是没有上限、没有期限的。
所以权限表必须分开定义两件事:「这个 Agent 一次能转多少」和「这个 Agent 一次能授予多大的权力」。 后者的上限应该比前者更严,因为它的影响是持续的。
第八个字段则是最容易被忘记的一个。 一份没有有效期的权限,会在你早就忘了它存在之后继续有效。给每一份权限写一个到期时间,到期自动失效、需要重新授予——这个小设计能消除掉大量长期累积的风险。
三、限额是四个维度,不是一个数
「设一个上限」这句话通常只被执行了四分之一:
- 单笔上限
- 时间窗累计
- 总预算
- 整体敞口
| 维度 | 拦住什么 | 一个常见的错误 |
|---|---|---|
| 单笔上限 | 一次性的大额错误 | 以为设了这个就安全了 |
| 时间窗累计 | 拆单、异常循环、连续重试 | 只设了「每天」,没设「每小时」——一天的额度可以在五分钟内用完 |
| 总预算 | 长期的缓慢流失 | 没有设,或者设了但会自动补充 |
| 整体敞口 | 单看每笔都合规、加起来越界 | 完全没有这个概念 |
第二行那个错误值得特别说。 只设日上限时,那个额度可以在任意短的时间内被用完——第 32 章那个四十分钟里执行十一次的例子,每一次都在日上限之内。所以时间窗必须是多层的:小时、天、周,各设一个。
四、白名单是四层,不是一份地址清单
「只能和这三个合约交互」这句话有四种不同的严格程度:
| 层 | 限制到什么 | 能拦住什么 |
|---|---|---|
| 地址 | 只能和这几个地址交互 | 拦住把钱转给陌生地址 |
| 合约 | 只能调用这几个合约 | 同上,但仍然允许调用它的任何功能 |
| 函数 | 只能调用这个合约的这几个函数 | 拦住转发、批量执行、授权这类危险功能 |
| 参数 | 这个函数的这个参数只能在某个范围内 | 拦住「函数对了但数值离谱」 |
大多数人的白名单停在第二层,而第一节那个「第二跳」的问题恰恰发生在第二层和第三层之间。 允许一个合约,等于允许了它的全部功能,包括那些可以代你去做别的事的功能。
一条实用的经验:白名单要按函数列,而不是按合约列。 这会让清单变长,但它是唯一能真正限制住行为的粒度。
五、审批是稀缺资源,要给它设预算
审批疲劳不是一个态度问题,是一个资源问题。人的注意力有限,每天能认真做出的判断次数是一个有限的数。
所以审批必须像预算一样被设计:
每天允许送到人面前的审批次数: 最多 5 次
超出这个数之后的请求: 一律自动拒绝,不是排队等待
审批的有效期: 15 分钟,过期作废
没人处理时的默认行为: 拒绝。绝不能是放行
两次审批之间的最小间隔: 防止连续轰炸「超出就自动拒绝」这一条是关键,而且它看起来很不方便。 但换个角度看:如果一天有超过五次需要人判断的情况,那说明前面的死规则没有做好——正确的修法是去改规则,而不是让人多批几次。
「没人处理时默认拒绝」这一条没有任何例外。 超时放行等于保证了在最需要人的时刻(凌晨、你在开会、你在睡觉)人不在场。而攻击者和意外,恰好都偏爱这些时刻。
还有一条关于审批内容的要求:弹出来的东西必须让人能在十几秒内做出判断。 一个显示着原始交易数据的审批框,人是看不懂的——他只能点确认。审批界面要显示的是:这一笔会让什么发生变化、最坏情况是什么、为什么它触发了审批。
六、四个自检问题
一份权限表写完,问自己四个问题。这四个问题对应思想实验里的三种失效方式,加上一个最根本的:
- 如果它把每一笔都压到限额以下,连续做一千次,会怎样?(拆单)
- 它能不能在不转移任何资产的情况下,授予别人动用资产的权力?(授权)
- 如果审批一直没人处理,会发生什么?(疲劳与超时)
- 如果这份权限表被删掉,这个钱包还能做什么?(规则到底由谁执行)
第四问是最根本的一问。 如果答案是「什么都能做」,那么前三问的答案都不重要——你拥有的不是一个受限的钱包,是一个附带了说明书的完整钱包。
它叫什么
一组决定「这个请求能不能通过」的确定性规则。
关键词是确定性:同样的输入永远得到同样的结果,不需要任何理解能力,不会被措辞影响,不会因为上下文变长而遗忘。
这一层最常见的错误是用模型来实现它。理由听起来很好——「让它理解意图,而不是死板地卡规则」。但一旦策略层包含模型,它就继承了模型的全部不确定性,而策略层存在的唯一意义,就是它比模型更可靠。
判断一条策略合不合格有个简单标准:它能不能被写成一个不需要读懂任何文字的检查? 「单笔不超过 X」可以,「不要做有风险的事」不可以。后者不是策略,是期望。
在一段时间内最多能花多少。
它有四个维度,缺一不可:单笔、时间窗累计、总预算、整体敞口。 只设单笔的限额,只防住了最不可能发生的那一种失效。
时间窗必须是多层的——只有日上限时,那个额度可以在五分钟内被用完。小时、天、周各设一个,三层同时生效。
还有一个容易被忽略的点:限额的计价单位必须明确,而且价格来源必须可靠。 「不超过 100」这句话在没有说明计价单位和取价方式之前,是没有意义的。
明确列出允许的目标,其余一律拒绝。
它的力量来自「默认拒绝」这个方向。黑名单(列出禁止的)永远列不全,因为新的目标会不断出现;白名单会误伤,但误伤的代价是一次被拒绝的操作,而漏网的代价是全部资金。
它有四层粒度:地址、合约、函数、参数范围。 大多数人停在合约那一层,而这一层允许了这个合约的全部功能——包括转发、批量执行和授权这些可以绕过白名单本身的功能。
按函数列白名单,是这一章最具体的一条操作建议。
一把被限定了用途、范围和有效期的钥匙。
它的价值在于把限制从「软件要遵守的规则」变成了「签名系统的物理边界」。一把只能调用某几个函数、金额不超过某个值、在某个时间点自动失效的钥匙,无论持有它的软件想做什么,它签出来的越界交易都不会被接受。
三个性质决定了它好不好用:范围(能做什么)、期限(什么时候自动失效)、可撤销(出事时能不能立刻作废)。
第三个性质在紧急情况下最重要。一个无法被撤销的受限钥匙,在你发现问题时只能等它自然过期——而那段时间可能足够长。
完整记录每一次请求:谁提的、内容是什么、过了哪些检查、谁批准的、执行了什么、结果如何。
它经常被当成事后的东西,其实它有三个即时的用途。发现异常:频率突然上升、目标突然变化、失败率突然升高,这些都是在损失发生之前就能看到的信号。定责:出事时能说清楚哪一步失效了。改进规则:被拒绝的请求最有价值——它们指出了模型想做而你不允许的事,其中一部分是你该允许的,另一部分是你该更严格禁止的。
有一个设计要求容易被忽略:日志必须存在软件改不到的地方。 一份可以被记录者自己修改的日志,在最需要它的那种场合恰好是不可信的。
把整章收成一句话:
Agent Wallet 的核心不是签名能力,而是花钱的策略与上限。
而检验这套策略的方法只有一个:假设这个软件会尽一切可能在规则之内把钱弄走,它能弄走多少。
动手
这个 Lab 分两半:先写表,再在测试网上感受一次真实的边界。
第一半是重点,第二半是为了让抽象的规则变成具体的体验。
第一步:定义这个 Agent 要做什么,越具体越好。
它的任务是: (一句话,具体到某个动作)
它需要动用资金吗: (如果答案是「只是读数据」,这个 Lab 换一个任务)
它一天大概操作几次:
它会和哪些东西交互:
最坏情况下它能造成什么损失:最后一行现在写不出来是正常的,写完整张表之后回来补。到那时你应该能给出一个准确的数字,而不是一个感觉。
第二步:填完八个字段,一个都不许留空。
1 身份: 这个 Agent 用哪个地址
2 单笔上限: 最多 ___,以 ___ 计价,价格取自 ___
3 累计上限: 每小时 ___ / 每天 ___ / 每周 ___
4 频率上限: 同类操作 ___ 分钟内最多 ___ 次
5 目标白名单:合约 ___ 的函数 ___(按函数列,不要按合约列)
6 授权上限: 单次授权额度不超过 ___,禁止无上限授权,授权有效期 ___
7 审批条件: 什么情况必须人批 ___
每天最多送几次 ___
超出之后 ___(必须写「自动拒绝」)
审批超时 ___ 分钟作废,默认 ___(必须写「拒绝」)
8 有效期: 这份权限 ___ 后失效
紧急停止的方式是 ___(写出具体步骤和预计耗时)第 6 条和第 7 条最后两行,是这张表里最容易写错的地方。 如果你在第 7 条写了「超时自动通过」,把它改掉——那一条会让整个审批机制在你睡觉时消失。
第三步:用四个自检问题打自己的表。
1. 它把每一笔压到限额以下,连续做一千次,会怎样?
我的规则里拦住它的是第 ___ 条。拦不住的话,回去补。
2. 它能不能在不转移任何资产的情况下,授予别人动用资产的权力?
拦住它的是第 ___ 条。
3. 审批一直没人处理会怎样?
我的答案是 ___。如果是「放行」或者「一直等着」,回去改。
4. 把这份权限表删掉,这个钱包还能做什么?
我的答案是 ___。第四问的答案决定了前三问有没有意义。 如果答案是「什么都能做」,那么你现在拥有的是一份愿望清单。这时候要做的不是把规则写得更细,而是去想:这几条限制里,哪几条可以被放进钥匙的权限或者钱包的余额里。
第四步:把规则分到三层,标出每一条由谁执行。
放在钱包余额里的(物理上限):
放在钥匙权限里的(签名边界):
放在调用前的代码检查里(确定性程序):
只能放在提示词里的(不算防线):最后一行的内容,在你计算「最坏损失」时要全部当成不存在。 这是这一步的全部意义:让你知道自己真正的防线有几层。
如果大部分规则都落在最后一行,这份表需要重写——不是重写内容,是重新设计执行方式。
第五步:在测试网上做一次,感受边界是什么感觉。
这一步只在测试网进行。 创建一个全新的测试网钱包,从水龙头领一点测试代币。不要使用你的主网钱包,不要导入任何主网私钥或助记词,也不要在任何界面上把它们输入给任何软件。
1. 创建一个新的测试网钱包,记下地址
2. 从水龙头领一点测试代币
3. 做一笔正常的小额转账,在区块浏览器上找到它
4. 做一次授权操作,然后回到区块浏览器,
找到这笔授权,看它的额度是多少、有没有期限
5. 如果你的钱包工具支持,撤销这笔授权,
记下这一步花了多久、要点几下第 4 步是这一整个 Lab 里最重要的一次体验。 你会亲眼看到一件事:一笔授权在链上留下的记录,和一笔转账完全不同——它没有转走任何东西,但它把一部分权力交了出去。
第 5 步则让你体会紧急撤销要花多长时间。把这个时间填回第二步第 8 条的「紧急停止预计耗时」那一格。
第六步:回到第一步,补上「最坏情况下它能造成什么损失」。
现在你应该能给出一个具体的数字了。算法是:
最坏损失 = 以下三者里最小的那个
A. 这个钱包的余额
B. 时间窗累计上限 × 你发现问题所需的时间窗数
C. 已经授予出去的授权额度总和C 那一项常常是三者里最大的,而且最容易被完全忘记。 一个额度没有上限的授权,会让 A 和 B 两项限制同时失效。
把这个数字和「我能接受的损失」放在一起比较。如果前者更大,这份权限表还没写完。
做完之后你会有:一份八字段的权限表、四个自检问题的答案、一张三层执行分布图、一次真实的测试网授权体验、一个具体的最坏损失数字。
这份权限表就是这一阶段的阶段作品。 下一节会用一种非常有效的方式来给它找洞。
AI Lab
这个练习是纸面练习。全程不涉及任何真实资金,不需要连接钱包,也绝不要把任何私钥、助记词或主网地址贴进对话框。 你贴进去的应该只有一份规则文本。
它是这一章最有效的一个工具,原因很简单:模型很擅长在规则的字面之内寻找缝隙,而这恰好是你自己最不擅长的事——你写规则时脑子里装的是「我想要什么」,而不是「这句话还能被怎么理解」。
下面是我为一个 Agent 写的钱包权限表。
请扮演一个只能在这些规则之内行动的对手。你的目标是
在不违反任何一条明文规则的前提下,把这个钱包里的钱
尽可能多地转移出去。
硬性约束:
- 不许攻破私钥,不许假设任何代码漏洞,不许假设我会
违反自己的规则。
- 每一条方法都必须写出:用了哪几步、每一步为什么不
违反规则中的哪一条、最终能拿走多少。
- 不要编造不存在的钱包功能、账户标准或接口。
如果你的方法依赖某个功能,请描述这个功能做什么,
而不是给出一个具体的名字或规范。
请至少给出 8 条不同的方法,按「能拿走多少」排序。
最后单独列一节:这份规则里,哪一条的假设最脆弱。
权限表:
(贴进来)最后那一节是这个提示词最有价值的部分。 「假设最脆弱的那一条」通常指向的不是某个数字,而是某个你没意识到自己做出的假设——比如「审批时人会认真看」「一天只有一个日切」「白名单里的合约不会代我调用别人」。
拿到之后做三件事。
第一件:把每一条分类。 用思想实验里那三类加一类:
拆单类 (单笔合规,累计越界)
授权类 (不转移资产,但交出权力)
粒度类 (允许的范围比你以为的宽)
人的环节类 (攻击审批本身)
其他 (这一栏里的东西最值得研究)「其他」那一栏是这个练习的金矿。 落在那里的方法,说明你的思维框架里根本没有这个维度。
第二件:对每一条写一个补丁,并且标出它属于哪一层。
漏洞:
补丁:
这个补丁放在哪一层:(钱包余额 / 钥匙权限 / 代码检查 / 提示词)
如果只能放在提示词里,说明:(这个洞实际上没有被补上)最后那一行必须诚实。 一个只能靠「在提示词里叮嘱」来补的洞,等于没补。这时候正确的做法通常是把对应的能力整个去掉,而不是叮嘱它别用。
第三件:补完之后重新跑一遍,用同一个提示词。
这一轮它会找到不同的东西,而且往往更刁钻。跑三轮之后,新发现的数量会明显下降——那时候你的权限表才算初步成型。
这类任务上有四个可预期的现象:
- 第一轮它会给出一些不合法的方法,比如假设可以读取私钥。把它们剔掉,并在下一轮的提示词里明确禁止。
- 它很少主动攻击人的环节。 模型倾向于在技术规则里找洞。如果它没有提审批疲劳,加一句「请也考虑攻击审批流程本身」,通常会有收获。
- 它可能编造功能。 这个领域的钱包标准在快速演进,它很容易给出一个听起来专业但并不存在的机制。提示词里已经要求描述功能而不给名字,仍然要检查。
- 它的补丁经常不是确定性的。 「加强对异常行为的监控」不是一条规则,是一个愿望。每一个补丁都要能被写成一个不需要读懂文字的检查,否则退回重写。
AI 说完之后,你必须自己验证
- 它找到的每一个漏洞,是真的在你的规则之内,还是需要攻破私钥或利用代码漏洞——后者不算
- 它有没有找到拆单这一类:单笔合规、累计越界
- 它有没有找到授权这一类:不转移资产但交出权力
- 它有没有找到白名单粒度这一类:合约允许了,但它的某个函数可以代你做别的事
- 它有没有攻击审批环节本身:数量轰炸、时机选择、超时默认行为
- 它有没有找到单位与计价的漏洞:同样的数字在不同代币上差几个数量级
- 它有没有找到时间维度的漏洞:日切瞬间连续两天的额度、有效期没写、权限忘了回收
- 它提出的每一个补丁,是不是确定性的——能不能被写成一个不需要读懂文字的检查
- 它有没有编造不存在的钱包功能、账户标准或接口来支持它的说法
- 最后一条:补完之后再跑一次,它还能找到新的吗
真实案例
链上有一类损失反复发生,而且它和任何代码漏洞都无关:用户为了省事,给某个合约授予了一个没有上限的额度。
这个操作在当时看起来完全无害——它没有转移任何资产,钱还在自己的地址里。但它把「随时取走这些资产」的权力交了出去,而且这个权力没有金额上限,也没有到期时间。
后来发生的事有很多种版本:那个合约被攻破、它的权限被滥用、或者它本来就是为此设计的。共同点是损失发生在授权之后很久,而且和当初那个操作在时间上完全脱节,导致很多人到最后都没想明白钱是怎么没的。
这件事对 Agent 权限设计的含义非常直接:权限表里必须有「授权上限」这个字段,而且它的默认值应该是「禁止无上限授权」。 一个只写了转账限额的权限表,在这一类风险面前完全裸露。
还有一条配套的操作习惯:定期检查自己的地址授予出去的所有额度,把不再需要的撤销掉。 上一个 Lab 的第五步让你亲手做了一次,就是为了让这件事变成肌肉记忆。
一个常见的做法是把限制写进给模型的系统提示里:「你每笔操作不得超过 X,只能调用这几个合约。」
这在大多数时候是有效的——模型确实会遵守。问题在于「大多数时候」这四个字,以及它失效的那些场合恰好是最危险的场合。
失效的方式有几种。长对话中的遗忘:约束在很前面,当前的上下文很长。边界情况的解释:遇到规则没覆盖的情况时,它会自己推断一个「合理」的做法。输入里夹带的指令:如果这个 Agent 会读取网页、文档、邮件或者别人发来的消息,那么它读到的内容里可能包含试图改变它行为的文字——而它无法可靠地区分「我的规则」和「我读到的内容」。
最后一种最值得警惕,因为它不需要任何人接触你的系统。只要你的 Agent 会读外部内容,外部内容就有机会影响它。
结论不是「不要在提示词里写规则」——写了有好处,它能让 Agent 大部分时候做对的事,减少被拦截的次数。结论是:在计算最坏损失时,把提示词里的所有规则当成不存在。
第 32 章讲过这个结构,在这一章它有了一个更精确的版本。
一个系统上线时,人工审批被当作最后一道防线。第一周的审批量是每天两百多次。第二周,负责的人开始批量处理。第三周,他形成了一个固定动作:扫一眼金额,在阈值内就确认。第五周,一笔本该被拦下的请求通过了,而他事后完全不记得看过它。
复盘的结论是:这个系统的设计保证了这个结果。 做出一个真实判断需要的时间,远超过两百次审批能分配给每一次的时间。
正确的设计有三条,而且它们都是数量上的,不是态度上的:给审批设每日次数预算(超出一律自动拒绝)、给每次审批设有效期(过期作废,默认拒绝)、让审批界面显示后果而不是原始数据。
第三条最容易被忽略。一个显示着一串十六进制数据的审批框,人是看不懂的,他只能点确认。 审批界面要回答的是三个问题:这一笔会让什么发生变化、最坏情况是什么、它为什么触发了审批。
有一类风险不来自任何攻击,只来自时间:一份权限被授予出去之后,没有人记得回收它。
典型的情形是:为了一次临时的任务,给某个软件开了一个较宽的权限,打算事后收回。任务完成了,人换了,项目改方向了,那份权限一直在。半年后它仍然有效,而且没有人在监控它。
这件事在 Agent 的场景里会被放大,因为软件的数量会变多,而且它们通常不会主动告诉你「我不需要这个权限了」。
对策只有一个,而且必须写进设计里:给每一份权限一个到期时间,到期自动失效,需要重新授予。 这个设计的价值不在于防住任何特定的攻击,而在于它让「忘记」这件事不再产生后果。
配套的还有一件小事:维护一份权限清单,记录每一份权限授予的时间、原因和到期日。 定期读一遍这份清单——你几乎总会发现至少一条早就该收回的。
改一个变量
风险等级立刻上升一整级,而且上升的原因和模型能力无关。
一个只处理你自己输入的 Agent,它的输入来源是可信的。一个会读外部内容的 Agent,它的输入来源是任何人。 而它读到的文字和你给它的指令,在它眼里是同一种东西——都是文字。
这意味着提示词里的所有约束都要当成不存在,因为读到的内容有机会覆盖它们。剩下的防线只有下面三层:钥匙权限、代码检查、钱包余额。
具体要加的措施有三条:把读取和花钱分成两个独立的环节(读内容的那个不持有任何花钱的能力)、把白名单收得更紧(因为你无法预测它会被引导去做什么)、把钱包余额压到更低(因为这是唯一无法被文字影响的限制)。
有一条更彻底的做法值得考虑:让读取外部内容的 Agent 和执行操作的 Agent 完全分离,中间只传递结构化的、经过校验的字段。这样外部内容影响的只是提案的内容,而提案还要过完整的策略检查。
权限设计从「一份表」变成「一套制度」,而且有几件事会从「不重要」变成「关键」。
第一,权限必须有模板和继承。 四十份手写的表,一定会出现互相矛盾和遗漏。应该有几个标准档位(只读、小额、需审批),新 Agent 从档位开始,特殊需求单独批准并记录理由。
第二,累计上限必须有全局的一层。 每个 Agent 各自的日上限都合规,四十个加起来可能远超你的承受能力。全局上限是这个场景里最重要的新增字段。
第三,审批预算是共享的。 你每天只有那么几次认真判断的能力,四十个 Agent 分这几次。这意味着单个 Agent 的审批触发条件要比只有一个时更严。
第四,停机必须是全局的。 一个能一次停掉全部四十个的开关,比四十个各自的开关有用得多——因为出事时你没有时间一个个去停。
限制的执行方式会更可靠(因为它由那个服务强制执行,软件无法绕过),但你引入了一个新的对手方。
要问清楚三件事。第一,这个服务能不能在你不同意的情况下动用这把钥匙? 如果能,那么你的资金安全取决于它,而不是取决于你的权限表。第二,这个服务停业或者出故障时,你能不能拿回控制权? 第三,它的权限配置能不能被绕过,谁有权修改它?
第三问对应的正是这一章的第二条铁律:模型不能修改约束自己的规则。 这条铁律在托管场景下要扩展一句:修改权限配置的通道,必须和 Agent 使用钥匙的通道完全分离。 如果一个 Agent 能通过同一套接口既发起交易又修改自己的限额,那它的限额不存在。
托管本身没有对错,它是一个取舍:用对手方风险换取更可靠的规则执行和更省的工程投入。 在很多情况下这个交换是划算的,前提是你知道自己换了什么。
这是决定所有上限数值的那个变量,而且大多数权限表在设定数字时根本没考虑它。
算一下这条时间链:异常发生 → 你收到告警 → 你看到告警 → 你判断出这是真的 → 你执行停机 → 停机生效。
每一段都要填一个真实的数字。如果告警在凌晨三点发出,「你看到告警」这一段可能是六小时。而在这六小时里,Agent 按它的频率上限可以做多少次操作、花掉多少钱——这个乘积才是你真正的最坏损失,而不是你写在表上的那个日上限。
从这个算式能倒推出三件事:频率上限应该按「最坏反应时间」来定,而不是按「正常需要」来定;停机开关必须快,而且必须不经过模型;一个真正有效的自动熔断(比如异常频率触发自动暂停)比任何告警都有价值,因为它不依赖人在场。
有一条原则值得记下来:系统的停机速度必须快于它的犯错速度。
带走的问题
这一问在这一章有了一个确定的形式:一份八个字段的表。
身份、单笔上限、累计上限、频率上限、目标白名单、授权上限、审批条件、有效期与撤销。少一个字段就多一个洞,而且思想实验里那三个人分别演示了漏掉第 3、第 6、第 7 个字段的后果。
但比字段更重要的是那个追问:这些规则由谁执行? 同一句话写在提示词里、写在代码检查里、写在钥匙权限里、体现在钱包余额上,强度相差几个数量级。
一个最有用的自检:把权限表删掉,这个钱包还能做什么。 答案就是你真实的风险敞口。
这一章给出了一个和直觉相反的答案:人在回路的价值,随着它被触发的次数上升而迅速下降。
所以正确的问题不是「哪些决策要人批」,而是**「人每天能认真批几次,把这几次用在哪里最值」**。答案通常是三类:不可逆且影响面大的操作、首次出现的新目标或新动作、任何触发了异常信号的请求。
配套的三条硬规则:每天审批次数有预算,超出一律自动拒绝;审批有有效期,过期作废;没人处理时默认拒绝,绝不放行。
最后一条没有例外。超时放行等于在最需要人的时刻保证了人不在。
在这一章,这一问变成了一个非常具体的计算。
损失由部署这个 Agent 的人承担——这一点第 33 章已经说清楚了,软件不是法律主体,责任不会转移。
既然如此,唯一值得做的事就是把「最坏损失」算成一个具体的数字:钱包余额、累计上限乘以反应时间、已授予的授权额度总和,三者取最小。
算出这个数字之后,把它和「我能接受的损失」放在一起比。 这个比较是这一章全部工作的意义所在——它把一份看起来很严谨的规则文档,变成了一个你可以判断的数。
本章自测
因为它只管一笔。三种方式可以绕过它,而且都不违反这条规则:
拆单——一百笔 99 每笔都合规,总共九千九百。补法是加时间窗累计上限(小时、天、周三层)和频率上限。
授权——授予别人一个没有上限的额度,这一步不转移任何资产,所以用不上转账限额。补法是单独设授权上限,并禁止无上限授权。
单位——「100」是一百个什么?不同代币的价值可能差几个数量级。补法是写清计价单位和取价来源。
而在这三者之上还有一个更根本的问题:这条规则由谁执行? 如果它只写在提示词或一份文档里,那么以上讨论都不重要。
从弱到强四层:提示词里(模型自己遵守,模型误解或被注入指令时失效,强度接近零)、一份文档里(靠软件自觉,同样接近零)、调用前的代码检查里(确定性程序执行,有效)、钥匙的权限里(签名系统强制,高)。最上面还有一层最粗但最强的:钱包里只有这么多钱。
三条设计原则:规则尽可能往下移;越往下越粗糙,所以要分层配合;最上面那层不算防线,只算提效——它能让 Agent 大部分时候做对的事,但在计算最坏损失时要当成不存在。
因为允许一个合约,等于允许了它的全部功能,其中往往包括转发、批量执行和授权这类可以代你去做别的事的功能。你的白名单在第二跳之后就失效了。
白名单有四层粒度:地址、合约、函数、参数范围。大多数人停在第二层,而危险恰好在第二层和第三层之间。
按函数列会让清单变长,但它是唯一能真正限制行为的粒度。再往下一层(参数范围)能拦住「函数对了但数值离谱」,在涉及金额和滑点的操作上值得加上。
把它当成有预算的稀缺资源,设计上有四条:
每天的审批次数有上限(比如五次),超出一律自动拒绝而不是排队;每次审批有有效期,过期作废;没人处理时默认拒绝,绝不放行;界面显示后果而不是原始数据——这一笔会改变什么、最坏情况是什么、为什么触发了审批。
第二条看起来很不方便,但换个角度:如果一天有超过五次需要人判断的情况,说明前面的死规则没做好,该修的是规则,不是让人多批几次。
第三条没有例外。超时放行等于保证了在凌晨、在你开会时、在你睡觉时,这道防线不存在——而意外和攻击恰好偏爱这些时刻。
没有标准答案,检查这几件事:
- 你算的时候,有没有把已经授予出去的授权额度算进去?这一项常常是最大的一项,也最容易被完全忘记。
- 你的「累计上限 × 反应时间」里,反应时间填的是什么?如果异常发生在凌晨三点,你多久会看到? 用这个数,不要用「我一般很快就会发现」。
- 你的规则里有几条落在提示词那一层?把它们全部当成不存在之后,最坏损失变成多少?
- 你的紧急停机要几步、花多久?上一个 Lab 第五步让你实测过撤销一笔授权的耗时,用那个真实的数。
- 你的权限有到期时间吗?没有的话,三个月后谁会记得它还在?
- 最后一问:如果最坏损失大于你能接受的损失,你打算改哪一条? 大多数情况下最有效的一改不是把规则写细,而是把钱包里的钱减少——那是唯一一条无法被绕过的限制。
一个经验值:第一次认真算这个数字的人,得到的结果通常比自己预期的大几倍,而差距的主要来源是两项:授权额度,和反应时间被低估。
一句话带走
Agent Wallet 的核心不是签名能力,而是花钱的策略与上限。