插件加载方式选不好,迟早被同事吐槽——四种姿势全解析

「Regnexe 实战系列」第 5 篇(共 10 篇),对应仓库 ExampleReadme05PluginPackagingTest。上一篇:04. @Plugin 注解打包一切。

😤 一个真实的团队矛盾

做平台型项目应该都遇到过这种争论:

"工具应该写死在代码里,编译期就能查出问题。" "不对,运维要能随时加减插件,不能每次都重新编译发版。"

两边都没说错,只是关注点不一样。Regnexe 没有让我们选边站,而是把"插件怎么打包"和"插件怎么加载"拆成了独立的维度,四种方式并存,按场景挑。

1️⃣ 代码直打包:PluginDescriptor 才是底层真相

前几篇用的 withTool/withSkill/withSubAgent/@Plugin,最终都会落到同一个东西上:一个装着若干 CapabilityDescriptor 的 PluginDescriptor。想完全手动控制,PluginDescriptor.builder() 直接开放了这条路:

java 复制代码
PluginDescriptor tripPlugin = PluginDescriptor.builder()
        .pluginId("trip-plugin")
        .version("1.0")
        .name("Trip Plugin")
        .description("Bundles a tool, a skill, and a sub-agent for trip planning")
        .tool(weatherTool)                       // -> trip-plugin.get_weather
        .skillConfig(travelAdvisor)              // -> trip-plugin.travel_advisor
        .subAgentConfig(expenseEstimator)        // -> trip-plugin.expense_estimator
        .build();

regnexeAgentBuilder.withPlugin(tripPlugin) ...

一个 PluginDescriptor,三种能力类型,id 自动按 pluginId + "." + name 拼好。适合能力定义来自数据库、配置中心,需要在运行时动态拼装的场景。

⚠️ 踩坑提醒:三种能力共用一个 pluginId 时,Skill 的 allowedTools 必须写完整能力 id------"trip-plugin.get_weather",不是裸的 "get_weather"。

2️⃣ 包扫描:让插件"自己长出来"

插件类已经按 @Plugin/@AgentSkill/@AgentSubAgent 写好了,不用一个个 new 出来注册,直接扫包:

java 复制代码
regnexeAgentBuilder.withScanPackages("com.example.plugins") ...

DefaultPluginManager 会在指定包下找出所有带这三种注解的类,自动实例化、自动注册。适合插件数量较多、希望"加一个类就自动生效"的场景。

3️⃣ 文件系统目录:运维真正想要的那种

回到开头那个争论,运维要的"不重新发版就能加减插件",靠纯文件目录解决:

bash 复制代码
/opt/regnexe-plugins/
  weather-plugin/
    plugin.yaml              ← 元数据
    tools/
      get_weather.sh         ← 脚本工具
      get_weather.yaml       ← 配套说明
    skills/
      advisor/SKILL.md       ← Skill
    subagents/
      planner/AGENT.md       ← Sub-Agent
java 复制代码
regnexeAgentBuilder.withDirectory("/opt/regnexe-plugins") ...

加一个插件文件夹、删一个插件文件夹,应用都不用重启重新发布。

🧪 实测验证:不是只讲道理

仓库里每种方式都有真实测试,比如包扫描:

java 复制代码
DefaultPluginManager mgr = new DefaultPluginManager();
mgr.scanPackages("org.salt.regnexe.agent.core.example.testplugins");
SimpleMarketplace marketplace = new SimpleMarketplace();
marketplace.load(mgr);

CapabilityDescriptor cap = marketplace.resolveDescriptor("test-weather-plugin.get_weather");
Assert.assertNotNull(cap);   // 扫描成功,能力已注册

文件系统目录加载也是同理,临时建一个目录、写好 plugin.yaml 和脚本文件,addDirectory(...) 之后直接能 resolveDescriptor 拿到对应能力。

📋 四种方式怎么选

方式 适合场景
withTool/withSkill/withSubAgent 临时脚本、PoC、动态生成的简单能力
@Plugin + 嵌套注解 业务模块固定、需要打包管理和版本控制
PluginDescriptor.builder() 能力定义来自 DB/配置中心,运行时动态拼装
包扫描 withScanPackages 插件类已写好,希望自动发现注册
文件系统目录 withDirectory 运维要随时增删插件,不接受重新发版

它们不是互斥关系,一个应用里完全可以同时用------核心业务工具用 @Plugin 写死,运营临时配置的能力走目录加载。这套 harness 在这件事上的态度很明确:不替你做选择,只把每个选项的成本降到最低。


你们团队的插件管理最后是怎么落地的?欢迎评论区吐槽踩过的坑,点个赞再走 👍

📌 上一篇:04. @Plugin 注解打包一切 | 下一篇:06. 能力市场换成数据库 📌 项目地址:github.com/flower-tree...

相关推荐
张忠琳6 分钟前
【hermes-agent】Hermes Agent 自我进化原理之一
ai·agent·hermes
Bug收容所1 小时前
学习LangChain day1
学习·langchain·llm·agent
流浪0012 小时前
大模型技术全景(十一):智能体通信协议 MCP、A2A 与 ANP
llm·agent·通信协议·mcp·a2a·anp
小此方2 小时前
LangChain/LangGraph(二)大模型接入篇一:API接入,从API Key到API请求报文,使用Apifox完成大模型接口调用
ai·langchain
半糖程序员3 小时前
从零构建 Agent(10):保存并恢复会话
agent
熊猫钓鱼>_>3 小时前
越顺,越空:当 AI 把学习 “优化“ 到消失
人工智能·学习·ai·llm·agent·ai编程·metaai
每天都要写算法(努力版)4 小时前
【行业前沿报告】给智能体写工具:从接口能调用,到任务能完成
llm·agent
网络毒刘4 小时前
GPT-6.1 Sol 定位速读:成本效率型编码模型与「何时该换本地 Agent」
人工智能·gpt·openai·agent·cursor
后端小肥肠4 小时前
Claude Opus 5.5 做视频:从口播稿到成片,全流程跑通
人工智能·aigc·agent
网络毒刘4 小时前
Manual 模式精修补丁:在 Agent 提案后用最小编辑完成高风险改动
安全·agent·ai编程·cursor