De Docker a tu Cerebro Digital, el roadmap de IA para Linuxeros

Vistas: 2
0:00 / 0:00
De Docker a tu Cerebro Digital, el roadmap de IA para Linuxeros

Hace justo un año, cuando arranqué la Temporada 8, tenía un plan bonito, ordenadito, con sus meses temáticos. Mes 1: Docker y Podman a fondo. Mes 2: herramientas del día a día. Mes 3: automatización con systemd. Mes 4: Fish, Niri, el escritorio. Y durante los primeros meses, todo iba sobre raíles. Pero entonces, en abril, pasó algo que truncó cualquier planificación. Empecé a trastear con Ollama, con Open WebUI, con OpenCode. Y me di cuenta de algo que me incomodó profundamente: esto era mucho más interesante que lo que estaba haciendo. Así que, sin querer, sin planificarlo, la temporada se partió en dos mitades. La primera fue el plan original. La segunda fue un laboratorio de IA en directo. Y ahora, al abrir la Temporada 9, te voy a contar de dónde venimos, hacia dónde vamos y por qué este viaje que empieza hoy es lo más alucinante que hemos hecho en nueve temporadas de podcast.

Si empezaste la T08 esperando aprender a montar un servidor de Nextcloud con Podman y acabaste construyendo un knowledge graph con GraphRAG, bienvenido al club. A mí me pasó lo mismo. Y no voy a pedir disculpas por ello, porque lo que hicimos en la segunda mitad de la temporada pasada fue el preludio de lo que viene ahora. Lo que viene, oyente, es un cambio de paradigma.

El año que se partió en dos mitades

La Temporada 8 empezó en septiembre del año pasado, y durante los primeros cuatro meses cumplí el plan a rajatabla. Episodio 726 al 788, aproximadamente. Selfhosting, contenedores, Rust, secretos, Podman, Fish, Niri… todo en su sitio. Hubo episodios sobre Docker y Traefik, sobre Dockge y healthchecks, sobre escritorios como Niri y Kitty, sobre herramientas del día a día como fd, rg, fzf, bat, jc, jq y gron. Fue una primera mitad sólida, planificada, con una estructura clara: un tema por semana dentro de un mes temático.

Pero entonces llegó abril y el episodio 789, Tu propio Laboratorio de IA. Ese fue el punto de inflexión.

Recuerdo perfectamente la sensación. Estaba preparando el guión de un episodio sobre contenedores, otro más, y me topé con Ollama. Lo instalé por curiosidad, ollama run llama2, y en ese momento algo hizo clic. No era solo que el modelo respondiera preguntas. Era que podía ejecutarlo en mi máquina, sin internet, sin pagar a nadie, con mis datos. Y a partir de ahí, todo fue una carrera contrarreloj.

Ollama, llama.cpp, Open WebUI, OpenCode, Hermes Agent, MCPs, RAG, embeddings, skills, subagentes… cada semana salía un modelo nuevo, una herramienta nueva, un avance que te hacía preguntarte ¿y si esto lo conecto con aquello? Llegamos hasta el epílogo de la T08 con un asistente autónomo que tenía oídos (Whisper para escuchar), ojos (LLaVA para ver la pantalla) y manos (skills para ejecutar comandos), funcionando en producción con Quadlets y systemd timers.

La T08 no fue un fracaso del plan original. Fue una evolución natural. La IA local es el siguiente paso del selfhosting.

Lo que no funcionó: autocrítica necesaria

Pero no quiero venderte la moto. La T08 también tuvo sus sombras y creo que es justo reconocerlas. Porque si algo he aprendido en nueve temporadas es que la honestidad con los oyentes es lo único que mantiene un podcast vivo.

Primero: el ritmo fue errático. Los primeros meses tenían una estructura clara, un tema por semana dentro de un mes temático, pero a partir de abril se convirtió en una carrera contrarreloj. Cada episodio era lo último que he descubierto, sin tiempo para digerirlo. Y eso se notaba. Algunos episodios eran muy densos, otros demasiado ligeros. Faltó consistencia.

Segundo: la parte de Android prometida no llegó. En el opener de la T08 hablé de Android, de integración con el ecosistema móvil, de herramientas para Linuxeros que usan Android. Hicimos el episodio 771 sobre desarrollo remoto con tablet Android y el 781 sobre WhatsApp como PWA… pero no fue el bloque que prometí. Me quedé a medias. Y eso os lo debo.

Tercero: el giro hacia la IA fue emocionante pero desordenado. Cuando descubrí OpenCode y Hermes Agent, me sumergí tan rápido que no tuve tiempo de planificar una progresión lógica. Los episodios de MCPs, RAG, skills fueron surgiendo sobre la marcha. El resultado final fue bueno, pero el camino fue más caótico de lo que me habría gustado.

