Skip to content

Repository files navigation

TRACE-IEEE

Trazabilidad de evidencia y Control Anti-alucinación para convocatorias IEEE. Sistema multiagente que abstrae convocatorias IEEE (URL, PDF o HTML) y produce entregables verificables: informe PDF, deck Marp, ideas de proyectos y correo ejecutivo.


Tabla de contenidos


Resumen

TRACE-IEEE es un sistema multiagente construido sobre LangGraph que recibe una convocatoria IEEE (URL, PDF o HTML), la rastrea, canonicaliza, le aplica OCR si es necesario, extrae evidencia trazable desde las fuentes oficiales, valida reclamos con un estricto modo anti-alucinación, y genera cuatro entregables:

  1. Informe PDF (desde Markdown vía WeasyPrint).
  2. Deck de presentación (Marp).
  3. Ideas de proyectos alineadas a la convocatoria (modo híbrido creativo + endurecedor conservador).
  4. Correo ejecutivo resumiendo la viabilidad.

Cada reclamo (Claim) de tipo fact debe estar respaldado por evidence_ids que apunten a una fuente official verificada. El sistema se detiene en una puerta de aprobación humana antes del empaquetado final: la revisión es por archivo, no por paquete, y solo se conserva la última versión activa de cada entregable.

Diseñado para presentación en el Congreso Colombiano de Computación (20CCC).


Características clave

  • Multiagente orquestado con LangGraph (app/graphs/orchestrator.py): 14 nodos en flujo lineal con puerta de aprobación interactiva (interrupt_before).
  • Trazabilidad de evidencia: cada Evidence incluye Span (start, end), category, confidence, verifier y source_id. El SourceManifest diferencia fuentes official vs external_contextual.
  • Anti-alucinación por diseño: hooks de validación que rechazan fact sin evidencia oficial, y un prior_errors SUMMARY que se inyecta en cada reintento del LLM.
  • Modos por agente (NODE_PHASE en app/schemas/models.py):
    • conservative → analistas y generadores
    • hybrid → solo ideas_generator (creativo)
    • neutral → infraestructura (intake, crawler, ocr, packager)
  • Human-in-the-loop: parada obligatoria en approval_gate con aprobación/rechazo por archivo, persistencia del historial de revisiones y errores previos.
  • Stack 100% OSS local: PostgreSQL 16, tesseract + ocrmypdf, WeasyPrint, Marp, Streamlit. Sin nube para archivos, sin dependencias propietarias cerradas.
  • LLM NVIDIA free tier con fallback automático a MockLLMClient si no hay API key configurada.
  • MCP servers por dominio para aislar reglas deterministas del razonamiento LLM.

Arquitectura

Pipeline de agentes

El grafo maestro se construye en app/graphs/orchestrator.py:26 y se compila con

workflow.compile(checkpointer=checkpointer, interrupt_before=["approval_gate"])
START
  │
  ▼
intake ──▶ crawler ──▶ canonicalizer ──▶ ocr
  │                                       │
  └─ pre_job_policy (hook)                ▼
                              evidence_extractor ──▶ analyst ──▶ compliance_verifier
                                                                       │
                                                                       ▼
                                                            ideas_generator (HÍBRIDO)
                                                                       │
                                                                       ▼
                                                            ideas_hardener (conservador)
                                                                       │
                                                                       ▼
                                              report_generator ──▶ deck_generator ──▶ email_generator
                                                                                              │
                                                                                              ▼
                                                                                       approval_gate ◀── interrupt_before
                                                                                              │
                                                                          ┌───────────────────┴────────────────────┐
                                                                          │ has_rejections?                           │
                                                                          ▼                                           ▼
                                                                     analyst                               packager ──▶ END
                                                                  (reintento)

Los 14 nodos (Agent enum en app/schemas/models.py:85):

