ECCO
Estación de Captura
y Conocimiento Oficial
Introducción
ECCO (Estación de Captura y Conocimiento Oficial) es una plataforma de software desarrollada íntegramente en Python por PINGS, orientada a la gestión integral del ciclo documental de sesiones del Concejo Municipal. El sistema automatiza la cadena completa: grabación o importación de audio → transcripción ASR → diarización de hablantes → análisis semántico con LLM → generación de actas oficiales → búsqueda de conocimiento institucional.
El diseño prioriza la soberanía tecnológica: todos los modelos de IA corren localmente (Ollama, faster-whisper, pyannote), sin dependencia de APIs externas de pago ni envío de datos sensibles a terceros.
1.1 Objetivos del sistema
Reducir de 8–12 horas (transcripción manual) a menos de 30 minutos el tiempo de elaboración de un acta de sesión estándar de 2 horas.
Registrar cada intervención, voto y asistencia vinculada al concejal correspondiente, con evidencia de audio alineada al texto.
Motor de búsqueda híbrido (FTS5 + vectorial) sobre todo el archivo histórico de sesiones y la base legal municipal.
Operación 100% offline sobre hardware institucional. Sin dependencias de servicios cloud. Datos almacenados localmente en SQLite + ChromaDB.
1.2 Alcance funcional
| Módulo | Funcionalidad | Estado |
|---|---|---|
| Grabación en vivo | Captura multi-canal, pausa/resume, nivel dB en tiempo real | Implementado |
| Importación de audio | MP3, WAV, M4A, OGG, FLAC, AAC | Implementado |
| Transcripción ASR | Whisper large-v3 vía faster-whisper, CUDA int8 | Implementado |
| Diarización | pyannote/speaker-diarization-3.1 | Implementado |
| Análisis LLM | Temas, resumen, asistencia, votaciones, sentimiento | Implementado |
| Generación de acta | Borrador completo con estilo institucional | Implementado |
| Exportación | DOCX (python-docx) + PDF (reportlab) | Implementado |
| Búsqueda híbrida | FTS5 + ChromaDB + reranking | Implementado |
| Base legal RAG | Legislación municipal con RAG contextual | Implementado |
| Perfiles concejales | CRUD, estadísticas, fotos | Implementado |
| Dashboard analytics | KPIs, gráficos Plotly interactivos | Implementado |
| OCR de PDFs | pytesseract para documentos escaneados | Implementado |
1.3 Restricciones y supuestos de diseño
- Requiere GPU NVIDIA con soporte CUDA 12.x para rendimiento óptimo. Sin GPU, el pipeline corre en CPU con tiempos 5–10× mayores.
- La diarización con pyannote requiere token de aceptación de Hugging Face (licencia de modelos).
- La base de datos SQLite no está diseñada para concurrencia multi-proceso. Un único proceso Streamlit accede a la BD en cada instancia.
- El modelo Ollama (qwen2.5:7b) requiere ~6 GB de RAM disponible.
- El sistema no provee autenticación ni control de acceso en v1.0; se asume red interna institucional.
Arquitectura General
ECCO sigue una arquitectura de capas desacopladas: la capa de presentación (Streamlit) invoca servicios de dominio (src/) que a su vez acceden a los datos (SQLite + ChromaDB). El pipeline de procesamiento es asíncrono: se lanza en hilo separado mientras la UI muestra progreso.
Flujo de datos principal
El flujo unidireccional garantiza que ninguna capa superior escriba directamente en la capa de persistencia obviando los servicios de dominio. La UI solo llama funciones de src/; los servicios de dominio ejecutan toda la lógica de negocio y acceden a la BD a través de un módulo centralizado src/database/db.py.
sqlite3 con queries parametrizadas, sin ORM (SQLAlchemy, etc.). Esto mantiene el código simple, portable y permite aprovechar características propias de SQLite (FTS5, window functions, JSON1) sin capas de abstracción.
Componentes Detallados
3.1 Capa UI — Streamlit Multipage
La interfaz se implementa como una aplicación Streamlit multipage. El archivo raíz app.py define el layout global (sidebar, tema, KPIs del dashboard principal). Cada página en pages/ es un módulo independiente que se carga bajo demanda.
- KPIs: sesiones totales, horas transcritas, concejales activos
- Gráfico de sesiones por mes (Plotly)
- Últimas sesiones procesadas
- Estado del sistema (Ollama, GPU, espacio)
- Sidebar de navegación global
- Selector de dispositivo de audio (sounddevice)
- VU-meter en tiempo real (nivel dB)
- Grabación con pausa / resume / detener
- Importación drag-and-drop (MP3/WAV/M4A/OGG/FLAC/AAC)
- Formulario de metadatos (fecha, tipo, orden del día)
- Lanzamiento del pipeline con barra de progreso
- Listado paginado de todas las sesiones
- Filtros: fecha, tipo, estado, concejal
- Indicador de estado del pipeline por sesión
- Acceso rápido a transcripción y acta
- Eliminar sesión con confirmación
- Visor segmentado por hablante y timestamp
- Player de audio HTML5 sincronizado
- Edición inline de texto y asignación de hablante
- Resumen automático colapsable
- Lista de temas identificados
- Export de transcripción en TXT/JSON
- Generación bajo demanda con LLM
- Editor de texto enriquecido integrado
- Secciones editables: apertura, asistencia, orden del día, votaciones, cierre
- Export DOCX con estilos institucionales
- Export PDF con reportlab
- Historial de versiones del acta
- Barra de búsqueda unificada
- Toggle FTS / Vectorial / Híbrida
- Resultados con snippet de contexto
- Filtros por fecha, concejal, tipo
- Link directo a segmento de sesión
- Grid de perfiles con foto
- CRUD completo (crear/editar/eliminar)
- Stats por concejal: asistencia %, votaciones, palabras
- Timeline de participación
- Seed de 21 concejales 2024-2027
- Importación de documentos legales (PDF/DOCX)
- OCR para PDFs escaneados
- RAG: preguntas en lenguaje natural
- Citas con referencia al artículo/documento
- FTS5 sobre texto legal completo
- Asistencia histórica por concejal (heatmap)
- Distribución de votaciones (a favor/contra/abstención)
- Temas más frecuentes (wordcloud)
- Duración media de sesiones
- Sentimiento promedio por sesión
- Export de datos a CSV
3.2 Módulo de Inteligencia (src/intelligence/)
- Cliente HTTP para API REST de Ollama (localhost:11434)
- Métodos:
chat(),embed(),stream() - Retry automático con backoff exponencial
- Manejo de timeout configurable
- Verificación de disponibilidad del servicio
- Estrategia map-reduce para sesiones largas
- Chunk de ~2000 tokens con overlap de 200
- Fase map: resumen de cada chunk
- Fase reduce: síntesis final coherente
- Prompt especializado en lenguaje institucional
- Extracción de temas con LLM (qwen2.5:7b)
- Salida estructurada JSON: nombre, descripción, segmentos
- Deduplicación semántica de temas
- Vinculación tema ↔ sesión en BD
- Detección de frases de votación (regex + LLM)
- Extracción de resultado: a favor / en contra / abstención
- Identificación del asunto votado
- Parsing de votos individuales cuando se enuncian
- Confianza del resultado (0–1)
- Detecta llamado a lista en la transcripción
- Fuzzy-match nombre hablante → concejal en BD
- Infiere presente/ausente/excusado
- Umbral de confianza configurable
- Modelo pysentimiento/robertuito (español)
- Sentimiento por intervención: POS / NEU / NEG
- Agregación por sesión y por concejal
- Scores guardados en tabla
intervenciones
- Genera acta oficial completa desde datos de BD
- Secciones: encabezado, asistencia, desarrollo, votaciones, cierre
- Prompt con plantilla institucional parametrizable
- Modo borrador (automático) y modo revisado (manual)
- Retorno de HTML estructurado para editor
3.3 Módulo de Búsqueda (src/search/)
- ChromaDB como vector store persistente
- Modelo nomic-embed-text vía Ollama
- Chunking con overlap (500 chars / 50 overlap)
- Metadatos: sesion_id, concejal_id, timestamp, tipo
- Indexación incremental (solo nuevos documentos)
- SQLite FTS5 con tokenize=unicode61
- Índices virtuales:
intervenciones_fts,legislacion_fts - BM25 automático de FTS5
- Highlight y snippet integrados
- Búsqueda por frase exacta y booleana
- Fusión: 40% FTS5 + 60% vectorial
- Normalización de scores antes de fusión
- Reranking por relevancia combinada
- Deduplicación de resultados solapados
- Top-K configurable (default: 20)
- Retrieval-Augmented Generation para base legal
- Top-5 fragmentos relevantes como contexto
- Prompt con instrucción de citación
- Respuesta con referencias a artículo y documento
- Modo sin contexto (fallback a LLM puro)
3.4 Módulo de Audio (src/audio/)
- sounddevice con callback de buffer
- Threading: hilo de grabación + hilo de UI
- Pausa/resume sin cortar el archivo
- Nivel RMS en dB actualizado cada 100ms
- Guardado a WAV 48kHz 16-bit mono
- Detecta formato con python-magic
- Convierte a WAV 48kHz mono vía pydub
- Validación de duración mínima (30s)
- Copia a
data/audios/con UUID
- pydub: resample a 48kHz mono
- Normalización de volumen a -20 dBFS
- Recorte de silencios inicial y final
- Export a WAV antes de transcripción
- DeepFilterNet v3 para supresión de ruido
- Procesa en segmentos de 10s con overlap
- Opcional (configurable en config.yaml)
- Mejora WER hasta 15% en grabaciones ruidosas
- Lista todos los dispositivos de audio del sistema
- Detecta dispositivos de loopback (captura de sistema)
- Prueba de grabación de 2s por dispositivo
- Guarda preferencia en config
3.5 Módulo de Documentos (src/documents/)
- python-docx con estilos institucionales
- Plantilla base con logo, encabezado, pie
- Estilos: Título, Sección, Cuerpo, Tabla
- Tabla de asistencia formateada
- Tabla de votaciones con colores
- Numeración automática de páginas
- reportlab con platypus (flowables)
- Estilos definidos vía StyleSheet
- Header/footer por página
- Tabla de contenidos automática
- Firma digital visual (placeholder)
- pytesseract + Tesseract 5 (lang=spa)
- pdf2image para render de páginas
- Preprocesamiento: binarización, deskew
- Extrae texto estructurado por párrafo
- Inserta en tabla
legislacion
Pipeline de Procesamiento
El pipeline principal vive en src/transcription/pipeline.py y es el corazón del sistema. Transforma un archivo de audio crudo en datos estructurados listos para acta. Se ejecuta en un hilo de fondo (threading.Thread) para no bloquear la UI de Streamlit. El estado de progreso se persiste en la columna sesiones.estado_pipeline.
Estados del pipeline
| Estado | Descripción | Pantalla |
|---|---|---|
pendiente | Audio cargado, pipeline no iniciado | Botón "Procesar" |
procesando | Pipeline en ejecución (hilo activo) | Barra de progreso + log |
transcrito | Whisper + diarización completados | Transcripción disponible |
analizado | LLM completó análisis completo | Temas, resumen, asistencia |
acta_borrador | Listo para generar/editar acta | Botón "Generar Acta" |
acta_aprobada | Acta revisada y aprobada | Badge verde, export habilitado |
error | Fallo en algún paso del pipeline | Log de error, botón retry |
Modelo de Datos
La base de datos es un archivo SQLite 3 (data/ecco.db) con 12 tablas. Se inicializa con scripts/03_inicializar_bd.py que crea el esquema, los índices FTS5 y siembra los datos de concejales.
Tablas principales — descripción de campos clave
sesiones
| Campo | Tipo | Descripción |
|---|---|---|
id | INTEGER PK | ID autoincremental |
fecha | DATE | Fecha de celebración de la sesión |
tipo | TEXT | ordinaria | extraordinaria | especial | comision |
orden_dia | TEXT | JSON array de puntos del orden del día |
audio_path | TEXT | Ruta relativa al WAV normalizado |
duracion_seg | REAL | Duración total en segundos |
estado_pipeline | TEXT | Estado actual del pipeline (ver §4) |
resumen | TEXT | Resumen generado por LLM (markdown) |
num_concejales | INTEGER | Asistentes confirmados |
intervenciones
| Campo | Tipo | Descripción |
|---|---|---|
id | INTEGER PK | ID autoincremental |
sesion_id | INTEGER FK | → sesiones.id |
concejal_id | INTEGER FK NULL | → concejales.id (NULL si no identificado) |
speaker_label | TEXT | SPEAKER_00, SPEAKER_01… (pyannote raw) |
inicio_seg | REAL | Timestamp inicio en segundos |
fin_seg | REAL | Timestamp fin en segundos |
texto | TEXT | Transcripción del segmento |
confianza | REAL | Score WER inverso de Whisper (0–1) |
sentimiento | TEXT | POS | NEU | NEG (robertuito) |
sentimiento_score | REAL | Probabilidad del sentimiento asignado |
Tabla FTS5 — intervenciones_fts
-- Tabla virtual FTS5 sincronizada con intervenciones via triggers CREATE VIRTUAL TABLE intervenciones_fts USING fts5( texto, content='intervenciones', content_rowid='id', tokenize='unicode61 remove_diacritics 1' ); -- Trigger de sincronización al insertar CREATE TRIGGER intervenciones_ai AFTER INSERT ON intervenciones BEGIN INSERT INTO intervenciones_fts(rowid, texto) VALUES(new.id, new.texto); END;
embeddings_index actúa como índice relacional de los documentos almacenados en ChromaDB. Permite joins entre resultados vectoriales (que devuelven doc_id) y las tablas relacionales de SQLite sin tener que duplicar datos en el vector store.
Búsqueda Híbrida
El motor de búsqueda de ECCO combina dos paradigmas complementarios para maximizar la cobertura y relevancia de resultados sobre el corpus de sesiones del concejo.
SQLite FTS5 con BM25 ranker. Ideal para búsquedas exactas: nombres propios, artículos de ley, citas textuales. Tokenizador unicode61 con remoción de diacríticos permite encontrar "sesion" al buscar "sesión".
Fortaleza: velocidad (ms), precisión léxica.
Debilidad: no entiende semántica ("aprobaron" ≠ "votaron").
ChromaDB con embeddings nomic-embed-text (768 dim). Encuentra conceptualmente similar aunque las palabras sean distintas. Cosine similarity.
Fortaleza: semántica, sinónimos, paráfrasis.
Debilidad: puede traer resultados relacionados pero no relevantes para la query exacta.
Algoritmo de fusión y reranking
# src/search/hybrid_search.py — pseudocódigo simplificado def hybrid_search(query: str, top_k: int = 20) -> list[Result]: # 1. FTS5 search fts_results = fts_engine.search(query, limit=top_k * 2) # Normalizar scores FTS (BM25) a [0, 1] fts_norm = normalize_minmax([r.score for r in fts_results]) # 2. Vector search vec_results = embedder.query(query, n_results=top_k * 2) # Normalizar distancias coseno a similitud [0, 1] vec_norm = [1 - d for d in vec_results.distances] # 3. Fusión ponderada combined = {} for r, score in zip(fts_results, fts_norm): combined[r.id] = score * 0.40 for r, score in zip(vec_results, vec_norm): combined[r.id] = combined.get(r.id, 0) + score * 0.60 # 4. Deduplicar y ordenar por score combinado ranked = sorted(combined.items(), key=lambda x: x[1], reverse=True) # 5. Enriquecer con datos de BD (concejal, sesión, snippet) return enrich_results(ranked[:top_k])
Flujo de indexación
Chunking semántico
Las intervenciones se dividen en chunks de ~500 caracteres con overlap de 50 caracteres. Los chunks no cortan en medio de oración (se busca el último punto antes del límite).
Embedding con nomic-embed-text
Cada chunk se convierte en un vector de 768 dimensiones vía Ollama. El modelo nomic-embed-text está optimizado para recuperación de información en español.
Upsert en ChromaDB
Los vectores se almacenan en la colección intervenciones con metadatos: sesion_id, concejal_id, inicio_seg, tipo. ChromaDB persiste en disco en data/chroma_store/.
Registro en embeddings_index
Se registra en SQLite la correspondencia doc_id (ChromaDB) ↔ intervencion_id (SQLite) para poder hacer joins relacionales sobre resultados vectoriales.
RAG para Base Legal
El módulo rag_engine.py implementa Retrieval-Augmented Generation sobre la colección legislacion de ChromaDB. El flujo es:
- Usuario formula pregunta en lenguaje natural (ej: "¿Cuál es el quórum para sesiones extraordinarias?")
- Se generan embeddings de la pregunta con nomic-embed-text
- Se recuperan los 5 fragmentos más similares de la base legal
- Los fragmentos se inyectan como contexto en el prompt de qwen2.5:7b
- El LLM responde con cita al artículo y documento fuente
Requisitos de Instalación
7.1 Hardware recomendado
| Componente | Mínimo | Recomendado (ref.) | Notas |
|---|---|---|---|
| GPU | GTX 1060 6GB VRAM | GTX 1660 6GB / RTX 3060 | CUDA 12.1+, compute cap ≥6.1 |
| CPU | 4 cores / 3.0 GHz | 8 cores / 3.5 GHz | pyannote usa CPU intensivamente |
| RAM | 12 GB | 16 GB | Ollama necesita ~6 GB para qwen2.5:7b |
| Almacenamiento | 50 GB SSD | 200 GB SSD NVMe | 1h audio ≈ 200 MB WAV; acumulación histórica |
| Audio | Micrófono USB | Interfaz de audio 4ch | sounddevice compatible WASAPI/ALSA |
7.2 Software base (Windows)
| Componente | Versión | Fuente |
|---|---|---|
| Windows 10/11 Pro | 22H2+ | Microsoft |
| Python | 3.11.x | python.org |
| CUDA Toolkit | 12.1+ | developer.nvidia.com |
| cuDNN | 8.9+ | developer.nvidia.com |
| Ollama | 0.3+ | ollama.ai |
| Git | 2.40+ | git-scm.com |
| FFmpeg | 6.0+ | ffmpeg.org (en PATH) |
| Tesseract OCR | 5.3+ | UB Mannheim builds |
| Visual C++ Build Tools | 2022 | Microsoft (para compilar deps) |
7.3 Dependencias Python principales
# requirements.txt (extracto de dependencias críticas) streamlit==1.35.0 faster-whisper==1.0.3 whisperx==3.1.5 pyannote.audio==3.1.1 torch==2.3.0+cu121 chromadb==0.5.3 deepfilternet==0.5.6 python-docx==1.1.0 reportlab==4.2.0 plotly==5.22.0 sounddevice==0.4.7 pydub==0.25.1 pysentimiento==0.7.2 pytesseract==0.3.13 pdf2image==1.17.0 requests==2.32.0 pyyaml==6.0.1 python-magic-bin==0.4.14 # Windows
pip install torch==2.3.0+cu121 --index-url https://download.pytorch.org/whl/cu121. La versión CPU de PyTorch NO es suficiente para el rendimiento requerido.
Guía de Instalación — Windows
Instalar dependencias del sistema
Instalar Python 3.11, CUDA 12.1, cuDNN 8.9, Git, FFmpeg (agregar al PATH), Tesseract 5 (con paquete de idioma spa), Visual C++ Build Tools 2022.
# Verificar CUDA disponible nvcc --version python -c "import torch; print(torch.cuda.is_available())"
Instalar y configurar Ollama
# Descargar instalador desde ollama.ai e instalar # Luego descargar los modelos requeridos: ollama pull qwen2.5:7b ollama pull nomic-embed-text # Verificar que el servicio corre en localhost:11434 ollama list
Clonar el repositorio ECCO
git clone https://github.com/PINGS-DSinf/ECCO.git cd ECCO
Crear entorno virtual e instalar dependencias
python -m venv .venv .venv\Scripts\activate # PyTorch con CUDA primero pip install torch==2.3.0+cu121 torchaudio==2.3.0+cu121 ^ --index-url https://download.pytorch.org/whl/cu121 # Resto de dependencias pip install -r requirements.txt
Configurar pyannote (token Hugging Face)
Aceptar los términos de uso de los modelos pyannote/speaker-diarization-3.1 y pyannote/segmentation-3.0 en huggingface.co. Luego:
# En config.yaml, agregar: huggingface: token: "hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
Inicializar base de datos y estructura de directorios
python scripts/03_inicializar_bd.py
Este script crea los directorios data/, data/audios/, data/exportaciones/, data/fotos/, data/chroma_store/, logs/; inicializa data/ecco.db con todas las tablas y FTS5; y ejecuta el seed de 21 concejales 2024-2027.
Configurar config.yaml
Copiar config.example.yaml a config.yaml y ajustar los valores del municipio (ver §9).
Iniciar la aplicación
streamlit run app.py --server.port 8501 --server.address 0.0.0.0
Abrir en navegador: http://localhost:8501
Inicio automático con Windows Task Scheduler
# Crear tarea programada para inicio automático
schtasks /create /tn "ECCO" /tr ^
"C:\ECCO\.venv\Scripts\streamlit.exe run C:\ECCO\app.py" ^
/sc onlogon /ru SYSTEM /f
Configuración — config.yaml
Toda la configuración del sistema se centraliza en config.yaml en la raíz del proyecto. A continuación se documenta cada sección con sus campos y valores por defecto.
# ───────────────────────────────────────────────────────────── # config.yaml — ECCO v1.0 — Configuración completa # ───────────────────────────────────────────────────────────── municipio: nombre: "Concejo de Medellín" # Nombre que aparece en actas y UI ciudad: "Medellín" departamento: "Antioquia" pais: "Colombia" logo_path: "assets/logo_concejo.png" # Logo para cabecera de documentos periodo_actual: "2024-2027" num_concejales: 21 # Total de escaños quorum_ordinario: 11 # Mayoría simple para quórum quorum_calificado: 14 # 2/3 del total para temas especiales base_datos: path: "data/ecco.db" # Ruta al archivo SQLite backup_enabled: true backup_path: "data/backups/" backup_interval_days: 1 # Backup diario automático max_backups: 30 # Conservar últimos 30 backups ollama: base_url: "http://localhost:11434" # URL del servicio Ollama llm_model: "qwen2.5:7b" # Modelo LLM para análisis embed_model: "nomic-embed-text" # Modelo para embeddings timeout_seconds: 120 # Timeout por llamada LLM temperature: 0.1 # Temperatura (bajo = más determinista) max_tokens: 4096 # Máximo tokens de respuesta pipeline: denoising_enabled: true # Activar DeepFilterNet whisper_model: "large-v3" # large-v3 | medium | small whisper_compute_type: "int8" # int8 | float16 | float32 whisper_device: "cuda" # cuda | cpu whisper_language: "es" # Código ISO de idioma chunk_duration_seconds: 1800 # 30 min por chunk chunk_overlap_seconds: 10 diarization_enabled: true diarization_min_speakers: 2 diarization_max_speakers: 40 sentiment_enabled: true sentiment_batch_size: 32 busqueda: fts_weight: 0.40 # Peso del componente FTS5 vector_weight: 0.60 # Peso del componente vectorial top_k: 20 # Resultados máximos a devolver chunk_size: 500 # Caracteres por chunk para embeddings chunk_overlap: 50 chroma_path: "data/chroma_store" huggingface: token: "hf_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" # Token de acceso HF audio: sample_rate: 48000 # Hz — no cambiar (requerido por Whisper) channels: 1 # Mono target_dbfs: -20 # Normalización de volumen min_duration_seconds: 30 # Duración mínima de audio aceptada audio_path: "data/audios" documentos: export_path: "data/exportaciones" docx_template: "assets/plantilla_acta.docx" # Plantilla base Word pdf_font: "Times-Roman" # Fuente para PDFs reportlab watermark_borrador: true # Agregar marca "BORRADOR" en actas no aprobadas ui: titulo: "ECCO — Concejo de Medellín" favicon: "assets/favicon.ico" items_por_pagina: 20 tema_color: "#0D2E6E" # Color primario de la UI logs: path: "logs/" level: "INFO" # DEBUG | INFO | WARNING | ERROR max_bytes: 10485760 # 10 MB por archivo de log backup_count: 5
Adaptación a Otros Municipios
ECCO está diseñado para ser replicable en cualquier concejo municipal con cambios mínimos, todos concentrados en archivos de configuración y datos. No se requiere modificar código fuente para la mayoría de adaptaciones.
10.1 Cambios en config.yaml
| Campo | Qué ajustar |
|---|---|
municipio.nombre | Nombre del concejo (aparece en UI, encabezados y actas) |
municipio.num_concejales | Total de escaños del concejo destino |
municipio.quorum_ordinario | Mayoría simple según reglamento interno |
municipio.quorum_calificado | Mayoría calificada según reglamento interno |
municipio.logo_path | Logo institucional del concejo |
documentos.docx_template | Plantilla Word con membrete y estilos del concejo |
10.2 Reemplazar el seed de concejales
El script scripts/02_seed_concejales.py contiene los 21 concejales del periodo 2024-2027 de Medellín. Para otro municipio:
- Editar el array
CONCEJALESen el script con los nombres, partidos y comisiones del municipio destino. - Re-ejecutar:
python scripts/02_seed_concejales.py --reset - Cargar fotos en
data/fotos/con nombreconcejal_<id>.jpg
10.3 Plantilla de acta
El prompt de generación de acta en src/intelligence/acta_generator.py incluye una sección de configuración de plantilla. Los campos editables son:
ENCABEZADO_LEGAL: Texto de apertura conforme al reglamento del concejoFORMULA_APERTURA: Frase protocolar de apertura de sesiónFORMULA_CIERRE: Frase protocolar de cierreSECCIONES_ACTA: Lista de secciones obligatorias según normativa local
10.4 Base legal
Importar los documentos legales del municipio destino (Estatuto de Presupuesto, Reglamento Interno, Acuerdo Marco, POT, etc.) a través de la página Base Legal en la UI. El sistema los indexa automáticamente con FTS5 y vectores.
10.5 Adaptación de idioma / variante regional
Para concejos donde el español regional difiere significativamente (ej: España, México), se puede ajustar:
pipeline.whisper_language: código ISO (siempreespara español)- Los prompts de LLM en
src/intelligence/incluyen instrucción de idioma; editar si se usa variante castellana diferente. - El modelo
pysentimiento/robertuitoestá entrenado en español latinoamericano; para España, evaluar cambiar apysentimiento/bertweet-base-sentiment-analysis.
diarization_max_speakers en config al número real de concejales + 2 (para presidencia y secretaría). Esto mejora la calidad de la diarización al no buscar más hablantes de los que hay.
Limitaciones Conocidas y Roadmap
11.1 Limitaciones en v1.0
| Área | Limitación | Impacto | Mitigación |
|---|---|---|---|
| Diarización | Confusión entre hablantes con voz similar o micrófonos lejanos; error típico 10-20% | Medio | Editor manual de asignación de hablante por segmento |
| Transcripción ASR | WER ~5-8% con audio limpio; puede subir a 15-25% con ruido, acentos regionales o terminología técnica | Medio | Denoising activado + editor inline de corrección |
| Análisis LLM | qwen2.5:7b puede alucinar en votaciones con resultado ambiguo | Alto | Campo de confianza; revisión obligatoria antes de aprobación |
| Concurrencia | SQLite no soporta escrituras concurrentes; solo una instancia de pipeline a la vez | Bajo (uso típico) | Cola de procesamiento FIFO; bloqueo de UI durante pipeline activo |
| Autenticación | Sin sistema de usuarios ni control de acceso en v1.0 | Alto (seguridad) | Desplegar en red interna institucional únicamente |
| GPU requerida | Sin GPU NVIDIA CUDA, el pipeline es 5-10× más lento (CPU mode) | Medio | Usar modelo Whisper medium o small en instalaciones sin GPU |
| Idiomas | Optimizado para español latinoamericano; otros idiomas o variantes tienen menor rendimiento | Bajo (scope) | Configurable via whisper_language |
| Backup | Backup automático solo del archivo SQLite; ChromaDB no está incluido | Medio | Script de backup completo planificado para v1.1 |
11.2 Roadmap
- Sistema de autenticación (usuario/contraseña + roles: admin, secretario, concejal)
- Backup completo automatizado (SQLite + ChromaDB + audios)
- HTTPS con certificado autofirmado para acceso en red local
- Rate limiting en llamadas a Ollama
- Logging de auditoría de acciones
- Diccionario de términos institucionales para Whisper (hotwords)
- Fine-tuning de diarización con audios del concejo
- Identificación automática de hablante por voz (voice fingerprinting)
- Corrección ortográfica post-ASR con LanguageTool
- API REST para integración con sistemas externos (SIGAME, ORFEO)
- Notificaciones por email/Telegram al completar pipeline
- Módulo de comparativa entre sesiones (tendencias)
- Exportación a XML según estándar Akoma Ntoso (documentos legislativos)
- Arquitectura multi-tenant (varios concejos en misma instancia)
- Migración a PostgreSQL con pgvector (remplazo SQLite + ChromaDB)
- Despliegue Docker Compose
- Panel de administración centralizado para PINGS
- Modelo LLM propio fine-tuneado en actas colombianas