ekg m2+: add error-signature-aware provider ranking for fallback selection

This commit is contained in:
DBT
2026-03-01 04:32:34 +00:00
parent 1d5454066a
commit ed9e4203d3
2 changed files with 25 additions and 5 deletions

View File

@@ -350,7 +350,11 @@ func (al *AgentLoop) tryFallbackProviders(ctx context.Context, msg bus.InboundMe
lastErr := primaryErr lastErr := primaryErr
candidates := append([]string(nil), al.providerNames[1:]...) candidates := append([]string(nil), al.providerNames[1:]...)
if al.ekg != nil { if al.ekg != nil {
candidates = al.ekg.RankProviders(candidates) errSig := ""
if primaryErr != nil {
errSig = primaryErr.Error()
}
candidates = al.ekg.RankProvidersForError(candidates, errSig)
} }
for _, name := range candidates { for _, name := range candidates {
p, ok := al.providerPool[name] p, ok := al.providerPool[name]
@@ -361,11 +365,13 @@ func (al *AgentLoop) tryFallbackProviders(ctx context.Context, msg bus.InboundMe
if al.ekg != nil { if al.ekg != nil {
st := "success" st := "success"
lg := "fallback provider success" lg := "fallback provider success"
errSig := ""
if err != nil { if err != nil {
st = "error" st = "error"
lg = err.Error() lg = err.Error()
errSig = err.Error()
} }
al.ekg.Record(ekg.Event{Session: msg.SessionKey, Channel: msg.Channel, Source: "provider_fallback", Status: st, Provider: name, Model: al.model, Log: lg}) al.ekg.Record(ekg.Event{Session: msg.SessionKey, Channel: msg.Channel, Source: "provider_fallback", Status: st, Provider: name, Model: al.model, ErrSig: errSig, Log: lg})
} }
if err == nil { if err == nil {
logger.WarnCF("agent", "LLM fallback provider switched", map[string]interface{}{"provider": name}) logger.WarnCF("agent", "LLM fallback provider switched", map[string]interface{}{"provider": name})

View File

@@ -175,9 +175,14 @@ var (
) )
func (e *Engine) RankProviders(candidates []string) []string { func (e *Engine) RankProviders(candidates []string) []string {
return e.RankProvidersForError(candidates, "")
}
func (e *Engine) RankProvidersForError(candidates []string, errSig string) []string {
if len(candidates) <= 1 || e == nil { if len(candidates) <= 1 || e == nil {
return append([]string(nil), candidates...) return append([]string(nil), candidates...)
} }
errSig = NormalizeErrorSignature(errSig)
events := e.readRecentEvents() events := e.readRecentEvents()
score := map[string]float64{} score := map[string]float64{}
for _, c := range candidates { for _, c := range candidates {
@@ -191,13 +196,22 @@ func (e *Engine) RankProviders(candidates []string) []string {
if _, ok := score[p]; !ok { if _, ok := score[p]; !ok {
continue continue
} }
weight := 1.0
evSig := NormalizeErrorSignature(ev.ErrSig)
if errSig != "" {
if evSig == errSig {
weight = 2.5
} else if evSig != "" {
weight = 0.4
}
}
switch strings.ToLower(strings.TrimSpace(ev.Status)) { switch strings.ToLower(strings.TrimSpace(ev.Status)) {
case "success": case "success":
score[p] += 1.0 score[p] += 1.0 * weight
case "suppressed": case "suppressed":
score[p] += 0.2 score[p] += 0.2 * weight
case "error": case "error":
score[p] -= 1.0 score[p] -= 1.0 * weight
} }
} }
ordered := append([]string(nil), candidates...) ordered := append([]string(nil), candidates...)