Cargando…
Cargando…
Método de producción de los dos exámenes sobre una ley: el análisis del proyecto en tramitación (pipeline agéntico, contrato de evidencia, validación adversarial) y la ficha de seguimiento de la ley promulgada (núcleo congelado, cumplimiento, deriva de supuestos, pulso).
Función asíncrona lineal. No hay grafo de estados, no hay nodos condicionales, no hay reintentos de agente ni de pipeline. Los cuatro agentes corren siempre, en orden, y el paso entre ellos es texto a texto: ningún agente valida el JSON del anterior.
despacho
↓ selección de key BYOK del workspace (la más reciente que descifre)
↓ resolve_dispatch(provider) → backend, base_url, modelo por defecto
Scout → texto + fuentes_consultadas[]
Analyst → JSON del screen + claim_citations[]
Critic → mismo JSON, dem_score posiblemente reemplazado + critique_notes
Writer → JSON final (prosa/formato)
↓ parseo tolerante del JSON del Writer
↓ extracción y normalización del sidecar (sources, claims, stage_trace)
↓ repair_citations() determinista, sin LLM
↓ validate() adversarial → quality_status
↓ gate Pydantic pre-INSERT
↓ INSERT usage_events (4 stages) → INSERT analyses| Parámetro | Valor |
|---|---|
| Pipelines válidos | analysis (6 pantallas) · history (1 pantalla). Cualquier otro: 400. |
| Ejecución | Una background task independiente por pantalla, aisladas entre sí. |
| Anti doble-despacho | Pantalla con corrida running creada hace menos de 30 min se omite (skipped_running). |
| Iteraciones de tool-use | Máximo 40 por agente. |
status = failed){…}).quality_status = blocked no es un fallo de corrida. La fila queda status = done con quality_status = blocked: se persiste, no se publica, y genera alerta y correo con deduplicación de 1 hora.
| Situación | Respuesta |
|---|---|
| Workspace sin key activa | 402 byok_required, con la lista de proveedores soportados derivada del registro. |
Admin sin can_use_workspace_key | 402 igual. Ser admin habilita lectura cross-workspace, no gastar la key de la organización. |
| Hay keys pero ninguna descifra | No hay 402: la corrida se marca failed y retorna. |
| Proveedores | anthropic, openai, google, deepseek, openai-compat. El dispatch_backend y el modelo por defecto salen del registro; una key no-Anthropic se usa realmente. |
| Agente | Entrada | Salida | Herramientas |
|---|---|---|---|
| Scout | Boletín + screen_type | Texto + bloque JSON anexo con fuentes_consultadas[] | get_proyecto (máx 2), search_web (máx 5). Tool-set ampliado para history. |
| Analyst | Texto crudo del Scout | JSON del screen + claim_citations[] | compute_gini, compute_brecha_pp, compute_dem_score, bootstrap_ci |
| Critic | Texto crudo del Analyst | Mismo JSON + critique_notes; DEM eventualmente reemplazado | compute_dem_score |
| Writer | Texto crudo del Critic | JSON final (prosa y formato) | Ninguna |
Es el único control de contenido dentro del pipeline. Su procedimiento:
sources; marca confidence: low las que no.|total_critic − total_analyst| > 1.0, reemplaza el bloque dem_score del Analyst.pull_quote (≤ 280 caracteres) citando el pasaje._stage_trace.critic: preguntas levantadas, ajustes aplicados, delta de DEM, criterios bajo 6.No bloquea ni puede fallar una corrida. Un delta de DEM mayor a 1.0 se traduce después en la señal L1.critic_dem_delta_high (tier unverified), no en un rechazo.
Antes de validar, un normalizador determinista rellena huecos previsibles del payload en vez de dejar que el schema falle:
metrics.fiscal.{central, ci_low, ci_high} nulos → 0.0; delta_pct derivado contra el PIB.agent_msg.speaker por defecto según pantalla; agent_msg.cite → primer src_id disponible.comparative: gap_vs_chile = burden − chile_burden; si el payload luce degenerado, se marca comparison_not_applicable: true.pull_quote se anula si mide menos de 40 caracteres o si el mínimo de los cuatro criterios es ≥ 6.0.Seis pantallas en el pipeline analysis, una en history. Trazabilidad (/07) y deliberación (/08–/09) no corren agentes del pipeline.
| Ruta · screen_type | Pregunta | Salida principal |
|---|---|---|
/01 · simulator | Impacto fiscal, distributivo y calidad de diseño. | metrics.fiscal con intervalo de confianza (bootstrap), metrics.gini (delta, dirección, baseline), dem_score, deciles[], sectors[]. |
/02 · comparative | Posición de Chile frente a OCDE/LATAM y suerte de reformas comparables. | position (rank), gap_pp, reforms_history (vigentes / revertidas / en trámite), tabla país por país con gap_vs_chile. |
/03 · actors | Quién apoya, quién se opone, con qué influencia y qué narrativa. | actors[] (posición, influencia 1–100), relations[], narratives[], evolution[]. |
/04 · tracking | Compromisos públicos y su avance. | promises[] con progress_pct y estado. Umbrales: track ≥ 75%, delay 40–75%, stuck < 40% del avance esperado. |
/05 · memory | Qué ocurrió cuando se intentó algo equivalente. | historical_cases[] (mínimo 3, con desenlace y lecciones), cross_synthesis, dem_score. |
/06 · contradictions | Conflictos con normativa vigente o entre reguladores. | conflicts[] (3–7), severidad A / B / C, regulador A vs regulador B con norma y cita literal. |
/historia · history | Tramitación completa, año por año y voto por voto. | years[].events[] con vote_bar, pictograma, coaliciones y nominal. El Scout computa; el Analyst transcribe. |
En comparative, tracking, actors y contradictions, los agregados declarados por el modelo se sobrescriben con los derivados del arreglo detallado (auto-corrección, no bloqueo). En history sí bloquean:
| rule_id | Tier | Condición de fallo |
|---|---|---|
L1.HIST_VOTEBAR | block | total ≠ favor + contra + abstención |
L1.HIST_PICTOGRAM | block | Número de votos del pictograma ≠ total de la barra |
L1.HIST_COALICIONES | block | Σ por coalición ≠ barra (salvo desglose declarado aproximado) |
L1.HIST_POSITION_SUMMARY | block | Resumen de posiciones ≠ Σ de las barras |
El search_web del Scout no es una búsqueda abierta. Ejecuta dos pasadas:
1ª pasada: restringida a include_domains = official_domains()
2ª pasada: abierta, solo para rellenar cupos
→ dedupe por URL → oficiales al frente
post-Scout: máximo 4 fuentes NO oficiales en el set finalLos topes de herramientas se imponen en el handler, no en el prompt: el modelo no puede excederlos.
Un solo archivo alimenta al Scout (is_official) y al validador adversarial (check_authoritative).
| Allowlist | Frescura | Dominios |
|---|---|---|
| fiscal | 180 d | dipres · sii · hacienda · bcentral · ine · ministeriodesarrollosocial · contraloria · presupuestoabierto · bcn |
| legislative | 365 d | congreso · senado · camara · bcn · leychile |
| regulatory | 365 d | cmfchile · sernac · fne · uaf · sec · cne · suseso · sii · dt · contraloria |
| academic · other | — | Sin allowlist: no se les exige autoridad. |
Un dominio se considera autoritativo si pertenece a la unión de las tres listas, es subdominio de uno de ellos, o termina en .gob.cl. Además hay confianza por procedencia a profundidad 1: si una fuente autoritativa enlaza la URL, no se marca.
| Herramienta | Uso |
|---|---|
compute_gini | Delta de Gini del escenario simulado. |
bootstrap_ci | Intervalo de confianza del impacto fiscal. |
compute_brecha_pp | Brecha en puntos porcentuales contra el benchmark. |
compute_dem_score | Promedio ponderado (Relevancia 20% · Efectividad 40% · Eficiencia 20% · Sostenibilidad 20%, Cuadro 2.2 DEO 2024). Determinista: el modelo no aritmetiza el total. |
coalicion_breakdown | Desglose determinista de una votación por coalición (solo history). |
La búsqueda web degrada a lista vacía ante cualquier fallo (timeout 30 s): no cuelga la corrida ni propaga la excepción.
{ id: "src_N", url, title,
type: boletin | web | PDF | dataset | academic | other, // obligatorio
domain,
claim_type: fiscal | legislative | regulatory | academic | other,
raw_content_excerpt, // ~2000 chars — NUNCA se persiste
fetched_at, last_modified }{ path: "/metrics/fiscal/central", // JSON Pointer RFC 6901
source_id: "src_1",
source_quote: "USD 340 M / año", // LITERAL del raw_content_excerpt
quote_context: "..." }Instrucción de contrato: si no existe un fragmento literal que respalde la cifra, se omite la cita. Es preferible no citar a citar mal.
| Regla | Detalle |
|---|---|
| Paths concretas | Cada una debe estar citada. |
Comodines (/deciles/*/impact_pct) | Basta una expansión no nula citada. |
| Campos nulos o ausentes | No exigen cita. |
comparison_not_applicable: true | Exención total. |
tracking, actors y contradictions tienen su lista de paths citables vacía. No se les exige ninguna cita y por defecto salen verified. Es un vacío documentado, no una decisión metodológica.
Corre antes del validador. Sin modelo de lenguaje. Por cada cita:
| Condición | Acción |
|---|---|
La path no resuelve en el payload | Descartar. |
source_id desconocido | Descartar. |
raw_content o quote vacíos | Descartar. |
| El quote ya es literal | Conservar byte a byte (idempotencia). |
| El quote no es literal | Reparar: extraer anclas (cifras, porcentajes, montos, unidades), segmentar el texto original en ventanas y elegir la ventana verbatim más ajustada que contenga todas las anclas. |
| No hay ancla confiable | Descartar. Nunca se inventa una cita. |
El normalizador usado es exactamente el del grounding (L3); de lo contrario, una cita «reparada» fallaría igual río abajo. El set reparado es el que se persiste, y el contador de afirmaciones numéricas del stage_trace se reconcilia contra él.
quality_status = blocked si existe ≥1 failure con tier=block
unverified si existe ≥1 failure con tier=unverified
verified en otro caso
advisory NO degrada el status.
validate() nunca lanza: toda excepción interna degrada a advisory (fail-open).| rule_id | Tier | Disparo |
|---|---|---|
L1.schema_invalid | block | El payload no valida contra el schema de la pantalla. |
L1.citation_missing | block | Una path citable con valor no nulo carece de cita. |
L1.citation_path_dangling | block | La path de una cita no resuelve en el payload. |
L1.ai_meta_leak | block | Jerga de IA o de agentes en prosa publicable. |
L1.source_id_dangling | unverified | El source_id citado no existe en sources[]. |
L1.dem_score_inconsistent | unverified | |total − promedio de los 4 criterios| > 0.5 |
L1.stage_trace_mismatch | unverified | Diferencia > 2 entre afirmaciones numéricas declaradas y citas observadas. |
L1.critic_dem_delta_high | unverified | El Critic movió el DEM más de 1.0 punto. |
| rule_id | Tier | Disparo |
|---|---|---|
L2.source_404 | unverified | URL muerta o error HTTP. |
L2.source_not_authoritative | unverified | Dominio fuera de la unión de allowlists y sin procedencia desde una fuente autoritativa. |
L2.source_stale | unverified | Fuente no oficial más antigua que su ventana de frescura. |
| rule_id | Tier | Pantalla | Verifica |
|---|---|---|---|
L2.label_payload_mismatch | block | comparative | El rótulo de columna corresponde a los valores de la tabla. |
L2.fiscal_label_narrative_mismatch | block | simulator | El rótulo fiscal no dice «recaudación» mientras la prosa describe un egreso. |
L2.narrative_number_mismatch | block | reportes | La prosa no afirma un concepto que la cifra interpolada no sostiene. |
L2.ai_meta_leak | block | todas | Términos ambiguos de IA en el texto publicable. |
L2.*_judge_exception | advisory | — | Caída del juez: no degrada el estado. |
| rule_id | Tier | Disparo |
|---|---|---|
L3.quote_not_in_raw | unverified | El source_quote normalizado no es substring del contenido de la fuente. |
El excerpt puede legítimamente no contener una cifra computada a partir de él. La garantía dura es la existencia de la cita (L1, tier block), no su literalidad (L3).
Un job reconstruye el envelope desde el result persistido y recorre el validador con los jueces desactivados (determinista y sin costo). Regla: nunca confiar en un quality_status almacenado; siempre recomputarlo desde una lista de failures recién producida. Se originó tras detectar filas con failures de tier block almacenadas como unverified. Advertencia: el contenido crudo de las fuentes no sobrevive a la persistencia, por lo que L3 puede ser más estricto offline.
No es una pantalla del pipeline: es una derivación pura sobre filas ya persistidas (análisis, eventos de uso, política, reporte adversarial). Sin I/O propio.
facts_total = número de claim_citations
facts_with_source = citas cuyo source_id resuelve a una fuente con URL http*
traceability_pct = round(100 × facts_with_source / facts_total)
invariante: |pct − round(100 × n / total)| ≤ 1
si se viola → 0/0/0 (fail-closed)El porcentaje se recomputa. Antes se asignaba por enum del quality_status (100 / 50 / 0), lo que producía un número que no correspondía a las citas efectivas.
El bloque de proceso reconstruye los cuatro pasos (Scout, Analyst, Critic, Writer) desde los eventos de uso. El paso del Critic es el único marcado como crítico y el único que exhibe el pull_quote. Las fuentes se clasifican en chilenas · internacionales · académicas · otras.
Un análisis done con menos de 4 eventos de uso levanta excepción. La contabilidad de tokens es condición de existencia del análisis, no telemetría opcional.
Fuera del pipeline de agentes. El backend arma un brief y delega la deliberación a un servicio externo por protocolo agente-a-agente; la respuesta llega por streaming de eventos.
| Elemento | Detalle |
|---|---|
| Precondición | La pantalla simulator debe estar done y no blocked. comparative es opcional. Si no se cumple: 400. |
| Panel | 6 comisionados con sesgo, horizonte y postura por defecto declarados: izquierda progresista, centro reformista, derecha tradicional, derecha republicana, pragmático ciudadano, territorial regionalista. |
| Tensiones | 7 pares de tensión ideológica estructural, declarados de antemano. |
| Presidencia | Moderador neutral, intervención moderada. |
| Eventos persistidos | turn.appended, tension.detected, deliberation.memo_drafted, deliberation.completed. |
| Cierre | Al completarse, el Critic puntúa el memo contra los cuatro criterios BID-DEM y emite el resumen con su score. |
| Clave | Exige key BYOK del proveedor Anthropic. Sin ella: 402. |
La ficha tiene dos mitades con reglas opuestas.
| Mitad | Regla | Contenido |
|---|---|---|
| Núcleo | Congelado y versionado. Modificarlo exige bump de nucleo_version y nota de metodología. | Identidad, teoría del cambio, indicadores, variación de implementación, plan de hitos, catálogo de fuentes, evaluabilidad. |
| Seguimiento | Append-only. Crece en el tiempo; nunca reescribe el núcleo. | Observaciones, hitos cumplidos, eventos de modificación. |
Niveles de la cadena de valor (lógica vertical): insumo → producto → resultado_intermedio → resultado_final. Solo los dos últimos cuentan para la disponibilidad de línea base (ver documento I, §12).
SMART: cinco booleanos (específico, medible, alcanzable, pertinente, con plazo). es_smart es la conjunción de los cinco; no hay crédito parcial.
| Estado | Definición |
|---|---|
capturada | Valor real medido antes de la entrada en vigencia. |
estimada | Proxy o estimación documentada. |
no_disponible | El dato se perdió. Irreversible: nadie puede reconstruirlo. |
cero_por_construccion | El indicador no existía antes de la vigencia; su valor pre-vigencia es cero por definición. |
ventana_pre_vigencia_dias: días entre la fecha de corte y la entrada en vigencia. Menor es mejor.
Un indicador de nivel resultado_intermedio o resultado_final no puede declarar cero_por_construccion: un resultado es un estado del mundo que preexiste a la ley (el gasto de bolsillo catastrófico existía antes de la Ley Ricarte Soto). Sin esta regla, toda ley nueva declararía cero por construcción sobre conteos tautológicos de sus propios beneficiarios y capturaría íntegro el 30% de peso de la línea base.
Registra cómo entra en vigencia la ley, sin pre-comprometer un diseño causal.
| Tipo de rollout | Identificabilidad futura |
|---|---|
por_umbral | Corte de elegibilidad. Potencial diseño de discontinuidad. |
escalonado_geografico · escalonado_por_grupo | Variación entre unidades tratadas y no tratadas. |
escalonado_temporal | Variación en el tiempo de adopción. |
simultaneo | Sin variación explotable. |
posibles_grupos_comparacion lista candidatos (no elegibles, rezagados, justo bajo el umbral). Es un inventario de opciones, no un diseño causal.
Cada fuente declara tipo (administrativa · encuesta · registro público · scraping), organismo, nivel de agregación, linaje (cómo se obtiene y transforma el dato hasta el indicador), frescura y licencia. Acceso: abierto · convenio_requerido · restringido. Solo los dos primeros puntúan en trazabilidad.
| Campo | Contenido |
|---|---|
| Problema público | El problema que la ley declara atacar. |
| Objetivo declarado | Objetivo explícito de la ley o del mensaje. |
| Mecanismo causal | La cadena «si se hace X, entonces ocurre Y, porque Z» que la ley supone. |
| Población objetivo | Definición, criterios de elegibilidad, tamaño estimado, fuentes. |
| Instrumentos | Mínimo 1. Palanca de política: regulatorio · fiscal · transferencia monetaria · provisión directa · institucional · informacional. Cada uno con su artículo de referencia. |
| Supuestos | Cada uno con supuesto_id estable y riesgo asociado: qué le pasa a la teoría del cambio si el supuesto falla. El id es lo que después enlaza los eventos con la deriva. |
El componente de teoría del cambio aporta el 20% del score de evaluabilidad y se computa como los seis chequeos cumplidos sobre seis (documento I, §12).
Mide hitos normativos, no indicadores. Es el cumplimiento del diseño, no del efecto.
| Capa | Origen | Contenido |
|---|---|---|
| Plan | Núcleo (congelado) | Hito con su tipo (reglamento, decreto, resolución, norma de carácter general, designación institucional, asignación presupuestaria, entrada en vigencia de componente, plazo legal), el artículo que lo manda y su fecha límite legal. |
| Realizado | Seguimiento (append-only) | Estado declarado con fuentes: pendiente · en_plazo · cumplido · cumplido_con_atraso · incumplido · judicializado. |
El estado de un hito viene declarado en la capa de seguimiento, con sus src_ids. La ficha no deriva «en plazo» ni «vencido» comparando la fecha límite legal contra la fecha de hoy. Sin registro de seguimiento no se sabe si un mandato se cumplió: solo se sabe cuándo vencía.
A día cero, con la lista vacía, todo hito cae al estado neutro «Seguimiento no iniciado». Nunca se codifica a mano «0 vencidas», «al día» ni un porcentaje de avance.
Merge: un hito es único por hito_id y el delta reemplaza el estado anterior — el hito avanza.
El estado de cada supuesto es derivado, no almacenado. Se recomputa en lectura plegando los eventos en orden cronológico sobre el estado inicial vigente. Reductor puro, igual que el score.
Un evento de modificación: ley modificatoria, dictamen de Contraloría, requerimiento o sentencia del Tribunal Constitucional, cambio reglamentario, shock de contexto. Cada evento declara qué supuestos afecta y con qué efecto.
orden: eventos ascendente por (fecha, evento_id) // determinista ante empate
transiciones:
tensiona {vigente, restablecido} → tensionado
tensionado → tensionado
incumple cualquier estado no terminal → incumplido
restablece {tensionado, incumplido} → restablecido
desde vigente: NO hay downgrade; el evento no se registra
supera_por_enmienda → superado_por_enmienda [TERMINAL]
fija ventana_archivo = fecha del evento
Un efecto sobre un supuesto_id inexistente se ignora. Nunca lanza.No es un fracaso del supuesto: es el legislador jubilándolo por la vía de una enmienda. Se cuenta aparte para que no contamine el diagnóstico de incumplimiento, y ningún evento posterior lo mueve.
Salida: por supuesto, su estado, los eventos que efectivamente lo movieron (en orden) y su ventana de archivo; más el conteo por estado, con los estados ausentes en cero. Un indicador seguimiento_iniciado distingue el tablero real de la vista defensiva de día cero.
Tres bloques con procedencia distinta. No se mezclan.
índice(t) = valor(t) / valor_linea_base × 100 // día cero = 100
Un indicador tiene serie SOLO si:
≥ 1 observación registrada
Y línea base capturada o estimada, no nula
La direccion_deseada (sube / baja / estable) determina si un movimiento
es mejora o retroceso.Se deriva del plan de implementación y los tramos de rollout. Es calendario, no dato de seguimiento: se renderiza siempre, incluso a día cero. No se contrasta contra la fecha actual («en vigencia» vs «programado»): se rotula neutro como calendario del núcleo, para ser determinista entre corridas.
El bloque de quiebre de régimen (núcleo v1 → v2) es constante e hipotético, rotulado como ejemplo ilustrativo, con la advertencia de empalme prohibido entre regímenes. No es dato real.
Muestra la serie observada, la gradualidad programada y el contexto coincidente. No separa el efecto de la ley del efecto del contexto. Es variación observada, no efecto atribuible.
NÚCLEO (build_nucleo):
Scout → Analyst → parseo defensivo
→ placeholder de evaluabilidad (sin_datos / valor=None)
→ gate Pydantic
→ computar_evaluabilidad() // score determinista
→ model_copy con el score real
→ Critic (deja constancia; NO reescribe el núcleo)
→ upsert
SEGUIMIENTO (refresh_seguimiento): // requiere núcleo existente
Scout(núcleo, desde_fecha) → Analyst → parseo
→ gate Pydantic
→ filtro de ids contra el núcleo
→ merge append-only
→ Critic (constancia) → upsertEl Analyst tiene contrato explícito de omitir el campo de evaluabilidad. La pipeline inyecta un placeholder y luego lo reemplaza con el score computado. Sembrar un score a mano es la falla que este diseño previene.
| Dato huérfano | Acción |
|---|---|
Observación con indicador_id desconocido | Descartada. |
Hito con hito_id desconocido | Descartado. |
Evento con supuesto_id desconocido | La referencia se depura; el evento se conserva. |
| Entidad | Clave | Conflicto |
|---|---|---|
| Observación | (indicador_id, fecha) | El delta gana (permite corregir). |
| Hito de seguimiento | hito_id | El delta reemplaza (el hito avanza). |
| Evento de modificación | evento_id | El existente se conserva (el evento es inmutable). |
Ficha ensamblada desde artefactos JSON durables producidos fuera del proceso del backend, verificados adversarialmente. La lógica de merge y de score es la real: se recomputa, nunca se siembra. La escritura a producción está gateada y requiere autorización por corrida.
Un análisis marcado done debe acreditar el trabajo que lo produjo. El trigger acepta dos procedencias, cada una con su evidencia, y falla cerrada ante cualquier otro valor.
| Procedencia | Evidencia exigida |
|---|---|
pipeline | ≥ 4 eventos de uso con stage distinto (Scout, Analyst, Critic, Writer) dentro de la ventana de la corrida. Solo los escribe el pipeline cuando gasta tokens contra un proveedor. |
curated_artifacts | Una fila de artefacto con ruta, sha256 y authorized_by. Cubre el trabajo producido fuera del backend, que no consume API. |
| Cualquier otro valor | Excepción. Agregar un valor sin darle rama no puede convertirse en un bypass silencioso. |
Ambas ramas prohíben marcar done si el resultado contiene un campo de error. Fabricar eventos de uso para una vía que no gastó API mentiría sobre el costo y sobre quién produjo el análisis.
Si el score almacenado no existe o su formula_version difiere de la vigente (2.1), se recomputa sobre el núcleo cargado. La vista pública nunca expone una ficha despublicada.
tracking, actors y contradictions no tienen enforcement de citas: por defecto salen verified sin haberlo acreditado.nucleo_version, pero no hay maquinaria de versionado v1→v2.no_disponible es irreversible: el día cero no se recupera.