¿Por qué te cuento esto? Porque la Temporada 9 está diseñada para corregir exactamente esos tres problemas. Esta temporada tiene un plan, un hilo conductor claro, una progresión de menos a más y un destino concreto: GraphRAG.

El roadmap de la T09: un viaje en siete etapas

Vale, manos a la obra. La Temporada 9 tiene una estructura clara, como un viaje por carretera. Son siete etapas, cada una con su propósito, sus herramientas y sus episodios. Y todas llevan al mismo destino: un sistema de IA local que no solo responde preguntas, sino que entiende las relaciones entre tus datos.

Etapa 1: los recursos básicos (episodios 827 al 830)

Esto ya lo hemos empezado. El episodio 827 fue Recursos IA: el botiquín del explorador, una guía de las herramientas imprescindibles para cualquier Linuxero que quiera jugar con IA local. Ollama, Open WebUI, modelos, cuantización, lo básico para tener el laboratorio en pie. Hoy es el 828, este opener. Y el lunes que viene, episodio 829: Skills imprescindibles para tu agente IA. Porque tener un modelo es una cosa, pero si no le enseñas a usar herramientas (a buscar en internet, a leer archivos, a ejecutar comandos) es como tener un coche sin ruedas.

Etapa 2: skills y MCPs (episodios 831 al 834)

Aquí profundizamos en los MCPs (el Model Context Protocol), que es el estándar que está revolucionando la forma de conectar agentes con fuentes de datos. Veremos MCPs que no pueden faltar, workflows para el día a día y herramientas como alloy para gestionar tu infraestructura Docker.

Te avanzo que el MCP ha pasado de ser un experimento de Anthropic a un estándar con gobernanza formal. Hoy está soportado por Claude Desktop, ChatGPT, VS Code, Cursor, Open WebUI y OpenCode. Tiene grupos de trabajo oficiales para Agents, File Uploads, Security, Transports y una docena más. Es el USB-C de la IA, como lo llaman algunos, y no es una exageración.

Etapa 3: GraphRAG, el gran hito (episodios 835, 837 y 839)

Tres episodios dedicados exclusivamente a GraphRAG. Esto es el plato fuerte de la temporada. Te avanzo algo para que te hagas una idea: no es lo mismo buscar fragmentos de texto que entender relaciones entre conceptos. Y de eso, justamente de eso, va GraphRAG.

Pero no quiero adelantarme, porque la etapa 3 tiene su propio bloque más abajo.

Etapa 4: herramientas multimedia (episodios 841 al 847)

Whisper para transcripción local, TTS para darle voz a tu IA, visión artificial para que vea tu pantalla, y herramientas como ffmpeg, maim e ImageMagick para procesar audio, vídeo e imágenes. Tu asistente va a adquirir los sentidos que le faltan.

Etapa 5: orquestación y automatización (episodios 848 al 851)

Systemd timers, asyncio, just, CrewAI. Cómo hacer que tu IA trabaje mientras duermes, cómo orquestar tareas concurrentes, cómo montar un equipo de agentes especializados.

Etapa 6: proyectos finales (episodios 857 y 858)

El asistente que te conoce (un sistema completo que integra todo lo aprendido) y cómo ponerlo en producción con Quadlets.

Etapa 7: el futuro (episodios 859 y 860)

Mantenimiento, actualizaciones y una mirada a lo que viene después. Porque esto no acaba aquí, ni de lejos.

GraphRAG: de buscar fragmentos a entender relaciones

Voy a detenerme un momento en GraphRAG, porque es el corazón de esta temporada. Y quiero que entiendas por qué.

Cuando hicimos RAG en la T08 (episodios 798, 809, 819, 821), aprendiste a construir un sistema de búsqueda semántica. Coges tus notas, las partes en fragmentos (chunks), calculas embeddings, y cuando preguntas algo, el sistema busca los fragmentos más parecidos y se los pasa al modelo. Eso funciona, y funciona bien para muchas cosas. Pero tiene una limitación fundamental: no entiende relaciones.

Pongamos un ejemplo. Imagina que tienes en tus notas tres frases:

  • He configurado Traefik como reverse proxy para mis servicios.
  • Los contenedores de Docker se comunican a través de una red interna.
  • Para que Traefik vea los contenedores, necesitan estar en la misma red.

Con RAG clásico, si preguntas ¿por qué Traefik no ve mi contenedor?, el sistema te devuelve los tres fragmentos sueltos. Tú tienes que unir los puntos manualmente. El LLM recibe tres bloques de texto y hace lo que puede, pero la relación causal entre red interna, Traefik y contenedores no está explicita en ningún sitio.

