mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-01 06:52:53 +00:00
内核缺口(都是 P3 落地时暴露的真实缺陷): · **GetAllTools 丢 ParallelSafe**:它只带出 Name/Description/Parameters, 插件看到的设备工具一律"不可并发" ⇒ 设备工具的并发声明**对插件不可见**。 · **ToolAPI 缺按名查**:新增 ToolDefByName。插件需要在**运行前**判断目标 是否存在/是否并发安全(工具动态注册,"不存在"是常态), 而 GetAllTools 只能拿到全量列表。查不到返回 nil,不 panic。 seq 插件: · plugin.go:插件骨架 + kernelRunner(把 sdk.ToolAPI 收窄成三个方法, 判据因此能用假实现驱动,不必构造整个内核) · tools.go:六个工具定义(独立真相源,注册/判据/文档都从它取) · handlers.go:seq_create/list/delete/run/call/when_call 的实现 · register.go + all.go:按 skillmgr 同一范式 init 注册 ★ 过程中解决一个**我自己的设计矛盾**: 判据原先要求 `seq_call` / `seq_when_call` 进黑名单,但"按名调用 group/序列"恰恰是本包的核心能力——禁掉它,序列就退化成单层脚本。 分层澄清后:黑名单只管**对外发消息 / 起子 agent / 改插件表 / 再跑整条 序列**;seq_call 系列留给序列内部组合,其递归由 maxCallDepth + 环检测 负责(设计文档 §8.3 本来就是这么定的,我把两层混了)。 `seq_run` 留在黑名单:序列内再跑整条序列语义上是递归。 **六个工具一律不声明 ParallelSafe**:seq_run/seq_call 会执行一串工具, 其中可能含写操作;标成并发安全会让内核把两条 seq_run 并发跑, 两个序列的执行顺序交错、变量表互相污染。 安全性:序列名与文件路径都做穿越防护(`..` 段、分隔符、空名)。 过程中四次自伤: 1. 臆造 `jsonMarshalIndent`(不存在)→ 改 encoding/json.MarshalIndent; 并把 execGroup 的 runner 传错成 p(应 p.runner)。 2. seq 判据里写了 `black(name)`,而 blacklisted 是**谓词**不是函数。 3. 一次 python 替换删漏,把「跨序列目标存在性检查」那段从 CheckNew 里整段摘掉又贴回原处——靠编译错误发现。 4. ★ 注册失败我写了 panic:内置插件在 main() 装配期加载,panic 会 **直接拖垮内核启动**,而"某个工具没注册上"只该让该工具不可用。 已改为 log.Printf + 继续(与 clawhubadapter / mcp 一致)。 变异验证:把 seq_run 移出黑名单 ⇒ 黑名单判据 FAIL。 判据(plugin_test.go,6 条):六个工具全部注册且 description/参数 schema 非空;seq_create 声明 required 并说明 groups/file 二选一; seq_run 说明"按数组顺序";六个工具均未声明 ParallelSafe; 黑名单含递归风险项且**不误伤** seq_call 与普通工具。 回归:seq -race 全绿;internal/sdk/... internal/plugins/... 12 包全绿。
86 lines
2.2 KiB
Go
86 lines
2.2 KiB
Go
package sdk
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io"
|
||
)
|
||
|
||
// toolImpl 桥接 StageHost(插件工具)与 IOManager(设备/通道工具)。
|
||
type toolImpl struct {
|
||
stageHost ToolSource
|
||
iom *agentIO.IOManager
|
||
}
|
||
|
||
func NewTool(sh ToolSource, iom *agentIO.IOManager) ToolAPI {
|
||
return &toolImpl{stageHost: sh, iom: iom}
|
||
}
|
||
|
||
func (t *toolImpl) GetToolDefs() []ToolDef {
|
||
if t.stageHost == nil {
|
||
return nil
|
||
}
|
||
return t.stageHost.GetToolDefs()
|
||
}
|
||
|
||
func (t *toolImpl) GetAllTools() []ToolDef {
|
||
if t.iom == nil {
|
||
return nil
|
||
}
|
||
defs := t.iom.GetAllTools()
|
||
out := make([]ToolDef, 0, len(defs))
|
||
for _, d := range defs {
|
||
// ⚠️ ParallelSafe 必须一并带出:它决定该工具能否被并发执行。
|
||
// 此前这里漏了它 ⇒ 插件看到的设备工具一律"不可并发",
|
||
// 设备工具的并发声明等于对插件不可见。
|
||
out = append(out, ToolDef{
|
||
Name: d.Name,
|
||
Description: d.Description,
|
||
Parameters: d.Parameters,
|
||
ParallelSafe: d.ParallelSafe,
|
||
})
|
||
}
|
||
return out
|
||
}
|
||
|
||
func (t *toolImpl) ExecuteTool(name string, args map[string]interface{}) (interface{}, error) {
|
||
if t.stageHost != nil {
|
||
if def := t.stageHost.ToolDef(name); def != nil {
|
||
return t.stageHost.ExecuteTool(name, args)
|
||
}
|
||
}
|
||
if t.iom != nil {
|
||
return t.iom.ExecuteTool(name, args)
|
||
}
|
||
return nil, fmt.Errorf("tool %s not found", name)
|
||
}
|
||
|
||
var _ ToolAPI = (*toolImpl)(nil)
|
||
|
||
// ToolDefByName 按名字查任一来源的工具声明(StageHost 优先,再查 IO 设备)。
|
||
//
|
||
// 用途:插件在**运行前**判断目标工具是否存在、是否并发安全。工具是动态
|
||
// 注册的,"不存在"是常态(插件未加载/已卸载/崩溃),因此查不到一律返回
|
||
// nil 交由调用方按"不存在"处理——不得 panic。
|
||
func (t *toolImpl) ToolDefByName(name string) *ToolDef {
|
||
if t == nil || name == "" {
|
||
return nil
|
||
}
|
||
if t.stageHost != nil {
|
||
if def := t.stageHost.ToolDef(name); def != nil {
|
||
return def
|
||
}
|
||
}
|
||
if t.iom != nil {
|
||
if def, ok := t.iom.ToolDefOf(name); ok {
|
||
return &ToolDef{
|
||
Name: def.Name,
|
||
Description: def.Description,
|
||
Parameters: def.Parameters,
|
||
ParallelSafe: def.ParallelSafe,
|
||
}
|
||
}
|
||
}
|
||
return nil
|
||
}
|