一个能够访问钱包的 AI 代理听起来功能强大。

它也听起来可能存在风险。

语言模型可能会误解请求、选择错误的工具、重复操作,或自信地追求一个原本就不应被允许的目标。如果给予它无限制的签名权限,这些错误就会转化为实际交易。

Arc 14 的目标是构建比这更有用的东西。

在第 92–98 天,我们让代理访问 Solana 数据,然后逐步引入工具、钱包、MCP 服务器、策略引擎和自主工作流。

在每个阶段,都保持着相同的边界:

模型可以决定它想做什么。

代码决定它被允许做什么。

代理从只读工具开始

第一个挑战将风险控制在较低水平。

我们使用 Claude 和一组 Solana devnet 工具构建了一个小型代理循环。该代理可以回答自然语言问题,例如:

  • 这个钱包持有多少 SOL?
  • 谁拥有这个账户?
  • 这个账户是否可执行?
  • 它存储了多少数据?

该模型从未直接连接到 Solana。

它没有构建任意 RPC 请求,也没有获得无限制的网络访问。该应用定义了一小部分工具,描述了每个工具的功能,并决定如何执行它们。

Claude 可以检查这些描述并选择调用哪个工具,但周围的应用始终保持控制。

结果仍然感觉像是对话式的。

我们用普通英语提出问题。

代理选择了余额工具。

应用调用了 RPC 端点。

结果返回给模型。

模型向我们解释了结果。

这个交互背后是一个简单的循环:

  1. 用户给代理一个目标或问题。
  2. 模型决定是否需要工具。
  3. 应用验证并执行工具调用。
  4. 结果返回给模型。
  5. 循环继续,直到模型能够回答。

模型提供判断。

应用提供能力。

工具定义决定了代理能做什么

每个工具都有四个重要部分:

  • 描述,告诉模型何时应该使用它。
  • 输入模式,定义模型可以请求什么。
  • 实现,决定实际发生什么。
  • 输出,为模型的下一步提供证据。

这些细节很重要。

模糊的描述可能导致模型选择错误的工具。

宽松的模式可能允许格式错误或模糊的请求。

信任每个参数的实现可能将模型错误转化为系统问题。

即使是只读工具也需要边界。

余额工具应该接受有效的 Solana 地址。

账户检查工具应该返回有用的结构化元数据。

错误应该以模型能够理解的形式返回,而不向开发者隐藏原始细节。

提示词描述了我们希望代理如何表现。

工具定义建立了它实际上可以要求应用做什么。

然后我们给了代理一个钱包

第 93 天提高了风险。

代理获得了自己的 devnet 钱包和两种新能力:

  • 检查余额。
  • 发送 SOL。

这将它从只读助手转变为能够改变链上状态的系统。

钱包属于应用,而不属于模型。

其私钥保留在构建和签署交易的进程内部。它从未被插入提示词、返回在工具结果中或通过模型上下文暴露。

模型不需要密钥。

它只需要一个接受提议接收者和金额的工具。

然后应用可以决定是否构建和签署交易。

这种分离很重要,因为模型上下文不是存放秘密的安全地方。

提示词、工具调用、日志和响应可能会被存储、检查或在组件之间传递。放在那里的私钥可能会意外泄露或在生成的输出中重复。

将密钥保留在签名层意味着模型可以请求操作,而无需拥有执行该操作所需的凭证。

代理可以请求向某个地址发送 0.05 SOL。

只有应用才能将该请求转化为已签名的交易。

转账限制存在于代码中

我们没有依赖提示词来限制代理可以发送多少 SOL。

转账工具强制执行硬性上限。

如果提议的金额超过该限制,应用会在构建交易之前拒绝该请求。

我们可以告诉模型永远不要发送超过 0.1 SOL。它可能会大部分时间遵循该指令。

但提示词可能被误解、被冲突指令覆盖或应用不一致。

代码级检查的行为不同。

如果金额太高,转账就不会发生。

无论请求多么有说服力。

无论模型听起来多么自信。

无论提议的操作看起来是否支持更广泛的目标。

签名层拒绝。

涉及金钱、权限和不可逆操作的规则就应该放在这里。

单一转账上限还不够

每次转账限制可以防止一笔大额交易。

