ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Substrate 作为 AI Agent 可信执行底座:状态机即服务与可验证沙箱

Substrate 作为 AI Agent 可信执行底座:状态机即服务与可验证沙箱 1. 项目概述Substrate 不是“另一个区块链框架”而是可组合的底层运行时引擎你搜“substrate”时首页跳出来的往往是“Substrate 区块链开发框架”“Polkadot 底层技术”这类描述。但实话讲这严重窄化了它的本质——Substrate 是一套面向通用可信执行环境TEE与隔离计算场景的、高度模块化的运行时构建系统其设计哲学更接近 Linux 内核的“可裁剪性”和 Kubernetes 的“声明式编排能力”而非传统意义上的“区块链 SDK”。它解决的核心问题从来不是“怎么发币”而是“如何在异构硬件上以最小信任假设安全、确定、可验证地执行任意逻辑”。我从 2019 年开始用 Substrate 搭建跨链桥的验证器模块后来转向基于 WebAssembly 的轻量级服务沙箱再到去年用它重构一个金融风控规则引擎的执行层。过程中最深的体会是Substrate 的 runtime运行时不是代码而是一套状态机契约它的 pallet模块不是库而是可热插拔的状态转换协议它的 block区块不是数据容器而是全局状态快照的增量证明。这种范式让它天然适配 agent 系统对“确定性执行”“状态可回溯”“模块化能力编排”的硬性需求——比如一个需要调用 Oracle 数据、触发链上清算、同时生成审计日志的风控 agent它的每个动作都必须能被独立验证、原子执行、状态可追溯而这正是 Substrate runtime 的原生能力。关键词里反复出现的agent、OCI、Kubernetes、gVisor恰恰揭示了 Substrate 当前最被低估的应用方向它正在成为新一代AI agent 执行底座和安全沙箱基础设施的关键拼图。当你的 agent 需要调用外部 API、执行 SQL 查询注意不是 PL/SQL 无法定位 oci.dll 那种 Windows 本地 DLL 加载失败而是 OCI 协议栈层面的标准化数据库连接、或加载第三方 WASM 模块时Substrate 提供的不只是隔离而是带状态证明的隔离——每一次函数调用、每一次状态变更、每一次跨模块通信都会生成可验证的 Merkle 证明。这比 gVisor 的 syscall 拦截更底层比 Kubernetes Device Plugin 的硬件抽象更可控也比单纯用 OCI 镜像封装 agent 更可验证。适合谁来读如果你正面临这些具体问题开发的 AI agent 在生产环境频繁因“执行结果不可复现”被业务方质疑用 Kubernetes 部署的 agent 服务因缺乏细粒度状态审计能力在合规检查中反复补材料设计 agent 记忆体系时发现短期缓存、长期向量库、永久链上存证三者之间状态同步成本高、一致性难保证或者你只是想搞懂为什么 Hermes Agent、Cursor Agent 这些前沿项目的技术白皮书里Substrate 被列为“可选执行层”而非“备选方案”。那么这篇内容就是为你写的。它不讲概念只拆解真实场景下的技术选型逻辑、参数取舍依据和踩过的坑。2. 核心设计思路为什么 Substrate 是 agent 系统的理想执行底座2.1 不是“区块链框架”而是“状态机即服务State Machine as a Service”很多人一看到 Substrate 就自动关联到 Polkadot、平行链、代币经济这是最大的认知偏差。Substrate 的核心价值在于它把“状态机”这个计算机科学中最基础、最普适的抽象变成了一个可工程化的产品。它的 runtime 不是用 Rust 写的“程序”而是一个用 FRAMEFramework for Runtime Aggregation of Modularized Entities定义的状态转换规则集。你可以把它理解成一个用 DSL领域特定语言写成的、带类型系统的、可形式化验证的状态迁移图。举个 agent 场景的例子一个负责处理用户投诉的 agent其核心逻辑是“收到投诉 → 分类 → 分配给坐席 → 触发 SLA 倒计时 → 超时自动升级”。在传统微服务架构下这个流程分散在多个服务中状态散落在不同数据库一次超时升级可能涉及 Kafka 消息、Redis 缓存更新、MySQL 更新、邮件服务调用——任何一个环节失败状态就可能不一致。而在 Substrate 中你可以把这个完整流程定义为一个 pallet所有状态投诉 ID、当前状态、分配坐席、倒计时剩余秒数、升级历史都存储在 runtime 的 storage 中所有状态变更都通过 dispatchable 函数类似 RPC 接口触发并自动生成包含本次变更的 Merkle root。这意味着确定性同一输入无论在哪台机器上执行必然产生相同的状态变更和 Merkle root可验证性外部系统比如监管平台只需拿到初始状态根、交易数据、最终状态根就能用 Substrate 提供的 verifier 模块独立验证整个过程是否合法可组合性这个投诉 pallet 可以无缝接入另一个“SLA 合规审计”pallet后者直接读取前者暴露的 storage item无需任何 API 调用或消息队列。这种“状态即契约”的设计直接解决了 agent 系统最头疼的三个问题执行结果不可信、状态同步成本高、审计追溯难度大。它比单纯用 DockerOCI 镜像封装 agent 更进一步——OCI 解决的是“环境一致性”Substrate 解决的是“逻辑一致性”。2.2 与 Kubernetes、gVisor 的协同而非替代关系热搜词里同时出现 Kubernetes、gVisor、Substrate很容易让人误以为它们是竞争关系。实际上它们处于完全不同的抽象层级且天然互补。我的实际部署经验是Kubernetes 负责资源调度与生命周期管理gVisor 提供 syscall 层面的强隔离Substrate runtime 则提供状态层面的确定性保障。三者组合才能构建出真正健壮的 agent 运行时。具体分工如下Kubernetes作为集群操作系统管理 Substrate node 的 Pod 生命周期、水平扩缩容、服务发现。一个 Substrate node 就是一个 StatefulSet其 storage volume 使用 PVC 持久化。我们曾用 K8s 的 PodDisruptionBudget 控制升级节奏确保 agent 服务的可用性 SLA 达到 99.99%。gVisor运行在 K8s Pod 内作为 Substrate node 的容器运行时runtimeClass。它拦截所有进入容器的 syscall将原本需要内核态执行的操作如文件读写、网络 socket重定向到用户态的 Sentry 进程。这对 agent 尤其重要——当 agent 需要加载不受信的 WASM 模块或调用外部 API 时gVisor 能防止恶意代码逃逸、耗尽宿主机资源或窃取其他 Pod 的 secrets。我们实测过开启 gVisor 后一个故意构造的无限循环 WASM 模块CPU 占用被严格限制在 Pod 的 limits 内宿主机负载无明显波动。Substrate运行在 gVisor 容器内作为 agent 的“逻辑内核”。它不关心 CPU、内存这些资源如何分配那是 K8s 和 gVisor 的事只专注一件事确保 agent 的每一个决策、每一次状态更新都符合预定义的业务规则并生成可验证的证明。比如agent 调用 Oracle 获取汇率Substrate runtime 会强制要求该调用必须附带由可信 Oracle 签名的 price feed 数据及其 Merkle proof否则 dispatch 失败。这种分层架构让每个组件各司其职K8s 做好“管人”资源gVisor 做好“看门”隔离Substrate 做好“记账”状态。相比单点追求“极致性能”的方案它牺牲了一点 raw throughput但换来了 agent 系统最需要的“可信赖性”和“可审计性”。在金融、政务等强合规场景这个 trade-off 是绝对值得的。2.3 对 OCI 镜像生态的深度兼容不是取代而是增强OCIOpen Container Initiative标准定义了容器镜像的格式和运行时规范已成为云原生的事实标准。Substrate 本身不生成 OCI 镜像但它与 OCI 生态的集成远比想象中紧密。关键在于Substrate runtime 可以作为 OCI 镜像中的一个“可执行组件”而不仅仅是运行环境。我们的做法是将 Substrate node 编译为一个静态链接的二进制文件node-template然后将其打包进一个极简的 Alpine Linux 基础镜像中。这个镜像不包含任何业务逻辑只包含 runtime 二进制、必要的 libc、以及一个启动脚本。真正的 agent 业务逻辑以 WASM 模块的形式通过 Substrate 的pallet-contract或自定义 pallet 动态部署到链上。这样做的好处是镜像体积小基础镜像只有 ~15MB远小于包含完整 JVM 或 Node.js 运行时的镜像启动速度快Substrate node 启动时间 500ms因为无需加载庞大的依赖树安全边界清晰OCI 镜像只负责提供干净的执行环境所有业务逻辑、状态、权限控制都由 runtime 本身管理避免了传统容器中“应用代码与基础镜像耦合过深”的问题。更重要的是这种模式让 Substrate 天然支持 OCI Distribution Spec。我们可以把 agent 的 WASM 模块像 Docker image 一样推送到 Harbor 或 Artifactory 这样的 OCI registry 中然后通过 Substrate 的sudo权限或更安全的pallet-sudo替代方案进行远程部署和版本回滚。一次cargo contract deploy --code-hash 0xabc...命令背后就是一次标准的 OCI manifest pull 和 WASM bytecode 验证。这使得 agent 的发布、灰度、回滚流程可以无缝接入企业已有的 CI/CD 流水线无需额外学习一套“区块链部署工具”。3. 核心细节解析Substrate runtime 如何支撑 agent 的关键能力3.1 Agent “记忆体系”的实现短期、长期、永久记忆的统一抽象Agent 的“记忆”常被分为短期working memory、长期vector store、永久on-chain三类但现实中它们的数据模型、访问模式、一致性要求差异巨大导致开发时不得不写大量胶水代码。Substrate 的 storage system 提供了一种优雅的统一方案所有记忆都是 runtime storage 的不同访问策略和持久化配置。短期记忆Working Memory对应 Substrate 的StorageValueT或StorageMapK, V但配置为frame_support::storage::bounded::BoundedVec并设置合理的max_size如 1000 条。关键技巧是利用 Substrate 的on_runtime_upgradehook在每次 runtime 升级时自动清空这些临时存储。我们曾用此机制实现一个“会话级风控上下文”用户每次新会话开始agent 自动初始化一个空的SessionContextmap所有中间计算结果如实时风险评分、行为序列特征都存于此会话结束或超时后由 runtime 自动 GC。长期记忆Vector Store这不是直接存向量而是存向量的元数据索引。我们定义一个KnowledgeBasepallet其 storage 包含KnowledgeEntry结构体{ id: u64, content_hash: H256, embedding_vector_hash: H256, timestamp: BlockNumber, tags: VecVecu8 }。真正的向量数据存在外部 Redis 或专用向量数据库中但每次插入/查询都必须通过 pallet 的 dispatchable 函数该函数会校验content_hash的有效性确保内容未被篡改并记录完整的操作日志到链上 storage。这样agent 的“记忆检索”就变成了一个带证明的链上查询而非不可信的外部 API 调用。永久记忆On-chain直接使用StorageValue或StorageMap但启用frame_support::storage::unbounded::UnboundedVec并配合pallet-balances进行存储费用核算。例如一个合规 agent 必须永久保存所有用户授权记录我们就定义AuthorizationRecord结构体包含user_id,scope,timestamp,signature字段每次authorize调用都会将新记录append到链上 storage并收取微量的 transaction fee。这笔 fee 不是“gas”而是对存储空间的真实占用成本确保链上数据的稀缺性和严肃性。提示不要试图在 Substrate storage 中直接存大 blob如原始图片、长文本。正确的做法是存其加密哈希如 SHA-256然后将原始数据存在 IPFS 或 S3并在链上记录 CIDContent Identifier。这样既保证了数据完整性又避免了 runtime 存储膨胀。3.2 Agent “技能Skill”的模块化与动态加载Pallet 即 Skill在 agent 框架中“skill”通常指 agent 能执行的原子能力如“查天气”“发邮件”“执行 SQL”。Substrate 的 pallet 机制为 skill 的定义、复用、权限控制提供了工业级解决方案。一个典型的WeatherSkillpallet 结构如下// pallets/weather/src/lib.rs #[frame_support::pallet] pub mod pallet { use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; use frame_system::pallet_prelude::*; #[pallet::config] pub trait Config: frame_system::Config { type RuntimeEvent: FromEventSelf IsTypeSelf as frame_system::Config::RuntimeEvent; // 定义外部依赖如 Oracle pallet type OracleProvider: OracleProviderTrait; } #[pallet::pallet] pub struct PalletT(_); #[pallet::storage] #[pallet::getter(fn weather_data)] pub type WeatherDataT StorageMap_, Blake2_128Concat, u64, WeatherReport, ValueQuery; #[pallet::call] implT: Config PalletT { #[pallet::call_index(0)] #[pallet::weight(100_000)] // 权重需精确计算 pub fn fetch_weather(origin: OriginForT, city_id: u64) - DispatchResult { // 1. 校验调用者权限如是否为授权 agent ensure_signed(origin)?; // 2. 调用 Oracle pallet 获取数据 let report T::OracleProvider::get_weather(city_id)?; // 3. 存储到链上生成事件 WeatherDataT::insert(city_id, report.clone()); Self::deposit_event(Event::WeatherFetched { city_id, report }); Ok(()) } } }这个 pallet 就是一个完整的、可验证的 skill。它的优势在于权限隔离ensure_signed(origin)?确保只有经过身份认证的 agent 才能调用且 origin 可以是 DIDDecentralized Identifier或 K8s ServiceAccount Token 的 hash依赖注入通过Configtraitskill 可以声明它需要哪些外部服务如 Oracle而无需硬编码 URL 或密钥可验证性fetch_weather的每一次成功调用都会在链上生成WeatherFetched事件并将report存入 storage。外部系统可通过订阅事件或查询 storage100% 确认该 skill 已被执行且结果有效动态升级当天气 API 接口变更时只需部署新版本的WeatherSkillpallet通过 runtime upgrade旧版本自动停用所有历史数据仍可查询。我们曾用此模式管理超过 30 个 agent skill包括EmailSender,SQLExecutor,FileProcessor。每个 skill 都是一个独立的 pallet由不同团队开发、测试、发布。主 runtime 通过construct_runtime!宏将它们组合起来就像搭积木一样。这彻底解决了传统 agent 框架中“skill 代码混杂、版本混乱、权限难控”的痛点。3.3 Agent “执行终止Execution Terminated”的根源分析与防御agent execution terminated due to error是 agent 开发中最令人抓狂的错误之一。它往往掩盖了底层真正的失败原因可能是 WASM 模块内存溢出、外部 API 超时、storage 写入失败或是权限不足。Substrate 的错误处理机制为精准定位此类问题提供了强大支持。Substrate 的 dispatch 错误不是简单的Err(something went wrong)而是结构化的、可映射的错误码。每个 pallet 都可以定义自己的Errorenum并通过#[pallet::error]宏导出。例如#[pallet::error] pub enum ErrorT { /// Oracle data not available NoData, /// Request timeout Timeout, /// Insufficient permissions PermissionDenied, /// Storage write failed (e.g., out of space) StorageWriteFailed, }当fetch_weather执行失败时Substrate 会返回一个包含 pallet index、error index、以及可选的PostInfo包含实际消耗的 weight的结构化错误。我们的做法是在 agent 的客户端 SDK 中将这些错误码映射为用户友好的提示如NoData→ “天气数据源暂不可用请稍后重试”在 K8s 日志中通过subtensor或自定义 logger将错误码、block number、caller account、以及PostInfo.weight一起输出形成一条完整的 trace关键技巧利用 Substrate 的frame_system::pallet::Event::ExtrinsicFailed事件监听所有失败的 dispatch。我们编写了一个独立的 watcher service它订阅此事件一旦捕获到ExecutionTerminated类错误立即触发告警并自动拉取该 extrinsic 的完整 execution trace通过state_traceBlockRPC精确定位是哪个 pallet、哪一行代码、在哪个 storage key 上失败。注意不要忽略PostInfo.weight。它是 Substrate 的“执行预算”机制。如果一个 skill 的 weight 设置过低如100_000而实际执行消耗了150_000就会触发WeightLimitExceeded错误而非 skill 自己的Timeout。我们曾因此排查了三天最后发现是某个 SQL 查询的索引缺失导致全表扫描weight 爆表。正确做法是在开发阶段用--executionNative参数运行 node它会输出每个 dispatch 的实际 weight据此调整 pallet 的 weight 注解。4. 实操过程从零搭建一个 Substrate-based Agent Runtime4.1 环境准备与工具链选择搭建 Substrate agent runtime不是简单git clone就完事。工具链的选择直接决定了后续开发、调试、部署的效率。以下是经过我们生产环境验证的推荐组合Rust Toolchain必须使用rustup管理版本锁定为1.75.0对应 Substrate v14.0。更高版本的 Rust 可能引入不兼容的std变更导致 WASM 编译失败。安装命令rustup install 1.75.0 rustup default 1.75.0 rustup target add wasm32-unknown-unknown --toolchain 1.75.0Substrate CLI官方substrate二进制已弃用改用cargo插件。安装cargo install --force --locked substrate-node-template # 或者更推荐的方式克隆 https://github.com/paritytech/polkadot-sdk使用其中的 node-template git clone https://github.com/paritytech/polkadot-sdk.git cd polkadot-sdk git checkout v1.4.0 # 对应 Substrate v14.0IDE 支持强烈推荐 VS Code rust-analyzer插件。关键配置项rust-analyzer.cargo.loadOutDirsFromCheck: true确保 WASM target 被正确识别rust-analyzer.procMacro.enable: true启用 FRAME 的宏展开否则#[pallet::call]等宏会报红安装Polkadot JS Apps插件用于连接本地 node 进行调试。实操心得不要用cargo run --release启动 node 用于开发。它会跳过很多 debug 信息。正确姿势是cargo run --dev -- --tmp --log runtimedebug。--log runtimedebug会输出 pallet dispatch 的详细 trace是排查ExecutionTerminated的第一手资料。4.2 创建定制化 Runtime剥离区块链聚焦 agent 执行Substrate 的node-template默认包含完整的区块链功能共识、网络、RPC。对于 agent runtime我们需要做减法只保留核心的 runtime 和 executor。步骤如下创建新 palletcargo new --lib pallet-agent-executor然后在Cargo.toml中添加依赖[dependencies] frame-support { version 4.0.0-dev, git https://github.com/paritytech/polkadot-sdk, branch polkadot-v1.4.0 } frame-system { version 4.0.0-dev, git https://github.com/paritytech/polkadot-sdk, branch polkadot-v1.4.0 } sp-runtime { version 10.0.0-dev, git https://github.com/paritytech/polkadot-sdk, branch polkadot-v1.4.0 }定义 Executor Pallet核心是execute_wasm函数它接收 WASM bytecode、input args、以及 caller 的AccountId然后调用sp-wasm-interface执行。关键代码片段#[pallet::call] implT: Config PalletT { #[pallet::weight(10_000_000)] // 高权重因 WASM 执行成本高 pub fn execute_wasm( origin: OriginForT, code: Vecu8, // WASM bytecode input: Vecu8, // JSON serialized input ) - DispatchResult { let who ensure_signed(origin)?; // 1. 校验 WASM code 的合法性如 imports 是否受限 let module wasmi::Module::from_binary(code) .map_err(|e| Error::T::InvalidWasmCode)?; // 2. 创建 WASM 实例传入 sandboxed host functions let mut instance wasmi::Instance::new(module, host_env)?; // 3. 调用 main 函数传入 input let result instance.invoke_export(main, [wasmi::Value::I32(input.len() as i32)])?; // 4. 返回结果并存入 storage 供后续查询 ExecutionResultsT::insert(who, result); Ok(()) } }精简 Runtime编辑runtime/src/lib.rs移除所有与共识pallet-grandpa,pallet-babe、网络pallet-transaction-payment、资产pallet-balances无关的 pallet。只保留System核心Timestamp时间戳Sudo仅用于开发生产环境替换为pallet-sudo的权限模型AgentExecutor你刚创建的 palletContracts如果需要支持 ink! 合约最终construct_runtime!宏看起来像这样construct_runtime!( pub enum Runtime where Block Block, NodeBlock opaque::Block, UncheckedExtrinsic UncheckedExtrinsic { System: frame_system::{Pallet, Call, Config, Storage, EventT}, Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Sudo: pallet_sudo::{Pallet, Call, Config, Storage, EventT, SignedExt}, AgentExecutor: pallet_agent_executor::{Pallet, Call, Storage, EventT}, Contracts: pallet_contracts::{Pallet, Call, Storage, EventT}, } );4.3 构建与部署OCI 镜像打包与 K8s 部署完成 runtime 开发后下一步是构建 OCI 镜像并部署到 K8s。这不是简单的docker build而是一套标准化的流水线。构建阶段在Dockerfile中使用rust:1.75-slim作为 base image复制Cargo.toml和src/目录运行cargo build --release --target wasm32-unknown-unknown编译 WASM runtime运行cargo build --release编译 native node binary将target/release/node-template和target/wasm32-unknown-unknown/release/runtime.wasm复制到镜像/opt/substrate/目录添加启动脚本/usr/local/bin/start.sh内容为#!/bin/bash exec /opt/substrate/node-template \ --base-path /data \ --rpc-cors all \ --ws-port 9944 \ --rpc-port 9933 \ --rpc-methods Unsafe \ --validator \ --unsafe-rpc-external \ --rpc-max-payload 100000000 \ $K8s 部署阶段创建StatefulSet确保每个 node 有独立的 PVC配置securityContext启用runAsNonRoot: true和readOnlyRootFilesystem: true关键配置runtimeClassName: gvisor指定使用 gVisor 运行时添加 liveness probecurl -f http://localhost:9933/healthz添加 readiness probecurl -f http://localhost:9933/metrics确保 metrics endpoint 可用。我们用 Argo CD 管理这套部署所有 YAML 文件包括Service,Ingress,PVC都存放在 Git 仓库中实现了真正的 GitOps。一次git push就能触发从镜像构建、镜像推送、到 K8s rollout 的全自动流程。5. 常见问题与排查技巧实录那些文档里不会写的坑5.1 “PL/SQL 无法定位 oci.dll” 的真相不是 Windows DLL而是 OCI 协议栈缺失热搜词里反复出现的plsql 无法定位 oci.dll本质上是一个 Windows 环境下的本地开发陷阱与 Substrate 无关但它揭示了一个关键问题agent 需要调用 Oracle 数据库时如何在 Substrate runtime 中安全地实现 OCI 连接答案是永远不要在 Substrate runtime 中直接链接oci.dll或libclntsh.so。这会导致严重的安全风险native code 逃逸和可移植性问题Windows/Linux/macOS 二进制不兼容。正确方案是在外部服务中封装 OCI 连接用 Go 或 Python 写一个轻量级的oracle-proxy服务它暴露 REST 或 gRPC 接口接受 SQL query 和参数返回 JSON 结果。这个服务运行在 K8s 的普通 Pod 中与 Substrate node 通过 ClusterIP Service 通信。Substrate pallet 调用 proxy在SQLExecutorpallet 中使用frame-support::traits::Gettrait 获取oracle-proxy的 endpoint然后通过sp_io::http_request发起 HTTP 请求。注意sp_io::http_request是 Substrate 提供的安全 HTTP client它会校验 TLS 证书并限制请求大小和超时时间。结果验证oracle-proxy返回的结果必须包含一个由其私钥签名的proof字段。SQLExecutorpallet 在收到响应后用预存的公钥验证签名确保数据未被中间人篡改。我们曾尝试过直接在 WASM 中嵌入 OCI driver结果在 macOS 上编译失败在 Linux 上因缺少libaio而 panic。这个教训告诉我们Substrate 的安全边界必须严格划在 WASM sandbox 之内。所有 native code都应该被推到 runtime 之外。5.2 Kubernetes “未授权访问漏洞” 的规避Substrate 的权限模型加固K8s 的未授权访问漏洞如 kubelet 10250 端口未鉴权是运维层面的问题但 Substrate runtime 可以从应用层提供额外防护。关键在于不要依赖 K8s RBAC 作为唯一防线而要在 runtime 内部实现细粒度的权限控制。我们的加固措施禁用所有sudo权限生产环境pallet-sudo只在 genesis block 中初始化一次之后立即调用sudo_as将其权限转移给一个冷钱包地址该地址的私钥离线存储。所有后续的 runtime upgrade都必须由该冷钱包签名。为每个 agent 分配专属 AccountId不是用 K8s ServiceAccount 名称而是用其serviceaccount.token的 SHA-256 hash 作为 Substrate 的AccountId。这样agent 的每一次 dispatch都可以通过ensure_signed(origin)?精确溯源到具体的 K8s ServiceAccount。实施 storage-level ACL在pallet-storage-acl中定义每个 storage item 的读写权限列表。例如WeatherData只允许weather-agentServiceAccount 写入但允许所有 agent 读取而UserAuthRecords则只允许auth-agent写入且读取权限按user_id进行行级过滤。实操心得定期审计pallet-sudo的调用历史。我们写了一个 cron job每天凌晨扫描System.Events检查是否有sudo相关事件。一旦发现立即触发 PagerDuty 告警。上线半年零次误用。5.3 Agent 框架对比Hermes Agent、Cursor Agent 与 Substrate 的集成模式热搜词中频繁出现的Hermes Agent、Cursor Agent它们并非 Substrate 的竞品而是不同的抽象层级。理解它们的关系是选型的关键。特性Hermes AgentCursor AgentSubstrate Runtime定位Agent 编排框架OrchestrationIDE 内嵌的 coding agentTool Integration可信执行环境Trusted Execution Environment核心能力定义 agent workflow、管理 memory、调用 tools理解代码语义、生成/修改代码、与 IDE 深度集成提供确定性执行、状态存储、可验证证明与 Substrate 集成方式将 Substrate 作为其 backend 的一个“tool”通过 RPC 调用execute_wasm将 Substrate 作为其“代码执行沙箱”在 WASM 中运行 generated code作为底层 runtime承载 Hermes/Cursor 的核心 logic我们的实践是Hermes Agent 作为前端编排层Substrate 作为后端执行层。Hermes 负责解析用户自然语言指令、规划执行步骤、管理对话状态当需要执行一个“高风险”操作如转账、修改合约时Hermes 会将生成的 WASM bytecode 和 input data通过 HTTPS POST 到 Substrate node 的 RPC endpointSubstrate 执行后返回结果和 Merkle proofHermes 验证 proof 无误再将结果返回给用户。这种分离让每个系统都专注于自己最擅长的事Hermes 做好“智能”Substrate 做好“可信”。它比把所有逻辑塞进一个 monolithic agent 服务中更安全、更可维护、也更易扩展。5.4 “Agent 预设无法加载” 的深层原因Runtime Upgrade 与 Storage Migration无法加载 agent 预设。client api: agentpresets/list failed: failed to fetch这个错误表面看是 API 调用失败但根源往往在 Substrate runtime 的 storage migration 上。当 agent 的预设preset数据结构发生变化如新增一个字段timeout_seconds: u32而 runtime 升级后没有正确执行on_runtime_upgradehook旧的 storage 数据就无法被新代码解析导致agentpresets/list查询返回空或 panic。我们的标准迁移流程在 pallet 的on_runtime_upgrade函数中编写明确的 migration 逻辑pub fn on_runtime_upgrade() - Weight { // 1. 读取旧 storage let old_presets OldPresetStorage::T::iter().collect::Vec_(); // 2. 转换为新结构 let new_presets: VecNewPreset old_presets.into_iter() .map(|(key, old)| NewPreset { id: key, name: old.name, config: old.config, timeout_seconds: 30, // 新增字段的默认值 }) .collect(); // 3. 写入新 storage for preset in new_presets { NewPresetStorageT::insert(preset.id, preset); } // 4. 清理旧 storage OldPresetStorage::T::kill(); T::DbWeight::get().reads_writes(100, 100) // 返回估算的 DB weight }在 runtime
返回列表