package agent import ( "context" "encoding/json" "fmt" "git.gopher.ink/mirrors2/AutoZero/llm" "git.gopher.ink/mirrors2/AutoZero/tools" ) // SkillTool 构造 load_skill 工具:按名加载 SKILL.md 正文进上下文。 // 若 skill 声明了 allowed-tools,则从加载起后续轮次的工具表收敛到 // 该白名单(∪ load_skill 自身),未授权的工具调用会被拒绝。 // 过滤持续到会话结束或加载下一个带 allowed-tools 的 skill(借鉴 ccb // skill 的 contextModifier 思路,简化为会话级)。 func (a *Agent) SkillTool() *tools.Tool { return &tools.Tool{ Name: "load_skill", Description: "Load a skill's full instructions by name (available skills are listed in the system prompt). Call this before performing a task that matches a skill.", ReadOnly: true, ConcurrencySafe: true, Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "name": map[string]any{"type": "string", "description": "Skill name from the system prompt list"}, }, "required": []string{"name"}, }, Run: func(_ context.Context, input string) (tools.Result, error) { var p struct { Name string `json:"name"` } if err := json.Unmarshal([]byte(input), &p); err != nil { return tools.Result{}, fmt.Errorf("invalid load_skill input: %w", err) } for _, s := range a.Skills { if s.Name != p.Name { continue } if len(s.AllowedTools) > 0 { filter := map[string]bool{"load_skill": true} for _, t := range s.AllowedTools { filter[t] = true } a.skillFilter = filter } return tools.TextResult(s.Body), nil } return tools.Result{}, fmt.Errorf("unknown skill %q", p.Name) }, } } // toolDefs 返回当前轮次可见的工具声明:skill 白名单生效时做交集过滤。 func (a *Agent) toolDefs() []llm.ToolDef { defs := a.Tools.Defs() if a.skillFilter == nil { return defs } out := make([]llm.ToolDef, 0, len(defs)) for _, d := range defs { if a.skillFilter[d.Name] { out = append(out, d) } } return out }