Arquitectura de Inteligencia Evolutiva
Fases 21.5–40.12 — Sistema de skills auto-mejorante para Arc OS. Estado: Listo para producción. Sage Worker (mejora autónoma) + benchmarks (pruebas A/B) + Marketplace Discovery + búsqueda semántica NotebookLM.
Resumen
Arc OS opera una arquitectura de bots federados donde los child bots actúan como proxy de mensajes de usuario hacia Claude CLI. La Fase 21.1 introdujo el seguimiento de calidad (logs de ejecución, botones de feedback). La Fase 21.5 cierra el ciclo: el sistema ahora valida su propia salida, recuerda las correcciones, enfoca el contexto y propone mejoras de forma autónoma.
Siete pilares forman la capa de inteligencia:
Mensaje del Usuario
│
├─► Context Router ──► SKILLS_HINT (top 5 skills relevantes)
│
├─► Learnings ──► bloque LEARNINGS (correcciones pasadas)
│
▼
buildGsdPrompt() ──► Claude CLI ──► Respuesta
│
├─► Binary Evals ──► Notas de advertencia
│
└─► Quality Tracker ──► Métricas
│
Nightly Improve ──► Aprobación CEO
Pilar 1: Motor de Binary Evals
Módulo: shared/evals.ts
Archivos de spec: skills/<name>/<name>.evals.json
Qué hace
Cada skill puede declarar reglas de validación en un archivo JSON. Tras generar una respuesta, el motor de evals la comprueba contra todas las reglas aplicables. Los fallos producen notas de advertencia no bloqueantes que se añaden al mensaje de Telegram.
Tipos de reglas
| Tipo | Verifica | Ejemplo de uso |
|---|---|---|
string_contains |
La respuesta incluye una cadena literal | Verificar que la salida JSON tiene el campo "verdict" |
string_not_contains |
La respuesta NO incluye una cadena | Bloquear --force en instrucciones de git |
regex_match |
La respuesta coincide con un patrón regex | Asegurar que la auditoría del sistema menciona disk/RAM/CPU |
regex_not_match |
La respuesta NO coincide con un regex | Prevenir patrones de filtración de credenciales |
max_length |
Longitud de la respuesta <= N caracteres | Mantener la salida concisa |
min_length |
Longitud de la respuesta >= N caracteres | Garantizar respuestas sustanciales |
Niveles de severidad
- warning: Mostrado como
⚠️en la nota. Indica un problema de calidad. - info: Mostrado como
ℹ️en la nota. Informativo, no crítico.
Cómo afectan las evals a las métricas
Los resultados de las evals se registran junto a los eventos de calidad. Cuando el loop de mejora nocturna detecta un skill con baja tasa de éxito, los patrones de fallo de las evals ayudan a identificar la causa raíz sin necesidad de análisis con IA.
Ejemplo de archivo de evals
{
"version": 1,
"skill": "code-review",
"rules": [
{ "id": "cr-001", "name": "Must return JSON verdict", "type": "string_contains", "value": "\"verdict\"", "severity": "warning" },
{ "id": "cr-002", "name": "No console.log debug", "type": "regex_not_match", "pattern": "console\\.log\\(", "severity": "warning" }
]
}
Principio de diseño
Inspirado en el framework de evals de Anthropic's Skill Creator: aserciones declarativas sobre salidas, sin IA en el bucle de validación. El pass/fail binario elimina la ambigüedad.
Pilar 2: Context Router
Módulo: shared/context-router.ts
Fuente de datos: skills/_registry.json (campos triggers + keywords)
Qué hace
Antes de construir el prompt GSD, el router puntúa cada skill registrado contra el mensaje del usuario. Los 5 mejores coincidentes se inyectan como bloque SKILLS_HINT, orientando a Claude hacia las capacidades relevantes.
Algoritmo de puntuación
Para cada skill:
puntuación = 0
para cada trigger: si el mensaje contiene trigger → puntuación += 2
para cada keyword: si el mensaje contiene keyword → puntuación += 1
Ordenar por puntuación DESC → tomar los top 5
Las coincidencias de triggers puntúan más porque los triggers son señales de invocación explícitas (ej. "review", "deploy"). Las keywords ofrecen coincidencia semántica más amplia (ej. "OWASP", "Docker").
Por qué es consultivo, no filtrante
El router es solo consultivo. Claude CLI sigue cargando todos los skills via CLAUDE.md. Razones:
- Seguridad: El filtrado duro via
--allowedToolso mutaciones de symlinks puede romper la sesión si el router clasifica mal un mensaje. - Estabilidad: Sin mutaciones del sistema de archivos en un proceso en ejecución.
- Degradación elegante: Si el router falla, Claude sigue teniendo acceso completo a los skills.
Economía de la ventana de contexto
Sin el router, los 23 skills compiten por atención en el prompt. El bloque SKILLS_HINT le dice a Claude "enfócate en estos 3-5" — reduciendo la activación de skills irrelevantes y manteniendo las respuestas centradas. Es priming de contexto, no reducción de contexto.
Pilar 3: Reflect Loop (Learnings Persistentes)
Módulo: shared/learnings.ts
Almacenamiento: {PROJECT_CWD}/learnings.md
Qué hace
Cuando el CEO presiona "Fix It" o "👎", el sistema captura una regla de aprendizaje y la persiste en un archivo markdown. En cada mensaje posterior, los learnings acumulados se inyectan en el prompt GSD como bloque LEARNINGS.
Pipeline Evento → Aprendizaje
CEO presiona 🛠️ Fix It
│
├─► addLearning(source: "fixit", rule: "Fix requested for: <context>")
│
└─► projectLearnings recargados desde disco
CEO presiona 👎
│
├─► addLearning(source: "negative", rule: "Negative feedback on: <context>")
│
└─► qualityTracker.logFeedback(positive: false)
Formato del archivo de learnings
# Learnings
> Auto-generado. Inyectado en el prompt GSD al inicio de la sesión.
## Rules
- [2026-04-03T14:22:00Z] [fixit] Always use t-call for translations in Odoo QWeb
- [2026-04-03T15:10:00Z] [negative] Avoid sudo in deployment scripts
Inyección en el prompt
La función formatForPrompt() toma los learnings más recientes (hasta 2000 caracteres), los invierte (más nuevo primero) y los formatea como:
LEARNINGS (past corrections — follow these rules):
- Avoid sudo in deployment scripts
- Always use t-call for translations in Odoo QWeb
Principio de diseño
Inspirado en el Claude Reflect System: las correcciones no solo arreglan la respuesta actual — se convierten en reglas persistentes que previenen la regresión. El sistema construye "memoria inmune" con el tiempo.
Pilar 4: Karpathy Loop (Auto-Mejora Nocturna)
Módulo: scripts/nightly-improve.ts
Horario: Diariamente a las 03:00 UTC via cron
Estado: mcp-server/state/improvement-proposals.json
Qué hace
- Lee
config/bot_registry.jsonpara enumerar todos los child bots - Para cada hijo: lee
quality-metrics.jsonde su directorio de estado - Llama a
findUnderperformingSkills()— filtra skills donde:applied_count >= 3(tamaño mínimo de muestra)- Y
success_rate < 80%Ofeedback_negative > feedback_positive
- Lee
learnings.mdpara patrones de corrección relacionados - Genera propuestas basadas en plantillas (deterministas, sin IA involucrada)
- Envía informe de resumen al CEO via Telegram
- Envía tarjetas de propuesta individuales con botones inline de Aprobar/Rechazar
Flujo de aprobación del CEO
Script nocturno ──► Telegram: Tarjeta de Propuesta
│
┌────────────┼────────────┐
▼ ▼
✅ Aprobar ❌ Rechazar
│ │
Backup skill.md Marcar rechazado
como skill.v1.md en proposals.json
(máx. 3 versiones)
│
Marcar aprobado
en proposals.json
Versionado de skills
Al aprobar, el master bot crea copias de seguridad con versión:
skill.md→skill.v1.md(actual → backup)skill.v1.md→skill.v2.md(rotar)- Máximo 3 versiones de backup por skill
Principio de diseño
Inspirado en el AutoResearch Loop de Karpathy: modificar → verificar → mantener/descartar → repetir. La diferencia crítica: las propuestas son basadas en plantillas y requieren aprobación humana. Sin reescritura autónoma de skills — el CEO sigue siendo la autoridad final.
Fase 36+ — Capa Semántica NotebookLM
La Fase 36.3 añadió un quinto canal de feedback: Google NotebookLM como memoria semántica a largo plazo.
Eventos CRM (cierre de issue, actualización de wiki)
│
└─► POST fire-and-forget al bridge /sync
│
▼
NotebookLM Bridge (:19213)
├── SyncWorker (asyncio.Queue, 3 reintentos)
└── notebooklm-py → Google NotebookLM
│
▼
Notebook actualizado con nueva fuente
│
┌─────────────────────┤
▼ ▼
tool ask_notebooklm Neural Skill Generator
(chat Cloud PM, 15s) (POST /skills/generate, 30s)
│ │
▼ ▼
Respuesta semántica en Skill Markdown para
contexto del proyecto revisión + guardar (Fase 36.6)
La Fase 36.6 añadió el Neural Skill Generator: el CEO define un objetivo de extracción, el bridge consulta NotebookLM con un prompt estructurado de AI Architect, y devuelve un Rulebook Markdown estricto que puede guardarse como skill.
La Fase 36.7 expuso los notebooks en la UI del sidebar (punto de estado verde/rojo, contador de fuentes, enlace externo).
Pilar 6: Sage Worker (Fase 40.11c)
Módulo: shared/sage.ts
Disparador: POST /api/crm/sage/analyze o scripts/nightly-improve.ts
Qué hace
Sage es un motor autónomo de mejora de skills. Analiza los skills usando métricas de calidad + feedback del usuario (learnings), luego genera contenido mejorado concreto via API de Anthropic (modelo Haiku por eficiencia de costos). Los resultados se almacenan como skill_update_requests (PRs) en la base de datos para revisión del CEO.
Flujo
Solicitud de Análisis Sage
│
├─► Cargar skills activos desde DB (skillQueries)
├─► Cruzar referencias con métricas de calidad (readMetrics)
├─► Priorizar los de bajo rendimiento (findUnderperformingSkills)
│
▼ Para cada skill objetivo (máx. 5):
├─► Cargar contenido del skill + fork (si es de ámbito de proyecto)
├─► Cargar métricas de calidad (tasa de éxito, conteos de feedback)
├─► Cargar learnings relacionados (correcciones del usuario)
├─► Omitir si ya existe un PR pendiente
├─► Construir prompt (contenido + métricas + learnings)
├─► Llamar a la API de Anthropic (Haiku, timeout 30s)
├─► Parsear respuesta (NO_CHANGE = omitir)
├─► Insertar skill_update_request (proposed_by: 'sage')
└─► Ejecutar benchmark automático (Pilar 7) en nuevo PR
Por qué un endpoint CRM y no un worker
Sage necesita acceso directo a la DB (skillQueries, benchmarkQueries) — los workers corren como subprocesos sin conexión a DB. Sage no recibe mensajes del usuario; es un analizador en segundo plano cuya salida = filas de DB, no texto de chat.
Pilar 7: Aprobaciones Basadas en Datos — Benchmarks (Fase 40.11d)
Módulo: shared/sage.ts (runBenchmark())
DB: shared/migrations/005_skill_benchmarks.ts
API: POST /api/crm/sage/benchmark, GET /api/crm/skill-updates/:id/benchmarks
Qué hace
Antes de que el CEO apruebe un PR de Sage, el sistema demuestra que el cambio funciona. Genera 3 escenarios de prueba, ejecuta tanto el contenido antiguo como el nuevo del skill a través de ellos (prueba A/B ciega), y usa un juez LLM para determinar cuál es mejor. Puntuación combinada: eval_rules deterministas (60%) + juez LLM (40%).
Flujo del Benchmark
Ejecutar Benchmark (requestId)
│
├─► Cargar skill_update_request (current_content + proposed_content)
├─► Cargar eval_rules desde DB
│
▼ Generar 3 escenarios de prueba (Haiku)
│
▼ Para cada escenario:
├─► Ejecutar contenido antiguo del skill → old_output
├─► Ejecutar contenido nuevo del skill → new_output
├─► Aleatorizar orden A/B (prevención de sesgo por posición)
├─► Juez LLM puntúa ambos (1-10) + razón
├─► Puntuación determinista de eval_rules (si disponible)
├─► Puntuación combinada: evals 60% + LLM 40%
└─► Guardar en tabla skill_benchmarks
│
▼ Actualizar metadatos del PR
├─► veredicto: PASSED / FAILED / TIE
├─► improvement_pct: ((new_avg - old_avg) / old_avg * 100)
└─► BenchmarkBadge visible en el header del PR en el frontend
Frontend: Modo Battle
SkillEvolution.jsx → componente BenchmarkReport:
- Carga lazy por PR (se obtiene al expandir)
- Resumen: victorias/derrotas/empates/veredicto/improvement_pct
- Por escenario: Antiguo vs Nuevo lado a lado, estrella de ganador, puntuación/10, razón del juicio
- Botón "Run Benchmark" en la pestaña Content (azul cielo)
- Botón de ancla por PR para re-ejecutar benchmarks
Mapa de Archivos
shared/
├── evals.ts ← Pilar 1: Motor de Binary Evals
├── context-router.ts ← Pilar 2: Context Router (routeContextFromDb)
├── learnings.ts ← Pilar 3: Reflect Loop
├── quality.ts ← Extendido: findUnderperformingSkills()
├── sage.ts ← Pilares 6+7: Sage Worker + Benchmarks (runSageAnalysis, runBenchmark)
├── db.ts ← SQLite SSOT: skillQueries, benchmarkQueries, chatQueries
├── migrations/
│ ├── 004_skill_system.ts ← skills_global, forks, evolution_logs, update_requests
│ └── 005_skill_benchmarks.ts ← skill_benchmarks (resultados pruebas A/B)
├── crm-routes.ts ← CRM API: 55+ endpoints (Sage + benchmarks + evolución de skills)
└── ui_templates.ts ← Extendido: improvementProposal()
scripts/
├── nightly-improve.ts ← Pilar 4: Karpathy Loop (ahora usa Sage + DB)
└── migrate-skills-to-db.ts ← Migración nuclear: archivos → DB
clients/
├── arc-cli.ts ← ARC CLI (Fase 31.5): login, start, projects
└── knowledge-mcp.ts ← Obsoleto (Fase 38 → subcomandos CLI)
services/
└── notebooklm-bridge/ ← Pilar 5: búsqueda semántica NotebookLM (Fase 36.3)
├── main.py ← FastAPI (:19213), /query, /sync, /notebooks/init, /health
└── seed_knowledge.py ← Importación masiva de conocimiento existente
frontend/src/crm/pages/
└── SkillEvolution.jsx ← UI de dos paneles: explorador + detalle (Content/Evals/Evolution/PRs)
BenchmarkBadge, BenchmarkReport, Modo Battle, botón de análisis Sage
child-bot/bot.ts ← Integración: arranque + prompt GSD + notas de evals + /learnings
child-bot/ingest-watcher.ts ← Auto-ingest: fs.watch en raw/ → CRM inbox (Fase 28)
master-bot/bot.ts ← Integración: trigger cron nocturno de Sage