news 2026/9/28 16:17:58

用Substrate搭建自定义区块链:Runtime与Pallet模块化解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
用Substrate搭建自定义区块链:Runtime与Pallet模块化解析

一开始接触 Substrate,我是带着不少问号的。区块链框架那么多,为什么偏偏要选一个名字听起来像“底料”的东西?但当你真正用它搭起一条链,跑通第一个自定义模块,再把链升级到新的逻辑之后,那种“原来区块链还可以这样造”的感觉,确实会颠覆很多固有认知。如果你也想了解从零构建一条自定义区块链、想搞懂 Runtime 和 Pallet 到底在干嘛,或者只是好奇波卡系项目的底层技术,这篇文章应该能给你一个比较完整的参考。

Substrate 本质上是一套开源的区块链构建框架,由 Parity 团队维护,很多知名项目比如 Polkadot、Kusama 的平行链内核,以及大量独立链,都是用这套框架开发出来的。它最大的特点是模块化得彻底,把区块链需要的网络层、共识层、存储层、Runtime 执行环境全部抽象成了可插拔的组件。开发者不需要从零去实现密码学、P2P 网络和数据库,只需要专注在自己的业务逻辑上。这种做法的好处很明显:你可以在几十分钟内跑起一条具备出块能力的链,然后慢慢把核心逻辑加进去,把一个原型变成生产系统。

这篇文章我会从“为什么需要 Substrate”讲起,然后拆解它的核心架构,再带你把一条自定义链从编译到跑通,创建一个简单的存证 Pallet,最后整理一些我实际开发中踩过的坑和排查方法。内容偏实操,也会解释一些背后的设计思想和原理,适合刚入门区块链开发、对波卡生态感兴趣,甚至是想选型区块链框架的技术同学阅读。

1. 为什么是 Substrate:从“造链”说起

1.1 Substrate 到底解决了什么问题

传统开发区块链,通常绕不开两条路:一条是自己从底层开始造轮子,密码学、共识、网络、状态存储、RPC、交易池全部自己写,工作量巨大而且容易出安全漏洞。另一条是拿以太坊、Bitcoin 这类现成代码改成联盟链或私链,但改起来会非常痛苦,因为这些代码的目标本身就是一套特定业务,很多逻辑是写死的,你想加一个自定义交易类型,往往要动到核心代码。Substrate 想解决的,就是这两个极端之间的平衡:既要保留区块链底层的完整能力,又要让上层业务可以像写普通应用一样自由扩展。

它给出的方案是“分层 + 模块化”。底层 Fixed 的部分——比如网络层、数据库、共识协议、交易队列——由 Substrate 提供;而业务逻辑所在的 Runtime 部分,则被刻意设计成可替换、可升级的。这意味着你可以像更新一个普通服务器程序一样更新一条链的规则,不需要硬分叉,不需要强制所有节点停止服务。这个能力在传统区块链领域是很难想象的,因为大多数链一旦上线,规则修改往往要靠社区硬分叉来解决。Substrate 则是通过把 Runtime 编译成 Wasm 字节码,放在链上,通过投票或治理机制,节点可以自动加载新的 Wasm,完成链上升级。

我个人的理解是,Substrate 不是做一个通用公链,而是提供一条“生产链的流水线”。它默认帮你处理了区块链团队最头疼的部分,比如如何安全地存储账户余额、如何处理交易手续费、如何治理和升级,而你只需要关注自己那条链独特的业务。比如你要做一条供应链溯源链,你不需要重新发明转账、无需担心 P2P 通信,只需要关注“商品流转记录”这个 Pallet 怎么设计就好。

1.2 和其他框架/开发方式的对比

