From 74c0374a2acde2c429d3014d19d2e8cd42d37032 Mon Sep 17 00:00:00 2001 From: urieljareth Date: Tue, 28 Jul 2026 18:59:42 -0600 Subject: [PATCH] Propuesta IA: correcciones encontradas probando contra la API real de MiniMax Tres defectos que solo se veian llamando al modelo de verdad. 1. min(2) en los factores obligaba a inventar relleno. Con una dimension sin cifras, MiniMax produjo "herramienta_de_seguimiento_actual=0 x canal=1" solo para satisfacer la restriccion. Ahora los dos factores se exigen unicamente cuando hay montoAnualMXN. 2. El modelo OMITE montoAnualMXN en vez de mandar null explicito, que es lo natural para un LLM. Exigirlo presente quemaba los tres intentos del paso. Ahora es .default(null) y la omision se tolera. 3. Sin confianza por factor, el modelo la metia dentro del nombre ("tasa_conversion (por_validar)=0.1"). Ahora es un campo. Y el hallazgo que mas importa, porque es comercial y no de formato: el modelo puede OMITIR un factor y aun asi cuadrar la aritmetica. En una corrida calculo 40 mensajes x 0.5 sin contestar x 52 semanas x 3,000 de utilidad POR CLIENTE = 3,120,000, asumiendo que cada mensaje sin responder es un cliente perdido. R5 lo acepto porque los factores si multiplican al monto: R5 no puede ver lo que falta. El ratio habria dicho "subcotizado" con el denominador inflado diez veces. Como no se puede impedir que el modelo produzca estimaciones plausibles y erroneas, lo que se hace es que el asesor las cache de un vistazo: - R5b avisa cuando una cifra no tiene ni un factor confirmado. - El anexo interno desglosa cada factor con su confianza, y alerta en rojo si el valor descansa entero en estimaciones. Ademas, el presupuesto de reintentos sube (5 en extraccion, 4 en los otros dos): MiniMax se equivoca de array de vez en cuando con schemas anidados, y la extraccion es el paso fundacional. Una corrida real necesito los 5. Co-Authored-By: Claude Opus 5 (1M context) --- scripts/verificar-propuesta-pdf.ts | 13 ++++++- scripts/verificar-propuesta-schemas.ts | 17 ++++++--- scripts/verificar-propuesta-validacion.ts | 9 +++-- src/lib/propuesta/cliente-ia.ts | 6 +++- src/lib/propuesta/pdf.ts | 35 ++++++++++++++++++ src/lib/propuesta/pipeline.ts | 7 ++-- src/lib/propuesta/prompts.ts | 12 +++++-- src/lib/propuesta/schemas.ts | 43 +++++++++++++++++++---- src/lib/propuesta/validacion.ts | 22 ++++++++++++ 9 files changed, 143 insertions(+), 21 deletions(-) diff --git a/scripts/verificar-propuesta-pdf.ts b/scripts/verificar-propuesta-pdf.ts index f9ecdb6..c527b7d 100644 --- a/scripts/verificar-propuesta-pdf.ts +++ b/scripts/verificar-propuesta-pdf.ts @@ -72,7 +72,7 @@ const propuesta: PropuestaConsultiva = { { id: "D02", urgencia: "verde", titulo: "MARCADORVERDE", descripcion: "Ya tienen la base de clientes ordenada.", confianza: "confirmado", citas: ["C01"], hechos: ["H01"] }, ], valorProblema: { - dimensiones: [{ tipo: "tiempo", descripcion: "MARCADORVALOR horas perdidas", calculo: { factores: [{ nombre: "h", valor: 8 }, { nombre: "s", valor: 52 }, { nombre: "c", valor: 400 }], montoAnualMXN: 166400 }, confianza: "estimado", hechos: ["H01"] }], + dimensiones: [{ tipo: "tiempo", descripcion: "MARCADORVALOR horas perdidas", calculo: { factores: [{ nombre: "h", valor: 8, confianza: "confirmado" }, { nombre: "s", valor: 52, confianza: "confirmado" }, { nombre: "c", valor: 400, confianza: "estimado" }], montoAnualMXN: 166400 }, confianza: "estimado", hechos: ["H01"] }], notaMetodologia: "MARCADORMETODO ocho horas por semana a costo cargado.", }, resultados: [{ enunciado: "MARCADORRESULTADO cero mensajes sin respuesta", metrica: "tiempo", lineaBase: null, periodoMedicion: "mensual" }], @@ -156,6 +156,17 @@ async function main() { check("el anexo calcula el ratio", txtA.includes("75.3"), "125280/166400"); check("el anexo interpreta el ratio", txtA.includes("objecion probable")); check("el anexo sugiere responder con alcance", txtA.includes("no con descuento")); + check("el anexo desglosa de donde sale el valor anual", txtA.includes("De donde sale el valor anual")); + check("el anexo muestra los factores con su confianza", txtA.includes("confirmado") && txtA.includes("estimado")); + + // R5b: una cifra sostenida solo por estimaciones debe llevar alerta visible. + const soloEstimado = await generarAnexoInternoPDF({ + ...datos, + propuesta: { ...propuesta, diagnostico: { ...propuesta.diagnostico, valorProblema: { ...propuesta.diagnostico.valorProblema, + dimensiones: [{ ...propuesta.diagnostico.valorProblema.dimensiones[0], calculo: { factores: [{ nombre: "a", valor: 8, confianza: "estimado" as const }, { nombre: "b", valor: 20800, confianza: "estimado" as const }], montoAnualMXN: 166400 } }] } } }, + }); + check("el anexo alerta si la cifra descansa solo en estimaciones", textoDePdf(soloEstimado).includes("puede cuadrar y aun asi estar inflada")); + check("el anexo NO alerta si hay factores confirmados", !txtA.includes("puede cuadrar y aun asi estar inflada")); // Robustez del branding: la API de configuracion no valida los valores. for (const malo of ["