它不能阻止代理进行多次较小额转账。

每次转账限制在 0.1 SOL 的代理仍然可以尝试十次转账,总共花费 1 SOL。

这表明转账工具内部的验证只是开始。

更广泛的系统还需要决定:

  • 哪些接收者被允许?
  • 一次交易中可以发送多少?
  • 整个会话中可以花费多少?
  • 代理可以进行多少次工具调用?
  • 循环可以持续多长时间?
  • 交易失败后应该发生什么?
  • 哪些操作需要记录?

这些决定属于专用的策略层,而不是分散在提示词和工具实现中。

MCP 使工具可重用

第 94 天将 Solana 工具移入 Model Context Protocol 服务器。

在此之前,这些工具存在于一个应用中。

这可行,但它将功能绑定到特定的代理实现。

MCP 服务器使它们可重用。

它暴露了用于以下任务的工具:

  • 读取钱包余额。
  • 检查链上金库状态。
  • 调用受保护的程序指令。

任何兼容的 AI 客户端都可以通过相同的协议发现和使用这些工具。

服务器描述了可用的内容,接受结构化请求并返回结构化结果。

这类似于 Arc 13 中 IDL 所扮演的角色。

IDL 使 Solana 程序对软件可发现。

MCP 使面向代理的功能对 AI 客户端可发现。

有用的部分不仅仅是另一个客户端可以调用这些工具。

钱包密钥和安全规则保留在服务器上。

新客户端不会获得对这两者的直接访问。

它获得与每个其他客户端相同的受控接口。

这意味着我们可以更改模型、桌面客户端或代理框架,而无需重建 Solana 集成或将权限移入新客户端。

服务器仍然是信任边界

MCP 使工具更容易被发现和重用。

它并没有使每个客户端都值得信任。

兼容的客户端可以请求工具调用,但服务器仍然必须验证它。

客户端可能配置错误。

模型可能选择错误的工具。

用户可能请求超出预期范围的操作。

请求可能包含无效地址或过大的金额。

服务器仍然负责:

  • 验证工具输入。
  • 应用策略检查。
  • 保护私钥。
  • 构建和签署交易。
  • 返回有用的错误。
  • 记录发生的事情。

工具结果也是不可信的。

链上文本和元数据可以由其他人写入。返回给模型的备注、代币名称或解码的账户字段可能包含旨在影响其行为的指令。

应用不能仅仅因为内容来自工具就将其视为可信的指导。

即使敌对数据改变了模型的推理,相同的策略规则仍然控制什么可以被签名。被污染的响应可能影响模型提出的内容,但它不能扩展允许列表、提高支出上限或重置会话预算。

这延续了 Arc 12 的安全教训。

不可信输入不仅限于交易账户。它还包括代理读取并带入其上下文的数据。

我们构建了默认拒绝策略引擎

第 95 天将简单的转账上限转变为适当的策略层。

除非提议的转账通过每条规则,否则将被拒绝。

策略检查:

  • 接收者是否是有效的 Solana 地址。
  • 接收者是否出现在允许列表中。
  • 金额是否低于每次转账上限。
  • 会话总支出是否在预算范围内。

应用在模型上下文之外跟踪累积支出。

代理无法记住、重置或重新解释自己剩余的预算。策略引擎根据应用拥有的会话状态计算该值。

这是一个默认拒绝的设计。

系统不会寻找拒绝转账的理由。

它要求转账满足所有条件才能获得批准。

如果任何检查失败,交易就不会被签名。

缺少允许列表条目会导致拒绝。

格式错误的地址会导致拒绝。

金额超过上限会导致拒绝。

会话预算耗尽会导致拒绝。

意外输入不会进入执行。

它会停止。

意图和权限是不同的问题

模型的工作是推理目标。

策略引擎的工作是决定提议的操作是否符合规则。

代理可能正确计算出钱包缺少 0.4 SOL。

这并不意味着它有权限转移 0.4 SOL。

金额可能超过每次转账上限。

目的地可能不在允许列表中。

剩余会话预算可能只有 0.2 SOL。

因此,即使模型的推理是合理的,正确的结果也可能是拒绝。

这是系统的一个重要属性。