在选型时,我们一般会拿 Substrate 和以太坊、Cosmos SDK 做对比。如果拿以太坊来说,核心开发方式是写 Solidity 智能合约,然后部署到一条已经存在的链上。这种模式优点是有现成的生态、稳定、安全,但缺点是链本身的规则不可变,合约之间的交互受限于 Gas 机制,举一个简单例子:如果我想自定义一个交易手续费模型,或者把共识从 PoA 改成 PoS,在以太坊上是很难的,除非再发一条链或用 Layer2。而 Substrate 可以直接在 Runtime 层面修改所有这些规则。

再来看 Cosmos SDK,它和 Substrate 思路很像,都是模块化造链框架。两者的区别在于:Cosmos 倾向于通过 IBC 协议做跨链互操作,SDK 的模块化程度也很高,但 Runtime 的升级机制没有 Substrate 那么原生和简洁;Substrate 原生的 Wasm Runtime 升级和治理框架,让链的演进变得更加顺畅。此外 Substrate 对平行链的支持是波卡特有的,如果你未来想接入波卡生态,获得共享安全性,那几乎是绕不开的选择。

总结来说,Substrate 的优势不是它比所有方案都“简单”,而是它在“可定制程度”和“开发效率”之间做了非常好的取舍。你想快速验证一条链,它提供了现成的模板;你想做一条完全独特的链,它也允许你替换掉几乎每一层组件,除了那层固定的核心基础。从我接触过的项目来看,Substrate 特别适合做有明确业务场景、需要自成一体的链,比如企业级联盟链、游戏链、DeFi 专项链,以及想要成为波卡平行链的项目团队。

2. 核心架构拆解,去掉没搞懂的部分就白搭了

2.1 Client、Runtime 与 Runtime 升级机制

如果只是会用命令行,不理解架构,很容易在后续开发中陷入迷茫。Substrate 的架构其实分成两个大层次:一个是外层 Client,一个是内层 Runtime。Client 可以理解成节点的“操作系统”,它负责处理网络连接、同步区块、调用数据库、运行共识,一直到验证和执行区块时,才会把具体的状态转换逻辑交给 Runtime。Runtime 则是一段 Wasm 字节码,它定义了这条链的所有业务规则,比如账户余额怎么变、交易怎么处理、手续费怎么收、治理怎么投票。

这两个层次的分离,是 Substrate 最核心的设计。它允许 Runtime 被当作一个“可替换的发动机”:当链上的 Runtime Wasm 被更新为新的版本时,节点只需要编译并应用新 Wasm,就能完成升级。这样一来,链的规则升级不依赖硬分叉,因为区块头里的spec_version和authority等元信息会标记版本变化,节点在导入区块时自动执行新逻辑。实际开发中,我们用sudoPallet 调用set_code接口即可完成 Runtime 替换,或者通过治理模块发起公投。这个机制对开发者来说极其友好,因为代码上线后还能纠错、迭代,在主网上线前尤其有用。

我建议新人在理解 Substrate 时,先记住一句话:客户端提供框架和确定性执行环境,Runtime 提供状态转换规则。你写的大部分业务代码都在 Runtime 里,而编译出来的 Wasm 会嵌入到客户端之外,还会被存到链上以便验证和升级。所以你会看到 Substrate 项目编译后会有两个核心产物:一个是原生 Runtime(native runtime),一个是 Wasm runtime。开发模式下默认优先使用原生 Runtime 提升性能,但链上验证和同步节点会使用 Wasm Runtime,保持一致性和确定性。

2.2 FRAME 与 Pallet 的模块化思想

FRAME(Framework for Runtime Aggregation of Modular Entities)是 Substrate 官方提供的一套模块化开发框架,如果你用官方模板,几乎都在写 FRAME。FRAME 的核心是 Pallet,也就是一个个独立的模块,好比一套积木里的基础积木。系统自带很多常用 Pallet,比如System、Balances、TransactionPayment、Sudo、Aura、Grandpa、Council、Democracy等,它们分别处理账户体系、余额转账、手续费、超级管理员、出块共识、治理投票等。你可以选择启用或禁用这些 Pallet,也可以编写自己的自定义 Pallet。

