Tribunal de Contratación Pública
¿Se puede hacer búsqueda jurídica útil sobre miles de sentencias en PDF, sin pagar una base de datos legal?
FastAPI · SQLite FTS5 · BM25+TF-IDF · 5.995 causas · :8007
- Estado
- En vivo · scrape incremental
- Fuente de datos
- Tribunal de Contratación Pública (portal)
- Stack
- FastAPI · SQLite FTS5 · BM25+TF-IDF · pdfplumber
- Cadencia
- Incremental · solo lo nuevo
- Cobertura
- 5.995 causas · 1.716 sentencias · 1.531 PDF
- Infraestructura
- Raspberry Pi · $0 cloud
El problema
El Tribunal de Contratación Pública resuelve las impugnaciones contra las grandes licitaciones del Estado. Su jurisprudencia es pública, pero vive dispersa en un portal donde la sección de sentencias quedó desactualizada por años y cada fallo es un PDF suelto, sin buscador real por contenido. Quise responder una pregunta concreta: ¿cuánto cuesta — en plata y en código — tener esa jurisprudencia indexada al día y buscable por lo que dice cada sentencia, no solo por su número de rol?
Qué construí
Prototipé un monitor que scrapea las causas y sentencias del Tribunal, descarga los PDFs, extrae su texto y los deja buscables desde un dashboard. Hoy el índice tiene 5.995 causas (2008–2026), 1.716 sentencias y 58.314 trámites; de las sentencias, 1.531 tienen el PDF descargado y su texto indexado.
- check_circle Un scraper reconstruye la sesión del portal del Tribunal y recorre sus APIs internas para traer causas, trámites y sentencias, con sus metadatos (rol, carátula, materia, mecanismo, demandante, demandado, licitación asociada).
-
check_circle
Cada sentencia se descarga en PDF y se le extrae el texto con
pdfplumber, que es lo que después permite buscar por contenido y mostrar extractos del fallo. - check_circle La búsqueda es híbrida: combina BM25 (palabras) con TF-IDF y fusiona ambos rankings con Reciprocal Rank Fusion, además de FTS5 de SQLite. Hay también una búsqueda avanzada por campos (demandante, demandado, rol, licitación).
- check_circle Un scheduler vuelve a pasar de forma incremental para capturar las sentencias nuevas, sin re-descargar todo. Corre 24/7 sobre una Raspberry Pi, con $0 de costo cloud.
Cómo funciona
El backend (FastAPI) mantiene la base en SQLite con índices FTS5 y reconstruye en memoria el
índice de búsqueda al arrancar. La API expone el listado de jurisprudencia, el detalle de cada
sentencia con su texto y trámites, y el buscador con filtros por año, materia y mecanismo.
Apache hace de proxy hacia el servicio interno bajo /tribunal.
Decisiones técnicas
Búsqueda híbrida BM25 + TF-IDF con fusión de rankings
Combiné dos rankings léxicos y los fusioné con Reciprocal Rank Fusion en vez de quedarme con uno solo. Cada método falla distinto; fusionarlos da resultados más robustos sin el costo ni la dependencia de un modelo de embeddings.
Indexar el texto del PDF, asumiendo que es imperfecto
Extraigo con pdfplumber sabiendo que algunos fallos quedan con texto incompleto. El detalle siempre enlaza al PDF original como fuente de verdad: mejor un índice imperfecto y honesto que ningún buscador por contenido.
Scrape incremental, no full cada vez
Reconstruyo la sesión del portal y traigo solo lo nuevo. Mantiene el índice al día —la sección oficial de sentencias estuvo años desactualizada— sin re-descargar miles de PDFs en cada corrida.
Qué es real y qué es parcial
Es un prototipo, no una base de datos legal oficial — la fuente de verdad es siempre la sentencia original.
- verified Real: causas, sentencias y trámites vienen del propio portal del Tribunal; 1.531 sentencias tienen el PDF descargado y su texto indexado.
- schedule Parcial: la extracción de texto desde PDF no es perfecta —algunos fallos quedan incompletos— y la búsqueda es léxica, no entiende sinónimos jurídicos. El detalle siempre enlaza a la sentencia original.
- build Limitación heredada: si en el portal oficial algo falta o llega tarde, acá también. No reemplaza a la fuente oficial.
¿Quieres buscar en la jurisprudencia?
El buscador corre ahora mismo sobre la Pi.