Filtro de políticas (Bhutan GNH + NZ LSF + Humphrey)
Rúbrica publicada del filtro determinista que alimenta recommend_action (server/chat/policy-screen.ts, 213 líneas, sin red y sin LLM). Que sea determinista es el punto: la misma propuesta produce el mismo veredicto siempre, y el veredicto es auditable leyendo listas de palabras clave versionadas en el repositorio, no interrogando a un modelo.
Sirve al Pilar 3 — Gobernanza y Democracia: un regulador puede reproducir el resultado sin depender de nosotros.
Qué hace
Puntúa un texto de propuesta (o el pitch de un piloto) contra tres lentes:
- Los 10 ejes del atlas (salud … gobernanza-democrática).
- Los seis capitales intergeneracionales estilo NZ Living Standards Framework:
natural,human,social,financial,physical,cultural. - Una lectura Humphrey de "¿qué se mueve y para quién?", hoy: tono
positive/negative/mixed/neutralpor eje, sin inventar cifras.
Cómo puntúa
Cada lente se resuelve con scoreKeywords() sobre listas positive / negative propias, y el tono sale de qué polaridades encontraron coincidencia:
| Coincidencias | Tono |
|---|---|
| positivas y negativas | mixed |
| solo positivas | positive |
| solo negativas | negative |
| ninguna | neutral |
El tono global (overall) agrega los tonos de ejes y capitales descartando los neutral: si no queda ninguno → neutral; si queda más de uno distinto → mixed; si queda exactamente uno → ese. Una propuesta con un eje positivo y otro negativo es mixed, nunca un promedio que borre la tensión.
Reglas
- La coincidencia es por palabra completa (
desempleono coincide conempleo). - Keywords por eje: no se infiere el tono de las celdas ambientales del país. El filtro lee la propuesta, no el corpus del país, así el resultado no depende de qué tan bien cubierto esté un país.
- Tokens de producto (
cribado,latamgpt,futuros,odisea) no se tratan como geografía (Costa Rica / CRI). human_override_required: trueyexport_allowed: falsesiempre, como literales de tipo: una persona debe autorizar cualquier exportación del resultado. Están tipados como constantes, así que el compilador rechaza cualquier código que intente emitir un resultado auto-exportable.- Determinista y aceptable; no es un LLM.
Forma del resultado
{
"rubric": "bhutan-gnh+nz-lsf+humphrey/v1", // versionada: un cambio de rúbrica cambia el id
"proposal": "…",
"ejes": [{ "eje": "salud", "tone": "positive", "hits": ["…"], "note": "…" }],
"capitals": [{ "capital": "natural", "tone": "mixed", "hits": ["…"] }],
"overall": "mixed",
"human_override_required": true,
"export_allowed": false,
"warnings": [],
"truncated": true // solo presente cuando la propuesta superó los 8.000 caracteres
}Dónde vive
- Código:
server/chat/policy-screen.ts(+ tests). - Consumo: el campo
policy_screenen la respuesta derecommend_action(herramienta disponible tanto en el asistente como en el servidor MCP). - API:
POST /api/policy-screencon{ "proposal": "…" }. Cuerpo limitado a 16 KB (413 body_too_largepor encima); JSON inválido →400 parse_error;maxDurationde 10 s.proposaldebe ser una cadena no vacía. Cualquier otro tipo JSON (objeto, arreglo, número, booleano) devuelve400 proposal_required: no se convierte a texto ("[object Object]") ni se analiza como si fuera una propuesta real. La propuesta se limita a 8.000 caracteres. Por encima se analizan los primeros 8.000 y la respuesta llevatruncated: true, para que quien llama sepa que el veredicto cubre solo parte de su documento; truncar en silencio un documento de política cambiaría el resultado sin avisar a nadie, y por eso existe la bandera. - Superficie humana: /gobernanza/cribado (demo con ejes mixtos + autorización humana para exportar).