Details (original message): Add catalog-driven orchestration: intent catalog injection, deterministic plan validation, and system-function steps.
102 lines
2.3 KiB
Go
102 lines
2.3 KiB
Go
package squad
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// BlueprintRegistry 任务模板注册中心:空表起步。
|
|
// 命中模板的 Goal 走基线工作流,未命中走规划器自主拆解。
|
|
type BlueprintRegistry struct {
|
|
mu sync.RWMutex
|
|
bps map[string]Blueprint
|
|
// OnChange 可选钩子:注册/删除后回调(store 层用于落盘)。
|
|
OnChange func(bp Blueprint)
|
|
}
|
|
|
|
// NewBlueprintRegistry 创建空注册表。
|
|
func NewBlueprintRegistry() *BlueprintRegistry {
|
|
return &BlueprintRegistry{bps: make(map[string]Blueprint)}
|
|
}
|
|
|
|
// Register 注册或更新模板。
|
|
func (r *BlueprintRegistry) Register(bp Blueprint) error {
|
|
if strings.TrimSpace(bp.Key) == "" {
|
|
return fmt.Errorf("blueprint key cannot be empty")
|
|
}
|
|
if len(bp.Steps) == 0 {
|
|
return fmt.Errorf("blueprint %s has no steps", bp.Key)
|
|
}
|
|
for i := range bp.Steps {
|
|
if bp.Steps[i].ID == "" {
|
|
bp.Steps[i].ID = fmt.Sprintf("step_%02d", i+1)
|
|
}
|
|
if bp.Steps[i].Targets == nil {
|
|
bp.Steps[i].Targets = []string{"output.md"}
|
|
}
|
|
}
|
|
r.mu.Lock()
|
|
r.bps[bp.Key] = bp
|
|
r.mu.Unlock()
|
|
if r.OnChange != nil {
|
|
r.OnChange(bp)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Get 按 Key 获取模板。
|
|
func (r *BlueprintRegistry) Get(key string) (Blueprint, bool) {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
bp, ok := r.bps[key]
|
|
return bp, ok
|
|
}
|
|
|
|
// List 列出全部模板(按 Key 排序)。
|
|
func (r *BlueprintRegistry) List() []Blueprint {
|
|
r.mu.RLock()
|
|
defer r.mu.RUnlock()
|
|
out := make([]Blueprint, 0, len(r.bps))
|
|
for _, bp := range r.bps {
|
|
out = append(out, bp)
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Key < out[j].Key })
|
|
return out
|
|
}
|
|
|
|
// Remove 删除模板。
|
|
func (r *BlueprintRegistry) Remove(key string) bool {
|
|
r.mu.Lock()
|
|
_, ok := r.bps[key]
|
|
if ok {
|
|
delete(r.bps, key)
|
|
}
|
|
r.mu.Unlock()
|
|
return ok
|
|
}
|
|
|
|
// Match 按目标文本匹配模板:命中 Match 关键词数最多者;无命中返回 false。
|
|
// 显式 taskKey 非空时直接按 Key 精确查找。
|
|
func (r *BlueprintRegistry) Match(goalText, taskKey string) (Blueprint, bool) {
|
|
if taskKey != "" {
|
|
return r.Get(taskKey)
|
|
}
|
|
q := strings.ToLower(goalText)
|
|
var best Blueprint
|
|
bestScore := 0
|
|
for _, bp := range r.List() {
|
|
score := 0
|
|
for _, kw := range bp.Match {
|
|
if kw != "" && strings.Contains(q, strings.ToLower(kw)) {
|
|
score++
|
|
}
|
|
}
|
|
if score > bestScore {
|
|
best, bestScore = bp, score
|
|
}
|
|
}
|
|
return best, bestScore > 0
|
|
}
|