为什么用模块化?因为业务逻辑和链底层彻底解耦了。比如你的业务需要一种全新的数字资产,不用去处理什么 Merkle 树、哈希算法,只需写一个 Pallet 来定义资产存储、转账逻辑、发行销毁规则,然后通过construct_runtime!宏把这个 Pallet 装配到 Runtime 里。这个模式很像后端开发中的微服务,但比微服务更严格,因为所有 Pallet 都在同一个 Runtime 中运行,共享同一个交易上下文和状态数据库,调用顺序和权重都是确定的。模块与模块之间可以通过Configtrait 配置关联类型、通过call接口调用,也可以发送事件(Event)供外部监听。

实际编写 Pallet 时会发现,它非常强调声明式。每个 Pallet 的核心就是三部分:存储项(Storage)、事件(Event)、可调用函数(Callable)。存储项对应链上状态,例如#[pallet::storage]定义键值映射;事件用于通知外部某个操作发生了;可调用函数就是链上交易/外部调用入口。此外还有错误(Error)、常量(Constant)等定义。这些结构在编译时会自动生成对应的 encode/decode、metadata 等代码,不需要手写繁琐的序列化和 JSON-RPC 接口,这也是开发效率高的原因之一。

2.3 存储模型与状态管理

Substrate 的存储模型看似简单,实则细节满满。它默认使用 RocksDB 作为数据库,所有状态都以键值对的形式组织,并带有一个装饰性的 Merkle 化层,生成状态根。存储设计上,Runtime 存储项是显式声明在某个 Pallet 里的,编译后会有一个固定的 prefix,从而避免不同 Pallet 之间的键冲突。每个存储项可以是一个简单的Value(单值)、Map(键值映射)或者DoubleMap(双键映射),事实上还要更复杂的存储变体。这相当于你把业务数据直接放进链的“数据库”中,不需要额外设计索引表。

一个重要的概念是“缓存储存”(Storage Overlay)。简单说,每个区块执行期间,Runtime 所使用的存储是一个覆盖层,它先读取底层数据库,然后把读写操作记录在内存缓存中,区块执行完毕后再统一提交。这保证了执行过程中的原子性:如果执行失败,所有状态修改都会被回滚,不会留下半截脏数据。所以 Pallet 里的简单函数调用并不需要自己实现事务,Substrate 已经帮你处理了。唯一需要注意的是,在一个交易内部多次写入同一个存储项时,以最后一次写入为准,访问存储有一定的开销,所以不要在高频调用的交易里做不必要的存储操作。

理解存储对调试和性能优化非常重要。比如我见过有人盲目使用Map存储大量数据,结果链上状态无限膨胀,最终每个区块执行时间明显变长。实际上如果数据可以计算出来,就不要上链;如果非得上链,可以考虑只存储哈希或者校验和。Substrate 还提供了多种存储迭代方式,但代价是性能,因此设计存储时要克制,尽量把存储量控制在业务必需的最小集。

2.4 共识层与 Libp2p 网络

Substrate 的共识是可插拔的。常见的出块共识有 Aura(Authority Round,轮流出块)、BABE(用于波卡的随机出块共识),最终确定性机制则有 GRANDPA。对于大多数自定义链,模板默认使用 Aura 的 PoA 方式,也就是预先设定一组 validator 轮流生成区块。这种共识简单高效,非常适合开发测试和企业链场景。如果你的链需要更高程度的去中心化,可以替换为 PoS 等更复杂的共识,也可以组合使用 BABE + GRANDPA,这与 Kusama 或 Polkadot 的机制一致。

由于 Substrate 内置了 Libp2p 网络,所以节点之间的发现、连接、协议通信全都是开箱即用的。你只需要关心如何维护一组 bootnode,让新节点能接入网络。开发模式下,本地单节点不需要外部连接,也能自动出块,加上--dev参数后会自动生成固定的测试账号,非常方便。

