Subrayado: fragmentos que señalamos el juez o yo. Pasa el ratón o toca, o llega con el tabulador, para ver por qué.
Un hotel recibe cientos de reseñas, y en una frase mal escrita se puede admitir una culpa que nadie ha comprobado. Este sistema interpreta la reseña, redacta la respuesta, la revisa y decide si se publica sola o pasa a una persona.
Trabajé años en hostelería respondiendo a incidencias de clientes y conozco lo delicado que es hacerlo bien.
Empieza por la guía orientativa: 9 casos, de un acierto claro al fallo que el juez no ve.
Subrayado: fragmentos que señalamos el juez o yo. Pasa el ratón o toca, o llega con el tabulador, para ver por qué.
Un clasificador decide si la reseña es negativa, neutral o positiva, y con cuánta confianza. Es mRoBERTa, un modelo entrenado para español que ajusté con reseñas reales (fine-tuning), promediando tres semillas. En 11.414 reseñas que no vio al entrenar saca un F1 macro de 0,781: una nota de 0 a 1 que da el mismo peso a las tres clases. La neutral, la más difícil, se queda en 0,549.
Un modelo de lenguaje redacta bien, pero tiende a inventar. Para limitarlo, recibe la ficha completa del hotel y, mediante RAG, las políticas y las respuestas aprobadas más parecidas a esta reseña. RAG (Retrieval-Augmented Generation) en este caso consiste en buscar los documentos pertinentes y dárselos al modelo antes de que escriba. En las políticas combina búsqueda por palabras (BM25) y por significado (embeddings de bge-m3).
Qwen3.6 27B, un LLM que corre en local, identifica de qué habla el cliente y redacta el borrador. Después lo revisan dos filtros: reglas fijas (servicios que no están en la ficha, compensaciones, nombres de personas) y un segundo modelo, Gemma 4 12B, al que llamo juez. Es de otra familia para no compartir los sesgos del generador: contesta sí o no a 8 criterios y cita la frase que justifica cada suspenso.
La puerta toma la decisión final. Lo negativo, lo neutral, lo dudoso (confianza por debajo de 0,85), las trampas y cualquier accidente o riesgo legal pasan a una persona. De las 54 reseñas de la prueba, 8 superan todos los controles. De las 24 reales de 1 a 3★, ninguna se publicó sin revisión. No es autopublicación: prepara borradores, con una mediana de 48 s por reseña en un PC.
Este proyecto nació de una versión anterior que en apariencia funcionaba, pero el resultado era muy deficiente, de modo que la audité: el 50,8 % del examen final (el test) estaba también, letra por letra, en los datos de entrenamiento. El modelo se sabía las respuestas. Es lo que se llama fuga de datos (reproducción). Además, elegía el modelo con ese mismo examen y nunca había visto una reseña de 4★. Lo rehíce desde cero: duplicados fuera antes de separar los datos y un conjunto de validación aparte.
Casi todas las respuestas empiezan igual, así que probé versiones más variadas del prompt, las instrucciones que recibe el modelo. Al compararlas a ciegas, sin saber cuál era cuál, preferí la actual en 15 reseñas y la nueva en 10, una diferencia que podría ser azar (p = 0,42). Me quedé con la actual, y las respuestas siguen siendo repetitivas. Volveré a intentarlo cuando disponga de más reseñas, para no ajustar el prompt a estos mismos casos y provocar sobreajuste.
Comparé al juez con mis etiquetas, puestas a ciegas, en 24 respuestas. Suspendió 7 y yo 4, todas dentro de sus 7: no se le escapó ninguna de las mías, con 3 falsas alarmas. La muestra es pequeña, y al leer otras respuestas encontré admisiones implícitas de culpa que deja pasar (p08 y rag-carteles). Eso obliga a que lo delicado pase siempre por una persona.
Cada cifra de esta página sale de un fichero del repositorio. Aquí están todas, con su tamaño de muestra y su origen. Construí el proyecto con Claude Code: qué decidí yo y qué se hizo con la herramienta.
Cada cifra enlaza a su fichero. Los intervalos de proporciones son de Wilson; los de F1 vienen del bootstrap de cada informe. La calibración usa la rúbrica v2, y producción usa v2g.
| Conjunto | n | Métrica | Valor | IC 95 % | Origen |
|---|---|---|---|---|---|
| AHR, nunca usado para entrenar | 11.414 | F1 macro | 0,781 | 0,770 a 0,791 | phase3b_realista.json |
| AHR | 11.414 | F1 de la clase neutral | 0,549 | — | phase3b_realista.json |
| AHR, confianza ≥ 0,85 | 8.487 | Error | 264 de 8.487 (3,1 %) | 2,8 % a 3,5 % | phase3b_realista.json |
| Test limpio de COAH | 269 | F1 macro | 0,797 | 0,732 a 0,853 | phase3b_realista.json |
| COAH, confianza ≥ 0,85 | 191 | Error | 2 de 191 (1,0 %) | 0,3 % a 3,7 % | phase3b_realista.json |
| SFU, otra web | 50 | Aciertos | 39 de 50 (78 %) | 64,8 % a 87,2 % | phase3b_realista.json |
| Calibración, rúbrica v2 | 24 | Precisión del juez | 4 de 7 (57 %) | 25,0 % a 84,2 % | phase6_calibracion.md |
| Calibración, rúbrica v2 | 24 | Recall del juez | 4 de 4 (100 %) | 51,0 % a 100,0 % | phase6_calibracion.md |
| Calibración, rúbrica v2 | 24 | Kappa en «no admite culpa» | 0,70 | — | phase6_calibracion.md |
| Semillas, rúbrica v2 | 10 | Fallos de la persona que detecta el juez | 1 de 3 (33 %) | 6,1 % a 79,2 % | phase6_semillas.json |
| Cola de demostración | 54 | Pasan todos los controles | 8 de 54 (15 %) | 7,7 % a 26,6 % | pipeline_cola.json |
| Reales de 1 a 3★ | 24 | Publicadas sin revisión (por diseño) | 0 de 24 (0,0 %) | 0,0 % a 13,8 % | pipeline_cola.json |
| Comparación a ciegas, v6 y v9 | 25 | Prefiere v6, sin 3 empates (p = 0,42) | 15 de 25 (60 %) | 40,7 % a 76,6 % | phase9_ciegas.md |
| Comparación a ciegas, v6 | 30 | Publicable tal cual | 8 de 30 (27 %) | 14,2 % a 44,4 % | phase9_ciegas.md |
| Latencia en un PC | 15 | Mediana hasta el veredicto | 48,2 s (p90 52,2 s) | — | phase9_latencia.md |
Solo 6 casos tienen mi veredicto, con el criterio escrito antes de generar, y 1 no cumple: la matriz enseña qué casos son, no mide al juez. Cada identificador abre el caso en el visor.
Miden cosas distintas y apenas se solapan, así que no hay una única «n» del sistema.
| Conjunto | Tamaño | Origen | Uso |
|---|---|---|---|
| Test limpio de COAH | 269 | Real, TripAdvisor | Medir el clasificador |
| AHR | 11.414 | Real, TripAdvisor, 2021 | Medir el clasificador con reseñas que no vio |
| SFU | 50 | Real, ciao.es | Medirlo con otra web |
| Cola de demostración | 54 | 14 sintéticas y 40 reales del AHR | Sistema completo y puerta |
| Comparación a ciegas | 30, valoradas 28 | Escritas por mí | Elegir entre versiones del prompt |
| Calibración | 24 | Respuestas de 3 versiones del prompt | Juez frente a una persona |
| Semillas | 10 | Respuestas de las primeras versiones | Fallos reales del generador |
| Selector de esta página | 15 | 6 escritas por mí y 9 ilustrativas | Enseñar casos; no es una muestra |
ollama_gpu.py: la comprobación que impide ejecutar en CPU sin avisar, añadida tras ese incidente.