# Nodo Fase Rol
1 intake neutral Descarga la entrada, crea SourceRef seed, aplica pre_job_policy
2 crawler neutral Recursión de enlaces hasta crawl_max_depth
3 canonicalizer neutral Normaliza HTML/PDF a CanonicalDoc con Sections
4 ocr neutral Aplica tesseract + ocrmypdf si enable_ocr=True
5 evidence_extractor conservative Extrae evidencia determinista (fechas, presupuestos)
6 analyst conservative Genera Claims tipo fact con retry anti-alucinación
7 compliance_verifier conservative Verifica deadlines, topes presupuestales, elegibilidad
8 ideas_generator hybrid LLM creativo para propuestas alineadas
9 ideas_hardener conservative Endurece ideas con validación presupuestal vía MCP
10 report_generator conservative Informe PDF desde templates/report.md
11 deck_generator conservative Deck Marp desde templates/marp.md
12 email_generator conservative Correo ejecutivo (templates/email.txt)
13 approval_gate neutral Pausa el grafo para revisión humana
14 packager neutral Empaqueta entregables finales

Hooks anti-alucinación

Middleware en app/hooks/ que se ejecuta antes/después de cada llamada al LLM:

Hook Archivo Responsabilidad
pre_job_policy pre_job_policy.py Valida la solicitud antes de iniciar el job
evidence_register evidence_register.py Indexa evidencias en evidence_index
claim_validator claim_validator.py Rechaza fact sin evidence_ids apuntando a official
grounding_guard grounding_guard.py Inyecta contexto de evidencia permitido al prompt
budget_guard budget_guard.py Valida topes presupuestales contra reglas IEEE
language_guard language_guard.py Refuerza idioma español
semantic_diff_guard semantic_diff_guard.py Evita regenerar entregables duplicados
artifact_pre_validator artifact_pre_validator.py Valida artefactos antes de guardarlos
prior_error_recorder prior_error_recorder.py Resume errores previos; se inyecta en el prompt siguiente

MCP servers por dominio

app/mcps/ expone servicios deterministas (sin LLM) accesibles por los agentes:

  • documents — parsing y resumen de documentos.
  • ieee_knowledge — glosario oficial (HAC, SIGHT, CS, R9, MGA), validación de temáticas prioritarias IEEE.
  • budget_rules — reglas matemáticas rígidas de presupuesto IEEE (ej. SIGHT ≤ $5,000 USD).
  • templates — plantillas de entregables (report.md, marp.md, email.txt, ideas.md).
  • audit — registro de auditoría por job.

Persistencia

  • PostgreSQL 16 (app/persistence/init.sql):
    • jobs(job_id, status, payload JSBON, created_at, updated_at) — estado completo del JobState serializado como JSON.
    • job_events(event_id, job_id, node, verdict, tokens_in, tokens_out, detail) — auditoría por nodo.
    • Índice idx_job_events_job(job_id, created_at).
    • Extensión pg_trgm para matching fuzzy.
  • Checkpoint LangGraph: CheckpointPostgres (app/services/checkpoint_postgres.py) con upsert, load, iter_jobs e insert_event.
  • Almacenamiento de archivos: Docker volume storage/ con subcarpetas sources/, output/, logos/, uploads/. Sin nube.

Stack tecnológico

Capa Tecnología
Lenguaje Python 3.11
Orquestación de agentes LangGraph + langchain-core
API FastAPI 0.115 + uvicorn
UI prototipo Streamlit ≥ 1.36
Validación Pydantic v2 + pydantic-settings
Base de datos PostgreSQL 16 (Docker)
OCR local tesseract + ocrmypdf + poppler
Generación PDF WeasyPrint
Generación deck Marp CLI
LLM NVIDIA free tier (langchain-nvidia-ai-endpoints), Llama 3.1 8B/70B
Gestor de paquetes uv (astral)
Lint/tipos ruff + mypy strict
Pruebas pytest + pytest-asyncio + respx
Cliente HTTP httpx + curl-cffi

Estructura del repositorio

