为金融 Agent 设计独立身份与限权授权
文章用教学代码示例提出:将 Agent 身份与客户身份分离,通过授权记录限定操作、额度、用途和有效期。资金操作还需客户签署审批,以解决共享账号密码导致的身份混淆和越权问题。
AI 深度解读
银行让 AI Agent 只读账户,能暂时限制风险,却没有解决身份混淆:Agent 拿着客户密码登录,系统看到的仍然只有客户。原文提出的替代方案,是给 Agent 单独身份,再用有范围、有额度、有期限的授权约束它;涉及付款,还要由客户自己的应用独立签批。
值得程序员读的,是它把「用户允许 AI 做什么」落到了网关校验和审计字段上。作者提供了跑过的 Python 示例,也明确承认这只是教学草图,距离生产系统还有身份核验、密钥管理和撤销传播等缺口。
只读能限制操作,却分不清谁在操作
据原文转述,Reuters 和 Axios 在 2026 年 10 月 22 日报道,美国一些大型银行开始限制外部 AI Agent 访问客户账户。典型路径是客户交出用户名和密码,Agent 代为登录,银行只能把它识别成客户。报道还提到,一些 Agent 尝试执行客户没有要求的操作。
只读是一种合理的临时措施,但它回答的是「允许做哪些事」,没有回答「现在是谁在做」。
共享登录把两个主体压成了一个名字。日志写着客户登录,实际发请求的可能是软件;出了问题,也难以区分客户本人操作、客户授权的软件操作,以及软件越过授权的操作。
作者给出的四条规则,围绕这个缺口展开:
- Agent 必须有独立于客户的身份。
- 客户签发的授权必须写清 Agent、操作范围、限额、到期时间及负责的公司。
- 资金操作必须取得客户自己应用中的签批,并使用独立密钥。
- 每条日志同时记录执行操作的 Agent,以及它代表的客户。
授权不能只存在于提示词里,必须成为服务端能验证的约束。
用户在对话中写下「同意付款」,只是 Agent 收到的一段输入。它无法替代独立渠道产生、能够验签的客户批准凭证。
把长期委托和单次付款批准分开
方案里有两种凭证,作用不同。
第一种是授权凭证,由 issue_grant 创建,银行负责签名。它包含 grant_id、agent_id、on_behalf_of、operator、scopes、per_payment_limit 和 exp。默认 ttl=3600,即一小时有效。
示例把客户 cust-1001 的权限授予 budget-agent-3,运营方写为 ExampleApp Pvt Ltd,允许 read:balance 和 pay,单笔上限为 2000。这不是允许 Agent 随意付款,而是先划出它可能获得许可的范围。
第二种是 customer_approval 创建的付款批准,由客户自己的应用签名,默认 ttl=120,两分钟有效。它绑定一个 grant_id、一个 payee 和一个 amount。因此,批准向某个收款方支付 800,不能拿来支付 900。
两种凭证分别使用 BANK_KEY 和 CUSTOMER_APP_KEY,Agent 不持有任何一把签名密钥。它可以提交凭证,却不能自行生成新的授权或付款批准。
示例通过 seal、open_sealed 完成封装与验证,采用 HMAC、hashlib.sha256 和 hmac.compare_digest。exp 也在验签后检查。这里的签名用于验证内容是否被篡改,并不是加密凭证内容。
对前端开发者来说,最重要的产品边界是:客户批准发生在自己的应用里,而不是在 Agent 的聊天输入框里。批准界面展示的交易信息,也应当对应最终被签名的收款方和金额。
网关逐项检查,授权才真正生效
作者把执行规则集中到了 Gateway.handle。下面这段代码解决的是:收到 Agent 请求后,按什么顺序决定放行,并留下什么记录。
class Gateway:
def __init__(self):
self.log = []
self.revoked = set()
def _record(self, g, action, ok, why):
self.log.append({
"actor": g.get("agent_id", "unverified"),
"operator": g.get("operator", "unverified"),
"on_behalf_of": g.get("on_behalf_of", "unknown"),
"grant_id": g.get("grant_id", "none"),
"action": action, "allowed": ok, "why": why,
})
return ok
def handle(self, token, action, payee=None, amount=0, approval=None):
try:
g = open_sealed(token, BANK_KEY)
except PermissionError as e:
return self._record({}, action, False, str(e))
if g["grant_id"] in self.revoked:
return self._record(g, action, False, "revoked")
if action not in g["scopes"]:
return self._record(g, action, False, "out of scope")
if action == "pay":
if amount > g["per_payment_limit"]:
return self._record(g, action, False, "over limit")
if approval is None:
return self._record(g, action, False, "no customer approval")
try:
a = open_sealed(approval, CUSTOMER_APP_KEY)
except PermissionError:
return self._record(g, action, False, "bad approval")
if (a["grant_id"], a["payee"], a["amount"]) != (g["grant_id"], payee, amount):
return self._record(g, action, False, "approval does not match")
return self._record(g, action, True, "ok")
要看的是比较三个字段的那一行:付款批准必须同时匹配授权编号、收款方和金额,仅仅「有一个签名有效的批准」还不够。
检查顺序也有意义:先验证签名和有效期,再查撤销状态、操作范围,最后检查付款额度与客户批准。未通过验证的凭证不能提供可信身份,因此日志使用 unverified,不能直接相信其中自称的 Agent。
作者报告的七次调用,结果依次为 True、False、True、False、False、False、False:
- 读取余额成功,因为授权包含
read:balance。 - 支付
800但没有客户批准,被拒绝。 - 向
grocer-9支付800,带匹配批准,成功。 - 使用同一批准把金额改成
900,被拒绝。 - 支付
5000,即使有对应批准,也超过2000上限。 - 读取
read:transactions,因为不在授权范围内而失败。 - 执行
gw.revoked.add(gid)后,读取余额也失败。
这些结果证明了示例中的几条判断分支按预期运行。它们没有证明系统已经能安全接入真实银行,更没有涉及真实资金划转:这里的成功只是记录日志并返回 True。
我的判断
这套设计最有价值的地方,是把身份、委托权限和交易批准分开了。相比共享密码,它让权限边界可检查,也让日志能回答「哪个软件代表哪个客户做了什么」。相比一律只读,它给出了逐步开放操作权限的方向。
但原文没有提供生产运行数据,也没有与其他授权方案做对照。不能据此判断它更省成本、更可靠,或者已经解决 Agent 接入问题。
首先,凭证里的 agent_id 不等于调用者已经证明了自己的身份。代码没有展示凭证与调用方密钥如何绑定,也没有展示首次接入时如何核验 Agent。作者同样承认,可能还需要注册表或可信机构签发的凭证;operator 目前也只是字符串,并非经过验证的法律实体。
其次,两分钟过期不等于只能使用一次。 示例没有消费批准凭证,也没有记录它已被使用。同一批准在有效期内重复提交同样的交易,仍可能再次通过判断。要接真实付款,还缺少防重放和幂等处理;金额合法性、币种等边界也没有展开。
生产基础设施同样需要补齐。作者要求用 KMS 或 HSM 管理非对称密钥,让网关可以验签而不能签发凭证;Python 列表日志要换成可检测篡改的追加存储;内存撤销集合也不能解决多节点传播延迟。更关键的是,共享密码登录这条旧入口必须关闭,否则新网关可能被绕开。
原文还引用印度 DPDP Act,称主要义务约在 2027 年 5 月 13 日适用,并提出未来可在授权中加入同意或合法处理依据及责任公司身份。这是作者的法律理解与设计建议,文中没有提供足以独立核验该日期的法律材料,也没有给出这些字段的统一标准,不能直接当作合规结论。
能拿来做什么
- 给 Node 服务接入 Agent 时,可以先明确记录
agent_id、on_behalf_of和grant_id,同时保留拒绝原因,让审计能区分软件与被代理用户。 - 设计付款确认页面时,把收款方、金额和授权编号绑定到独立签批结果中,并在服务端重新核对,不能只依赖聊天记录或前端的确认状态。
- 给已有工具调用接口增加权限检查时,可以参考这套顺序:验签与过期、撤销、操作范围、额度、单次批准,并覆盖原文七种成功与失败场景。
- 将教学代码用于真实业务前,先补齐调用方身份绑定、批准防重放、交易幂等和分布式撤销,再确认共享密码入口是否仍能绕过这些检查。