扩展开发
ArkPilot 提供多种扩展方式,方便第三方插件或扩展包添加自定义工具、技能和上下文。
扩展方式概览
| 方式 | 适用场景 | 复杂度 |
|---|---|---|
| 脚本工具(tools/) | 简单工具,快速开发 | ⭐ |
| JAR 扩展包(expansions/) | 多工具打包,功能丰富 | ⭐⭐ |
| 插件 API 注册 | 其他 Bukkit 插件集成 | ⭐⭐⭐ |
方式一:脚本工具
在 plugins/ArkPilot/tools/ 目录下创建脚本工具,零编译、热加载。
目录结构
tools/
└── give-item/
├── manifest.yml # 工具定义(必需)
└── main.js # 执行脚本(必需)manifest.yml
# 工具名称(AI 调用时使用的函数名)
toolName: give_item
# 工具描述(告诉 AI 这个工具做什么)
description: "给予玩家指定物品和数量"
# 工具 key(可选,默认取目录名)
# key: give-item
# 参数定义(JSON Schema 格式)
parameters:
type: object
properties:
item:
type: string
description: "物品 ID,如 diamond、iron_ingot"
amount:
type: integer
description: "数量,默认 1"
default: 1
required: [item]
# 执行器配置
executor:
# 入口文件
entry: main.js
# 执行类型(可选,默认按后缀推断)
# javascript / python / process
type: javascript
# 超时(毫秒,可选)
timeout: 5000
# 权限要求(可选)
# permission: "arkpilot.tool.give-item"
# 是否需要玩家确认(可选)
# requireConfirmation: false脚本类型
| 类型 | 语言 | 机制 | 特点 |
|---|---|---|---|
javascript | JS | GraalJS 内嵌执行 | 可直接访问 Bukkit Java 对象 |
python | Python | GraalPy 内嵌执行 | 同上 |
process | 任意语言 | 子进程 stdin/stdout JSON 通信 | 语言无限制 |
使用脚本工具
- 创建
tools/my-tool/manifest.yml和脚本文件 - 执行
/ark reload - 在助手配置的
tools.load中添加工具 key:
tools:
load:
- execute-command
- my-tool # 新增方式二:JAR 扩展包
在 plugins/ArkPilot/expansions/ 目录下放置 JAR 扩展包。一个 JAR 可注册多个工具、技能和上下文提供者。
核心接口
// 扩展包基类
abstract class ArkPilotExpansion {
abstract val id: String
abstract val name: String
open val requiredPlugins: List<String> = emptyList() // 可选:声明依赖的 Bukkit 插件名
// 启用时调用,在此注册工具/技能
abstract fun onEnable(context: ExpansionContext)
// 禁用时调用
open fun onDisable() {}
}
// 工具定义
abstract class ExpansionTool {
abstract val key: String
abstract val toolName: String
abstract val description: String
abstract val parameters: String // JSON Schema
// 执行工具
abstract fun execute(player: Player, params: Map<String, Any?>): String
}
// 上下文提供者(使用 AgentScope,支持玩家和非玩家场景)
fun interface ExpansionContextProvider {
fun provide(assistant: AssistantConfig, scope: AgentScope): String
}
// 注册上下文
interface ExpansionContext {
val dataFolder: File
fun registerTool(tool: ExpansionTool)
fun registerSkill(key: String, markdown: String)
fun registerContextProvider(id: String, provider: ExpansionContextProvider)
fun registerCommand(command: ExpansionCommand)
}requiredPlugins 用于声明前置插件依赖。加载器会在调用 onEnable 前检查这些插件是否已安装且已启用;若缺失,将跳过该扩展并输出日志。
注册命令
扩展包可以通过 ExpansionCommand 注册独立的顶级命令(如 /blueprint),与主插件 /ark 完全解耦:
abstract class ExpansionCommand {
/** 命令名称,注册为 /<name> 顶级命令 */
abstract val name: String
/** 命令别名 */
open val aliases: List<String> = emptyList()
/** 权限节点 */
open val permission: String = ""
/** 命令描述 */
open val description: String = ""
/** 是否仅玩家可执行 */
open val playerOnly: Boolean = true
/** 执行命令 */
abstract fun execute(sender: CommandSender, args: List<String>)
/** Tab 补全 */
open fun tabComplete(sender: CommandSender, args: List<String>): List<String> = emptyList()
}在 onEnable 中注册:
override fun onEnable(context: ExpansionContext) {
context.registerCommand(MyCommand())
}命令通过 Bukkit CommandMap 动态注册,/ark reload 时自动注销并重新注册。
工具确认回调
需要玩家确认的工具可以通过 ToolConfirmCallback 完全控制确认消息的发送方式:
class MyTool : ExpansionTool() {
override val requireConfirmation = true
override val confirmNoExpire = true // 确认请求不超时
override val confirmCallback = ToolConfirmCallback { scope, displayName, confirmId, params ->
// 自定义确认消息发送逻辑(如添加预览按钮等)
val component = ComponentBuilder("确认执行 $displayName? ")
.append("[✓ 确认]").event(ClickEvent(ClickEvent.Action.RUN_COMMAND, "/ark confirm $confirmId"))
.append(" [✗ 取消]").event(ClickEvent(ClickEvent.Action.RUN_COMMAND, "/ark cancel $confirmId"))
.create()
scope.sendMessage(*component)
}
}如果不设置 confirmCallback,将使用默认的确认消息模板。
内置示例:活动追踪扩展包
项目 expansions/ 模块提供了一个完整的扩展包示例——活动追踪器(Activity Tracker),演示如何:
- 注册多个工具(
query_player_activity、query_server_activity) - 注册上下文提供者(向玩家对话注入近 30 分钟活动摘要)
- 监听 Bukkit 事件(方块破坏/放置、聊天、死亡、成就、击杀等)
- 支持全局工具调用(
supportsGlobal = true),心跳助手和对话助手均可使用
编译后的 JAR 放入 plugins/ArkPilot/expansions/ 即可使用。配合内置的 event-host 心跳助手,可实现自动观察服务器动态并发起活动。
自定义消息投递(DeliveryProvider)
扩展包可以注册 DeliveryProvider,接管 AI 回复的投递方式。例如 chemdah 扩展包将 AI 回复写入 Chemdah Session 而不是直接发送聊天消息。
import me.kzheart.arkpilot.core.agent.DeliveryFactory
import me.kzheart.arkpilot.core.agent.DeliveryProvider
// 在 onEnable 中注册
val provider = DeliveryProvider { assistant, scope, liveStreaming, agentStartTimeMs ->
// 返回 null 表示不接管,交给下一个 Provider 或默认 PlayerDelivery
if (assistant.id != "my-assistant") return@DeliveryProvider null
MyCustomDelivery(assistant, scope, agentStartTimeMs)
}
DeliveryFactory.registerProvider(provider)
// 在 onDisable 中注销
DeliveryFactory.unregisterProvider(provider)自定义 Delivery 需继承 AbstractPlayerDelivery,实现 onTextDelta、finish、cancel 等方法。
构建注意事项
由于 TabooLib 会将 kotlin. 包重定向为 kotlin1822.,JAR 扩展包必须做相同的 relocate,否则运行时会出现 ClassNotFoundException。推荐使用 Shadow 插件:
// build.gradle.kts
plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
}
tasks.shadowJar {
archiveBaseName.set("arkpilot-my-expansion")
archiveClassifier.set("")
relocate("kotlin.", "kotlin1822.")
// 不打包任何依赖,只做 relocate
dependencies { exclude(dependency(".*:.*:.*")) }
}
tasks.jar { enabled = false }
tasks.build { dependsOn(tasks.shadowJar) }使用扩展包
- 将 JAR 放入
expansions/目录 - 重启服务器或执行
/ark reload - 在助手配置中启用扩展包注册的工具/技能
方式三:插件 API 注册
其他 Bukkit 插件可通过 ArkPilotApi 注册工具和技能。
注册工具
import me.kzheart.arkpilot.api.ArkPilotApi
ArkPilotApi.registerTool("economy-balance", myAgentTool)注册后在助手 tools.load 中添加 key 启用。
注册技能
ArkPilotApi.registerSkill("economy-guide", """
---
name: economy-guide
description: 经济系统使用指南
---
# 经济系统
## 查看余额
使用 /money 查看当前余额。
...
""".trimIndent())注册上下文提供者
向 Prompt Layer 4 注入场景上下文:
ArkPilotApi.registerContextProvider("dungeon") { assistant, scope ->
// scope 是 AgentScope,可通过 is PlayerBound 判断是否有玩家
val playerScope = scope as? PlayerBound ?: return@registerContextProvider ""
val player = playerScope.player.cast<Player>()
val dungeon = DungeonPlugin.getCurrentDungeon(player)
if (dungeon != null) {
"Player is in dungeon: ${dungeon.name}, floor ${dungeon.floor}"
} else {
"" // 返回空字符串不注入
}
}监听事件
所有事件继承自 ArkPilotEvent,基类携带 scope: AgentScope 属性,可通过 is PlayerBound 判断是否来自玩家。
// TabooLib 注解方式
@SubscribeEvent
fun onConversationEnd(event: ConversationEndEvent) {
val playerScope = event.scope as? PlayerBound
// playerScope?.playerName, playerScope?.player 等
}
// 回调注册方式(回调参数为 AgentScope)
ArkPilotApi.onConversationEnd { scope, assistant, usage ->
// scope 是 AgentScope
}
ArkPilotApi.onToolExecution { scope, toolName, params ->
// scope 是 AgentScope
}可用事件
| 事件 | 说明 |
|---|---|
ConversationStartEvent | 对话开始 |
ConversationEndEvent | 对话结束(含 usage、success、error) |
ToolExecutionStartEvent | 工具执行开始(含 toolName、params) |
ToolExecutionEndEvent | 工具执行结束(含 success、durationMs) |
ToolConfirmRequestEvent | 工具确认请求发出 |
ToolConfirmResponseEvent | 玩家确认/取消工具执行 |
MemoryChangeEvent | 记忆变更(ADD/REMOVE) |
SessionResetEvent | 会话重置(MANUAL/IDLE_EXPIRE/DAILY_RESET) |
RouteResolvedEvent | 路由解析完成 |
动态修改 Prompt
ArkPilotApi.addBootstrapHook { assistant, scope, files ->
// scope 是 AgentScope
files["SOUL"] = files["SOUL"].orEmpty() + "\n\n额外的灵魂指引..."
}工具来源与优先级
所有方式注册的工具完全等同,用同样的 key 引用:
BUILTIN → 代码内置工具
PLUGIN → 其他插件通过 API 注册
EXTERNAL → tools/ 脚本工具
EXPANSION → expansions/ JAR 扩展包安全注意事项
WARNING
- 脚本工具和扩展包拥有与插件相同的权限级别
- 服主应审查所有第三方脚本和扩展包的安全性
- 子进程模式的工具有超时强制终止和输出大小限制
内置扩展包参考
想了解内置扩展包的使用方式和配置?请查看扩展模块文档:
- 建筑系统 (building) — AI 蓝图生成与动画建造
- 活动追踪 (allay) — 心跳驱动的服务器活动追踪与自动互动
- NPC 对话 (chemdah) — Chemdah NPC 对话 AI 化,支持任务发放