abstraccion-convocatorias-ieee/
├── app/
│   ├── agents/prompts/          # Prompts de los agentes
│   ├── api/                     # FastAPI: routes/{jobs,approval,artifacts,health}
│   ├── docker/                  # Dockerfile (multistage) por servicio
│   ├── graphs/
│   │   ├── orchestrator.py     # Grafo maestro LangGraph
│   │   └── subgraphs/          # 14 nodos (uno por agente)
│   ├── hooks/                   # Middleware anti-alucinación
│   ├── mcps/                    # MCP servers por dominio
│   ├── persistence/            # init.sql + migraciones alembic
│   ├── schemas/models.py       # JobState + entidades (FromData, Claim, evidence...)
│   ├── services/               # llm_nvidia, storage, checkpoint, settings, worker, audit
│   ├── templates/              # Plantillas report.md, marp.md, email.txt, ideas.md + CSS
│   ├── tools/                  # Tools deterministas (fetch, ocr, pdf, html_parse, etc.)
│   └── ui/                     # Streamlit app + pages
├── tests/
│   ├── unit/                    # test_schemas.py, test_phase2_nodes.py
│   ├── integration/             # test_api.py (requiere Docker)
│   ├── regression/
│   └── fixtures/
├── storage/                     # Volume local (sources, output, logos)
├── docker-compose.yml           # postgres + api + ui + worker
├── pyproject.toml               # Dependencias + ruff + mypy + pytest
├── Makefile                     # atajos: install, test, lint, start, up, down
└── AGENTS.md                    # Memoria de operación del repo

Requisitos previos

  • Docker 24+ y Docker Compose v2 (recomendado)
  • NVIDIA API key (opcional pero recomendado). Sin ella el sistema usa MockLLMClient y produce output simulado.
  • Para desarrollo sin Docker:
    • Python 3.11
    • uv instalado (pip install uv)
    • tesseract + poppler instalados en el sistema (solo si usas OCR fuera de Docker)

Instalación

git clone <repo-url> trace-ieee
cd trace-ieee

# Copiar variables de entorno
cp .env.example .env

# Editar .env y rellenar NVIDIA_API_KEY (opcional pero recomendado)
# $EDITOR .env

Configuración

Variables clave en .env (ver .env.example para el listado completo):

Variable Default Descripción
POSTGRES_USER ieee Usuario PostgreSQL
POSTGRES_PASSWORD ieee_local Contraseña (solo desarrollo local)
POSTGRES_DB ieee_calls Base de datos
DATABASE_URL postgresql+psycopg://... DSN completa (sobreescribe las anteriores)
NVIDIA_API_KEY (vacío) API key NVIDIA free tier. Si está vacío, se usa MockLLMClient
NVIDIA_MODEL_SMALL meta/llama-3.1-8b-instruct Modelo para tareas rápidas
NVIDIA_MODEL_MEDIUM meta/llama-3.1-70b-instruct Modelo para análisis
NVIDIA_MODEL_LARGE meta/llama-3.1-70b-instruct Modelo para el analista
NVIDIA_MAX_TOKENS_PER_JOB 500000 Tope de tokens por job
CRAWL_MAX_DEPTH 1 Profundidad del crawler (0–3)
CRAWL_REQUEST_TIMEOUT 20 Timeout HTTP del crawler (segundos)
STORAGE_ROOT /app/storage Raíz del almacenamiento local
COVERAGE_MIN 0.70 Cobertura mínima exigida al analyst
LANG_EXPECTED es Idioma esperado para los outputs

Importante: en el contenedor, STORAGE_ROOT debe apuntar a un volumen Docker persistente (configurado en docker-compose.yml).


Uso

Opción 1: Docker Compose (recomendado)

# Levanta postgres, api, ui y worker en segundo plano
make start
# Equivalente: docker compose up -d

Esto expone:

Servicio URL Descripción
UI Streamlit http://localhost:8501 Interfaz web para crear, monitorear y aprobar jobs
API FastAPI http://localhost:8000 Endpoints REST /jobs, /healthz
Docs OpenAPI http://localhost:8000/docs Swagger UI autogenerada
PostgreSQL localhost:5432 Solo si necesitas inspeccionar la BD

