package tools // 工具延迟加载系统. // // 当工具数量多时(超过阈值),不把所有工具描述都塞进系统提示. // 而是只加载核心工具,其余通过 ToolSearch 按需发现和激活. // // 设计思路: // - DeferredRegistry 包装 Registry,维护「始终加载」和「延迟加载」两个分区 // - 始终加载的工具(Bash,Read,Edit 等核心工具)直接提供给模型 // - 延迟工具通过 SearchTools 按关键字搜索,由模型调用 ToolSearch 发现 // - 激活后的延迟工具在本次会话中变为活跃状态 // - 如果工具总数不超过阈值,所有工具都视为活跃 import ( "strings" "sync" ) // 默认延迟加载阈值:超过此数量启用延迟加载. const defaultDeferredThreshold = 15 // defaultAlwaysLoadTools 是始终加载的核心工具列表(不可延迟)的默认值. // 这些工具是 Agent 工作的基础,必须始终可用. // 精妙之处(CLEVER): 核心工具白名单--这些工具是 Agent 最基本的能力(读,写,搜索,执行), // 缺少任何一个都会严重影响 Agent 的工作能力.ToolSearch 必须始终可用, // 否则模型无法发现延迟加载的工具(鸡和蛋问题). // // 升华改进(ELEVATED): 提取为可访问的包级默认值(原方案:直接赋给 alwaysLoadTools,不可复用)-- // NewDeferredRegistry 和 WithAlwaysLoad 都可以从此处派生,向后兼容. var defaultAlwaysLoadTools = map[string]bool{ "Bash": true, "Read": true, "Edit": true, "Write": true, "Glob": true, "Grep": true, "Agent": true, "ToolSearch": true, } // alwaysLoadTools 是包级全局始终加载工具集(向后兼容,供 deferred_test.go 引用). // 精妙之处(CLEVER): 保留包级变量作为测试和默认值来源, // DeferredRegistry 实例的 alwaysLoad 字段从此处拷贝后独立演化, // 避免修改一个 Registry 实例时意外影响另一个实例的白名单. var alwaysLoadTools = defaultAlwaysLoadTools // RegisterDeferredTool 在全局默认核心工具列表中注册新工具名. // // 升华改进(ELEVATED): 可注册的动态白名单-- // 第三方工具库可以将自己的核心工具(如仓储场景的 ScanBarcode)注册到始终加载列表, // 无需 fork 源码修改 defaultAlwaysLoadTools. // 替代方案(原方案):硬编码 map 字面量,第三方只能 fork 改源码. // // 注意:此函数修改全局状态.如需按 Engine 实例定制,请使用 NewDeferredRegistry + WithAlwaysLoad. func RegisterDeferredTool(name string) { // 精妙之处(CLEVER): 写时拷贝(copy-on-write)-- // defaultAlwaysLoadTools 初始化后多个地方可能持有对它的引用. // 直接修改会污染所有引用方;新建 map + 拷贝旧内容 + 追加新项,保证独立性. newMap := make(map[string]bool, len(alwaysLoadTools)+1) for k, v := range alwaysLoadTools { newMap[k] = v } newMap[name] = true alwaysLoadTools = newMap } // DeferredRegistry 包装 Registry,支持延迟加载. // 当注册的工具数量超过阈值时,非核心工具会被延迟加载. type DeferredRegistry struct { mu sync.RWMutex registry *Registry // 底层注册表(所有工具都在这里) alwaysLoad map[string]bool // 始终加载的工具名集合 activated map[string]bool // 已激活的延迟工具 threshold int // 超过此数量启用延迟加载 } // NewDeferredRegistry 创建一个支持延迟加载的注册表包装器. // // registry 是底层工具注册表,threshold 为启用延迟加载的工具数量阈值. // threshold <= 0 使用默认值(15). func NewDeferredRegistry(registry *Registry, threshold int) *DeferredRegistry { if threshold <= 0 { threshold = defaultDeferredThreshold } // 精妙之处(CLEVER): 从全局默认值深拷贝而非直接共享引用-- // 实例级 WithAlwaysLoad 修改不会回流到全局 alwaysLoadTools, // 多 Engine 实例的核心工具配置相互隔离. localAlways := make(map[string]bool, len(alwaysLoadTools)) for k, v := range alwaysLoadTools { localAlways[k] = v } return &DeferredRegistry{ registry: registry, alwaysLoad: localAlways, activated: make(map[string]bool), threshold: threshold, } } // WithAlwaysLoad 将额外的工具名加入当前 DeferredRegistry 实例的始终加载列表. // // 升华改进(ELEVATED): 实例级核心工具定制-- // 不同租户(仓储/医疗/编程)各自的"核心工具"不同,通过此方法按实例注入, // 不污染其他实例也不修改全局状态. // 替代方案(原方案):只有包级 RegisterDeferredTool(影响全局,不支持多租户隔离). // // 返回 *DeferredRegistry 自身,支持链式调用. func (dr *DeferredRegistry) WithAlwaysLoad(names ...string) *DeferredRegistry { dr.mu.Lock() defer dr.mu.Unlock() for _, name := range names { dr.alwaysLoad[name] = true } return dr } // ActiveTools 返回当前活跃的工具列表. // // 如果工具总数不超过阈值,返回所有工具. // 否则只返回始终加载的工具 + 已激活的延迟工具. func (dr *DeferredRegistry) ActiveTools() []Tool { dr.mu.RLock() defer dr.mu.RUnlock() all := dr.registry.All() // 工具总数不超过阈值,全部返回 if len(all) <= dr.threshold { return all } // 超过阈值,只返回核心工具 + 已激活的工具 result := make([]Tool, 0) for _, t := range all { name := t.Name() if dr.isAlwaysLoad(name) || dr.activated[name] { result = append(result, t) } } return result } // SearchTools 搜索延迟加载的工具. // // 根据查询字符串匹配工具名称,描述和搜索提示词. // 返回匹配的工具列表(不包含已激活和始终加载的工具). func (dr *DeferredRegistry) SearchTools(query string) []Tool { dr.mu.RLock() defer dr.mu.RUnlock() all := dr.registry.All() queryLower := strings.ToLower(query) queryWords := strings.Fields(queryLower) var matches []Tool for _, t := range all { name := t.Name() // 跳过始终加载的工具 if dr.isAlwaysLoad(name) { continue } // 计算匹配度 if dr.matchTool(t, queryLower, queryWords) { matches = append(matches, t) } } return matches } // ActivateTool 将一个延迟工具激活. // 激活后的工具在后续的 ActiveTools 调用中会被包含. func (dr *DeferredRegistry) ActivateTool(name string) { dr.mu.Lock() defer dr.mu.Unlock() dr.activated[name] = true } // IsDeferred 检查指定工具是否处于延迟加载状态. // // 返回 true 表示该工具存在但未激活(即延迟中). // 如果工具不存在或已激活,返回 false. func (dr *DeferredRegistry) IsDeferred(name string) bool { dr.mu.RLock() defer dr.mu.RUnlock() all := dr.registry.All() // 工具总数不超过阈值,没有延迟工具 if len(all) <= dr.threshold { return false } // 始终加载的不算延迟 if dr.isAlwaysLoad(name) { return false } // 已激活的不算延迟 if dr.activated[name] { return false } // 检查工具是否存在 _, exists := dr.registry.Get(name) return exists } // Threshold 返回当前延迟加载阈值. func (dr *DeferredRegistry) Threshold() int { return dr.threshold } // Registry 返回底层注册表. func (dr *DeferredRegistry) Registry() *Registry { return dr.registry } // ResetActivations 重置所有已激活的延迟工具. // 通常在新会话开始时调用. func (dr *DeferredRegistry) ResetActivations() { dr.mu.Lock() defer dr.mu.Unlock() dr.activated = make(map[string]bool) } // isAlwaysLoad 检查工具名是否在始终加载列表中. // 同时检查别名. func (dr *DeferredRegistry) isAlwaysLoad(name string) bool { if dr.alwaysLoad[name] { return true } // 检查别名映射:如果工具的规范名在 alwaysLoad 中 t, ok := dr.registry.Get(name) if ok { meta := GetMetadata(t) for _, alias := range meta.Aliases { if dr.alwaysLoad[alias] { return true } } } return false } // matchTool 检查工具是否匹配查询. // 通过工具名称,SearchHint 元数据进行模糊匹配. func (dr *DeferredRegistry) matchTool(t Tool, queryLower string, queryWords []string) bool { nameLower := strings.ToLower(t.Name()) // 1. 名称包含匹配 if strings.Contains(nameLower, queryLower) { return true } // 2. SearchHint 匹配 meta := GetMetadata(t) if meta.SearchHint != "" { hintLower := strings.ToLower(meta.SearchHint) for _, word := range queryWords { if strings.Contains(hintLower, word) { return true } } } // 3. 别名匹配 for _, alias := range meta.Aliases { if strings.Contains(strings.ToLower(alias), queryLower) { return true } } return false }