Plugin Packages
AI4J 的 plugin package 解决的是:第三方开发者把工具、命令、Skill、Prompt、Guardrail 等运行时资源打包成一个普通 Java 依赖,使用者通过 classpath 引入后再检查、启用、授权和暴露。
它不是应用商店,也不是远程下载安装器。当前稳定路径是 Maven / Gradle 依赖 + ServiceLoader 发现 + ExtensionRegistry 安全门禁。
1. 它和 provider extension 不是一回事
AI4J 现在有两类容易混淆的扩展:
| 类型 | 解决什么 | 接入方式 | 当前状态 |
|---|---|---|---|
| Provider / model / service extension | 把新平台、新模型字段或新顶层服务接进核心 SDK | 修改核心工厂、配置和 starter 主链 | 显式代码接线 |
| Plugin package | 把工具、命令、Skill、Prompt、Guardrail 等资源交给 runtime 使用 | 第三方 jar + ServiceLoader + 显式 enable/expose | 独立扩展 API |
如果你要新增一个模型平台,仍然看 Provider Extension。 如果你要给 agent 或 coding agent 增加一组可复用工具、提示词或规则,才看这一页。
2. 使用者路径
普通 Java 使用者的完整路径分四步。
2.1 引入插件依赖
插件包就是普通 jar。使用 Maven 时,把插件加入应用依赖即可:
<dependency>
<groupId>com.example</groupId>
<artifactId>weather-ai4j-plugin</artifactId>
<version>1.0.0</version>
</dependency>
AI4J 不会自动远程拉取插件,也不会把插件写进你的项目依赖。依赖由你的构建系统管理。
2.2 发现并启用插件
ExtensionRegistry registry = ExtensionRegistry.discover()
.enable("weather-pack");
discover() 只负责从 classpath 发现实现。发现不等于启用。
enable(...) 是对这个插件包的运行时资源做整包信任:它会调用插件 apply(...) 注册 command、Skill、Prompt、Guardrail 和 tool 定义。为了兼容旧代码,默认模式下 command、Skill、Prompt 和 Guardrail 会随插件启用进入运行时;tool 仍然必须再经过 exposeTool(...) 才会给模型。
如果你要接入第三方插件,建议启用显式资源授权模式:
ExtensionRegistry registry = ExtensionRegistry.discover()
.enable("weather-pack")
.requireExplicitResourceActivation()
.allowCommand("weather.status")
.allowSkill("weather-skill")
.allowPrompt("weather-summary")
.allowGuardrail("weather-policy");
开启 requireExplicitResourceActivation() 后,未列入 allowlist 的 command、Skill、Prompt 和 Guardrail 不会进入 ExtensionRuntimeSnapshot。配置了不存在的资源名时,snapshot() 会 fail-fast,避免宿主以为已经授权成功。
2.3 显式暴露工具
ExtensionRegistry registry = ExtensionRegistry.discover()
.enable("weather-pack")
.exposeTool("weather.search");
启用也不等于把工具交给模型。只有 exposeTool(...) 后,工具才会进入 agent tool registry。
exposeTool(...) 和 allowCommand(...) 不是一回事:
| API | 影响范围 |
|---|---|
exposeTool("weather.search") | 让指定 tool 进入模型可见工具列表 |
allowCommand("weather.status") | 允许人工 / 宿主显式执行插件 command |
allowSkill("weather-skill") | 允许 Skill 被读取或投影到 Coding Agent 上下文 |
allowPrompt("weather-summary") | 允许 Prompt 被读取或投影到 Coding Agent 上下文 |
allowGuardrail("weather-policy") | 允许 Guardrail 接入 tool execution 前置判断 |
2.4 Spring Boot 配置路径
Spring Boot 项目可以用配置完成同一件事:
ai:
extensions:
enabled:
- weather-pack
tools:
expose:
- weather.search
如果要启用显式资源授权:
ai:
extensions:
enabled:
- weather-pack
explicit-resource-activation: true
tools:
expose:
- weather.search
commands:
allow:
- weather.status
skills:
allow:
- weather-skill
prompts:
allow:
- weather-summary
guardrails:
allow:
- weather-policy
starter 会自动创建两个 bean:
| Bean | 作用 |
|---|---|
ExtensionRegistry | 保存 classpath 发现、显式启用、资源授权和工具 allowlist 状态 |
ExtensionRuntimeSnapshot | 保存已启用 / 已授权资源和已暴露工具的只读快照 |
如果配置了不存在的插件包,或者只配置 tools.expose / commands.allow / skills.allow / prompts.allow / guardrails.allow 却没有启用贡献该资源的插件包,应用启动会失败。这是刻意设计的安全边界:Spring Boot 配置也不能绕过 discover / enable / allow / expose 门禁。
starter 不会自动创建 Agent 或 Coding Agent。需要 Agent 时,仍然把 ExtensionRegistry 传给 Agent builder:
@Bean
public Agent agent(ModelClient modelClient, ExtensionRegistry extensionRegistry) {
return Agents.react()
.modelClient(modelClient)
.model("glm-4.5-flash")
.extensions(extensionRegistry)
.build();
}
2.5 CLI 检查路径
CLI 可以先查看 classpath 上的插件:
ai4j-cli extension list
ai4j-cli extension inspect weather-pack --runtime
ai4j-cli extension validate weather-pack
ai4j-cli extension plan weather-pack --enable \
--expose-tool weather.search \
--allow-command weather.status \
--allow-skill weather-skill \
--allow-prompt weather-summary \
--allow-guardrail weather-policy \
--strict
ai4j-cli extension check weather-pack --enable \
--expose-tool weather.search \
--allow-command weather.status \
--allow-skill weather-skill \
--allow-prompt weather-summary \
--allow-guardrail weather-policy \
--strict
plan 用来预览某个插件在当前授权参数下的激活状态。它会列出每个 tool、command、Skill、Prompt、Guardrail 是 active 还是 inactive,并给出原因,例如 not exposed、not allowed、not registered by extension。它适合放在“依赖已经加入 classpath,但还没接入 Agent”之前做人工检查。
validate 会像 inspect --runtime 一样临时调用插件 apply(...) 做 runtime inspection,并把 manifest、capability 声明、工具 schema、Skill / Prompt classpath 资源和 apply(...) 失败情况整理成校验报告。它只报告问题,不会把工具暴露给模型,也不会执行插件 command。
check 是可脚本化门禁。它会先执行 validation;validation 有 error 时直接返回非零,不再继续 activation plan。validation 通过后,它会启用插件并套用本次 --expose-tool / --allow-* / --strict 参数,只要显式请求的资源没有进入 active 状态就返回非零。没有被请求的插件资源不会导致失败,这样使用者仍然可以保留最小授权 recipe。
因此插件作者要把 apply(...) 写成轻量注册函数:只注册 spec、executor、classpath resource 和 guardrail,不要在 apply(...) 里连接远程服务、发起网络请求、写文件、读取用户密钥或执行长耗时初始化。真实副作用应该放到 tool executor、command handler 或宿主显式初始化流程里。
如果要检查当前 classpath 上所有插件:
ai4j-cli extension validate --all
返回值语义:
| 结果 | 含义 |
|---|---|
status=pass | 没有发现 error 或 warning |
status=warn | 没有阻断性错误,但存在建议修正项,例如缺少 manifest vendor 或 command usage |
status=fail | 存在会影响接入的错误,例如 tool schema 不可用、资源不存在或 apply(...) 失败 |
有 error 时 CLI 返回非零退出码。插件作者可以把它放进插件项目的本地测试或 CI;使用者也可以在引入第三方 jar 后先校验,再决定是否在宿主应用里启用。
2.6 CLI 命令执行路径
如果插件声明了 command,可以显式启用插件后执行:
ai4j-cli extension run --enable weather-pack --allow-command weather.status weather.status beijing
--enable 是必填项。--allow-command 会让本次命令进入显式 command 授权模式;没有 --allow-command 时,CLI 保持兼容行为:启用插件后可执行它注册的 command。classpath 发现插件不会自动执行命令,也不会把工具暴露给模型。extension run 是人手动调用插件 command 的 CLI 入口;Agent / Coding Agent 的模型可见工具仍然只走 .exposeTool(...) 或 Spring Boot ai.extensions.tools.expose。
2.7 CLI 资源读取路径
插件声明的 Skill / Prompt 是 classpath 资源。开发者可以先用 inspect --runtime 查看资源名和路径,再显式启用插件读取内容:
ai4j-cli extension resource --enable weather-pack --allow-skill weather-skill skill weather-skill
ai4j-cli extension resource --enable weather-pack --allow-prompt weather-summary prompt weather-summary
--allow-skill / --allow-prompt 会让本次资源读取进入显式资源授权模式;没有 allow 参数时,CLI 保持兼容行为:启用插件后可读取它注册的 Skill / Prompt。这个命令只打印 UTF-8 文本资源,不会执行插件工具,也不会把工具暴露给模型。它的主要用途是让插件作者和使用者确认 jar 内资源是否可被 AI4J 正确读取。
3. 接入 Agent
插件工具可以直接进入通用 Agent loop:
Agent agent = Agents.react()
.modelClient(modelClient)
.model("glm-4.5-flash")
.extensions(registry)
.build();
运行时会做两件事:
- 把已暴露的
ExtensionToolSpec转成 AI4J 现有的Tool - 把已暴露的
ExtensionToolExecutor路由到现有ToolExecutor
Agent 主循环不用认识插件实现类。模型看到的是普通 tool schema,调用时走同一套 tool result 回传流程。
3.1 Guardrail 执行点
默认兼容模式下,已启用插件注册的 Guardrail 会在 Agent 执行 tool call 之前评估。如果 registry 开启了 requireExplicitResourceActivation(),只有 allowGuardrail(...) 列出的 Guardrail 会进入执行链。AI4J 当前给插件 Guardrail 的请求语义是:
| 字段 | 值 |
|---|---|
action | tool.execute |
target | tool name,例如 weather.search、bash、read_file |
attributes.toolName | 同 target |
attributes.arguments | 模型给出的原始 tool arguments 字符串 |
attributes.callId | 当前 tool call id |
attributes.type | 当前 tool call type,存在时传入 |
如果任意 Guardrail 返回 GuardrailDecision.deny("reason"),AI4J 不会调用后续 tool executor,而是把拒绝原因作为普通 TOOL_ERROR 回写给 Agent loop。这样插件既可以约束自己暴露的 extension tools,也可以约束宿主已经开放给 Agent 的其他工具。
4. 接入 Coding Agent
Coding Agent 也使用同一个入口:
CodingAgent agent = CodingAgents.builder()
.modelClient(modelClient)
.model("glm-4.5-flash")
.workspaceContext(workspaceContext)
.extensions(registry)
.build();
插件工具会和内置 workspace 工具一起进入 coding session:
read_filewrite_fileapply_patchbash- 已暴露的 extension tools
- 已配置的 delegate / subagent tools
这意味着插件作者可以提供“项目扫描”“代码生成辅助”“业务规则检查”等工具,但执行权限仍然由宿主应用决定。插件不会绕过 Coding Agent 原有的 workspace、tool policy、approval 和执行边界。
已授权插件注册的 Guardrail 也会覆盖 Coding Agent 的 tool execution。它不仅能拦截已暴露的 extension tools,也能拦截内置 workspace tools,例如 bash、read_file、write_file、apply_patch,前提是宿主已经把这些工具交给当前 Coding Agent 会话。Guardrail 的判断发生在实际工具执行前;被拒绝的调用不会触发 shell、文件写入或 extension tool executor。
插件 Skill / Prompt 也会进入 Coding Agent 的上下文装配:
- 默认兼容模式下,已启用插件贡献的 Skill / Prompt 会被物化成只读文件;显式资源授权模式下,只有
allowSkill(...)/allowPrompt(...)列出的资源会被物化。 - Skill 会进入
<available_skills>清单;Prompt 会进入<available_prompts>清单。 - Agent 不会在系统提示里直接塞入完整资源正文,而是先看到资源名、描述和可读路径,再按任务需要用
read_file读取。 - 这些物化文件只加入
allowedReadRoots,不会扩大 workspace 写入权限。
这和本地 / 全局 .ai4j/skills 的使用方式一致:资源是给 agent 按需读取的工作流和模板,不是安装后自动执行的代码。
5. 开发者路径
第三方插件至少包含三个部分。
如果你还没有项目骨架,可以先用 CLI 生成一个最小 Maven 项目:
ai4j-cli extension init weather-ai4j-plugin \
--id weather-pack \
--package com.example.ai4j.weather \
--name "Weather Pack"
如果你是使用者,建议接着看 Plugin Recipes,那里把 Java、Spring Boot、CLI 和多插件组合写成可复制接入路径。如果你是第三方插件作者,建议完整走一遍 Plugin Author Cookbook。那里按 scaffold、替换业务逻辑、校验、发布说明和常见错误组织,比本页更适合作为动手流程。
这个命令只写入一个不存在或空的本地目录。它不会把插件依赖安装到宿主应用,不会拉取远程插件,也不会启用插件。生成后目录结构类似:
weather-ai4j-plugin/
pom.xml
README.md
src/main/java/com/example/ai4j/weather/WeatherPackExtension.java
src/main/resources/META-INF/services/io.github.lnyocly.ai4j.extension.Ai4jExtension
src/main/resources/skills/weather-pack/SKILL.md
src/main/resources/prompts/weather-pack-summary.md
src/test/java/com/example/ai4j/weather/WeatherPackExtensionTest.java
本地验证:
cd weather-ai4j-plugin
mvn test
生成的测试会调用 ExtensionValidator,先证明 manifest、runtime 贡献、Skill / Prompt classpath 资源和 schema contract 能被 AI4J 稳定读取。插件作者再把示例 Tool / Command / Skill / Prompt / Guardrail 替换成真实业务逻辑。
可选参数:
| 参数 | 作用 | 默认值 |
|---|---|---|
--group-id | Maven groupId | --package |
--artifact-id | Maven artifactId | --id |
--version | Maven 和 manifest 版本 | 1.0.0 |
--class-name | Ai4jExtension 实现类名 | 从 --id 派生 |
--vendor | manifest vendor | example |
5.1 实现 Ai4jExtension
public final class WeatherExtension implements Ai4jExtension {
public ExtensionManifest manifest() {
return ExtensionManifest.builder()
.id("weather-pack")
.name("Weather Pack")
.capability(ExtensionCapability.TOOL)
.build();
}
public void apply(ExtensionContext context) {
context.tools().register(
ExtensionToolSpec.builder()
.name("weather.search")
.description("Search weather by city")
.inputSchema("{\"type\":\"object\",\"properties\":{\"city\":{\"type\":\"string\"}},\"required\":[\"city\"]}")
.build(),
new ExtensionToolExecutor() {
public String execute(ExtensionToolCall call) {
return "weather result";
}
}
);
}
}
5.2 注册 ServiceLoader
在插件 jar 中加入:
META-INF/services/io.github.lnyocly.ai4j.extension.Ai4jExtension
文件内容写实现类全名:
com.example.ai4j.weather.WeatherExtension
5.3 给工具写清楚输入 schema
ExtensionToolSpec.inputSchema(...) 使用 JSON Schema 的核心字段:
typepropertiesrequireddescriptionenumitems
AI4J 会把这些字段映射成现有 OpenAI-compatible tool schema。不要把大段自然语言塞进 description 代替结构化参数。
Validator 会做最小结构检查:
- schema 必须是合法 JSON object。
- 根
type必须是非空字符串,当前要求为object。 properties如果存在,必须是 object;每个 property value 也必须是 object。required和enum如果存在,必须是只包含非空字符串的 array。items如果存在,必须是 object。
这不是完整 JSON Schema 引擎,但足够提前拦住 AI4J 当前 tool mapper 无法稳定消费的 schema。
5.4 打包 Skill / Prompt 资源
插件可以把 Skill 和 Prompt 放在 src/main/resources 下,再在 apply(...) 中注册资源路径:
public void apply(ExtensionContext context) {
context.skills().register(ExtensionSkillResource.builder()
.name("weather-skill")
.description("Weather workflow")
.resourcePath("skills/weather/SKILL.md")
.build());
context.prompts().register(ExtensionPromptResource.builder()
.name("weather-summary")
.description("Weather summary prompt")
.resourcePath("prompts/weather-summary.md")
.build());
}
对应 jar 结构:
src/main/resources/
skills/weather/SKILL.md
prompts/weather-summary.md
资源路径默认按 classpath 查找,也可以写成 classpath:skills/weather/SKILL.md。资源路径不能包含 ..,避免插件把 resource contract 伪装成任意文件读取。
5.5 写插件本地校验
插件作者可以直接在测试里调用公共 validator,不必依赖 CLI 文本输出:
ExtensionRegistry registry = ExtensionRegistry.of(new WeatherExtension());
ExtensionValidationReport report = ExtensionValidator.validate(registry, "weather-pack");
if (!report.isValid()) {
throw new IllegalStateException("extension validation failed: " + report.getIssues());
}
这套校验关注“插件包是否能被 AI4J 稳定消费”,不是第三方代码安全审计。它会检查:
- manifest 是否有 id / capability,并建议补齐 name、version、vendor
- 声明的 capability 是否真的贡献了对应资源
- tool 是否有基本可用的 input schema
- command 是否有描述和 usage
- Skill / Prompt 的 classpath 资源是否存在
apply(...)在 runtime inspection 中是否失败
它会调用插件 apply(...) 收集运行时贡献,但不会执行插件 command,不会把 tool 暴露给模型,也不会替宿主判断第三方插件是否可信。
6. 安全门禁
插件生态的默认语义是三段式门禁:
| 阶段 | 会发生什么 | 不会发生什么 |
|---|---|---|
| discover | 从 classpath 找到插件 manifest | 不执行工具,不暴露给模型 |
| enable | 调用插件 apply(...) 注册资源 | 工具仍不会进入模型可见列表 |
| allowCommand / allowSkill / allowPrompt / allowGuardrail | 在显式资源授权模式下允许非 tool 资源进入运行态 | 不会让 tool 进入模型可见列表 |
| exposeTool | 指定工具名进入 agent/coding tool registry | 只暴露被点名的工具 |
这个设计故意不做“安装后自动可用”。原因很直接:tool 一旦暴露给模型,就可能触发网络、文件系统、业务系统或工作区操作。AI4J 要求宿主应用明确决定哪些工具能进入模型上下文。
为了兼容旧代码,enable(...) 默认仍会激活 command、Skill、Prompt 和 Guardrail。需要更严格边界时,调用 requireExplicitResourceActivation(),或在 Spring Boot 中设置 ai.extensions.explicit-resource-activation=true。开启后,非 tool 资源必须通过对应 allow* API 或配置项逐项进入运行态。
CLI 的 extension run 和 extension resource 是人手动触发的命令 / 资源读取路径,不属于 Agent tool loop,因此当前不走 tool.execute Guardrail。它们可以通过 --allow-command、--allow-skill、--allow-prompt 使用同一套显式资源授权语义。
7. 命名建议
插件 ID 和工具名应该稳定、可读、可冲突排查:
weather-pack
weather.search
repo.scan
ticket.create
guardrail.prompt-policy
公共 ID / name 的硬性规则是:必须以英文字母或数字开头,只能包含英文字母、数字、点、下划线和连字符。这个规则适用于 extension id、tool name、command name、Skill name、Prompt name 和 Guardrail name。
Command name 本身不要带 /;/weather-check <city> 这种写法只放在 usage 文本里。CLI 为了兼容人工输入,会接受 ai4j-cli extension run --enable weather-pack /weather-check beijing,内部会去掉开头的 / 后再按 command name 查找。
Classpath resource path 不是公共 name,仍然使用路径语义,例如 skills/weather/SKILL.md。路径可以带 /,但不能包含 ..。
避免使用过宽的名字:
search
run
create
check
工具名进入模型上下文后,也会进入执行路由。名字过宽会让调用意图和冲突排查都变困难。
8. 官方样板插件
AI4J 当前提供一个官方样板插件:
| Artifact | Extension id | 能力 | 文档 |
|---|---|---|---|
ai4j-plugin-ask-user | ask-user | tool + command + Skill + Prompt | Ask User Plugin |
它的作用不是替代第三方插件,而是给插件作者一个可编译、可测试、可通过 ServiceLoader 发现的参考模块。它展示的重点是:
- 官方插件也只是普通 Maven jar。
- 插件启用后不自动把工具暴露给模型。
- tool / command 可以返回宿主可识别的结构化 envelope。
- Skill / Prompt 可以随 jar 分发,再由 Agent / Coding Agent 按需读取。
9. 发布建议
插件包发布时至少给出:
- Maven / Gradle 坐标
- 支持的 AI4J 版本范围
- manifest id
- 注册的 tools / commands / skills / prompts / guardrails 清单
- 每个 tool 的输入 schema
- 是否触发网络、文件系统、数据库或外部 API
- 所需环境变量名,不要要求用户把密钥写进代码
- 本地 smoke test 命令,例如
ai4j-cli extension validate <extension-id>和ai4j-cli extension check <extension-id> --enable ... --strict
AI4J 当前不维护远程插件市场。推荐做法是让插件作者用自己的包管理、README 和版本策略维护插件。
10. 当前边界
当前已经可用:
ai4j-extension-api定义 manifest、discovery、enable、expose 和 runtime snapshotai4j-extension-api提供ExtensionActivationPlan,并支持 command、Skill、Prompt、Guardrail 的显式 allowlistai4j-extension-api会在公共 ID / name 构造时执行格式校验,并在ExtensionValidator中检查 tool schema 的基础 JSON 结构ai4j-plugin-ask-user提供官方样板插件,展示 host-mediated 用户提问 tool / command / Skill / Promptai4j-extension-api提供ExtensionValidator,插件作者可以复用同一套 validation report 做本地测试- CLI 可以
extension list / inspect / plan / validate / check查看、预览、校验和门禁 classpath 上的插件,也可以extension run --enable <id> [--allow-command <name>] <command>显式执行插件 command - CLI 可以
extension resource --enable <id> [--allow-skill <name>|--allow-prompt <name>] <skill|prompt> <name>显式读取插件 Skill / Prompt 资源 - Agent 可以通过
.extensions(registry)调用暴露的插件工具,并在 tool execution 前应用已启用插件注册的 Guardrail - Coding Agent 可以通过
.extensions(registry)在 coding session 中调用暴露的插件工具,把已启用插件贡献的 Skill / Prompt 投影成只读可读资源,并在内置 / extension tool execution 前应用 Guardrail - Spring Boot starter 可以通过
ai.extensions.enabled、ai.extensions.tools.expose和ai.extensions.{commands,skills,prompts,guardrails}.allow装配ExtensionRegistry/ExtensionRuntimeSnapshot
当前不包含:
- 远程 marketplace
- CLI 自动安装插件依赖
- 运行时热加载 jar
- provider 自动注册
这些能力可以继续演进,但不应该在文档里暗示已经存在。