Para detener y limpiar:

make down

Opción 2: Sin Docker (desarrollo local)

# 1. Instalar dependencias
make install
# Equivalente: uv sync --all-extras

# 2. Levantar PostgreSQL (alternativa: contenedor suelto)
docker run -d --name trace-ieee-pg \
  -e POSTGRES_USER=ieee -e POSTGRES_PASSWORD=ieee_local \
  -e POSTGRES_DB=ieee_calls -p 5432:5432 postgres:16-alpine

# 3. Apuntar DATABASE_URL al host local en .env:
#    DATABASE_URL=postgresql+psycopg://ieee:ieee_local@localhost:5432/ieee_calls

# 4. Arrancar API, UI y worker en terminales separadas:
make run-api      # uvicorn en 0.0.0.0:8000
make run-ui       # streamlit en 0.0.0.0:8501
make run-worker   # worker asíncrono que procesa jobs pendientes

Flujo de trabajo en la UI

  1. En la barra lateral izquierda, elige tipo de entrada: url, pdf o html.
  2. Pega la URL de la convocatoria IEEE o sube el archivo PDF/HTML.
  3. Ajusta la profundidad del crawler (0–3) y si quieres OCR habilitado.
  4. Pulsa Iniciar Trabajo. Se asigna un job_id y el worker arranca el pipeline.
  5. Monitorea el progreso en las pestañas:
    • Progreso — estado actual y eventos (JobEvent) por nodo.
    • FuentesSourceManifest con hashes y tipos.
    • Evidencias — tabla con Evidence extraída (categoría, confidence, verifier).
    • Entregables — links de descarga a report.pdf, deck.marp, ideas.md, email.txt.
    • Revisión — aprueba o solicita corrección (observaciones requeridas si corriges).
    • Errores previos — historial de PriorErrorSummary usados por el LLM.
  6. En Revisión, si apruebas → packager empaqueta final y el job queda packaged. Si rechazas → el flujo vuelve a analyst con tu observación registrada en prior_errors.

API REST

Para crear un job por URL:

curl -X POST http://localhost:8000/jobs \
  -H "Content-Type: application/json" \
  -d '{
        "source_type": "url",
        "source_uri": "https://www.ieee.org/about/sight.html",
        "options": {"crawl_max_depth": 1, "enable_ocr": true}
      }'
# => {"job_id": "job_a1b2c3...", "status": "intake"}

Para subir un PDF local:

curl -X POST http://localhost:8000/jobs/upload \
  -F "file=@./sight_call.pdf" \
  -F "source_type=pdf" \
  -F 'options={"crawl_max_depth": 0, "enable_ocr": true}'

Para consultar estado:

curl http://localhost:8000/jobs/<job_id>/status

Para aprobar/rechazar:

curl -X POST http://localhost:8000/jobs/<job_id>/approve/all \
  -H "Content-Type: application/json" \
  -d '{"decision": "approve", "observations": "Revisado y conforme"}'
# decisiones válidas: approve | correct | retry

Para descargar un artefacto:

curl -OJ http://localhost:8000/jobs/<job_id>/artifacts/report.pdf

Endpoints de la API

