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>
62 lines
2.1 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|