共识和网络虽然是底层能力,但它和业务是有关联的。比如你如果要做一条联盟链,可能需要限制 validator 集合,甚至实现准入机制;如果你做公链,则要考虑安全性和抗女巫攻击。理解 Substrate 的共识抽象,有助于你选型正确组件,而不会在后期因为共识能力不足而返工。

3. 从零到一:使用 Substrate 搭建一条自定义链

3.1 环境准备与 node-template 快速上手

我建议第一次尝试的人直接用官方维护的substrate-node-template,它会搭好一个最小可运行链,目录结构清晰,甚至包含一个前端模板。首先你的机器需要具备 Rust 工具链。Substrate 对 Rust 的版本敏感,建议直接安装最新稳定版 Rust,然后添加 nightly 工具链,因为很多依赖(如parity-scale-codec、wasm-builder)会用到 nightly 特性。

Ubuntu/Debian 环境下,可以先安装基础依赖:

curl https://sh.rustup.rs -sSf | sh rustup update nightly rustup target add wasm32-unknown-unknown --toolchain nightly rustup component add rust-src --toolchain nightly

然后获取模板代码:

git clone https://github.com/substrate-developer-hub/substrate-node-template cd substrate-node-template

第一次编译通常比较慢,尤其是需要构建 wasm builder,可能要拉取大量 crate 并且编译很久。建议把系统内存控制在 16GB 以上,否则可能因为并行编译导致内存不足。我们可以限制并行度,例如设置CARGO_BUILD_JOBS=4或者使用SCCACHE缓存。第一次编译成功后,直接运行:

cargo run --release -- --dev

如果一切正常,你可以看到节点开始出块,每几秒产生一个区块。这时候再打开另一个终端,进入substrate-front-end-template或者其他 Polkadot.js 钱包应用,连接本地节点(默认端口是 9944 WebSocket),就能看到余额、区块信息等。

这段快速体验的核心价值是建立“造链”的信心:原来区块链不是一个神秘的黑盒,而是一个你拥有完全控制权的应用。从这一秒开始,你相当于拥有了一条自己的测试链。

3.2 修改 Runtime 与创建第一个 Pallet

跑通模板后,我就带你动手改一改 Runtime。打开runtime/src/lib.rs,你会看到construct_runtime!宏,里面列出了许多 Pallet,例如:

construct_runtime!( pub enum Runtime { System: frame_system, Balances: pallet_balances, TransactionPayment: pallet_transaction_payment, Sudo: pallet_sudo, TemplateModule: pallet_template, } );

这个TemplateModule是模板自带的一个空壳 Pallet,位于pallets/template/目录下。它展示了 Pallet 的基本结构。我们可以在它的基础上修改,或者新建一个 Pallet,比如做一个“存证”模块,让用户能够把一段数据的哈希存到链上,然后提供查询验证功能。

创建新 Pallet 最简单的方式是复制pallets/template目录,重命名为pallets/evidence,然后修改其中的 Cargo.toml、src/lib.rs,再把模块名称和依赖引入 Runtime。这个过程需要修改三个地方:

  • pallets/evidence/Cargo.toml中的包名和pallet名称;
  • runtime/Cargo.toml中添加对pallet-evidence的依赖;
  • runtime/src/lib.rs中添加模块的mod声明、impl块和construct_runtime!中的条目。

其实如果只是为了体验,直接在pallet_template里改也行。但新建一个模块会让你更明白模块之间的装配关系。在改的时候注意命名一致性,否则编译会报找不到类型的错误。这个步骤最烦,但也最能让你搞懂 Rust 模块系统和 Substrate 的配置模式。

3.3 编写并测试一个简单的存证 Pallet

现在我来写一个简化版的存证 Pallet,目的是展示存储、事件、Callable 的用法。首先在pallet/evidence/src/lib.rs里,定义存储项:

#[pallet::storage] #[pallet::getter(fn evidence_records)] pub type EvidenceRecords<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, Vec<u8>, ValueQuery>;

