Categoría: Conector con interpretación SoA pequeña (LLM extrae intención de comments) Estado: Propio sano (componente nuevo crítico en V3) Tamaño esperado: ~2-3k LOC
Cerrar el loop de aprendizaje del SoA: detectar cualquier cambio en Holded posterior a la ejecución del plan de Intelia y propagarlo como señal estructurada al resto del sistema (T3, M1, dashboard).
Sin T4, el SoA es ciego al criterio del contable. Es el sustrato de toda la calcificación.
positive | negative | neutral | criterion_change); no toma decisión propia.Para cada documento que Intelia ha ejecutado (registrado en executed_plan_history con su plan_id y external_id en Holded), T4 vigila:
| Campo en Holded | Cambio que detecta | Signal que emite |
|---|---|---|
approved |
Pasa de false a true |
APPROVED_BY_HUMAN |
accounts (líneas) |
Cambio de cuenta en una línea | EDITED_ACCOUNT_LINE |
tax_keys |
Cambio en taxes | EDITED_TAX |
tags (excluyendo intelia-*) |
El contable añadió tag | TAGGED_BY_HUMAN |
comments (excluyendo [Intelia]) |
El contable comentó | COMMENTED_BY_HUMAN |
accountingDate |
Cambió la fecha contable | DATE_OVERRIDDEN |
notes |
Cambió la nota | NOTE_EDITED |
| Estado del payment | Pasa de pending a paid o se reconcilió manualmente | PAYMENT_STATE_CHANGED |
| Documento eliminado | Detectado por 404 en readback |
DELETED_BY_HUMAN |
T4 NO monitoriza cambios que hizo Intelia misma (filtrados por timestamp + actor cuando posible, o por hash del estado pre/post-ejecución de Intelia).
Holded no tiene webhooks fiables. T4 funciona por polling.
Adaptable por team: clientes con contables muy activos → cadencia más alta.
Para detectar cambios sin enviar el documento entero por cada poll, T4 mantiene:
HoldedDocumentMonitor {
document_id: str (external),
intelia_plan_id: UUID,
monitored_fields: ["account_lines", "approved", "tax_keys", ...],
last_hash: str, # hash del subset monitoreado en el último poll
last_polled_at: datetime,
poll_tier: "hot" | "warm" | "cold",
edits_detected: list[EditSignal]
}
En cada poll: lee Holded → calcula hash → si distinto → diff detallado + emite signals.
last_hash evita rehacer trabajo en polls sin cambios.Los comments libres del contable son la señal más rica pero la más ruidosa.
Poll detecta comment nuevo (no escrito por Intelia).
LLM corto (modelo barato, e.g. gpt-5-nano) procesa el comment con prompt:
Eres un clasificador. El contable ha comentado lo siguiente en un documento contable
procesado por Intelia. Extrae la intención.
Contexto: [resumen del plan + cuenta usada + cliente]
Comentario: "[texto del contable]"
Devuelve JSON: {
intent: "positive_confirmation" | "negative_disagreement" | "criterion_change" | "question" | "noise",
subject: "account" | "tax" | "imputation" | "date" | "general" | null,
confidence: 0.0-1.0,
extracted_correction: { ... } | null // solo si negative_disagreement con cambio sugerido
}Si confidence < 0.7 → no se propaga signal (ambiguo). Queda en low_confidence_comments para revisión humana opcional.
Si criterion_change → signal estructurado a Intelia ops dashboard con el comment original.
Si negative_disagreement con extracted_correction → signal estructurado a T3 (evidencia negativa de la regla aplicada).
T4 NO procesa comments que parezcan auto-respuestas o que ya fueron procesados. Hash + dedupe por comment_id.
T4 emite signals al bus de signals (TX) tipados:
ReverseSyncSignal {
signal_type: "APPROVED_BY_HUMAN" | "EDITED_ACCOUNT_LINE" | ...,
document_id: str,
intelia_plan_id: UUID,
detected_at: datetime,
delta: {
field: str,
old_value: any,
new_value: any,
},
contable_id: str?, // si Holded lo expone
audience: ["t3_calcification", "m1_motor", "intelia_ops"],
actionable: bool,
}
Los consumidores deciden qué hacer con los signals:
| Consumidor | Acción |
|---|---|
| T3 Calcification | Suma evidencia (positiva si confirmación, negativa si edición) para reglas que aplicaron |
| M1 Motor | Recalibra ProviderHistoryFacts (la próxima vez que el documento vuelva por este proveedor, M1 sabe que el último plan fue editado) |
| Intelia ops dashboard | Surface las correcciones recientes; permite filtrar por cliente, por regla aplicada, por tipo de edición |
Cuando T4 detecta una edición ambigua, puede dejar un comment al contable en Holded preguntando la razón:
[Intelia] Detectamos que cambiaste la cuenta 62980000 por 62890000 en este documento.
¿Razón del cambio? Responde con uno de:
- #criterio_cambiado (queremos actualizar la regla)
- #extraccion_incorrecta (el OCR leyó algo mal)
- #bug_intelia (el agente se equivocó)
- O ignora este mensaje si fue una excepción puntual.
Esto convierte un signal ambiguo en uno estructurado.
Política: solo se hace si la edición es alta importance (cuenta cambiada vs comment añadido al texto libre). Si lo haces para todo, satura al contable.
Algunos cambios en Holded afectan a múltiples documentos:
T4 también monitoriza estos cambios "sistémicos":
SystemicChangeSignal {
signal_type: "CHART_OF_ACCOUNTS_CHANGED" | "PERIOD_LOCKED" | ...,
team_id: UUID,
detected_at: datetime,
delta: { ... },
affected_intelia_plans: [list of plan_ids que dependen],
audience: ["m1_motor", "t3_calcification", "i1_facts_snapshot"]
}
Estos signals disparan invalidación masiva en T3 (reglas afectadas pasan a re-shadow) y refresh de facts/erp_snapshot/ en M1.
| Riesgo | Mitigación |
|---|---|
| Polling no detecta cambios entre polls (contable edita rápido) | Hash de estado captura el "antes/después" aunque los cambios intermedios se pierdan. Para auditoría, se documenta la limitación |
| Holded API rate limit saturado por polling | Batch polling + tiers de cadencia + monitoring de rate-limit headers |
| LLM extractor de comments confunde positive con negative | Confidence threshold (0.7); fall-through a low_confidence_comments para review humano |
| Cambios sistémicos no se detectan a tiempo (cliente cerró trimestre el viernes, lunes Intelia escribe en período locked) | Polling de systemic changes con cadencia más alta (cada 1h). Pre-write check en I1 valida estado fresco antes de cada write |
| El contable comenta cosas no útiles ("ok", "👍") y T4 las procesa como confirmation, inflando false positives | Lista de patrones positive ruidosos. Solo se propaga signal si el comment es suficientemente específico |
| Cambios entre polls pueden sobreescribirse: contable edita y luego "deshace"; T4 solo ve estado actual | Política aceptada como limitación. Documentar en runbook |
| Procesar comments con LLM gasta tokens por nada (comments de Intelia procesados como del contable) | Filtro por prefix [Intelia] antes de pasar a LLM |
Patología 1: T4 satura Holded API. Polling agresivo + clientes grandes (50k+ documentos vivos) → 1000+ calls/min → rate limit + bloqueo Holded. Mitigación: cadencia adaptativa, tiers, batch. Monitor de holded_calls_per_minute con alarma.
Patología 2: false positives en interpretación de comments. LLM clasifica un comment ambiguo como "positive_confirmation" cuando era negativo. T3 calcifica un patrón que el contable rechaza. Mitigación: confidence threshold; algunos clientes pueden activar modo "siempre Intelia ops revisa comments" si su contable comenta mucho.
Patología 3: latencia entre cambio y propagación. Contable edita a las 9am; T4 polling caliente cada 15 min lo detecta a las 9:15am; T3 lo procesa en su próximo ciclo (cada hora?). En cliente activo, la latencia hace que el ciclo de aprendizaje sea de horas, no minutos. Aceptable trade-off por el coste de polling más alto.
Patología 4: el contable no edita en Holded pero edita el plan en dashboard Intelia. Las dos rutas (auto-Holded con T4 + dashboard) generan evidence de tipos distintos. Si el cliente migra de una a otra (porque empezó en dashboard y ahora confía en auto), T4 puede no captar correlaciones bien. Mitigación: tracking unificado de "edition events" sin importar el canal.
Cadencia óptima. ¿15 min caliente es el balance correcto entre frescura y coste? Necesita medición real.
¿Comments del contable en Holded se procesan inmediatamente o en batch? Inmediato = latencia baja pero más calls LLM. Batch = más eficiente, peor para evidencias urgentes (criterion_change que afecta a plans en flight).
Política de retención de signals históricos. ¿Cuánto tiempo guardamos EditSignal? Propuesta: 12 meses caliente, archivar.
¿T4 puede inferir intención por sólo el patrón de cambios? Si el contable cambia 6209 → 6231 en 10 documentos del mismo proveedor sin comentar, ¿T4 emite CRITERION_CHANGED automáticamente? Sí, probablemente. Threshold + signal específico.
Reverse-sync de comentarios sistémicos (no atribuibles a un documento). Si el contable deja un comment a nivel cliente "a partir de ahora todo este proveedor a 6210", ¿T4 lo capta? Probablemente requiere monitoreo de surfaces distintas en Holded.
Cómo se filtran los cambios hechos por Intelia misma vs por el contable. Holded a veces no atribuye autoría claramente. Workaround: hash del estado pre-ejecución de Intelia + diff. Si el diff matchea lo que Intelia esperaba escribir → es Intelia. Si difiere → es humano.
| Métrica | Target |
|---|---|
t4_polling_latency_p95 |
<30s (desde edición del contable hasta detección) en tier caliente |
t4_signals_emitted_per_day_per_team |
Variable. Si 0 días seguidos, alerta (sistema caído o cliente inactivo) |
t4_comment_extraction_confidence_avg |
>0.75. Si bajo, el extractor LLM no funciona bien |
t4_low_confidence_comments_per_week_per_team |
<5. Si alto, comments del contable son muy ambiguos |
t4_holded_api_calls_per_doc_monitored |
<0.2 (con cache eficiente) |
t4_systemic_change_detection_lag |
<1h para chart_of_accounts cambios |
t3-calcification.md — el principal consumidor de signals T4s1-ux-hybrid.md — surface complementario para evidence