它的安全性不依赖于模型每次都做出正确的安全决策。

模型可能犯错、过度自信、被用户操纵或受敌对工具输出的影响。

策略应该对相同的提议操作和相同的会话状态产生相同的结果。

模型行为是可变的。

策略行为应该是可预测的。

拒绝是正常的系统行为

被拒绝的操作不一定是错误。

有时它是系统按照设计工作的结果。

如果代理提议向允许列表之外的地址发送资金,策略引擎会拒绝该请求并返回原因。

然后代理可以决定下一步做什么。

它可能会告诉用户该操作不被允许。

它可能会要求不同的接收者。

它可能会提议较小的转账。

它可能会得出结论,在当前限制下无法完成目标。

这比将每个拒绝都视为导致工作流崩溃的异常更好。

策略结果成为代理可以推理的信息。

批准的操作可以继续进行签名。

被拒绝的操作会返回结构化的解释。

代理可以适应该决定,但不能覆盖它。

自主工作流将一切整合在一起

第 96 天将代理循环、钱包工具和策略引擎组合成一个目标驱动的工作流。

示例目标是确保储蓄钱包持有至少指定数量的 SOL。

代理需要:

  1. 检查储蓄钱包余额。
  2. 与目标进行比较。
  3. 计算任何不足。
  4. 检查资金钱包。
  5. 提议转账。
  6. 让提议通过策略。
  7. 提交已批准的交易。
  8. 验证新余额。
  9. 记录发生的事情。

这不仅仅是一个工具调用后跟着一个答案。

代理必须检查当前状态,决定是否需要采取行动,在其限制内行动,然后检查结果。

最后的验证很重要。

交易签名表明交易已提交。

它本身并不能证明预期目标已达成。

代理再次读取链上状态并与目标进行比较。

这就闭合了循环:

观察。

推理。

行动。

验证。

正常、受限和不可能的场景表现不同

我们在几种条件下测试了该工作流。

在正常情况下,目标钱包低于最低余额,资金钱包有足够的 SOL,所需转账通过策略。

代理计算了不足,请求转账并验证了新余额。

在受限情况下,目标只能在转账上限和剩余会话预算内实现。

代理必须在这些限制内工作,而不是简单地选择最直接的操作。

在不可能的情况下,策略或可用资金使目标无法实现。

接收者可能不在允许列表中。

不足可能超过会话预算。

资金钱包可能没有足够的 SOL。

代理可以正确推理,但仍然无法完成目标。

这不是系统的失败。

正确行为是停止,解释约束并保持策略边界。

「无法继续」有时是正确的结果。

回合限制防止了无限循环

代理循环需要停止条件。

没有它,模型可能继续调用工具、重试失败的操作或无限期地重新审视相同的推理。

Arc 14 包含了显式的回合限制,以便应用可以在固定步骤数后停止工作流。

这可以防止意外循环,并限制一个请求可能消耗的时间、代币和网络活动量。

回合限制是一个简单的控制,但它解决了代理系统的一个实际特性。

模型决定下一步做什么。

应用决定它被允许继续决定的时间。

其他系统也可能对特定操作使用时间限制、工具调用限制或批准门。

确切的控制可以变化。

重要的是代理不能给自己授予无限的时间或无限的尝试次数。

结构化日志使工作流可检查

当我们只能看到最终答案时,自主行为很难被信任。

因此工作流为每个重要步骤编写结构化日志。

这些日志可以记录:

  • 代理接收到的目标。
  • 它选择了哪个工具。
  • 它提出的参数。
  • 相关的策略决策。
  • 操作被批准或拒绝的原因。
  • 交易签名。
  • 验证步骤的结果。
  • 最终结果。

这创建了系统如何从原始目标移动到结果的跟踪。

日志对调试有用,但其价值更进一步。

它们显示代理是否正确解释了目标。

它们揭示策略引擎是否应用了预期的规则。

它们使重复操作可见。

它们提供了工作流成功验证其工作的证据。

它们还使拒绝更容易解释。

没有日志,我们可能只知道转账没有发生。

有了日志,我们可以看到接收者不在允许列表中,或者剩余预算太低。

自主系统需要这种证据。

最终响应是不够的。

文档迫使我们描述边界