Con GraphRAG, el sistema construye un grafo de conocimiento. Extrae entidades (Traefik, red interna, contenedores, volúmenes) y las relaciones entre ellas: Traefik necesita red interna, contenedores usan red interna, Traefik enruta a contenedores. Cuando preguntas lo mismo, el sistema recorre el grafo y te responde: Revisa que tu contenedor y Traefik estén en la misma red Docker. Si no lo están, Traefik no puede enrutar el tráfico. 😊

Esa es la diferencia. No es más información. Es mejor estructura.

Para que lo veas en acción, he preparado una demo completa con SQLite y NetworkX que construye un grafo de conocimiento con el stack del podcast. Te dejo el script aquí:

#!/usr/bin/env python3
"""
knowledge_graph.py — Visualización de un grafo de conocimiento con NetworkX

Construye un grafo de conocimiento con las herramientas del stack del podcast,
muestra consultas sobre el grafo, y genera una imagen PNG del mismo.

Propósito para el episodio 828:
  Demostrar visualmente qué es un "grafo de conocimiento" — la base de GraphRAG.
  Se ejecuta después de la comparativa RAG vs GraphRAG para mostrar un grafo real.

Uso:
  python knowledge_graph.py              # Modo interactivo (consulta + PNG)
  python knowledge_graph.py --no-png     # Solo consultas, sin guardar imagen
  python knowledge_graph.py --png-only   # Solo generar PNG

Requisitos:
  pip install networkx matplotlib
"""

import argparse
import sys
from typing import Dict, List, Tuple

try:
    import networkx as nx
except ImportError:
    print("❌ Falta networkx. Instala con: pip install networkx matplotlib")
    sys.exit(1)

try:
    from networkx.drawing.nx_pydot import graphviz_layout
except ImportError:
    print("❌ Falta pydot. Instala con: pip install pydot")
    sys.exit(1)

try:
    import matplotlib.pyplot as plt
except ImportError:
    print("❌ Falta matplotlib. Instala con: pip install matplotlib")
    sys.exit(1)


# ─── Datos del grafo ─────────────────────────────────────────────────────────

# Nodos: (id, nombre, tipo, descripcion)
ENTIDADES: List[Tuple[int, str, str, str]] = [
    (1, "Traefik", "software", "Reverse proxy para servicios Docker"),
    (2, "Docker", "software", "Plataforma de contenedores"),
    (3, "Podman", "software", "Alternativa a Docker sin daemon"),
    (4, "Ollama", "software", "Ejecución local de modelos LLM"),
    (5, "Open WebUI", "software", "Interfaz web para LLMs locales"),
    (6, "LightRAG", "software", "GraphRAG ligero con actualización incremental"),
    (7, "sqlite-vec", "libreria", "Extensión vectorial para SQLite"),
    (8, "Whisper", "software", "Speech-to-text local"),
    (9, "Piper TTS", "software", "Text-to-speech local"),
    (10, "LLaVA", "modelo", "Modelo de visión local"),
    (11, "MCP", "protocolo", "Model Context Protocol"),
    (12, "OpenCode", "software", "CLI/TUI para desarrollo asistido con IA"),
    (13, "Hermes Agent", "software", "Agente autónomo con skills y MCP"),
    (14, "Anacleto", "software", "Motor de orquestación de agentes en Rust"),
    (15, "CrewAI", "libreria", "Framework Python para equipos de agentes"),
    (16, "systemd", "software", "Init system y gestor de servicios"),
    (17, "Quadlets", "software", "Contenedores gestionados por systemd"),
    (18, "NetworkX", "libreria", "Biblioteca Python para análisis de grafos"),
]

# Aristas: (origen_id, destino_id, relacion)
RELACIONES: List[Tuple[int, int, str]] = [
    # Traefik
    (1, 2, "gestiona tráfico de"),
    (1, 3, "también funciona con"),
    (1, 17, "se despliega con"),
    # Docker
    (2, 3, "es alternativa de"),
    (2, 17, "gestionado por"),
    # Ollama
    (4, 5, "tiene interfaz en"),
    (4, 11, "expone API compatible"),
    # Open WebUI
    (5, 11, "soporta"),
    (5, 6, "integra RAG con"),
    # LightRAG
    (6, 7, "puede usar"),
    (6, 18, "construye grafos con"),
    # Agentes
    (12, 11, "implementa"),
    (13, 11, "implementa"),
    (14, 11, "implementa"),
    (12, 4, "usa modelos de"),
    (13, 4, "usa modelos de"),
    (14, 4, "usa modelos de"),
    (15, 12, "comparable con"),
    # Multimedia
    (8, 4, "se integra con"),
    (9, 4, "se integra con"),
    (10, 4, "se ejecuta vía"),
    # Infraestructura
    (16, 17, "gestiona"),
    (17, 2, "gestiona"),
    (17, 3, "gestiona"),
]