这段代码表示一个映射,从账户地址到存证内容。我们只存哈希摘要,也就是Vec<u8>。接着定义事件:

#[pallet::event] #[pallet::generate_daemon] pub enum Event<T: Config> { EvidenceStored(T::AccountId, Vec<u8>), }

当然,不能少的是 Call 函数。因为链上操作都用#[pallet::call],下面的接口让用户提交存证:

#[pallet::call] impl<T: Config> Pallet<T> { #[pallet::weight(10_000)] pub fn store_evidence( origin: OriginFor<T>, evidence_hash: Vec<u8>, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; // 这里可以加入长度校验等逻辑 ensure!(evidence_hash.len() <= 64, Error::<T>::TooLong); <EvidenceRecords<T>>::insert(&who, evidence_hash.clone()); Self::deposit_event(Event::EvidenceStored(who, evidence_hash)); Ok(().into()) } }

这个函数做的事情就是:验证调用者是已签名的账户,检查哈希长度,存储映射,然后发一个事件。上面的Weight我随便写了一个值,实际项目里要用T::DbWeight::get().writes(1)之类的方式计算更准确。编译如果报了 trait 相关错误,大概率是Config没有正确实现或者没有引入对应的依赖。

写完 Pallet 后,在链上执行这个函数前,需要构造一条交易。用模板自带的 front-end 可以调用函数,也可以通过写集成测试来模拟。我强烈推荐在 Pallet 内部写 Rust 单元测试,因为可以直接用 Mock Runtime 测试,不需要启动节点。模板里已经包含了测试框架,你可以在tests模块里构造new_test_ext(),然后调用store_evidence,最后检查存储是否被写入。这种方式跑起来快,还能在编译阶段发现业务逻辑错误。

3.4 构建与启动本地节点、使用前端交互

当你完成 Pallet 编写后,回到项目根目录执行:

cargo build --release

构建完成后,启动节点。如果你在前三步中已经启动过,需要先停掉旧节点,然后重新启动:

./target/release/substrate-node-template --dev

启动后,打开前端模板或者直接使用 Polkadot.js Apps,选择“本地节点”连接。在 Extrinsics 页面中,选择你的模块和storeEvidence调用,填入一个十六进制或普通字节数组作为evidenceHash,然后点击提交。如果你用的是 dev 模式,默认的 Alice 账户会有余额,可以支付手续费。提交成功后,可以在 Network 页面看到事件EvidenceStored被触发,也可以在 Chain State 页面查询evidenceRecords映射。

这里有一个很容易踩的坑:前端输入框要求的数据格式不一定是我们习惯的字符串。有时是十六进制数据,有时是 UTF-8。如果传入的 hash 太长或格式不对,就会出现解码错误。建议在本地测试时先用一个简单的十六进制值,比如0x1234,确保流程跑通,再逐步加深。通过这个交互过程,你会体验到一条链的核心流程:签名交易、执行 Runtime 逻辑、状态更新、事件触发、状态可查。这一步跑通了,你就完全具备自定义链开发的基础能力。

4. 常见问题与排查技巧实录

4.1 编译慢、内存不足怎么办

Substrate 项目编译慢是出了名的,尤其是第一次编译。首先建议内存 16GB 以上,不行就给系统增加 swap 空间。第二个解决方法是设置环境变量控制并行编译:

export CARGO_BUILD_JOBS=4

或者在项目根目录创建.cargo/config.toml,加入:

[build] jobs = 4

还可以用sccache作为编译缓存,后续多次编译会快很多。如果你是 Linux 用户,可以用systemd开启一个小内存交换空间。不要用cargo build不带--release跑大型项目,因为 debug 模式的 Wasm 性能和运行速度都可能出现问题,而且 debug 编译也需要时间,不如直接 release。另外,每次修改 Pallet 后,如果你只改了业务逻辑,没有改依赖版本,可以使用:

cargo build --release -p pallet-evidence

先单独验证 Pallet 是否能编译通过,避免整个项目反复全量编译。