第 97 天从构建系统转向解释系统。

我们编写了涵盖以下内容的公开技术演练:

  • 代理循环。
  • 可用工具。
  • MCP 服务器。
  • 钱包边界。
  • 策略引擎。
  • 工具合约。
  • 整体架构。
  • 真实运行的日志。

最重要的区别是可变行为和不变行为之间的区别。

模型的推理是可变的。

它可能选择不同的工具,用不同的方式表达其解释,或采取另一条通向同一目标的路径。

策略层是不变的。

相同的提议操作和会话状态应该产生相同的批准决定,无论模型如何解释其意图。

这使架构更容易理解。

代理不是因为模型被指示要表现得好而安全。

它之所以安全,是因为模型只能通过执行固定规则的代码才能到达签名能力。

文档需要准确显示该边界所在的位置。

工具合约与提示词同样重要

代理系统的公开演练很容易变成对提示词的冗长解释。

提示词很重要,但它们只是这个弧的一部分。

更有力的材料在于工具合约和策略规则中。

转账请求需要哪些字段?

策略之前发生了哪些验证?

拒绝后返回了哪些策略结果?

私钥存在于哪里?

累积会话支出存储在哪里?

哪个组件构建了交易?

工作流如何确认成功?

工具调用和策略决策是如何记录的?

不可信的工具输出是如何被阻止绕过签名规则的?

这些细节解释了系统实际上能保证什么。

提示词描述了预期行为。

工具合约定义了模型可以请求的操作。

策略引擎定义了哪些请求可以继续。

签名者控制什么到达网络。

日志显示了发生的事情。

这就是完整的安全故事。

最终演示展示了操作和拒绝

第 98 天将工作流转化为简短的录制演示。

演示以自然语言目标开始。

代理检查余额,选择工具,提出操作并让它通过策略。

已批准的交易出现在 devnet 上,可以在链上验证。

但演示还需要展示拒绝。

这与成功的转账同样重要。

能够移动资金的系统很容易演示。

能够拒绝不安全或不允许的请求的系统更有说服力。

拒绝表明即使模型提出了操作,策略引擎仍然保持控制。

因此最有用的演示包括:

  • 原始目标。
  • 代理的工具调用。
  • 策略决策。
  • 已批准操作的交易签名。
  • 结果的链上状态。
  • 被策略拒绝的请求。
  • 拒绝的原因。

这使边界可见,而不是要求观众相信它们存在于代码的某个地方。

Arc 14 教会了我们什么

Arc 14 不是关于把钱包交给 AI 模型并期待最好的结果。

它是关于在一个保留控制权的系统中构建代理。

到这个弧结束时,我们学会了如何:

  • 围绕明确定义的 Solana 工具构建代理循环。
  • 将网络访问和私钥保留在模型上下文之外。
  • 将工具结果和链上数据视为不可信输入。
  • 在应用代码中强制执行转账限制、允许列表和预算。
  • 在 MCP 服务器中打包可重用的 Solana 功能。
  • 跨不同的 AI 客户端应用相同的安全规则。
  • 运行观察、行动和验证的工作流。
  • 在模型之外跟踪会话状态。
  • 限制代理回合并记录结构化执行日志。
  • 演示已批准和已拒绝的操作。

核心教训是自主和权限不是同一回事。

模型可以决定哪个工具可能有帮助。

它可以解释目标。

它可以计算不足。

它可以提议转账。

它可以解释拒绝。

但它不持有私钥。

它不决定哪些接收者被允许。

它不设置自己的支出上限。

它不记住或重置自己的会话预算。

它不覆盖策略引擎。

模型进行推理。

代码持有权限。

回顾 Arc 14 的挑战

第 92 天:构建一个通过定义工具回答问题的只读 Solana 代理

第 93 天:给代理一个 devnet 钱包,同时将密钥和支出限制保留在代码中

第 94 天:将 Solana 工具打包为可重用的 MCP 服务器

第 95 天:构建带有允许列表和预算的默认拒绝策略引擎

第 96 天:运行并验证自主钱包管理工作流

第 97 天:记录代理架构、工具合约、策略规则和运行日志

第 98 天:录制展示成功操作和策略拒绝的公开演示