// Package intentgate 接入 agent-intent(生态仓):插件意图 overlay → 注册表 → // HITL 自由输入路由的确定性识别(设计 4.5 三分支之「命中其他意图→转轨」)。 // 未配置/未装载意图 = 行为回退(T0:直接按澄清回填),识别失败不阻断。 package intentgate import ( "context" "encoding/json" "fmt" "sync" agentintent "agent-intent/intent" ) type Gate struct { mu sync.RWMutex reg *agentintent.Registry rec *agentintent.FunnelRecognizer specs int } func New() *Gate { return &Gate{rec: agentintent.NewRecognizer(agentintent.Options{}, nil, nil)} } // Rebuild 用插件意图 overlay 重建注册表;失败保留上一快照(fail-closed)并返回错误。 func (g *Gate) Rebuild(ctx context.Context, overlay []json.RawMessage) error { if len(overlay) == 0 { g.mu.Lock() g.reg, g.specs = nil, 0 g.mu.Unlock() return nil } specs := make([]agentintent.IntentSpec, 0, len(overlay)) for i, raw := range overlay { var s agentintent.IntentSpec if err := json.Unmarshal(raw, &s); err != nil { return fmt.Errorf("intentgate: spec[%d]: %w", i, err) } specs = append(specs, s) } b := agentintent.NewRegistryBuilder(agentintent.Options{TopN: 3}) if err := b.Register(specs...); err != nil { return fmt.Errorf("intentgate: register: %w", err) } reg, err := b.Build() if err != nil { return fmt.Errorf("intentgate: build: %w", err) } g.mu.Lock() g.reg, g.specs = reg, len(specs) g.mu.Unlock() return nil } // Match 识别自由输入;返回 (branch, topID)。 // branch:matched | ambiguous | fallback | ""(未配置/系统级错误 → 回退通道)。 func (g *Gate) Match(ctx context.Context, input string) (string, string) { g.mu.RLock() reg, rec := g.reg, g.rec g.mu.RUnlock() if reg == nil { return "", "" } res, err := rec.Recognize(ctx, input, reg.Snapshot()) if err != nil { return "", "" } topID := "" if res.Top != nil { topID = res.Top.ID } switch res.Branch { case agentintent.BranchMatched: return "matched", topID case agentintent.BranchAmbiguous: return "ambiguous", topID default: return "fallback", topID } } // Enabled 报告是否装载了意图(诊断/设置展示用)。 func (g *Gate) Enabled() bool { g.mu.RLock() defer g.mu.RUnlock() return g.reg != nil && g.specs > 0 } // SpecCount 返回已装载意图数。 func (g *Gate) SpecCount() int { g.mu.RLock() defer g.mu.RUnlock() return g.specs }