把一个语言模型接到钱包上,只要几行代码。让"一次糟糕的输出"只造成有界的损失,才是全部的工程问题。
能让这件事保持可控的框架是:agent 负责决策,确定性的代码负责执行。 一旦模糊掉这条边界,每一次模型失误都会变成一次资金损失。
两层,一条边界
┌───────────────────────────────┐
│ agent:推理,非确定性 │
│ ★产出意图,不产出交易★ │
└───────────────┬───────────────┘
│ 校验过的意图
┌───────────────▼───────────────┐
│ executor:确定性代码 │
│ ★构建、签名、提交、重试★ │
└───────────────────────────────┘
★agent 绝不能持有私钥,也绝不能产出已签名的交易。★ 它产出一个结构化的意图,executor 用agent 无法修改的规则去校验它,只有校验通过之后才会有东西被签名。
// agent 唯一产出的东西。
type Intent = {
action: "swap" | "close_position";
inputMint: string;
outputMint: string;
amountLamports: bigint;
maxSlippageBps: number;
reason: string; // ★记录下来,但绝不据此行动★
};
reason 这个字段值得保留,也值得永远不作为行动依据。它让你在事后能够复盘一笔奇怪的交易,而把它当成理由,正是"一段有说服力的解释"替代掉一次有效性检查的方式。
校验每一个意图
executor 的校验才是真正的安全机制。它对每一个意图都跑,不管 agent 听起来多有把握。
function validate(intent: Intent, state: AgentState): void {
// ★硬边界 —— 不是可以让 agent 争辩的建议。★
if (intent.amountLamports > LIMITS.maxPerTrade) throw new Error("单笔规模");
if (state.spentToday + intent.amountLamports > LIMITS.maxDaily)
throw new Error("每日上限");
if (!ALLOWED_MINTS.has(intent.outputMint)) throw new Error("代币不在白名单");
if (intent.maxSlippageBps > LIMITS.maxSlippage) throw new Error("滑点");
if (state.tradesThisHour >= LIMITS.maxHourly) throw new Error("频率");
}
★频率限制是最常被漏掉的那一个,也正是它能拦住循环。★ 一个误读了自己仓位、于是决定买入的 agent —— 反复地买,每一次都从一个错误前提出发做出正确推理 —— 会在一个"单独看每笔都批准"的规模限制下把钱包抽干。
每日上限加上每小时计数,才能把"模型判断错了"压成"损失有界"。
如果你的 executor 很扎实、交易还是会错过,免费领个 BoltTx key 改一行就能测测提交路径。
在链上给权限设边界
应用层的限制防的是一个糊涂的 agent,防不了一个被攻破的进程 —— 因为持有密钥的代码可以无视它自己的检查。
按强度递增的几个选项:
一个余额很小的专用钱包。 粗糙、有效、而且一眼就能理解 —— 用 getBalance 查它,并且有意识地往里充值。最大损失就是你往里放了多少。
委托的代币授权。 给 agent 的密钥批准一个特定额度,让它只能花这么多。
由程序强制的限制。 一个小的链上程序,拒绝任何超出参数的东西。★这是唯一一种"攻破 agent 进程还不足以拿走资金"的选项。★
★这是一个风险决策,不是技术决策,而且应该被明确地做出,而不是默认落到某个选项上。★ 大多数团队从一个有余额的热钱包开始,关键在于你知道那就是你选的。
Agent 很慢,而这改变了整个设计
模型推理要花几秒。Solana 的 slot 比这短得多。
★这排除掉了一整类策略。★ agent 抢不了新盘、赢不了套利竞速、也没法在单个区块内做出反应。任何比拼延迟的环节都归确定性代码管。
agent 真正擅长的是上面那一层:解读局面、权衡那些难以表达成阈值的取舍、决定要不要而不是什么时候。
// ★agent 定策略。确定性代码执行它。★
const policy = await agent.decide(marketContext);
// → { action: "reduce_exposure", target: 0.5, urgency: "high" }
await executor.rebalance(policy); // 快速通道,链路上没有模型
任何需要在一个 slot 内做出反应的路径上,都不能有模型调用 —— getSlot 和 sendRawTransaction 之间不能夹一次推理。 这不是一个需要绕过去的限制,而是一条要照着去设计的边界。
针对重复的幂等
Agent 会重复自己。一次重试循环、一次重新提示、或者一次含糊的状态读取,都会产出同一个意图两次。
// ★按意图去重,不是按交易去重。★
const key = hashIntent(intent);
if (await recentlyExecuted(key, windowMs)) {
return { skipped: "重复意图" };
}
★Solana 在签名层面的防重放在这里帮不上忙★,因为第二次尝试确确实实是一笔不同的交易 —— 新的 blockhash、新的签名 —— 只是表达了同一个决策。去重必须发生在意图这一层,在任何东西被构建之前。
再配上"把每次提交收敛到一个终态",这样 executor 才知道之前那次尝试到底有没有落块:
switch (outcome.status) {
case "success": await recordExecuted(key, outcome.slot); break;
case "reverted": await recordFailed(key, outcome.err); break; // ★不要自动重试★
case "expired": await maybeRetry(key, intent); break; // ★可以安全重试★
}
绝不要把一次 revert 直接丢回给 agent、说"再试一次"。 revert 意味着状态拒绝了这个动作,而一个被要求重试的 agent,通常会更加确信地推理出同一个意图。
给不确定的那一环做日志
标准的交易日志在这里不够用,因为有意思的问题不是"提交了什么",而是"为什么"。
| 记录什么 | 为什么重要 |
|---|---|
| 完整的输入上下文 | ★复现这个决策★ |
| 模型的原始输出 | 区分"推理错了"和"解析错了" |
| 校验过的意图 | executor 实际看到的东西 |
| 拒绝记录,以及是哪条规则触发的 | ★你的限制卡在哪里★ |
| 签名和终态 | 链上结果 |
| 模型与提示词版本 | 把行为变化归因 |
★拒绝日志是最物有所值的那一条。★ 针对某一条特定规则的拒绝率上升,是"agent 行为已经漂移"最早可得的信号 —— 而且不同于这里的其它每一个指标,它出现在钱损失之前。
上链应该是什么水平
我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。
★对 agent 系统来说,可预测的提交才让你能正确归因失败。★ 当执行不可靠时,每一个糟糕结果都在"决策错了"和"交易丢了"之间含混不清 —— 而在反馈嘈杂的情况下调试一个非确定性组件,基本是不可能的。
BoltTx 在这里的位置
我们负责提交。不管 agent、不托管、不管你的校验规则。
提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前、传输途中观察不到。
你的 executor 用你选定的那种密钥模式在本地签名。我们不托管资金、不代签、不修改交易内容 —— agent 的权限完全留在你自己的边界之内。tip 带在交易里、从你自己的钱包链上支付,交易失败时跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。
免费领 API key,没有月费:
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
常见问题
怎么让 AI agent 安全地在 Solana 上交易? 把决策和执行分开。agent 产出结构化的意图,确定性代码用 agent 无法修改的限制去校验,只有那段代码才签名和提交。
AI agent 该持有私钥吗? 不该。密钥属于 executor,由它强制那些 agent 无力改变的边界。一个持有密钥的 agent,意味着每一次模型失误都可能是一次资金损失。
agent 钱包该设哪些限制? 单笔规模、每日总额、允许的代币、最大滑点,以及一个频率上限。频率上限最常被漏掉,而它正是防止一个推理循环用"每笔单独看都有效"的交易抽干钱包的那一条。
AI agent 能参与 Solana 套利竞速吗? 在延迟上不能。模型推理要几秒,而 slot 短得多,所以任何比拼延迟的东西都必须跑在确定性代码里。agent 适合执行之上的策略层。
怎么防止 agent 重复同一笔交易? 在一个时间窗口内,按意图的哈希去重。签名层面的防重放帮不上忙,因为一次重复确实是一笔不同的交易,只是表达了同一个决策。
失败的交易该反馈给 agent 吗? 可以反馈结果,但不要自动让它去重试一次 revert。revert 意味着状态拒绝了这个动作,而被要求再试的 agent,通常会更加确信地产出同一个意图。
怎么在链上限制 agent 的权限? 选项从"余额很小的专用钱包",到"委托的代币授权",再到"由程序强制参数"。只有最后一种,能在 agent 进程本身被攻破时依然成立。
AI 交易 agent 该记录什么日志? 输入上下文和模型的原始输出,校验过的意图,带触发规则的拒绝记录,以及签名和终态。没有前两项,一笔奇怪的交易就无法复现。
怎么发现 agent 行为不对劲? 盯着按规则统计的拒绝率。针对某一条限制的上升是漂移最早的信号,而且不同于基于结果的指标,它出现在钱损失之前。
agent 能自己设滑点吗? 它可以提议一个,而 executor 应该把它夹到一个硬上限内。一个被说服"非常时期值得用非常滑点"的模型,恰恰是这个上限存在的理由。
agent 输出格式不对会怎样? executor 在校验阶段拒绝它,不会构建、也不会签名任何东西。解析失败应该和规则拒绝分开记录,因为它们指向不同的问题。
agent 该持续运行还是按计划运行? 按计划或按事件触发更容易划定边界。持续运行的 agent 无论如何都需要频率上限,而这个上限在两种模式下做的是同一件事。
怎么测试一个 agent 交易系统? 用对抗性的意图确定性地测 executor:超额的金额,不在白名单里的代币,极端滑点,以及短时间内的快速重复。每一种都要确认会被拒绝。校验才是那个必须正确的组件。
agent 需要知道 blockhash 过期这回事吗? 不需要,而且不该知道。过期、重试、终态完全属于 executor。 把链上机制暴露给 agent,只是多给它一个可以推理错的东西。
agent 想在拥堵时交易该怎么办? 手续费策略由 executor 决定,不是 agent。 从涉及账户的近期费用推导、设上限,并且把撞到上限当成一个信号,而不是默默付掉。
给 agent 配钱包的最低安全配置是什么? 一个专用钱包、里面放你能全部亏掉的金额,executor 里的硬限制(包括频率上限),意图去重,以及完整到足以重建任何一个决策的日志。