diff --git a/src/content/ensayos/browser-use-navegacion-web-agentes.md b/src/content/ensayos/browser-use-navegacion-web-agentes.md new file mode 100644 index 0000000..a7b0876 --- /dev/null +++ b/src/content/ensayos/browser-use-navegacion-web-agentes.md @@ -0,0 +1,280 @@ +--- +title: "Browser-use: cuando tu agente necesita navegar la web como un humano" +description: "Browser-use es una biblioteca Python open source que convierte a cualquier LLM en un agente capaz de controlar un navegador real: hacer clics, rellenar formularios, extraer datos estructurados y completar tareas web complejas de principio a fin. Con más de 100K estrellas en GitHub, soporte para 15+ modelos de lenguaje y una arquitectura que combina Chromium via CDP con un bucle ReAct aumentado con visión, browser-use representa el estado del arte en agentes de navegación web." +fecha: 2026-06-21 +tags: ['agentes','web','computer-use','python'] +tipo: investigacion +estado: pendiente-revision +autor: "Alejandro de la Fuente" +--- + +# Browser-use: cuando tu agente necesita navegar la web como un humano + +Hay una categoría de tareas que todo agente de IA debería poder hacer pero casi ninguno hace bien: abrir un navegador, buscar información, rellenar un formulario, comparar precios entre tres sitios, extraer datos de una tabla y devolverlos estructurados. + +Los LLMs pueden razonar sobre texto. Pueden generar código. Pero no pueden hacer clic en un botón. No pueden escribir en un campo de texto de una web real, con JavaScript, popups de cookies, infinite scroll, captchas y redirecciones. Para eso necesitan un cuerpo. Y ese cuerpo, en el caso de la web, es un navegador. + +**Browser-use** es una biblioteca Python open source (MIT, ETH Zurich) que resuelve exactamente esto. Proporciona a cualquier LLM — OpenAI, Anthropic, Google, modelos locales via Ollama, o su propio modelo optimizado `ChatBrowserUse` — un navegador Chromium completo que el modelo controla paso a paso mediante un bucle de razonamiento y acción. El resultado: un agente que ve la pantalla, decide qué hacer, ejecuta acciones reales (click, type, scroll, navigate, extract) y repite hasta completar la tarea. + +Este artículo explora qué es, cómo funciona por dentro, cómo empezar con código real, qué lo diferencia de alternativas como Computer Use de Anthropic o CUA de OpenAI, y dónde están sus límites. + +--- + +## El problema: la web no es una API + +La mayoría de herramientas de "browser automation" que conocemos — Selenium, Playwright, Puppeteer — resuelven el problema del control del navegador. Te dan una API para decir "ve a esta URL", "haz clic en este selector CSS" o "escribe este texto". Son deterministas y rápidas. + +El problema es que requieren que tú, el programador, sepas exactamente qué selectores usar, qué flujo seguir, qué esperar. Escribes un script para cada sitio web. Si el sitio cambia su DOM, tu script se rompe. No hay inteligencia: solo ejecución ciega. + +Los LLMs invierten esta ecuación: pueden entender una página web mirándola, razonar sobre qué acción tomar a continuación, y adaptarse a cambios en la interfaz. Son robustos donde los scripts tradicionales son frágiles. Pero necesitan un puente entre su razonamiento y el navegador real. + +Ese puente es browser-use. + +--- + +## Qué es browser-use + +Browser-use es una biblioteca que implementa el patrón **ReAct** (Reasoning + Acting) sobre un navegador Chromium controlado mediante el protocolo **CDP** (Chrome DevTools Protocol). Su arquitectura es: + +``` +Task → Agent (LLM) → Browser Session (CDP) → Web Page + ↑ | + └────────── Observation (DOM + screenshot) ────┘ +``` + +Cada paso del bucle: + +1. **Observación**: el agente captura el estado actual del navegador — DOM simplificado como árbol de accesibilidad, screenshot de la página, elementos clickables con índices numéricos. +2. **Razonamiento**: el LLM recibe esa observación junto con el historial de acciones previas y decide qué hacer. Puede razonar con visión (viendo el screenshot) o solo con el DOM textual. +3. **Acción**: el agente ejecuta una o varias acciones — navegar a una URL, hacer clic en un elemento, escribir texto, hacer scroll, extraer datos, descargar un PDF, esperar. +4. **Evaluación**: el LLM evalúa si la acción anterior tuvo éxito y si se acerca al objetivo. Si falla, reintenta con otra estrategia. + +El proyecto nació en ETH Zurich y ha acumulado más de 100.000 estrellas en GitHub. Su versión actual (0.13.x) incluye un núcleo reescrito en Rust para la parte de mayor rendimiento y un "beta agent" con una arquitectura renovada inspirada en coding agents. Soporta más de 15 proveedores de LLM, extracción de datos estructurados con Pydantic, herramientas personalizadas, integración MCP, perfiles de navegador reales con cookies y autenticación, y despliegue en producción mediante sandboxes en la nube. + +--- + +## Instalación y primeros pasos + +La instalación es directa. Necesitas Python ≥ 3.11 y un entorno virtual: + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install browser-use +``` + +El paquete base pesa menos de 1 MB. Las dependencias de cada proveedor LLM (openai, anthropic, google-genai, groq, ollama) vienen incluidas, así que no necesitas instalar nada adicional para usar el modelo que prefieras. + +Para el modelo recomendado — `ChatBrowserUse`, el modelo propio de browser-use optimizado específicamente para tareas de navegación web — necesitas una API key gratuita de [cloud.browser-use.com](https://cloud.browser-use.com/new-api-key). La guardas en un `.env`: + +```bash +# .env +BROWSER_USE_API_KEY=bu-... +``` + +Y el agente más simple posible: + +```python +from browser_use import Agent, ChatBrowserUse +from dotenv import load_dotenv +import asyncio + +load_dotenv() + +async def main(): + llm = ChatBrowserUse() + task = "Find the number of stars of the browser-use repo on GitHub" + agent = Agent(task=task, llm=llm) + await agent.run() + +if __name__ == "__main__": + asyncio.run(main()) +``` + +Esto abre un navegador Chromium visible, navega a GitHub, busca el repositorio, lee el contador de estrellas y devuelve el resultado. Sin selectores. Sin lógica de scraping. El LLM "ve" la página y decide qué hacer. + +También puedes usar cualquier otro proveedor: + +```python +from browser_use import Agent, ChatOpenAI + +agent = Agent( + task="Busca en Google los 3 frameworks web más populares de Python en 2026", + llm=ChatOpenAI(model="gpt-4.1-mini"), +) +await agent.run() +``` + +O Anthropic: + +```python +from browser_use import Agent, ChatAnthropic + +agent = Agent( + task="Fill this form: https://httpbin.org/forms/post with test data", + llm=ChatAnthropic(model="claude-sonnet-4-0", temperature=0.0), +) +await agent.run() +``` + +O Gemini de Google (con tier gratuito generoso): + +```python +from browser_use import Agent, ChatGoogle + +agent = Agent( + task="Go to Hacker News and tell me the top 5 stories", + llm=ChatGoogle(model="gemini-3-flash-preview"), +) +await agent.run() +``` + +--- + +## Extracción de datos estructurados: el caso práctico real + +El escenario más común en el mundo real no es "haz clic aquí y allá" sino "entra en estas tres webs, busca este producto, extrae precios y características, y devuélvemelo en JSON". + +Browser-use lo resuelve con `output_model_schema`, que toma un modelo Pydantic y garantiza que el agente devuelva los datos con esa estructura: + +```python +from pydantic import BaseModel, Field +from browser_use import Agent, ChatBrowserUse, Browser + +class ProductListing(BaseModel): + title: str = Field(description="Product title") + price: float = Field(description="Price as number") + source: str = Field(description="Source website") + url: str = Field(description="Full URL to listing") + condition: str | None = Field(None, description="New, Used, Refurbished") + +class PriceComparison(BaseModel): + search_query: str + listings: list[ProductListing] + +async def compare_prices(item: str = "Used iPhone 12"): + llm = ChatBrowserUse(model="bu-2-0") + + task = f""" + Search for "{item}" on eBay, Amazon, and Swappa. + For each site, extract 2-3 listings with title, price, source, URL, and condition. + """ + + agent = Agent( + task=task, + llm=llm, + output_model_schema=PriceComparison, + ) + + result = await agent.run() + + if result and result.structured_output: + comparison = result.structured_output + for listing in comparison.listings: + print(f"{listing.title} — ${listing.price} ({listing.source})") + +asyncio.run(compare_prices()) +``` + +El agente navega autónomamente los tres sitios, extrae la información relevante y la devuelve tipada y validada. Sin escribir un solo selector. Sin parsear HTML. El LLM + Pydantic hacen de capa de extracción universal. + +--- + +## Cómo funciona por dentro + +El bucle principal del agente reside en `browser_use/agent/service.py` y sigue un patrón que ya es canónico en el ecosistema de agentes: + +1. **Planificación** (opcional, `enable_planning=True`): antes de ejecutar, el agente descompone la tarea en pasos. Si se estanca (`planning_replan_on_stall=3`), vuelve a planificar. + +2. **Paso del agente** (bucle principal): + - **Page extraction**: el DOM de la página se convierte a markdown mediante `markdownify`, priorizando elementos interactivos. Para modelos con visión, se incluye un screenshot. + - **Message manager**: gestiona el contexto del LLM, comprimiendo mensajes antiguos cuando el historial crece demasiado (`max_history_items`, `message_compaction`). + - **LLM call**: el modelo recibe el system prompt (que incluye la lista de acciones disponibles), el estado de la página, el historial de acciones y la tarea. Devuelve una o varias acciones. + - **Action execution**: las acciones se ejecutan secuencialmente contra el navegador via CDP. El agente soporta acciones múltiples por paso (`max_actions_per_step=5`): puedes rellenar cinco campos de un formulario en una sola llamada al LLM. + - **Evaluation + Judge**: el LLM evalúa su propia acción anterior. Un "judge" opcional (`use_judge=True`) puede hacer una evaluación independiente con otra llamada al modelo. + +3. **Detección de bucles** (`loop_detection_enabled=True`): si el agente repite las mismas acciones en una ventana de 20 pasos, se detecta el bucle y se fuerza un cambio de estrategia. + +4. **Fallback**: si el LLM primario falla (rate limit, error 5xx), un `fallback_llm` toma el relevo automáticamente. + +La novedad en la versión 0.13 es el **beta agent** (`from browser_use.beta import Agent`) que reescribe el núcleo en Rust y añade herramientas persistentes, bucles de recuperación inspirados en coding agents, y un espacio de acción más rico. Para la mayoría de casos, el agente clásico sigue siendo la opción recomendada. + +--- + +## Comparativa con alternativas + +Browser-use no es la única forma de darle un navegador a un agente. Las tres alternativas principales son: + +### Computer Use de Anthropic + +El **computer use tool** de Anthropic es una herramienta a nivel de API que permite a Claude controlar un ordenador: tomar screenshots, mover el ratón, hacer clic, escribir. Funciona mediante un bucle visión-acción: Claude recibe un screenshot, decide coordenadas (x,y) para el clic, y las devuelve a tu código para que las ejecutes en un entorno virtual (habitualmente un contenedor Docker con escritorio X11). + +**Diferencias clave:** +- **Nivel de abstracción**: Anthropic te da screenshots y coordenadas de píxel. Tú tienes que montar el entorno de ejecución. Browser-use te da el navegador completo gestionado. +- **Propósito**: Computer Use es genérico — puede controlar cualquier aplicación de escritorio. Browser-use está especializado en web. +- **Visión**: Computer Use funciona exclusivamente con screenshots. Browser-use puede funcionar con DOM textual + screenshots opcionales, lo que reduce drásticamente el consumo de tokens y la latencia. +- **Extracción de datos**: Browser-use incluye extracción estructurada con Pydantic. Computer Use requiere que implementes tu propio parsing. +- **Robustez web**: browser-use maneja iframes, popups, infinite scroll, formularios complejos y descargas de archivos. Computer Use trata la web como píxeles. + +**Cuándo usar cada uno**: Computer Use si necesitas controlar aplicaciones de escritorio más allá del navegador (Excel, un IDE, una herramienta interna). Browser-use si tu caso de uso es exclusivamente web. + +### CUA (Computer-Using Agent) de OpenAI + +CUA es el modelo que impulsa Operator, el agente de navegación web de OpenAI lanzado en enero de 2025. En marzo de 2025, OpenAI expuso CUA como herramienta dentro de su **Responses API**. + +**Diferencias clave:** +- **Modelo propietario cerrado**: CUA es un modelo específico de OpenAI (basado en GPT-4o) optimizado para interacción con interfaces. No puedes usar Claude, Gemini o Llama. Browser-use te deja elegir entre 15+ modelos. +- **Cloud-hosted**: CUA ejecuta el navegador en servidores de OpenAI. Tú no gestionas la infraestructura. Browser-use te da control total: puedes ejecutarlo local, en tu propio servidor, o en la nube de browser-use. +- **Precio**: CUA factura por uso de API + computación del navegador. Browser-use open source es gratuito; solo pagas las llamadas al LLM que elijas. +- **Madurez**: CUA está en sus primeras versiones. Browser-use lleva más de un año de desarrollo abierto, con benchmarks públicos, comunidad activa y casos de uso en producción documentados. +- **Open source vs propietario**: Browser-use es MIT. Puedes leer el código, modificarlo, contribuir. CUA es una caja negra. + +**Cuándo usar cada uno**: CUA si ya estás en el ecosistema OpenAI y quieres la opción más simple sin gestionar infraestructura. Browser-use si necesitas flexibilidad de modelos, control sobre el navegador, extracción estructurada avanzada, o no quieres depender de un solo proveedor. + +### Playwright / Selenium + LLM propio + +Una alternativa frecuente es montar tu propio bucle de agente usando Playwright o Selenium para el control del navegador y un LLM para la toma de decisiones. + +**Diferencias clave:** +- **Tiempo de desarrollo**: Implementar un bucle ReAct con gestión de contexto, detección de bucles, extracción de DOM, compresión de mensajes y fallbacks te llevará semanas. Browser-use lo trae resuelto. +- **Rendimiento**: browser-use 0.13 tiene un núcleo en Rust que acelera el parsing del DOM y la comunicación CDP. Tu implementación ad-hoc en Python probablemente será más lenta. +- **Mantenimiento**: los sitios web cambian. Browser-use tiene una comunidad que reporta y corrige edge cases continuamente. Tu solución casera tendrás que mantenerla tú solo. +- **Flexibilidad**: tu solución te da control total sobre cada decisión de diseño. Browser-use te da una arquitectura probada pero con menos libertad en los detalles internos. + +--- + +## Limitaciones + +Browser-use es impresionante pero no es magia. Estas son sus limitaciones reales: + +**Dependencia del LLM.** La calidad del agente es tan buena como el modelo que lo impulsa. Con modelos pequeños (Llama 3 8B, Gemma), la tasa de éxito baja drásticamente en tareas complejas. El equipo recomienda `ChatBrowserUse` (modelo optimizado) o modelos frontier (Claude Sonnet 4, GPT-5, Gemini 3 Pro) para obtener resultados fiables. + +**Coste por tarea.** Cada paso del agente consume tokens. Una tarea compleja de 10-15 pasos con screenshots puede consumir entre 50K y 200K tokens. A precios de API actuales, eso son entre $0.15 y $2 por tarea. Para automatización a escala, el coste se acumula. + +**Latencia.** Cada paso implica: capturar estado del navegador → llamar al LLM → ejecutar acciones. Con modelos rápidos (Gemini Flash, Groq) un paso puede tomar 2-3 segundos. Con modelos más lentos (Claude Opus), 8-12 segundos. Una tarea de 10 pasos son entre 20 segundos y 2 minutos. + +**Fragilidad ante cambios extremos del DOM.** Aunque los LLMs son más robustos que los selectores CSS, un rediseño completo de un sitio web puede confundir al agente. La visión ayuda, pero no es infalible. + +**Captchas y anti-bot.** Browser-use no incluye resolución de captchas en su versión open source. Para sitios con protección anti-bot (Cloudflare, DataDome), el equipo recomienda usar su cloud con proxies rotativos y navegadores stealth. + +**No determinismo.** Dos ejecuciones de la misma tarea pueden tomar caminos diferentes y producir resultados ligeramente distintos. Para flujos críticos donde necesitas garantías, esto es un problema. + +**Autenticación.** Aunque browser-use soporta perfiles de Chrome reales (con tus cookies y sesiones), la autenticación OAuth con 2FA sigue siendo un punto débil. El equipo recomienda usar `@sandbox` con sincronización de perfil para manejar auth en producción. + +--- + +## Conclusión + +Browser-use resuelve un problema real y lo resuelve bien: darle a un LLM la capacidad de interactuar con la web como lo haría un humano. No es un wrapper alrededor de Playwright con un prompt bonito. Es una arquitectura completa que gestiona el bucle de observación-razonamiento-acción, la extracción de datos estructurados, la gestión de contexto, los fallbacks y la detección de bucles. + +El ecosistema de "computer use" está fragmentado en tres enfoques: Anthropic te da la herramienta de bajo nivel (screenshots + coordenadas), OpenAI te da el modelo cerrado llave en mano (CUA), y browser-use te da la biblioteca open source que funciona con cualquier modelo. Cada uno tiene su sitio, pero para quien construye agentes web en Python, browser-use es la opción más flexible, madura y productiva hoy. + +La web no es una API. Pero con browser-use, tu agente puede fingir que sí lo es. + +--- + +**Recursos:** +- [Repositorio en GitHub](https://github.com/browser-use/browser-use) +- [Documentación oficial](https://docs.browser-use.com) +- [Browser Use Cloud](https://cloud.browser-use.com) +- [Benchmarks abiertos](https://github.com/browser-use/benchmark) diff --git a/src/content/ensayos/pdf-a-knowledge-graph-markitdown.md b/src/content/ensayos/pdf-a-knowledge-graph-markitdown.md new file mode 100644 index 0000000..f9fcd50 --- /dev/null +++ b/src/content/ensayos/pdf-a-knowledge-graph-markitdown.md @@ -0,0 +1,283 @@ +--- +title: "De PDF corporativo a knowledge graph: markitdown + GraphRAG en 3 pasos" +description: "De documentos inaccesibles a grafos de conocimiento consultables: cómo usar markitdown para extraer texto de PDFs y GraphRAG para indexarlo, construir un knowledge graph y hacer consultas globales sobre todo el corpus." +fecha: 2026-06-21 +tags: + - rag + - knowledge-graph + - graphrag + - documentos + - microsoft +tipo: investigacion +estado: pendiente-revision +autor: Alejandro de la Fuente +--- + +# De PDF corporativo a knowledge graph: markitdown + GraphRAG en 3 pasos + +Hay dos problemas que se repiten en casi todos los proyectos de IA que trabajan con datos reales: los documentos están en formatos inaccesibles y los pipelines de RAG tradicionales no entienden la imagen completa. + +El primer problema lo conoce cualquiera que haya intentado meter PDFs corporativos en un pipeline de LLMs. La mayoría de las herramientas de extracción producen texto sucio — tablas mal parseadas, saltos de página que rompen párrafos, contenido incrustado en imágenes que simplemente desaparece. El resultado es un LLM que responde con alucinaciones porque nunca vio la información relevante. + +El segundo problema es más sutil pero igual de grave. El RAG vectorial tradicional — ese que trocea documentos, los mete en embeddings y recupera fragmentos por similitud semántica — funciona para preguntas locales ("¿cuánto cuesta el producto X?"), pero falla estrepitosamente con preguntas globales ("¿cuáles son las tendencias principales en todos los informes trimestrales del último año?"). Como bien explica el paper fundacional de Microsoft Research, *From Local to Global: A Graph RAG Approach to Query-Focused Summarization* (arXiv:2404.16130), este tipo de preguntas requieren un enfoque completamente distinto. + +En este artículo voy a mostrar cómo combinar dos herramientas de Microsoft — `markitdown` y `GraphRAG` — para construir un pipeline que va desde un PDF corporativo hasta un knowledge graph consultable. Todo con código real, resultados reales, y sin maquillar las limitaciones. + +## ¿Qué es markitdown? + +`markitdown` es una utilidad Python de Microsoft (parte del ecosistema AutoGen) que convierte archivos de múltiples formatos a Markdown. Su propósito explícito es preparar documentos para ser consumidos por LLMs y pipelines de análisis de texto. Soporta: + +- **PDF** (vía pdfminer.six + pdfplumber) +- **Word** (.docx), **PowerPoint** (.pptx), **Excel** (.xlsx, .xls) +- **HTML**, CSV, JSON, XML +- **Imágenes** (metadatos EXIF y OCR vía LLM) +- **Audio** (transcripción de voz) +- **EPUB**, ZIP, URLs de YouTube +- Formatos adicionales vía plugins + +La filosofía es simple: Markdown está lo suficientemente cerca del texto plano para ser eficiente, pero conserva suficiente estructura (encabezados, listas, tablas, enlaces) para que los LLMs — que "hablan" Markdown de forma nativa — entiendan la jerarquía del documento. + +La instalación básica es inmediata: + +```bash +pip install 'markitdown[pdf]' +``` + +Y el uso desde Python no puede ser más directo: + +```python +from markitdown import MarkItDown + +md = MarkItDown() +result = md.convert("documento.pdf") +print(result.text_content) +``` + +También tiene CLI: + +```bash +markitdown documento.pdf -o salida.md +``` + +## Paso 1: Convertir PDF a Markdown + +Para esta prueba usé el paper original de GraphRAG: *From Local to Global: A Graph RAG Approach to Query-Focused Summarization*. Un PDF académico de 14 páginas, a dos columnas, con fórmulas matemáticas, tablas, figuras y referencias. Exactamente el tipo de documento que tortura a la mayoría de los extractores de texto. + +```bash +wget -q -O graphrag_paper.pdf "https://arxiv.org/pdf/2404.16130" +``` + +### Resultados reales de la conversión + +```python +from markitdown import MarkItDown + +md = MarkItDown() +result = md.convert("graphrag_paper.pdf") +print(f"Longitud: {len(result.text_content)} caracteres") +# Longitud: 96915 caracteres +``` + +El archivo Markdown generado tiene 1,348 líneas y 96,915 caracteres. A primera vista, es un éxito: todo el texto del paper está ahí. Sin embargo, al inspeccionar la salida, emergen las limitaciones reales: + +**Lo que funciona bien:** +- El texto se extrae en su totalidad — no hay pérdida de contenido +- Las tablas se preservan como tablas Markdown (con sintaxis de pipes) +- Las referencias y fórmulas matemáticas son legibles + +**Lo que falla:** + +1. **Concatenación de palabras.** En PDFs a dos columnas, las palabras de líneas adyacentes se fusionan sin espacios. El abstract del paper aparece como: + ``` + tousethemaximumnumberoftokens(unitsoftext)thatcanbeprocessedbytheLLM + atonce(Kuratovetal.,2024;Liuetal.,2023).InthecanonicalRAGsetup + ``` + +2. **Cero encabezados Markdown.** La salida no genera un solo `#`. Las secciones como "1 Introduction", "2 Related Work" o "3 GraphRAG Method" aparecen como texto plano, sin estructura jerárquica. Esto es crítico porque GraphRAG necesita entender la estructura del documento. + +3. **Artefactos de tabla en texto corrido.** pdfplumber a veces interpreta bloques de texto como tablas, generando pipes innecesarios: + ``` + | The use | of retrieval-augmented | | | generation | (RAG) | + | --------- | ---------------------- | --------- | --- | ---------- | ------- | + | tion from | an external | knowledge | | source | enables | + ``` + +4. **Pérdida de contexto visual.** Las figuras, gráficos y diagramas del paper simplemente desaparecen. No hay OCR de imágenes sin configurar el plugin `markitdown-ocr` con un `llm_client`. + +### Evaluación de calidad + +Para documentos empresariales típicos (informes de una columna, facturas, contratos), markitdown produce resultados excelentes. Para papers académicos a dos columnas, necesitas post-procesamiento: un paso de limpieza que reconecte palabras separadas y detecte estructura de secciones. + +La buena noticia es que markitdown ya incluye `graphrag-input` como dependencia opcional — Microsoft diseñó estas herramientas para funcionar juntas. + +## Paso 2: Indexar con GraphRAG + +[GraphRAG](https://github.com/microsoft/graphrag) es el sistema de Microsoft que construye un knowledge graph a partir de un corpus de texto para responder preguntas que requieren comprensión global. Su arquitectura funciona en cuatro fases: + +1. **Extracción de entidades y relaciones.** Un LLM procesa cada chunk de texto e identifica entidades (personas, organizaciones, conceptos, lugares) y las relaciones entre ellas. +2. **Construcción del grafo.** Las entidades se convierten en nodos y las relaciones en aristas, formando un knowledge graph del corpus completo. +3. **Detección de comunidades.** El algoritmo de Leiden particiona el grafo en comunidades jerárquicas de entidades relacionadas. +4. **Generación de resúmenes.** Un LLM genera resúmenes de cada comunidad, desde las hojas hasta la raíz, creando una descripción multinivel de todo el corpus. + +Cuando llega una consulta, GraphRAG hace map-reduce sobre los resúmenes comunitarios: en la fase *map*, cada resumen relevante genera una respuesta parcial; en la fase *reduce*, todas las parciales se combinan en una respuesta global. + +### Integración markitdown → GraphRAG + +La integración es natural porque GraphRAG espera texto como entrada. El pipeline mínimo es: + +```bash +# 1. Convertir PDFs a Markdown +mkdir -p input +for pdf in documentos/*.pdf; do + markitdown "$pdf" -o "input/$(basename $pdf .pdf).md" +done + +# 2. Crear proyecto GraphRAG +graphrag init --root ./mi_grafo + +# 3. Mover los archivos al directorio input de GraphRAG +cp input/*.md mi_grafo/input/ + +# 4. Indexar (construir el knowledge graph) +graphrag index --root ./mi_grafo + +# 5. Consultar +graphrag query --root ./mi_grafo \ + --method global \ + --query "¿Cuáles son los temas principales del corpus?" +``` + +**Requisito importante:** GraphRAG necesita acceso a un LLM (OpenAI, Azure OpenAI, o compatible). La configuración se define en `mi_grafo/settings.yaml`, donde especificas el modelo, endpoint y API key. + +Para esta prueba, la instalación de GraphRAG en el entorno fue problemática debido al tamaño de sus dependencias (onnxruntime ~18MB, litellm ~17MB, spacy con modelos de lenguaje, más de 80 paquetes transitivos). En un entorno con buena conectividad, la instalación es directa: + +```bash +pip install graphrag +``` + +> **Nota real de esta prueba:** La red inestable impidió completar la instalación de GraphRAG en el momento de escribir este artículo. Esto es un problema genuino que cualquiera puede encontrar al trabajar con stacks de IA pesados. La solución en producción es usar Docker, `uv` con mejor caché, o entornos pre-configurados. + +### Lo que GraphRAG hace con tu Markdown + +Cuando indexas documentos convertidos con markitdown, GraphRAG: + +1. **Chunkea** el texto en fragmentos manejables +2. **Extrae entidades** como "GraphRAG", "retrieval-augmented generation", "LLM", "knowledge graph", "Leiden algorithm" +3. **Establece relaciones** como `[GraphRAG] → USA → [LLM]`, `[GraphRAG] → particiona_con → [Leiden algorithm]` +4. **Agrupa** entidades en comunidades semánticas +5. **Genera resúmenes** de cada comunidad + +El resultado es un knowledge graph que entiende no solo qué dice cada documento, sino cómo se relacionan las ideas entre sí. + +## Paso 3: Consultar el grafo + +Una vez indexado, GraphRAG ofrece dos modos de consulta: + +### Local Search +Para preguntas factuales sobre entidades específicas. Similar al RAG vectorial tradicional, pero enriquecido con la estructura del grafo: + +```bash +graphrag query --root ./mi_grafo --method local \ + --query "¿Cómo construye GraphRAG las comunidades?" +``` + +### Global Search +La verdadera innovación. Para preguntas que requieren comprensión de todo el corpus: + +```bash +graphrag query --root ./mi_grafo --method global \ + --query "¿Cuáles son las principales contribuciones de este paper?" +``` + +En las evaluaciones del paper original, GraphRAG superó al RAG vectorial tradicional en *comprehensiveness* (47% más) y *diversity* (72% más) para preguntas de sensemaking global. La diferencia no es incremental — es cualitativa. + +## Limitaciones y realidades + +Este pipeline no es magia. Hay que ser honesto sobre lo que funciona y lo que no: + +**1. Calidad de extracción.** markitdown con PDFs académicos a dos columnas produce texto con concatenaciones. Necesitarás un paso de post-procesamiento: detectar palabras fusionadas (heuristicas de diccionario), eliminar artefactos de tabla, y reconstruir la jerarquía de secciones. + +**2. Coste de indexación.** GraphRAG hace múltiples llamadas al LLM durante la indexación: una por cada chunk para extraer entidades, una por cada comunidad para generar resúmenes, más las llamadas de consulta. Con GPT-4o y un corpus de 100 páginas, el coste puede superar los $10-20. Con modelos locales (Ollama + Llama 3), el coste es cero pero la calidad de extracción de entidades se degrada. + +**3. Volumen de dependencias.** La instalación conjunta de markitdown + GraphRAG requiere ~2GB de paquetes Python, incluyendo onnxruntime, spacy con modelos de lenguaje, y múltiples librerías de Azure. No es ligero. + +**4. Grafos vs embeddings.** GraphRAG no reemplaza al RAG vectorial — lo complementa. Para preguntas factuales simples, el RAG tradicional con embeddings sigue siendo más rápido y barato. GraphRAG brilla cuando necesitas entender patrones, tendencias y relaciones a través de todo el corpus. + +**5. Documentos que no son texto.** Si tu PDF tiene contenido importante en imágenes (diagramas, gráficos, capturas de pantalla), markitdown sin el plugin `markitdown-ocr` simplemente lo ignora. La solución es añadir: + +```python +from openai import OpenAI + +md = MarkItDown( + enable_plugins=True, + llm_client=OpenAI(), + llm_model="gpt-4o", +) +``` + +## El pipeline completo en producción + +Juntando todo, un pipeline robusto para producción se ve así: + +```python +import os +from pathlib import Path +from markitdown import MarkItDown + +# Fase 1: Conversión masiva +def convertir_documentos(entrada_dir, salida_dir): + """Convierte todos los PDFs en un directorio a Markdown.""" + md = MarkItDown() + salida_dir = Path(salida_dir) + salida_dir.mkdir(parents=True, exist_ok=True) + + for pdf in Path(entrada_dir).glob("*.pdf"): + print(f"Convirtiendo {pdf.name}...") + result = md.convert(str(pdf)) + output_path = salida_dir / f"{pdf.stem}.md" + output_path.write_text(result.text_content) + + print(f"Convertidos {len(list(salida_dir.glob('*.md')))} archivos.") + +# Fase 2: Limpieza post-conversión +def limpiar_concatenaciones(texto): + """Corrige palabras fusionadas típicas de PDFs a dos columnas.""" + import re + # Detectar camelCase artificial y separarlo + texto = re.sub(r'([a-z])([A-Z])', r'\1 \2', texto) + # Unir líneas partidas + texto = re.sub(r'(? output.md + +# Con OCR vía plugin +pip install markitdown-ocr +markitdown --use-plugins escaneado.pdf +``` + +### Puntos fuertes + +- **Cobertura de formatos inigualable**: de un PDF escaneado a un vídeo de YouTube, todo a Markdown. +- **Plugins**: ecosistema extensible para formatos adicionales. +- **OCR vía LLM Vision**: extrae texto de imágenes dentro de documentos. +- **MIT license**: uso sin restricciones. +- **Microsoft-backed**: 157K estrellas, mantenimiento activo por el equipo de AutoGen. + +### Puntos débiles + +- **No es un web scraper**: no navega la web, no ejecuta JavaScript, no maneja proxies. +- **Dependencias opcionales**: para formatos específicos necesitas instalar extras (`[pdf]`, `[pptx]`, `[all]`, etc.). +- **Limitaciones de OCR offline**: sin LLM, las imágenes dentro de PDFs no se procesan (solo metadata). + +--- + +## Tabla comparativa + +| Dimensión | Firecrawl | Crawl4AI | MarkItDown | +|-----------|-----------|----------|------------| +| **Tipo** | SaaS + self-hosted | Librería open source + Docker | Librería open source | +| **Licencia** | AGPL-3.0 / MIT SDKs | Apache 2.0 | MIT | +| **Lenguaje** | TypeScript | Python | Python | +| **Web scraping** | ✅ API gestionada | ✅ Browser automation total | ❌ (no es scraper) | +| **JavaScript rendering** | ✅ (gestionado) | ✅ (Playwright) | ❌ | +| **Markdown output** | ✅ Alta calidad | ✅ Configurable (raw, fit, BM25) | ✅ (desde documentos) | +| **JSON estructurado** | ✅ (vía LLM o schema) | ✅ (CSS/XPath o LLM) | ❌ | +| **Proxies gestionados** | ✅ | ❌ (manual) | N/A | +| **Anti-bot / CAPTCHA** | ✅ (Fire-engine) | ⚠️ (stealth mode, manual) | N/A | +| **Documentos (PDF, DOCX…)** | ✅ (PDFs web) | ❌ (solo web) | ✅ Todos los formatos | +| **Imagen/Audio → texto** | ❌ | ❌ | ✅ (LLM Vision + transcripción) | +| **Precio** | Desde 0 € (1K páginas/mes) | Gratis (+ cómputo/proxies) | Gratis | +| **Velocidad** | P95 3.4s (gestionado) | Depende de infraestructura | Instantáneo (local) | +| **Curva aprendizaje** | Baja (API REST) | Media-alta (configuración) | Muy baja | +| **Self-hosted** | ✅ (Docker, K8s, Helm) | ✅ (pip + Docker API) | ✅ (pip install) | +| **MCP / Agent skills** | ✅ Nativo | ✅ Docker API + MCP | ❌ | +| **Comunidad** | 136K ⭐ | 69K ⭐ | 157K ⭐ | +| **Uso ideal** | Producción, escalado, agentes | Control total, presupuesto ajustado, R&D | Documentos, archivos, complemento | + +--- + +## Recomendaciones por caso de uso + +### "Soy un desarrollador indie construyendo un agente" + +**Usa Firecrawl.** El tier gratuito (1.000 páginas/mes) es más que suficiente para prototipar. La API es trivial, el Markdown es excelente y no pierdes tiempo configurando navegadores. Si tu agente necesita scrapear documentos (PDFs de informes, por ejemplo), añade MarkItDown como complemento. + +```python +# Stack mínimo para un agente indie +from firecrawl import Firecrawl +from markitdown import MarkItDown + +fc = Firecrawl(api_key="fc-...") +md = MarkItDown() + +# Web → Markdown +web_content = fc.scrape("https://ejemplo.com", formats=["markdown"]) + +# PDF → Markdown +pdf_content = md.convert("informe_financiero.pdf") +``` + +### "Estoy en una empresa con restricciones de datos" + +**Usa Crawl4AI self-hosted.** Los datos nunca salen de tu infraestructura. El Docker server es robusto (v0.9 trajo hardening de seguridad), el browser pooling es eficiente, y tienes control total sobre proxies y autenticación. El coste real es ~100-300 €/mes en servidores + proxies. MarkItDown para los documentos internos. + +### "Necesito procesar cientos de miles de páginas al mes" + +**Firecrawl plan Standard o Growth.** A 72 €/mes por 100.000 páginas, el coste por página es de 0.0007 € — ridículo comparado con el tiempo de ingeniería que invertirías en mantener una infraestructura propia de ese calibre. La gestión de proxies, rate limits y anti-bot detection está incluida. + +### "Hago research y necesito extraer datos de papers, PDFs y webs académicas" + +**Crawl4AI + MarkItDown.** Crawl4AI para las webs académicas (muchas con poco JavaScript pero estructuras complejas de referencias). MarkItDown para los PDFs de papers (ArXiv, etc.). La flexibilidad de Crawl4AI para extraer tablas y citas con estrategias personalizadas es imbatible. + +### "Solo necesito convertir documentos a Markdown para mi RAG pipeline" + +**MarkItDown, sin dudarlo.** Es su razón de ser. Instalación en segundos, API mínima, cobertura de formatos bestial. Si además necesitas scrapear la web ocasionalmente, añade `httpx` + `beautifulsoup4` para páginas simples o Firecrawl para las complejas. + +--- + +## El stack ideal en 2026 + +Si tuviera que recomendar un stack completo para un agente de IA ambicioso, sería este: + +``` +Firecrawl (web scraping) + MarkItDown (documentos) + Crawl4AI (casos edge y self-hosted) +``` + +**¿Cuándo usar cada uno?** + +1. **Firecrawl** es tu primera opción para cualquier cosa que venga de la web. Si la URL está detrás de Cloudflare, tiene JavaScript pesado o necesitas crawl recursivo, Firecrawl lo resuelve en una llamada. + +2. **MarkItDown** es tu segunda herramienta. Todo archivo que llegue a tu agente (un PDF adjunto, un Excel de datos, un PowerPoint de un cliente) pasa por MarkItDown antes de llegar al LLM. + +3. **Crawl4AI** es tu plan B y tu laboratorio. Cuando necesitas control absoluto (extracción con JavaScript custom, scroll infinito, autenticación compleja) o cuando los datos no pueden salir de tu VPC. También es ideal si estás experimentando con estrategias de extracción que no caben en el modelo "one-shot" de una API. + +--- + +## Conclusión + +En 2026, el problema de "web a Markdown" está esencialmente resuelto. La cuestión ya no es _si puedes_ scrapear la web para tus agentes, sino _qué aproximación encaja mejor_ con tu contexto: + +- **Firecrawl** es la navaja suiza SaaS: calidad industrial, zero-ops, pricing predecible. Ideal para equipos que quieren construir producto, no infraestructura de scraping. + +- **Crawl4AI** es el motor que te montas tú: open source radical, control total, gratuito. Ideal para quienes necesitan soberanía de datos o tienen restricciones de presupuesto pero no de talento. + +- **MarkItDown** es el pegamento universal: convierte cualquier archivo en texto que tu LLM entiende. Complementa a cualquiera de los dos anteriores y resuelve el problema de "mis datos no están en la web". + +La web ya no es una barrera para los agentes de IA. Con estas tres herramientas, tu agente puede leer —y entender— prácticamente cualquier contenido digital. + +--- + +*¿Tienes experiencia con alguna de estas herramientas en producción? ¿Usas otro stack que no he mencionado? [Abre un issue en el repo del blog](https://github.com/codigosinsiesta/codigosinsiesta.github.io) y lo discutimos.*