# Colores por tipo de entidad (para el gráfico)
COLORES_TIPO = {
    "software": "#4A90D9",  # azul
    "libreria": "#50C878",  # verde
    "modelo": "#E8A838",  # naranja
    "protocolo": "#9B59B6",  # púrpura
}


# ─── Construcción del grafo ──────────────────────────────────────────────────


def construir_grafo() -> nx.DiGraph:
    """Construye el grafo dirigido con entidades y relaciones."""
    G = nx.DiGraph()

    # Añadir nodos con atributos
    for eid, nombre, tipo, desc in ENTIDADES:
        G.add_node(nombre, tipo=tipo, descripcion=desc, id=eid)

    # Añadir aristas con atributos
    for origen_id, destino_id, relacion in RELACIONES:
        # Buscar nombres por ID
        origen_nombre = next(n for eid, n, _, _ in ENTIDADES if eid == origen_id)
        destino_nombre = next(n for eid, n, _, _ in ENTIDADES if eid == destino_id)
        G.add_edge(origen_nombre, destino_nombre, relacion=relacion)

    return G


# ─── Consultas sobre el grafo ────────────────────────────────────────────────


def consultar_relaciones(G: nx.DiGraph, entidad: str) -> None:
    """
    Consulta todas las relaciones de una entidad: qué sale de ella y qué llega.
    Esta es la consulta equivalente a la del SQL en demo-graphrag.sh.
    """
    print(f"\n{'=' * 60}")
    print(f"  CONSULTA: ¿Qué está relacionado con «{entidad}»?")
    print(f"{'=' * 60}\n")

    if entidad not in G:
        print(f"  ⚠ La entidad «{entidad}» no existe en el grafo.\n")
        return

    # Relaciones salientes (entidad → destino)
    salientes = list(G.out_edges(entidad, data=True))
    if salientes:
        print(f"  ▶ {entidad} se relaciona CON:")
        for _, destino, data in salientes:
            print(f"     • {data['relacion']} → {destino}")
    else:
        print(f"  ▶ {entidad} no tiene relaciones salientes.")

    # Relaciones entrantes (origen → entidad)
    entrantes = list(G.in_edges(entidad, data=True))
    if entrantes:
        print(f"\n  ▶ {entidad} es relacionado POR:")
        for origen, _, data in entrantes:
            print(f"     • {origen} → {data['relacion']}")
    else:
        print(f"\n  ▶ {entidad} no tiene relaciones entrantes.")

    print()


def consultar_camino(G: nx.DiGraph, origen: str, destino: str) -> None:
    """
    Encuentra el camino más corto entre dos entidades en el grafo.
    Demuestra que el grafo permite navegación semántica.
    """
    print(f"\n{'=' * 60}")
    print(f"  CONSULTA: Camino de «{origen}» a «{destino}»")
    print(f"{'=' * 60}\n")

    if origen not in G:
        print(f"  ⚠ La entidad «{origen}» no existe.\n")
        return
    if destino not in G:
        print(f"  ⚠ La entidad «{destino}» no existe.\n")
        return

    try:
        camino = nx.shortest_path(G, origen, destino)
        print(f"  Ruta encontrada ({len(camino) - 1} saltos):")
        for i in range(len(camino) - 1):
            a, b = camino[i], camino[i + 1]
            relacion = G[a][b]["relacion"]
            print(f"     {a} ──[{relacion}]──→ {b}")
        print()
    except nx.NetworkXNoPath:
        print(f"  ⚠ No hay camino entre «{origen}» y «{destino}».\n")


def consultar_vecinos_comunes(G: nx.DiGraph, entidad_a: str, entidad_b: str) -> None:
    """
    Encuentra vecinos comunes entre dos entidades.
    Útil para descubrir conexiones indirectas.
    """
    print(f"\n{'=' * 60}")
    print(f"  CONSULTA: Vecinos comunes de «{entidad_a}» y «{entidad_b}»")
    print(f"{'=' * 60}\n")

    if entidad_a not in G or entidad_b not in G:
        print("  ⚠ Una de las entidades no existe.\n")
        return

    # Vecinos: predecesores + sucesores
    vecinos_a = set(G.predecessors(entidad_a)) | set(G.successors(entidad_a))
    vecinos_b = set(G.predecessors(entidad_b)) | set(G.successors(entidad_b))
    comunes = vecinos_a & vecinos_b

    if comunes:
        print(f"  Entidades conectadas tanto con «{entidad_a}» como con «{entidad_b}»:")
        for v in sorted(comunes):
            print(f"     • {v}")
    else:
        print(f"  No hay vecinos comunes entre «{entidad_a}» y «{entidad_b}».")
    print()


