让一个 Agent 给出调仓建议并不难,难的是让它在状态变化、网络超时和重复任务里只花掉被允许的那部分钱。模型说“已完成”,终端打印“已结算”,都不能替代链上回执。这是我更愿意在 1024 讨论的工程问题:一条自动化流水线,究竟在哪一步获得了执行权,又凭什么宣布任务结束?
以太坊基金会 10 月 5 日关于交易结果断言的讨论,把签名意图与最终结果的差距摆到了台面上。它提出的原生断言仍是研究与升级讨论中的机制,不能当成任意网络已经具备的现成功能。对今天的应用开发者而言,首先能做的是把提案、授权和结果核对拆开。官方讨论
六个阶段,六份不同证据
读取阶段固定链、区块身份和数据来源;策略阶段只输出结构化提案,不触碰密钥。校验阶段检查金额、合约白名单、有效期和重放标识;签名阶段由独立组件依据授权策略确认真实签名。发送阶段获得交易哈希;回执阶段再判断交易是否入块、是否成功,以及资产变化是否符合预期。
这六份证据不该用一个布尔值串起来。RPC 超时只说明客户端不知道结果;换节点重发前,应先查原哈希和账户 nonce。交易成功也不自动等于经济目标达成,最低接收量、支出上限等条件需要在执行路径中落实。
ERC-4337 定义了 UserOperation、Bundler 和 EntryPoint 的分工。它提供账户抽象基础设施,不负责证明模型的策略正确,也不承诺任何提案都能入块。协议原文
一个只负责“是否允许进入签名阶段”的例子
下面程序不生成签名,不连接节点。金额单位是整数最小单位,样例地址是符号名;它刻意把输出停在READY_FOR_SIGNING,避免把教学状态机写成真实链上集成。
from dataclasses import dataclass @dataclass(frozen=True) class Proposal: target: str amount: int nonce: int expires: int def authorize(p, now, expected_nonce): checks = { "target": p.target in {"vault-A"}, "amount": 0 < p.amount <= 100, "nonce": p.nonce == expected_nonce, "expiry": now <= p.expires, } failed = [name for name, ok in checks.items() if not ok] return "REJECT:" + ",".join(failed) if failed else "READY_FOR_SIGNING" assert authorize(Proposal("vault-A", 50, 7, 200), 100, 7) == "READY_FOR_SIGNING" assert authorize(Proposal("vault-A", 101, 7, 200), 100, 7) == "REJECT:amount" assert authorize(Proposal("vault-A", 50, 6, 99), 100, 7) == "REJECT:nonce,expiry" assert authorize(Proposal("other", 50, 7, 200), 100, 7) == "REJECT:target" print("4 policy cases passed; no signing or network call")保存为policy.py,运行python3 policy.py。这里的 nonce 只是策略输入,真实系统必须由持久化状态及链上账户状态共同管理,不能在并发请求中分别检查后再各自放行。签名者还应把策略版本、链标识、目标、金额和有效期绑定到同一份请求,避免校验后内容被替换。
什么时候不能继续自动化
拿不到固定区块的数据,就不能把两次读取拼成一份“快照”;旧提案对应的代码或授权策略变了,就需要重新审批;发送结果不明,就进入待核对队列。自动化最危险的地方往往不是拒绝太多,而是用成功状态掩盖不知道。
示例已做离线分支验证,只覆盖四项策略判断。真实模型推理、密钥保存、密码学签名、RPC 广播、链重组和资产结算均未接入或测试,不能据此推断生产安全性或性能。链上 Agent 的自主程度可以逐步提高,但执行权和结果证据要始终能够被人追问。