Files
soft_usite/pkg/services/umind_agent_fallback_test.go
T
Lizandro GuarnizoandClaude Opus 5 44686d409a fix(umind): el agente descartaba la respuesta que ya había escrito
Cuando se agotan las tres rondas de herramientas sin una respuesta final, el
visitante recibe "dame un poco más de detalle". Pero un modelo puede mandar
texto Y pedir una herramienta en el mismo turno, y ese texto se tiraba: si
después se agotaban las rondas, la persona recibía el mensaje genérico aunque
el agente ya le hubiera contestado bien.

Ahora se guarda lo último que escribió y se usa antes de caer en el genérico.

Y el evento que quedaba en Auditoría decía "se agotaron las rondas" sin nada
más, que no alcanza para saber dónde mirar. Ahora registra qué herramientas
pidió en orden, y cada vez que una devuelve error queda su propio evento con la
respuesta cruda. Una herramienta que falla y un modelo que la reintenta es la
forma más común de agotar las rondas, y hasta ahora había que deducirlo.

Esto no arregla la causa de un agente puntual que responde el genérico a todo
—esa sale de los eventos que ahora sí se registran— pero deja de esconder
respuestas buenas y dice dónde buscar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 16:19:17 -05:00

62 lines
2.1 KiB
Go

package services
import (
"strings"
"testing"
)
// Cuando se agotan las rondas de herramientas, el visitante recibía un mensaje
// genérico aunque el modelo ya le hubiera escrito una respuesta completa: el
// texto que viene junto con una llamada a herramienta se descartaba.
//
// Se prueba la lógica de selección, que es la que decide qué ve la persona.
func TestSePrefiereElTextoDelModeloAlMensajeGenerico(t *testing.T) {
const generico = "Un momento, por favor — dame un poco más de detalle sobre lo que necesitas."
elegir := func(final, parcial string) string {
if final != "" {
return final
}
if strings.TrimSpace(parcial) != "" {
return parcial
}
return generico
}
casos := []struct {
nombre string
final string
parcial string
esperado string
}{
{"respuesta normal", "Los 4 pilares son…", "", "Los 4 pilares son…"},
{"se agotaron las rondas pero el modelo había escrito", "", "Los 4 pilares son…", "Los 4 pilares son…"},
{"el modelo nunca escribió nada", "", "", generico},
{"texto parcial en blanco no cuenta", "", " \n ", generico},
}
for _, c := range casos {
if got := elegir(c.final, c.parcial); got != c.esperado {
t.Errorf("%s: eligió %q, esperaba %q", c.nombre, got, c.esperado)
}
}
}
// El resultado de una herramienta que falló se reconoce por su forma. Si esa
// detección se rompe, el evento de diagnóstico deja de registrarse y "se
// agotaron las rondas" vuelve a no decir dónde mirar.
func TestSeDetectaElErrorDeUnaHerramienta(t *testing.T) {
casos := map[string]bool{
`{"error": "consulta requerida"}`: true,
` {"error": "el servicio de embeddings no está configurado"}`: true,
`{"resultados": ["texto"]}`: false,
`{"resultados": [], "nota": "No se encontró información relacionada"}`: false,
}
for entrada, esError := range casos {
got := strings.HasPrefix(strings.TrimSpace(entrada), `{"error"`)
if got != esError {
t.Errorf("%q: detectado como error = %v, esperaba %v", entrada, got, esError)
}
}
}