def mostrar_estadisticas(G: nx.DiGraph) -> None:
    """Muestra estadísticas básicas del grafo."""
    print(f"\n{'=' * 60}")
    print(f"  ESTADÍSTICAS DEL GRAFO DE CONOCIMIENTO")
    print(f"{'=' * 60}\n")
    print(f"  • Nodos (entidades):     {G.number_of_nodes()}")
    print(f"  • Aristas (relaciones):  {G.number_of_edges()}")
    print(f"  • Densidad:              {nx.density(G):.4f}")
    print(f"  • ¿Es conexo?:           {'Sí' if nx.is_weakly_connected(G) else 'No'}")
    print(
        f"  • Diámetro:              {nx.diameter(G.to_undirected()) if nx.is_weakly_connected(G) else 'N/A'}"
    )
    print()


# ─── Visualización ───────────────────────────────────────────────────────────


def generar_png(G: nx.DiGraph, archivo: str = "grafo_conocimiento.png") -> str:
    """
    Genera una imagen PNG del grafo usando matplotlib.
    Los nodos se colorean por tipo y las aristas se etiquetan con la relación.
    """
    print(f"  Generando visualización: {archivo} ...", end=" ")

    plt.figure(figsize=(16, 12))

    # Layout: usar Graphviz si está disponible, sino spring_layout
    try:
        pos = graphviz_layout(G, prog="dot")
    except Exception:
        pos = nx.spring_layout(G, k=1.5, seed=42, iterations=50)

    # Dibujar nodos por tipo (cada tipo con su color)
    for tipo in COLORES_TIPO:
        nodos_tipo = [n for n, attr in G.nodes(data=True) if attr["tipo"] == tipo]
        nx.draw_networkx_nodes(
            G,
            pos,
            nodelist=nodos_tipo,
            node_color=COLORES_TIPO[tipo],
            node_size=2500,
            node_shape="o",
            edgecolors="white",
            linewidths=1.5,
            alpha=0.95,
        )

    # Dibujar aristas con flechas
    nx.draw_networkx_edges(
        G,
        pos,
        edge_color="#888888",
        arrows=True,
        arrowsize=20,
        arrowstyle="->",
        width=1.5,
        alpha=0.7,
        connectionstyle="arc3,rad=0.1",
    )

    # Etiquetas de los nodos
    nx.draw_networkx_labels(
        G,
        pos,
        font_size=11,
        font_weight="bold",
        font_family="sans-serif",
    )

    # Etiquetas de las aristas (relaciones)
    edge_labels = {(a, b): data["relacion"] for a, b, data in G.edges(data=True)}
    nx.draw_networkx_edge_labels(
        G,
        pos,
        edge_labels=edge_labels,
        font_size=8,
        font_family="sans-serif",
        alpha=0.8,
        label_pos=0.5,
    )

    # Leyenda
    legend_elements = []
    for tipo, color in COLORES_TIPO.items():
        legend_elements.append(
            plt.scatter(
                [], [], c=color, s=150, label=tipo, edgecolors="white", linewidths=1
            )
        )
    plt.legend(
        handles=legend_elements,
        title="Tipo de entidad",
        loc="upper right",
        fontsize=10,
        title_fontsize=12,
    )

    plt.title(
        "Grafo de conocimiento — Stack de herramientas del podcast",
        fontsize=16,
        fontweight="bold",
        pad=20,
    )
    plt.axis("off")
    plt.tight_layout()
    plt.savefig(archivo, dpi=150, bbox_inches="tight", facecolor="#FAFAFA")
    plt.close()

    print("✅")
    return archivo


# ─── Main ────────────────────────────────────────────────────────────────────


def main():
    parser = argparse.ArgumentParser(
        description="Demo de grafo de conocimiento con NetworkX",
    )
    parser.add_argument(
        "--no-png",
        action="store_true",
        help="No generar PNG, solo mostrar consultas",
    )
    parser.add_argument(
        "--png-only",
        action="store_true",
        help="Solo generar PNG sin consultas",
    )
    args = parser.parse_args()

    print()
    print("╔══════════════════════════════════════════════════════════╗")
    print("║  GRAFO DE CONOCIMIENTO — Demo para el episodio 828     ║")
    print("║  Construido con NetworkX + matplotlib                  ║")
    print("╚══════════════════════════════════════════════════════════╝")

    # Construir el grafo
    print("\n  Construyendo grafo...")
    G = construir_grafo()
    print(
        f"  ✓ Grafo creado: {G.number_of_nodes()} nodos, {G.number_of_edges()} aristas"
    )

    if not args.png_only:
        # Mostrar estadísticas
        mostrar_estadisticas(G)

        # Consulta principal: ¿Qué está relacionado con Traefik?
        consultar_relaciones(G, "Traefik")

        # Consulta: camino de OpenCode a Quadlets
        consultar_camino(G, "OpenCode", "Quadlets")

        # Consulta: vecinos comunes de Ollama y Open WebUI
        consultar_vecinos_comunes(G, "Ollama", "Open WebUI")

    if not args.no_png:
        archivo = generar_png(G)
        print(f"\n  📁 Imagen guardada: {archivo}")
        print(f"     Ábrela para ver el grafo visualmente.")

    print("\n  ✅ Demo completada.")
    print()