4.2 Runtime 升级失败或节点不同步

Runtime 升级是 Substrate 的亮点,但也是坑点。手动升级的时候,最容易遇到的问题是在sudo界面中调用sudoUncheckedWeight或者setCode时填错了 Wasm 文件。正确做法是,从target/release/wbuild/substrate-node-template-runtime/substrate_node_template_runtime.compact.compressed.wasm路径获取最新的压缩 Wasm。如果你上传的是未压缩的 Wasm 或者体积差异过大的文件,节点可能拒绝执行。

另一个常见问题是升级后节点上的 Runtime 版本不匹配,导致之后的区块无法导入。出现这种情况时,很可能是升级时网络中有节点落后了。首先确认所有节点都已经更新到同样的 Runtime 版本,可以通过 RPC 的state_getRuntimeVersion接口查询。如果是开发模式的单节点,遇到升级失败可以直接清空数据目录重启--dev模式,但在正式测试链上就不能简单清数据了,因此要养成每次升级前备份数据目录的习惯。

有时我们修改了 Chain Spec 中的一些参数,比如触发spec_version没有增加,就会导致升级被忽略。Substrate 的set_code方法要求新总量的spec_version大于旧版本,否则返回错误。所以每次修改 Runtime 后,记得在runtime/src/lib.rs的VERSION常量中递增spec_version。我在实际测试时就漏过这一步,浪费了不少时间。

4.3 节点同步异常、出块卡住

本地开发模式下,如果节点日志一直不出块,大概率是共识权威节点没有运行起来,或者你启动了多个节点互相冲突。--dev模式自带一个测试账户,通常不会有问题。但如果你修改过 Chain Spec 或使用了自定义的 validator 集,就需要确保证书(authorities)配置正确。如果使用 Aura,检查链上AuraPallet 的存储是否包含当前节点的公钥。如果是 BABE/Grandpa,还需要检查验签。

另一个常见问题是端口被占用。常见端口包括 30333(P2P)、9944(WebSocket)、9933(RPC)。如果本地已经有旧节点在运行,新节点会绑定失败。可以使用lsof -i :9944查看占用情况,然后重启或更换端口。如果是长期运行的节点停止出块,可以考虑调整系统时间是否漂移,或磁盘是否已满,因为这些会导致数据库无法写入。Substrate 对数据库的完整性要求很高,如果磁盘满导致写入中断,后续可能要从备份恢复。

总之,遇到出块卡住,不要急于删除数据,先看日志最后几行,特别是WARN或ERROR级别的记录,往往能直接定位到问题。如果是区块头不连续、节点一直等待导入,那有可能是网络分区或者 validator 离线。这些问题在小规模测试链上大多是配置问题,而不是代码 Bug。

4.4 一些节省时间的开发技巧

最后分享几个开发技巧。第一,善用try-runtime特性。你可以在编译时运行:

cargo run --release --features try-runtime -- try-runtime on-runtime-upgrade dev

这能在本地模拟链上升级时的迁移逻辑,提前发现 Runtime 迁移可能导致的数据不兼容问题。这个特性在项目上线前尤其重要。

第二,充分利用pallet-contracts。如果只是想快速测试业务逻辑,可以不直接写 Runtime Pallet,而是先创建一条带智能合约功能的 Substrate 链,用 ink! 写合约测试。合约模式更适合频繁迭代的小功能,而 Pallet 更适合与链底层深度绑定的大模块。二者可以并行存在,选择哪种取决于你和业务的复杂度。

第三,写测试时不要只测正常路径,还要测权限不足、存储溢出、重复提交等情况。比如我们的存证模块,如果不校验长度,恶意用户可以存一个超大数据,导致链上存储膨胀。类似的问题,必须在 Pallet 的dispatch中通过ensure!提前拦截,并且一定要有单元测试覆盖。好的单元测试是 Substrate 项目最容易被忽视但又最重要的资产。

