Files
HomeAgent/internal/agent/core/plugins.go
JianFeeeee 653dd34299 feat: 插件工具按需拉取(get_plugin_tools) + 设备命令类型拆分(shell-cmd/homeagent-cmd)
1. 提示词去污染: buildToolCatalog 从全量工具定义改为按插件分组摘要
   (插件名 + 工具数 + 能力概览), 完整工具定义由新工具 get_plugin_tools(plugin_name)
   动态拉取; 新增 executeGetPluginTools 支持按插件过滤 stageHost/io 工具
2. 设备命令类型: device_ctl_cmdrun 的 command 支持前缀区分
   - shell-cmd <cmd>      -> 设备端执行原生 shell
   - homeagent-<cap>      -> 设备端 HomeAgent 内置能力(如 camerasue/screensue)
   - homeagent-cmd <cap>  -> 同上(兼容写法)
   PushCmd 增加 cmd_type 字段下发给设备端分发
3. 测试: TestExecuteGetPluginTools 验证按插件拉取/全部摘要/未知插件
2026-08-18 09:07:21 +08:00

112 lines
2.8 KiB
Go

package core
import (
"encoding/json"
"fmt"
"log"
"strings"
)
func (a *Agent) executePluginReload() string {
if a.pluginReg == nil {
return "插件系统未启用"
}
msg, err := a.pluginReg.Reload(a.pluginDir)
if err != nil {
return fmt.Sprintf("插件重载失败: %v", err)
}
return msg
}
func (a *Agent) autoReloadPlugins() {
if a.pluginReg == nil {
return
}
for _, name := range a.pluginHealth.pendingReloads() {
if !a.pluginReg.AutoRestartEnabled(name) {
log.Printf("[agent] skip auto-reload plugin %s: auto-restart disabled by plugin", name)
continue
}
log.Printf("[agent] auto-reloading unhealthy plugin: %s", name)
if a.stageHost != nil {
a.stageHost.UnregisterPluginTools(name)
}
if err := a.pluginReg.ReloadOne(name); err != nil {
log.Printf("[agent] auto-reload plugin %s failed: %v", name, err)
} else {
a.pluginHealth.markReloaded(name)
log.Printf("[agent] plugin %s reloaded successfully", name)
}
}
}
func (a *Agent) resolveToolPlugin(name string) string {
if a.stageHost != nil {
if plugin := a.stageHost.ToolPlugin(name); plugin != "" {
return plugin
}
}
if idx := strings.IndexByte(name, '_'); idx > 0 {
return name[:idx]
}
return "core"
}
// executeGetPluginTools 返回指定插件的完整工具定义(名称/参数/用途)。
// 支持按插件名拉取, 未指定时返回全部插件的工具摘要。
func (a *Agent) executeGetPluginTools(pluginName string) string {
type toolItem struct {
name, desc string
params interface{}
}
var items []toolItem
// 收集 StageHost(SDK 插件)工具
if a.stageHost != nil {
for _, d := range a.stageHost.GetToolDefs() {
plg := d.Plugin
if plg == "" {
plg = a.resolveToolPlugin(d.Name)
}
if pluginName != "" && plg != pluginName {
continue
}
items = append(items, toolItem{d.Name, d.Description, d.Parameters})
}
}
// 收集 IOManager(设备/通道)工具
if a.io != nil {
for _, d := range a.io.GetAllTools() {
plg := a.resolveToolPlugin(d.Name)
if pluginName != "" && plg != pluginName {
continue
}
items = append(items, toolItem{d.Name, d.Description, d.Parameters})
}
}
if len(items) == 0 {
if pluginName != "" {
return fmt.Sprintf("插件 %s 没有可用的工具定义", pluginName)
}
return "当前没有可用的工具定义"
}
var sb strings.Builder
if pluginName == "" {
sb.WriteString(fmt.Sprintf("共 %d 个工具:\n", len(items)))
} else {
sb.WriteString(fmt.Sprintf("插件 %s 共 %d 个工具:\n", pluginName, len(items)))
}
for _, it := range items {
sb.WriteString(fmt.Sprintf("\n### %s\n", it.name))
if it.desc != "" {
sb.WriteString(it.desc + "\n")
}
if it.params != nil {
if b, err := json.Marshal(it.params); err == nil && len(b) < 600 {
sb.WriteString("参数: " + string(b) + "\n")
}
}
}
return sb.String()
}