if __name__ == "__main__":
    main()

La demo construye 18 entidades (Traefik, Docker, Podman, Ollama, Open WebUI, LightRAG, Whisper, MCP…) y 24 relaciones entre ellas. Y luego permite hacer consultas como esta:

SELECT e.nombre, r.relacion, d.nombre
FROM entidades e
JOIN relaciones r ON e.id = r.origen_id
JOIN entidades d ON r.destino_id = d.id
WHERE e.nombre = 'Traefik';

El resultado no es una lista de fragmentos de texto. Es un recorrido del grafo: Traefik gestiona tráfico de Docker, también funciona con Podman, se despliega con Quadlets. El sistema sabe cómo se relacionan las cosas.

Y esto no se limita a notas técnicas. Puedes construir grafos con tus notas personales (diario, ideas, proyectos), con tu base de conocimiento técnica (comandos, configuraciones, troubleshooting), con documentación de herramientas que usas, incluso con libros, artículos, cursos que hayas tomado. Cada entidad (persona, concepto, herramienta, proyecto) es un nodo. Cada relación (trabaja en, depende de, es parte de) es una arista. El grafo resultante no es solo un índice, es un mapa de tu conocimiento.

Dedicamos tres episodios enteros a esto. El 835 con los fundamentos. El 837 con la implementación práctica usando LightRAG. Y el 839 con la búsqueda híbrida combinando grafos con vectores. Te recomiendo especialmente LightRAG, que ha superado en estrellas al proyecto original de Microsoft GraphRAG (39.3k estrellas frente a 35.7k) y está en desarrollo activo con soporte multimodal, indexación incremental y configuración de LLM por rol.

Por cierto, un error común es pensar que GraphRAG reemplaza a RAG. No es cierto. Para preguntas factuales directas, como ¿cuál es la IP de mi servidor?, el RAG vectorial es más rápido y más barato. GraphRAG brilla en preguntas que requieren conectar puntos: ¿qué servicios dependen de mi base de datos? o ¿cómo afecta este cambio de configuración al resto del sistema?

El ecosistema de herramientas propias

Una de las cosas que más ilusión me hace de esta temporada es que voy a poder compartir herramientas que he ido desarrollando en Rust. No son proyectos enormes, pero resuelven problemas reales de mi día a día, y creo que a ti también te pueden servir.

alloy (episodio 834) es un dashboard Docker con actualizaciones automáticas. Está escrito en Rust con Axum en el backend y React con Mantine UI en el frontend. Monitoriza tus contenedores en tiempo real vía SSE (Server-Sent Events), te permite gestionarlos (start, stop, restart, remove), detecta stacks de Docker Compose y tiene un sistema de políticas de actualización por contenedor: none, pull, pull+restart, pull+restart+stack. Incluye autenticación OIDC obligatoria, notificaciones a Telegram, Matrix y Webhook, y tareas programadas con cron. Básicamente, un reemplazo de Watchtower hecho en Rust, pero con UI moderna y control granular. Te puedes imaginar por qué lo llamo el dashboard Docker que necesitabas.

watchbeat (episodio 832) es un monitor de uptime también en Rust. Checkers HTTP, TCP, Ping, TLS/SSL y Heartbeats por push. Ocho canales de notificación: Telegram, Matrix, ntfy, Webhook, Slack, Discord, Email y Gotify. Alertas inteligentes con confirmaciones configurables antes de marcar DOWN. Status pages públicas. Timeline de uptime con buckets consolidados. Y métricas Prometheus en /metrics. Todo en un solo binario con la SPA embebida. Es mi alternativa self-hosted a UptimeRobot o Better Uptime.

shuul (episodio 836) es el guardián de tus datos. El proyecto más reciente del ecosistema, también en Rust con frontend React y TypeScript. La idea es tener un sistema que vigile la integridad de tus datos, que te alerte si algo falla y que te permita recuperarte rápidamente. Todavía estoy explorándolo a fondo, pero promete.

populatrs (episodio 830) es una herramienta para publicar contenido desde tu RSS a redes sociales. Alternativa auto-hosteada a Buffer o Hootsuite. Porque si tienes un blog, sabes lo tedioso que es promocionar cada artículo manualmente en todas partes.

Todas estas herramientas comparten algo: están escritas en Rust, se despliegan con Quadlets, se autentican con OIDC y se integran con el ecosistema de IA local. No son proyectos aislados, son piezas de un rompecabezas más grande.

Para quién es esta temporada

Voy a hacer una pausa y hablar directamente con tres perfiles de oyentes.

Primero, el que ya siguió la T08 y tiene su laboratorio montado. Para ti, esta temporada es el siguiente nivel. Ya tienes Ollama, ya has trasteado con Open WebUI, ya has jugado con MCPs. Ahora toca estructurar todo eso. Construir el grafo, orquestar agentes, ponerlo en producción. Lo que hiciste por intuición en la T08, aquí lo vas a hacer con método.

Segundo, el que llega nuevo y piensa esto es demasiado. No lo es. Te prometo que no lo es. El episodio 827 (Recursos IA) está diseñado exactamente para ti. Te pone al día con lo mínimo indispensable. Y a partir de ahí, la progresión es suave. Cada concepto se explica desde cero, asumiendo que vienes de Linux pero no de IA. Eso sí, tendrás que poner de tu parte: leer los show notes, probar los comandos, romper cosas y arreglarlas. Pero si has usado Linux más de seis meses, estás capacitado para seguir esta temporada.

Tercero, el escéptico. El que piensa la IA es una moda pasajera. Te entiendo. Llevamos décadas de hype cíclico en tecnología. Pero la IA local no es una moda. Es un cambio de infraestructura. Como cuando pasamos de discos duros a SSD. Como cuando pasamos de servidores dedicados a contenedores. No es una herramienta nueva, es una nueva capa en tu sistema. Y esta temporada no va de hype, va de construir. De tener algo funcionando en tu máquina, con tus datos, bajo tu control. No te vendo humo, te vendo código.

Por qué ahora es el momento perfecto para el linuxero

Llevo haciendo este podcast nueve temporadas. Nueve años. He hablado de escritorios, de terminales, de contenedores, de lenguajes de programación, de servidores, de seguridad, de automatización. He visto herramientas nacer y morir, distros llegar y desaparecer.

Y nunca, nunca, había estado tan emocionado con un tema como lo estoy ahora con la IA local.

¿Por qué? Porque la IA local no es una herramienta más. Es una herramienta que cambia cómo usas todas las demás. Cuando tu terminal tiene un agente que entiende lo que quieres hacer y te ayuda a hacerlo, la terminal deja de ser un intérprete de comandos y se convierte en un colaborador. Cuando tu gestor de archivos tiene búsqueda semántica, deja de ser un listado de archivos y se convierte en un mapa de tu conocimiento. Cuando tu asistente personal entiende tus notas, tu calendario, tus proyectos, tus intereses… deja de ser un programa y se convierte en una extensión de tu mente.

Y lo mejor de todo es que estamos en el momento perfecto para un Linuxero. Las herramientas son maduras pero no están petrificadas. Los modelos son buenos pero no requieren hardware de supercomputadora. La comunidad es activa pero no es abrumadora. Es el momento de construir.

Ollama ha recibido 88 millones de dólares de financiación. Tiene 8.9 millones de desarrolladores usándolo, integración con MLX en Apple Silicon, búsqueda web integrada, generación de imágenes experimental y modelos en cloud. Ya no es solo un gestor de modelos locales, es una plataforma completa. Y lo mejor: sigue siendo open source, sigue siendo local si quieres.

Los modelos de 128K de contexto son el estándar. El chunking agresivo ya no es necesario. Puedes pasarle documentos enteros a tu asistente sin partirlos en migajas. Y con modelos como NVIDIA Nemotron 3.5 Lightning (30B, diseñado para agentes) o Muse Glimmer de Meta (30B, multimodal, Apache 2.0), la frontera de lo que puedes hacer en local se expande cada semana.

Cada episodio construye sobre el anterior

Una cosa importante que quiero dejar clara desde el principio: esta temporada no es una colección de episodios sueltos. Es un viaje progresivo. No puedes saltarte el episodio de skills y esperar entender el de MCPs. No puedes saltarte MCPs y esperar entender GraphRAG. No puedes saltarte GraphRAG y esperar que el proyecto final tenga sentido.

Cada episodio asume que has escuchado el anterior. Y cada episodio te deja preparado para el siguiente.

Esto tiene una ventaja y una desventaja. La ventaja es que, si sigues la temporada en orden, vas a terminar con un conocimiento sólido, estructurado, que no se te va a olvidar a los dos días. Porque cada concepto lo vas a aplicar inmediatamente en el episodio siguiente. El aprendizaje es acumulativo. La desventaja es que, si llegas a mitad de temporada, te va a costar ponerte al día. Pero para eso están los show notes, el blog y el repositorio que vamos a mantener actualizado con todos los scripts y ejemplos.

El próximo episodio (el 829, que se publica el lunes) es Skills imprescindibles para tu agente IA. Vamos a ver cómo dotar a tu agente de herramientas reales: búsqueda en internet, lectura de archivos, ejecución de comandos, acceso a APIs. Sin skills, un agente es un loro caro. Con skills, es un asistente que hace cosas.

El stack de herramientas de la temporada

Para que te hagas una idea de lo que viene, aquí tienes el stack completo de herramientas que vamos a usar. No hace falta que las tengas todas instaladas desde el principio, pero sí que sepas que existen:

HerramientaPropósitoEpisodio
OllamaModelos LLM locales827, base continua
Open WebUIInterfaz web para LLMs827
OpenCodeCLI/TUI para desarrollo asistido829
MCP serversConexión agentes-fuentes de datos831
watchbeatMonitor de uptime en Rust832
alloyDashboard Docker en Rust834
LightRAGGraphRAG ligero e incremental835-839
sqlite-vecExtensiones vectoriales para SQLite837
sqlite-utilsNavaja suiza para SQLite840
jq + yqProcesamiento JSON/YAML838
whisperSpeech-to-text local841
ffmpegProcesador audio/video842
Piper TTSText-to-speech local843
maim + ImageMagickCaptura de pantalla844
LLaVAVisión artificial local845
systemd timersAutomatización temporal848
asyncioConcurrencia Python849
justCommand runner850
CrewAIEquipos de agentes851

Y entre medias, episodios que conectan herramientas Linux con el pipeline IA: shuul (836), yq + jq (838), sqlite-utils (840), Rust en el kernel (852), AutoGen (853), Wayland vs X11 (854), LangChain agents (855) y la guerra de los filesystems (856).

Lo que no te he contado (y te vas a encontrar)

Hay algunas cosas que no he mencionado en el episodio pero que merecen la pena destacar aquí.

Primero, que el panorama de GraphRAG ha cambiado radicalmente en los últimos meses. El proyecto original de Microsoft GraphRAG está en modo mantenimiento desde 2025 (no acepta nuevas PRs ni implementa nuevas features) y la investigación se ha movido a LazyGraphRAG y DRIFT Search. LightRAG, por su parte, ha superado en estrellas a Microsoft (39.3k vs 35.7k) y ofrece características que el proyecto de Microsoft nunca tuvo: integración multimodal con MinerU y Docling para procesar PDFs e imágenes, cuatro estrategias de chunking, configuración de LLM por rol (EXTRACT, QUERY, KEYWORDS, VLM), soporte OpenSearch como backend unificado y reranker integrado. Además, la indexación incremental ya no es un problema: LightRAG permite borrar documentos y regenerar el grafo automáticamente.

Segundo, que el MCP ha evolucionado muchísimo. La especificación más reciente (julio de 2026) incluye Streamable HTTP como transporte principal, autorización OAuth 2.1 con PKCE y JWKS, multi round-trip requests para peticiones complejas, caching del lado del servidor y descubrimiento de capacidades. Ya no es un experimento de Anthropic, es un estándar abierto con gobernanza formal, soportado por Claude, ChatGPT, VS Code, Cursor y Open WebUI.

Tercero, que no necesitas una GPU para seguir esta temporada. Con una CPU moderna y 16 GB de RAM puedes ejecutar modelos de 7B a 14B a velocidades perfectamente aceptables. Si tienes GPU, mejor, pero no es un requisito. Esto es IA local para Linuxeros de a pie, no para centros de datos.

Conclusión: esto no es hype, es infraestructura

He puesto el mapa sobre la mesa. Sabes de dónde venimos (una T08 que empezó con Docker y acabó con agentes) y sabes hacia dónde vamos: una T09 que empieza con skills y acaba con GraphRAG, agentes autónomos y proyectos en producción.

Esta temporada no es un curso. No es un tutorial. Es una expedición. Exploramos juntos lo más profundo de la IA local desde la perspectiva de un Linuxero. Y al final, veinte episodios después, vas a tener algo que no tenías antes: un sistema de inteligencia artificial que funciona para ti, con tus datos, en tu máquina.

Y eso, no te lo quita nadie.

El próximo lunes, episodio 829: Skills imprescindibles para tu agente IA. Vamos a ver cómo dotar a tu agente de herramientas reales para que deje de ser un loro caro y se convierta en un asistente que hace cosas.


Más información

Deja una respuesta