Método Ruta Descripción
GET /healthz Health check (verifica conexión a PostgreSQL)
POST /jobs Crea un job nueva (URL o local://...)
POST /jobs/upload Crea un job subiendo un archivo PDF/HTML
GET /jobs/{job_id}/status Devuelve el JobState completo serializado
POST /jobs/{job_id}/approve/{kind} Registra revisión humana (approve/correct/retry)
GET /jobs/{job_id}/artifacts/{kind} Descarga el artefacto report/deck/ideas/email

kind en el endpoint de descarga corresponde a los valores del enum DeliverableKind: report, deck, ideas, email.


Pruebas

make test
# Equivalente: uv run pytest -q tests/

Marquer de pytest estándar:

  • @pytest.mark.integration — requiere Docker levantado (postgres + api).

Para correr solo unitarios:

uv run pytest tests/unit -q

Para correr integración (requiere make start):

uv run pytest tests/integration -m integration -q

Verificación de calidad (lint y tipos)

Obligatorios antes de cualquier commit (definidos en AGENTS.md):

make lint
# Equivalente:
#   uv run ruff check .
#   uv run mypy app/schemas app/services app/hooks app/tools app/graphs app/api

Configuración relevante en pyproject.toml:

  • ruff con reglas E, F, I, B, UP, N, RUF, SIM, TID, PT y line-length=100.
  • mypy --strict con plugin pydantic.mypy.
  • Exclusiones: app/persistence/alembic/versions/ y tests/fixtures/.

Modelos LLM y presupuestos de tokens

app/services/llm_nvidia.py define tres tiers seleccionables por agente:

Tier Modelo default Uso típico
small meta/llama-3.1-8b-instruct Tareas rápidas y barreras deterministas
medium meta/llama-3.1-70b-instruct Generadores de entregables
large meta/llama-3.1-70b-instruct analyst_node (reasoning más exigente)
  • Fallback automático: si NVIDIA_API_KEY está vacío o la API falla, NvidiaLLMClient delega a MockLLMClient (output simulado). El sistema nunca crashea por falta de LLM.
  • Tope por job: nvidia_max_tokens_per_job = 500_000 (JobConfig).
  • Reintentos anti-alucinación: analyst_node reintenta hasta 3 veces, inyectando prior_errors acumulados en cada prompt.

Reglas de negocio no negociables

Tomadas de AGENTS.md:

  1. Ningún Claim de tipo fact sin evidence_ids apuntando a fuente official._forzada por claim_validator.py.
  2. Modos por agente:
    • conservative para analistas y todos los generadores.
    • hybrid solo para ideas_generator.
  3. Aprobación por archivo, no por paquete. La approval_gate pausa el grafo (interrupt_before) y el usuario decide por cada entregable.
  4. Solo se conserva la última versión activa de cada entregable (Deliverable.active = True para la última; las previas quedan superseded).
  5. Resumen de errores previos consultado antes de regenerar (prior_error_recorder + format_prior_errors).

Roadmap por fases

  • Fase 0 ✅ Esqueleto del repo, JobState, MCPs básicos, Dockerfile multistage.
  • Fase 1 🚧 En progreso (estado actual): pipeline lineal completo, hooks anti-alucinación, UI con revisión por job.
  • Fase 2 🔜 structured_output real para Claim/AnalysisResult desde el LLM (actualmente el analyst devuelve texto envuelto en un único claim).
  • Fase 3 🔜 AsyncPostgresSaver para escalar workers; semantic_diff_guard funcional; integración full de los MCPs con tools reales del crawler.
  • Fase 4 🔜 Empaquetado del paper para 20CCC con resultados de trazabilidad.

Contribución

  1. Lee AGENTS.md antes de tocar código — es la memoria operativa del repo.

  2. No cambies el stack sin confirmación del usuario (Python 3.11, FastAPI + LangGraph, PostgreSQL, OCR OSS).

  3. Respeta convenciones del repositorio:

    • Sin comentarios explicativos salvo petición explícita.
    • Tipado estricto (mypy --strict).
    • Imports absolutos desde app.*.
    • Nombres en inglés para código; literales de salida en español, sin emoticones.
  4. Antes de abrir un PR, ejecuta y que pasen:

    make lint
    make test
  5. Commits atómicos: un cambio lógico por commit, mensaje en presente imperativo.


Licencia

(Por definir — el autor debe agregar SPDX. Se recomienda MIT para máximo alcance académico.)

About

Sistema multiagente que abstrae convocatorias IEEE (URL, PDF o HTML) y produce entregables verificables: informe PDF, deck Marp, ideas de proyectos y correo ejecutivo.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages