让 AI agent 能够发送 Solana 交易

有边界的权限、确定性的执行,以及当做交易决策的那一环本身就不确定时,会出现的失败模式。

BoltTx Team··15 min read
solanaai-agent自动化交易上链安全交易机器人

把一个语言模型接到钱包上,只要几行代码。让"一次糟糕的输出"只造成有界的损失,才是全部的工程问题。

能让这件事保持可控的框架是: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 内做出反应的路径上,都不能有模型调用 —— getSlotsendRawTransaction 之间不能夹一次推理。 这不是一个需要绕过去的限制,而是一条要照着去设计的边界

针对重复的幂等

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 里的硬限制(包括频率上限),意图去重,以及完整到足以重建任何一个决策的日志。

延伸阅读

返回博客列表