上周五晚上,我在日志里看到一条由AI代理自动发起的支付交易,金额18美元,收款方是一家API服务商,签名由运行在不同设备上的阈值密码学密钥网络共同完成。这事儿放在一年前我完全不敢想,因为当时所有Agent的私钥都躺在同一台测试机上,谁拿到那台机器,谁就能替它花光全部数字资产。这篇内容想讲的,就是这个项目——如何用Threshold Cryptography(阈值密码学)把AI Agent的支付和数字资产管理从单点密钥依赖里解放出来,让代理在无人盯守时也能安全地花钱、结算和管理链上资产。
如果你正在做AI Agent相关的基础设施,或者你已经把代理接到钱包里但总觉得私钥存放不踏实,这篇内容应该能帮上忙。我不打算只给概念,会把项目背景、签名协议原理、系统架构、代码骨架和实测踩坑都拆开讲,尽量让有后端或合约基础的读者可以直接照着搭。
1. 别急着让Agent“自主”,先弄清它怎么付钱
过去两年里,我接触到的AI Agent项目几乎都会遇到一个尴尬阶段:功能跑通了,但一旦涉及花钱,整个流程就卡住。更麻烦的是,很多人嘴上说要让Agent自主决策,手里却攥着私钥不敢放。
1.1 一个Agent真正需要的支付场景
先说最常见的几类需求。第一类是调用付费API,比如大模型接口、数据源订阅、图片生成服务,这类消费高频但单笔金额不大,代理需要连续调用几十次甚至上百次。第二类是代用户购买云资源或SaaS席位,频率低但金额高,每一笔都值得人工复核。第三类是链上数字资产管理,代理替用户完成兑换、转账、授权合约调用,这类操作直接作用在资产上,风险和收益都被放大。
我把这些场景列出来,是因为它们对签名机制的要求完全不同。高频小额需要让签名尽量自动化、低摩擦;低频大额需要引入更强的人工或策略干预;链上资产操作则要考虑链类型、签名算法兼容性、以及事后审计的可追溯性。一个只支持“把私钥放进环境变量”的方案,根本覆盖不了这些差异。
1.2 单点私钥是一个迟早会爆的雷
很多Agent项目最开始的方案非常简单:在服务器上生成一个钱包私钥,丢进环境变量或加密配置里,代理需要付款时直接用它签名。这个方案跑Demo很顺利,但在真实环境里非常脆弱。
服务器被入侵是最直接的威胁,但还有几个更隐蔽的问题。私钥常驻在应用内存里,意味着任何代码执行漏洞都可能把它带出来;如果团队里有人把日志打印全了,或镜像被错误拉取,私钥也会泄露。换个方向看,如果我把私钥放进用户手机上的硬件钱包,那Agent每次签名都要用户掏设备确认,自动化和“Agent感”就荡然无存。如果引入中心化托管服务,私钥不归自己控制,又等于把代理的支付能力外包给一个巨大攻击面。
实际项目里我见过最惊险的一次,是生产服务器上某张表被注入了可执行内容,攻击者差点就在Agent运行时环境里给出任意签名指令。虽然那次没有实际损失,但我意识到:把完整私钥放到任何一个单一环境,都是这个系统的致命弱点。所以才开始认真研究阈值密码学这条路。
1.3 阈值密码学提供的第三条路
阈值密码学最简单的解释,是“一个私钥被分成多份,必须凑够一定数量的分片才能完成签名”。它跟传统的私钥托管完全不同:没有任何一个设备上存在完整私钥,被攻破一两个分片也不会让攻击者拿到签名能力。
对于AI Agent场景,这个性质尤其有价值。我可以把分片分别放到用户手机、云端节点、硬件安全模块里,采用比如(2,3)的阈值结构,任意两方协作就能签名。这样Agent自动签名不需要人工插拔设备,但任何单一节点失陷都无法单独花钱。听起来像是一个完美的中间态,但真正落地时还有很多协议细节要处理,这就是接下来的核心。
2. 阈值签名不是“把钥匙切三段”,而是分布式地算出签名
刚开始研究这个方向时,我也犯过概念性错误,以为阈值签名就是把一把私钥切成三段,每一段叫一个分片,签名时先把三段合起来再签。但真正的阈值签名协议不是这么工作的,理解这一点对后续选型和排错非常重要。
2.1 从Shamir秘密共享到“不重构私钥”的签名
密码学里的Shamir秘密共享是一个很经典的构造:把一个秘密值变成n个分片,任意t个分片可以还原秘密,少于t个则什么都得不到。例如(2,3)方案里,三个分片分别来自三个参与方,任意两个分片可以还原出完整秘密。
但这个经典方案有一个问题:如果每次都需要先还原完整私钥再做签名,那签名现场就出现了单点。不管是哪个节点来做重组,那个节点都已经拿到完整私钥,一旦被监控或篡改就会泄露。阈值签名方案(Threshold Signature Scheme,TSS)解决的就是这个问题,它的核心思想是所有参与方在分布式状态下直接完成签名计算,过程中不出现任何位置的完整私钥。
打个比方:传统的Shamir方式是“把拼图碎片凑齐,拼出完整拼图”;阈值签名则是“碎片各自留在不同人手里,大家靠对话直接完成一次盖章”,盖出来的章和完整私钥盖出来的一模一样,但没人见过完整拼图。这个性质让私钥的单点风险从物理上消失了。
2.2 分布式密钥生成与签名分片
要让上述过程成立,首先要解决密钥从哪里来的问题。协议里一般采用分布式密钥生成(DKG),让n个参与方共同生成一个公共公钥,同时各自获得一份私钥分片。整个过程没有可信第三方,也没有谁先持有完整私钥再把它切开。
签名阶段则更像一场多人配合的投票。假设使用(2,3)阈值,某次签名需要两方参与,两个分片节点各自以私钥分片和待签名的消息摘要作为输入,生成各自的“部分签名”。这些部分签名被汇总后,可以由一个组合器合成一个完整的、标准格式的数字签名。关键点在于,任意一方在协议结束时,除了自己的分片和最终签名,看不到别人的分片,也推导不出完整私钥。
这个机制听起来抽象,但在工程上已经有许多成熟协议实现,例如FROST这类基于Schnorr的阈值签名,以及面向不同椭圆曲线库的多种MPC签名协议。它们通常在性能和安全证明上做了大量优化,例如在签名阶段引入随机盲化因子,来防止某些精心构造的消息导致分片信息泄露。
2.3 TSS和多签为什么不是一回事
在钱包场景里经常有人混用“阈值签名”和“多签”,直觉上它们很像,都是m-of-n,但底层差异很大。
多签在链上表现为一个多签地址或脚本,一笔交易的合法性来自多个独立私钥的多个签名。无论在比特币的脚本里还是以太坊的合约里,链上都能直接看到这个账户是“n取m”结构,交易成本也随签名数量上升。TSS则不同,它对外表现成一个普通地址和一个普通私钥,只不过这把私钥在生成时就被打散到多个节点。它产出的签名和普通签名没有区别,链上完全无感知,交易成本和单签一样低。
我在Agent场景里最终选择TSS而不是多签,原因很简单:我希望代理账户在链上就是一个普通账户,兼容所有现有协议和合约接口,又希望多节点对私钥进行联合控制。多签需要改造账户模型,TSS则可以在账户模型不变的前提下完成安全升级,这对数字资产管理和支付通道集成都友好得多。
3. 把阈值密码学装进Agent支付与资产管理网关
理论归理论,真正让这套密码学方案在业务里跑起来,还需要把它嵌进一个完整的支付与资产管理链路。这个项目里,我没有把TSS做成一个孤立服务,而是设计了一套多组件配合的网关。
3.1 系统里需要哪些组件
我按照职责把系统拆成了五层:Agent运行时、策略引擎、分片签名网络、交易构造与广播层、资产管理层。这几层各自负责一件事,彼此通过明确的接口协作。
| 组件 | 职责 | 关键考量 |
|---|---|---|
| Agent运行时 | 接收用户意图、规划任务、发起支付请求 | 不能让LLM直接构造交易,避免上下文注入 |
| 策略引擎 | 校验金额、收款方、频次、时间窗 | 用结构化规则限制边界,而不是依赖模型自律 |
| 分片签名网络 | 持有私钥分片,参与DKG和阈值签名 | 节点分布在多个信任域,单点失陷不致命 |
| 交易构造与广播层 | 根据签名结果构造原始交易并广播 | 兼容以太坊、比特币等不同链的RPC |
| 数字资产管理层 | 记录资产余额、处置授权、追踪历史 | 审计是安全模型的组成部分,不是事后补丁 |
组件分得越清晰,引入新Agent时就越不会碰到底层密钥逻辑。例如我可以今天让这个网关支持购买API额度,明天再让它支持链上资产兑换,两者共用同一个阈值签名网络和策略引擎。
3.2 一次授权支付的生命周期
我用一个具体的例子来说明这套系统的完整流程。假设用户给Agent设定了一条规则:“每周可以为推理服务最多支付20美元。”某个夜里Agent发现需要调用一个额外批次的数据处理任务,于是自动发起了支付。
第一步,Agent运行时生成一个结构化支付意图,里面包含收款方钱包地址、金额、使用的支付通道。第二步,策略引擎检查这条意图是否命中允许列表,金额是否在限额内,当前时间是否在允许执行窗口内。第三步,如果策略校验通过,交易构造层生成待签名的交易原文,并计算出摘要。第四步,系统向当前策略约定好的两个分片节点发起协作签名请求,例如用户手机端和云端节点。第五步,两个节点各自产生部分签名并返回,组合器合成最终签名。第六步,交易广播层把带签名的交易提交到链上,并等待确认。第七步,资产管理器记录这笔流水,更新该周期剩余预算。
这个流程看似简单,但每一步都有安全细节。举例来说,策略校验必须发生在签名之前,且必须在独立于LLM上下文的模块中执行。因为Agent的大模型上下文可以被诱导,如果直接让模型状态参与交易构造,攻击者完全可以通过Prompt注入改写收款地址。把意图变成结构化数据,再用独立策略引擎校验,就是为了把安全边界从“模型不可被攻击”改成“模型是否被攻击都很难更改边界”。
3.3 数字资产管理:高频低额与低频大额的分工
在数字资产管理层面,我遇到的高频需求不是直接把大额资产转来转去,而是小额多笔的结算。比如Agent调用另一家AI服务,以小时为单位沉淀费用,如果每次都在主网上广播一笔交易,手续费甚至比支付额还高。所以我引入了一层“短期会话密钥”机制:阈值密钥作为根密钥,通过门限授权派生出一批低权限的短期会话密钥,交给Agent在特定时间窗口内使用。
这样既满足了高频签名的自动化需求,又限制了会话密钥的最大额度和使用范围。一旦会话到期,密钥自动失效,根密钥仍然安全地存在于多个分片节点里。低频大额场景则回到直接阈值签名,每一笔都经过策略引擎显式审批。把这两类路径区分开,是这套系统性能和安全性都能兼顾的关键。
4. 落地实现:从DKG到签名编排
我接触过不少追求“一步到位”的朋友,一上来就想自己写椭圆曲线上的MPC协议。我的建议非常明确:如果不是密码学专项团队,不要自己实现TSS协议本身,随机数和Nonce管理的细微错误就可能让整把私钥泄露。项目里应该把协议层交给经过审计的实现,业务层才留给你自己写。
4.1 技术选型与项目结构
在选择具体实现时,我会先看协议是否匹配目标链。以太坊系和比特币核心常用ECDSA签名,所以面向这类资产时我会优先选择支持ECDSA的阈值签名协议或库;如果目标链支持Schnorr签名,FROST等方案则更简洁高效。还要检查库的审计记录、社区维护、依赖体积和许可证类型。
代码组织上,我建议至少分三个包:tss-core负责协议抽象和分片节点通信,policy-engine负责交易意图校验,gateway-api负责对外暴露签名和广播接口。这样即便以后换掉底层密码学库,业务层也不会被波及。
4.2 密钥生成仪式
正式上线前,所有分片节点需要完成一次分布式密钥生成。这个步骤通常在受控环境中进行,确保每个节点只持有自己的分片,没有任何环节把完整私钥落盘。
下面是一个非常简化的示意代码,用于说明我期待的业务接口长什么样。实际项目里协议层由成熟库提供,这里展示的是编排逻辑:
import asyncio from threshold_lib import DKG, TSSNode # threshold_lib 是示意模块,真实项目中请对接 # 经过审计的开源或商业库 async def setup_threshold(participants): nodes = [] for name in participants: nodes.append(await TSSNode.connect(name)) dkg = DKG(participants=participants, threshold=2) result = await dkg.run(nodes) # result.public_key 是所有节点共同生成的公钥 # 每个节点只保存自己的 share,不落完整私钥 print("group public key:", result.public_key.hex()) return nodes这段代码只是把DKG的调起过程抽象成几行,但它说明了重要的边界:我是通过库获得DKG能力,而不是自己实现离散对数计算。上线前还要对每个节点做安全加固,比如使用TEE或HSM存放分片、限制网络白名单、记录所有关键操作的审计日志。
4.3 策略与签名编排
我的策略引擎使用规则文件驱动,而不是在代码里硬编码。下面的JSON展示了一个简化策略:
{ "payment": { "max_amount_usd": 20, "allowlist": ["api-provider-xyz.example"], "time_window": ["09:00", "21:00"] }, "signing": { "threshold": 2, "signer_pool": ["user_device", "cloud_node", "hsm_node"] } }签名编排的大致逻辑是:收到Agent的支付意图后,先交给策略引擎判断是否放行,再调用分片节点签名。下面是一个抽象后的Python调用骨架:
from threshold_lib import TSSNode from policy_engine import PolicyEngine async def sign_payment(material): policy = PolicyEngine.from_file("policy_rules.json") if not policy.approve(material): return {"status": "rejected", "reason": "policy_violation"} nodes = { "user_device": await TSSNode.connect("env://USER_DEVICE_SHARD"), "cloud_node": await TSSNode.connect("env://CLOUD_NODE_SHARD"), "hsm_node": await TSSNode.connect("env://HSM_SHARD") } partials = [] for name in ["user_device", "cloud_node"]: partial = await nodes[name].sign(material.digest) partials.append(partial) signature = combine_partial_signatures(partials) return signature这里有两个容易忽略的点。第一,部分签名的组合不能随意拼接,必须由库提供的协议逻辑完成,否则会产生无效签名甚至泄露信息。第二,签名请求需要带上下文元数据,比如用途、请求方ID、时间戳,这些信息会进入审计系统,方便事后定位是哪条Agent任务发起的花费。
4.4 组合签名后的广播与验证
完成签名组合后,验证签名是否有效是最后一道防线。既可以调用密码学库的验签函数,也可以直接把交易广播到链上,由链本身完成校验。实际项目里我会两步都做,先本地验签避免把无效交易送到链上浪费费用,再调用节点RPC广播。
def verify_and_broadcast(signature, material, public_key): assert verify(signature, material.digest, public_key), "invalid signature" tx_hash = rpc.send_raw_transaction(material.raw_tx_with_sig(signature)) return tx_hash唯一需要留神的是不同链对签名格式的编码规则不一样,有的要求低位s值,有的要求特定哈希前缀。这些差异在跨链支持时很容易踩坑,我在项目中是把交易构造模块单独封装,逐一适配。
5. 实测中容易翻车的几个细节
项目跑通之后,工程化才是真正磨人的阶段。下面几个问题全都是我在真实环境里遇到过的,有些已经修复,有些只能靠制度流程兜底。
5.1 DKG跑完一次并不是终点
分布式密钥生成需要各节点同时在线协作,一旦某个节点在协议中途掉线,整个DKG通常宣告失败,只能重来。更麻烦的是,如果某个分片节点在后续运行中丢失数据,整个系统的阈值结构可能会从(2,3)降到(1,3),安全性大打折扣。
我目前的应对方案是给每个分片节点做高可用部署,并把节点分片做加密备份,但加密备份本身又是一个风险源。备份文件如果和节点存储在同一机房,攻击者可能会同时拿到分片和密码。如果分片被攻破一个,可以直接做“分片重新分享”,在不改变公钥的前提下把新分片发给新节点,但这个过程对运维要求很高。
5.2 随机数与Nonce是TSS的死穴
普通ECDSA签名里,Nonce必须是随机且绝不重用的。到了阈值签名语境里,Nonce的分布更复杂,每个参与方都要生成随机盲化因子,还要通过零知识证明向其他方证明自己确实按规则执行。哪怕随机数生成器出现弱随机性,或者某次Nonce被重放,都可能导致分片信息泄露,最终被推算出完整私钥。
我在一次测试环境里就遇到过签名分片重复的问题。当时因为开发机上的随机数熵源不足,连续两次签名使用了相同Nonce,好在只是测试网,发现问题后我立即给所有节点增加了硬件熵源,并在库的配置里启用强制重新生成Nonce的选项。这个教训说明,运行TSS节点的主机不能只当普通服务来对待,它需要质量过得去的随机源和严格受控的运行环境。
5.3 真正的敌手藏在Agent的上下文里
阈值密码学能保护私钥,但保护不了Agent的意图层。大模型可以被Prompt注入诱导,如果攻击者通过一段恶意指令让Agent生成高额支付请求,签名节点会照常完成任务,因为从密码学角度看这确实是一次合法签名。我一度以为做好私钥分片就万事大吉,后来才发现意图层的防护同样重要。
解决思路是让Agent永远不能直接构造交易。它只能输出结构化意图,而且策略引擎会把收款方、金额、频次进行强制校验。比如白名单之外的地址一律拒绝,超过单笔上限的请求必须二次人工确认,夜间窗口之外的高风险操作不自动执行。安全边界因此从“模型层”移到了“规则层”,更容易验证和维护。
5.4 日志与审计常常被遗忘,但它才是事后救星
分布式签名场景下,日志不是简单的记录,而是安全模型的一部分。如果出了安全事故,唯一能还原真相的就是审计系统。我在GateWay里给每笔签名请求生成了一个全局Request ID,记录Agent任务ID、策略判定结果、参与节点列表、签名耗时和链上交易哈希。
刚开始这套系统的日志零散在不同服务里,排查一次问题时我要在四个组件之间跳来跳去。后来我把所有事件统一输出到一套事件流中,按Request ID聚合检索,问题定位速度快了一个数量级。建议你在设计阶段就把日志格式定好,而不是等出了问题再补。
5.5 备份、恢复与密钥轮换的边界
处置分片就一定要考虑恢复流程。一个现实场景是用户手机丢失,他持有的分片随之消失。如果阈值是(2,3),系统里还剩余两个分片,仍然可以完成签名,但容错空间已经从“允许坏掉一个”变成了“坏掉一个就完蛋”。所以需要提前设计补充分片的流程,让现有节点参与生成新的分片替换丢失份,而不是重新生成整套密钥。重新生成会改变公钥地址,对已绑定资产和支付通道都是大工程。
轮换逻辑上,只有当确认某分片已经泄露,才有必要对整个密钥做替换,并且替换过程需要把老地址上的资产迁移出来。这里没有捷径,唯一能做的就是把资产按风险等级分层,高风险大额资产单独管理,降低整体损失的可能。
6. 最后再分享一段真实体验
项目推进到这里,我最大的感触是:最难解决的往往不是密码学,而是把密码学嵌入业务时暴露出来的系统性设计问题。
我曾经花两周优化TSS协议的节点通信时延,后来发现真正拖慢支付流程的反而是策略引擎里的白名单查询和一个没有加缓存的资产余额校验。把那些非密码学部分理顺之后,整套系统的签名流程反而更加顺畅。所以如果你也正在为AI Agent设计支付与数字资产管理方案,我建议先从意图边界和审计体系入手,再决定选哪条阈值签名技术路线。
一个小技巧收尾:可以把每次签名请求的发起原因字段设计成“必须由调用方显式提交,不能由Agent自由填写”。这不仅让事后追溯更容易,还会迫使上层应用把Agent的决策理由显性化,长期看对模型行为的可解释性也是一种反向推动。私钥被安全地藏进了分布式协议,但业务依然要让人看得懂、查得清。