第四,可以给节点开启--enable-logging或调整日志级别,比如-l runtime=debug,authority=info来查看更多执行细节。遇到异常调用时,可以通过 RPC 工具查询实际 dispatch 错误,并结合system_events查看错误信息。平常开发时把函数写细一点,每个可能的错误都定义成独立的 Error 枚举值,这样排查起来会非常轻松。

我在实际使用 Substrate 的过程中,最大的感受是——它确实让独立开发一条链这件事的门槛降低了很多,但门槛低不等于没有门槛。你仍然需要理解 Runtime 开发的基本模型,尤其是 Rust 的泛型 trait 和链上确定性执行的特点。多写几个 Pallet、多跑几次测试、多被几个编译错误折磨一下,你会进步得很快。造链不是把模块堆起来就完事,而是要像一个系统架构师那样思考状态、权限、升级和治理。如果你能用 Substrate 独立搭出一条稳定的链,那你会对区块链底层的理解上升一个台阶。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 16:17:04

Java网址导航站实战:Spring Boot全栈开发与部署指南

简介&#xff1a;这是一套基于Java开发的开源网址导航网站完整项目源码&#xff0c;面向计算机相关专业学生及初级开发者&#xff0c;适用于课程设计、大作业、项目实战与毕业设计参考。资源包含可直接运行的后端Java代码、前端HTML/JS/CSS页面、数据库SQL脚本及配套说明文档&a…

作者头像 李华
网站建设 2026/9/28 16:16:09

苹果缺陷语义分割数据集:4000张图支撑工业质检落地

简介&#xff1a;本资源是面向计算机视觉初学者与农业AI应用研究者的苹果缺陷图像语义分割数据集&#xff0c;专为训练和评估图像分割模型&#xff08;如U-Net、SwinUNet等&#xff09;提供高质量标注样本。数据集涵盖健康苹果及4类典型病害区域共5个语义类别&#xff0c;已按标…

作者头像 李华
网站建设 2026/9/28 16:15:06

Agent-Native架构落地:从状态机到事件日志的智能体实践

最近大半年我一直在折腾 agent-native 方向的东西&#xff0c;从原型到生产环境都跑过一遍。所谓 agent-native&#xff0c;简单说就是把智能体当作系统的一等公民&#xff0c;而不是在传统软件上缝一个 AI 聊天框。应用的任务编排、状态管理、工具调用、权限控制&#xff0c;全…

作者头像 李华
网站建设 2026/9/28 16:15:03

CLI-Anything:用Node.js和Ink打造统一命令行工作台的设计与实践

1. 项目概述与核心思路1.1 从一次“工具乱葬岗”说起我先交代下背景。做后端、运维或者 DevOps 的朋友应该都有这种体会&#xff1a;电脑里堆积的脚本和工具越来越多。这边一个 Python 脚本是用来清日志的&#xff0c;那边一个 Node 脚本是用来拉监控数据的&#xff0c;还有一组…

作者头像 李华
网站建设 2026/9/28 16:14:37

AI编程代理超能力指南:Codex技能包与MCP工作流实操

我一直觉得&#xff0c;程序员社区里最玄学的词就是“superpowers”。你在 GitHub 上搜这个关键词&#xff0c;能翻出一堆古早的 3D 粒子特效库&#xff0c;也能在 VSCode 插件市场里看到各种叫“Superpowers”的主题&#xff0c;甚至还有人用它给游戏作弊脚本命名。但最近半年…

作者头像 李华
网站建设 2026/9/28 16:13:26

用MFC写五子棋:GDI绘制、双缓冲与状态管理实战

简介&#xff1a;基于C与微软基础类库&#xff08;MFC&#xff09;实现的五子棋完整工程&#xff0c;面向学习Windows桌面编程或完成课程设计的开发者。项目包含棋盘棋子绘制、输赢判定、新建游戏、悔棋及棋盘背景样式修改等核心功能&#xff0c;代码结构清晰&#xff0c;便于观…

作者头像 李华