Graphify por dentro: un grafo de código sin embeddings y la fila del benchmark que nadie lee

Graphify convierte un repositorio en un grafo de conocimiento determinista, sin embeddings y sin coste de LLM. Su benchmark es de los mejores del sector — y contiene la fila que desmonta su propio titular: cuatro milésimas sobre un híbrido RRF clásico. Análisis técnico del pipeline, lectura crítica de las cifras y el problema de…

109.000 estrellas en cuatro meses, cero créditos de LLM para indexar y un benchmark propio, reproducible y publicado con su libro de gastos. Ahí está el mérito. Y también está, en la misma tabla, la fila que desmonta el titular.

Qué es exactamente

Graphify coge un repositorio —código, esquemas SQL, Terraform, configuración MCP, documentación, PDFs, imágenes, vídeo— y lo convierte en un grafo de conocimiento consultable. La idea es que tu asistente de programación pregunte al grafo en vez de hacer grep sobre ficheros y reconstruir mentalmente las relaciones en cada sesión.

Hasta aquí, nada nuevo: índices de código hay muchos. Lo que hace a Graphify interesante es la tesis, que es explícitamente anti-vectorial. Nada de embeddings. Nada de base vectorial. Extracción de AST determinista con tree-sitter, detección de comunidades por topología del grafo y trazado de caminos. El README lo dice con esas palabras: no embeddings, no vector store.

Los números de contexto, a 22 de agosto de 2026: 109.000 estrellas y 10.600 forks, 1.520 commits en la rama v8, 216 releases desde la primera (0.1.1, el 4 de abril de 2026) hasta la 0.9.48 del 20 de agosto, y unos 5,58 millones de descargas acumuladas en PyPI, de las cuales ~2,09 millones son solo de los últimos treinta días. Graphify Labs es una empresa estadounidense respaldada por Y Combinator (S26). El proyecto tiene cuatro meses de vida pública.

Un detalle práctico antes de seguir: el paquete de PyPI se llama graphifyy, con doble «y». El comando sigue siendo graphify. Hay otros paquetes graphify* en PyPI que no tienen nada que ver, así que si lo metes en un manifiesto, fija el nombre exacto.

El pipeline: siete funciones puras y tres pasadas

La arquitectura es sorprendentemente aburrida, y eso es un cumplido. Siete funciones encadenadas que se comunican con diccionarios de Python y grafos de NetworkX, sin estado compartido y sin efectos laterales fuera de graphify-out/:

detect() → extract() → build_graph() → cluster() → analyze() → report() → export()

detect() clasifica cada fichero. extract() es donde está el trabajo: 37 gramáticas de tree-sitter, más Apex por expresiones regulares, HCL, esquemas SQL e introspección en vivo de PostgreSQL, manifiestos de paquetes y configuraciones MCP. build_graph() monta nodos y aristas. cluster() corre Leiden. analyze() busca god nodes —los nodos con grado desproporcionado, que suelen ser exactamente donde duele tocar—. report() y export() escriben los tres artefactos: graph.json (node-link de NetworkX), GRAPH_REPORT.md (legible por humanos) y graph.html (visualización dirigida por fuerzas).

Pero la decisión arquitectónica que de verdad importa es otra: la separación en tres pasadas, porque es la que determina qué datos salen de tu máquina.

La pasada de código es 100% local y determinista. No llama a ningún modelo, no necesita clave de API y no toca la red. La pasada de audio y vídeo transcribe en local con faster-whisper. La tercera —documentación, PDFs, imágenes— sí necesita un modelo, y es la que rompe la etiqueta de local-first. Si ejecutas con --code-only, la única pasada que corre es la primera. Cero red, cero coste, cero exposición.

Esa asimetría es el producto. Todo lo demás es opcional.

Determinismo en lugar de similitud

Tres decisiones de diseño que merecen atención por separado.

Topología como señal. Leiden agrupa nodos por densidad de aristas. No hay paso de embedding: la «similitud» entre dos partes del sistema no es coseno entre vectores, es cuántos caminos las conectan. Es una apuesta fuerte, no una carencia. Y tiene una propiedad que los índices vectoriales no tienen: es reproducible. Mismo commit, mismo grafo.

Con un matiz que conviene decir, porque el eslogan absoluto no aguanta: el pipeline no usa embeddings, pero el arnés de benchmarks sí —usa un embebedor local BGE-m3, y hay una configuración alternativa con motor SurrealDB—. La afirmación correcta es «el índice no lleva embeddings», no «aquí no hay un solo vector».

Hiperaristas de primera clase. Las relaciones de grupo, esas que involucran a tres o más nodos, viven aparte en G.graph["hyperedges"] en vez de descomponerse en aristas por pares. Es modelado n-ario de verdad, y es raro verlo en herramientas de este segmento. Cuando descompones una relación ternaria en tres binarias, pierdes la información de que eran la misma cosa; aquí no se pierde.

Caché por hash y grafo versionado. El caché es SHA-256 por fichero: si el contenido no cambia, no se reextrae. Y graphify-out/ está pensado para commitearse al repositorio, con un merge driver que hace unión automática de graph.json y un hook post-commit que reconstruye la capa AST sin coste de API. El grafo deja de ser un artefacto efímero de tu portátil y pasa a ser algo que revisas en un pull request.

Encima de eso hay una capa de memoria de trabajo que me parece la parte más original del diseño: graphify save-result registra el desenlace de cada pregunta (useful / dead_end / corrected) y graphify reflect agrega esos resultados en reflections/LESSONS.md, más una superposición que etiqueta nodos como preferred / tentative / contested, ponderada por recencia, con procedencia, y invalidada automáticamente cuando el código subyacente cambia. La señal no es una recompensa aprendida ni una puntuación opaca: es el desenlace declarado de una consulta concreta.

Y el consumo va por un servidor MCP, en stdio y en HTTP Streamable, con herramientas tipadas: query_graph, get_node, get_neighbors, shortest_path, list_prs, get_pr_impact, triage_prs. Escucha en 127.0.0.1 por defecto; para exponerlo hacen falta --host 0.0.0.0 y --api-key. Bien.

La fila equivocada de la tabla

Aquí es donde el artículo se pone incómodo, y quiero dejar clara una cosa antes: Graphify publica un benchmark mejor que el 95% de su sector. Arnés reproducible, validación de juez con dos jueces independientes (90,6% de acuerdo, kappa de Cohen 0,81) y un libro de gasto por ejecución. Eso hay que reconocerlo, porque lo normal en este mercado es un gráfico de barras sin metodología.

El problema no es el benchmark. Es cómo se lee el benchmark.

El README titula comparándose contra mem0, que saca 0,048 de recall@10 en LOCOMO. De ahí sale el «10×» que ha reproducido media prensa técnica. Pero la línea base honesta está en la misma tabla, tres filas más arriba: un híbrido RRF clásico, BM25 más denso fusionados con reciprocal rank fusion, ejecutado en el mismo arnés.

Recall@10 sobre LOCOMO (n=300):

Sistemarecall@10
graphify0,497
híbrido RRF0,493
RAG denso0,439
BM250,362
supermemory0,149 *
mem00,048

* el propio documento marca este valor como confundido por su embebedor.

Cuatro milésimasLOCOMO (n=300) · RECUPERACIÓN FRENTE A CALIDAD DE RESPUESTARECALL@10graphify0,497híbrido RRF0,493RAG denso0,439BM250,362supermemory *0,149mem00,048La ventaja sobre un híbrido RRF clásico es de 0,004.ACIERTO EN QAsupermemory49,7%graphify45,3%híbrido RRF43,3%Y el sistema con peor recall contesta mejor.* recall confundido por su propio embebedor (declarado en BENCHMARKS.md)Fuente: Graphify-Labs/graphify, BENCHMARKS.md (actualizado el 5 de julio de 2026).
Recall@10 y QA en LOCOMO: la ventaja de Graphify sobre el híbrido RRF es de cuatro milésimas

Cuatro milésimas. Menos de un punto porcentual sobre una recuperación híbrida bien hecha, que es exactamente lo que llevas montando desde 2023 con dos librerías y una tarde.

Y hay una segunda fila que ni siquiera el análisis crítico habitual menciona. En la métrica de QA —responder bien la pregunta, no solo recuperar el documento— Graphify saca 45,3% frente al 43,3% del híbrido. Pero supermemory saca 49,7%, por encima de Graphify, con ese recall de 0,149 que su propio embebedor confunde. Es decir: el sistema con peor recuperación de la tabla contesta mejor las preguntas. Eso no invalida a Graphify, pero sí dice algo que casi nadie está diciendo en voz alta: en LOCOMO, recall@10 y calidad de respuesta están mucho menos correlacionados de lo que asumimos todos.

Dos avisos más de lectura. Primero, la categoría: LOCOMO y LongMemEval-S miden memoria conversacional, no comprensión de código. La única evidencia de inteligencia de código en el repo es ERPNext, donde la cobertura de hechos clave sube de 70,8% a 82,0%… sobre n = 6 preguntas. Eso es una anécdota bien instrumentada, no una medición. Segundo, el juez: el modelo que lee y el que puntúa son el mismo, Kimi K2.6, y reproducir el arnés exige MOONSHOT_API_KEY, o sea tráfico hacia servidores de Moonshot AI en China. No afecta a usar la herramienta, pero bloquea la reproducción independiente en cualquier entorno con restricciones de residencia de datos sin sustituir antes al juez. Y conviene fechar la cita: BENCHMARKS.md lleva sin actualizarse desde el 5 de julio de 2026.

Lo que la evidencia sostiene con solidez es otra cosa, y es lo que de verdad diferencia al proyecto: el coste y la escala. Ingesta de LOCOMO por ~1,40 $ frente a los 15,67 $ de supermemory (unas 11 veces menos), con construcción del grafo a 0 créditos de LLM. Y 689 checkpoints semanales de ERPNext entre 2011 y 2026 que muestran el grafo creciendo de 3.069 a 22.620 nodos y de 2.900 a 48.710 aristas con extracción AST estable todo el camino.

El diferencial de Graphify no es la precisión. Es que indexar no cuesta dinero y cada arista sabe de dónde salió.

El 71,5× tiene una curva completa, y está publicada

El otro número que circula solo es el ahorro de tokens. «71,5× menos contexto por consulta». Es cierto, y viene de un corpus concreto. La documentación publica la curva entera, y es honesta:

CorpusFicherosReducción de tokens
Repos de Karpathy + artículos + imágenes5271,5×
Fuente de graphify + artículo de Transformers45,4×
httpx (librería sintética)6~1×

La lectura correcta es que el beneficio es función del tamaño y la heterogeneidad del corpus, no una propiedad de la herramienta. Por debajo de unos 50 ficheros, lo que ganas es claridad estructural, no compresión. Y en el propio benchmark de código sobre ERPNext, el coste declarado sigue siendo de ~140.000 tokens por consulta: barato comparado con inyectar el repositorio entero, no tanto comparado con un grep bien apuntado.

La procedencia no es la verdad

Esta es, para mí, la parte conceptualmente más interesante del diseño, y también su fallo más instructivo.

Cada arista lleva una etiqueta de confianza con una rúbrica discreta, no una puntuación continua:

  • EXTRACTED = 1.0 — explícito en el código fuente.
  • INFERRED = 0,95 / 0,85 / 0,75 / 0,65 / 0,55 — cinco escalones fijos, cada uno con su criterio.
  • AMBIGUOUS — marcado para revisión humana.

Prefiero mil veces esto a un confidence: 0.7834 salido de un softmax. Una rúbrica discreta se puede auditar; un número continuo opaco, no.

Pero hay una trampa, y está bien documentada. EXTRACTED = 1.0 garantiza que la arista se leyó de la fuente. No garantiza que el nombre de la relación sea correcto. MikeWang documentó el caso más claro el 7 de agosto: un enum Event: String de Swift, donde String es el tipo bruto del enum y no una superclase, quedó modelado como Event --inherits--> String y etiquetado EXTRACTED, con confianza máxima. En el árbol sintáctico de Swift, enum Event: String es estructuralmente idéntico a class Foo: Bar. Y significa otra cosa completamente distinta.

Tree-sitter leyó la sintaxis perfectamente. Graphify le colgó la etiqueta equivocada. Y el sistema de confianza no tiene forma de saberlo, porque mide la procedencia del dato, no su interpretación. Lo formula muy bien el propio Wang: la etiqueta garantiza que la arista fue leída en vez de adivinada; no garantiza que el nombre de la relación sea correcto. Conviene decir, por cierto, que su revisión es en conjunto favorable — el caso del enum aparece como una limitación bien acotada, no como una enmienda a la totalidad.

Esto aplica a cualquier sistema de confianza basado en procedencia, no solo a Graphify: saber de dónde viene un hecho y saber que el hecho es correcto son dos problemas distintos, y confundirlos es cómodo precisamente porque el primero es fácil de instrumentar y el segundo no.

El orden de una lista de prioridades es una decisión de política de datos

Vamos con el hallazgo que más me ha hecho levantar la ceja.

Cuando ejecutas graphify extract sin pasar --backend, la herramienta resuelve el proveedor de modelo mirando qué variables de entorno tienes definidas, en este orden:

Gemini → Kimi → Claude → OpenAI → DeepSeek → Azure → Bedrock → Ollama
El octavo de ochoORDEN DE AUTODETECCIÓN DE BACKEND SIN –BACKEND EXPLÍCITO1Gemini2KimiEnruta a servidores de Moonshot AI en China3Claude4OpenAI5DeepSeek6Azure7Bedrock8OllamaÚnica opción 100 % local — la última en probarseEl orden de una lista de fallback no es una decisión de ingeniería.Es una decisión de política de datos.Fuente: README de Graphify-Labs/graphify (v8), sección de configuración de backend.
Orden de autodetección de backend en Graphify: Ollama, la única opción local, es la última de ocho

Dos consecuencias, y ninguna es un detalle de implementación.

Una: Ollama —la única opción totalmente local de las ocho— es la última. En una máquina de desarrollo real, donde casi todo el mundo tiene tres o cuatro claves de API en el perfil por motivos que no tienen nada que ver con esto, Ollama es la que menos probabilidades tiene de activarse. La herramienta que se vende como local-first solo lo es si se lo pides explícitamente.

Dos: Kimi es la segunda de la lista, y la propia documentación advierte —con todas las letras— que MOONSHOT_API_KEY enruta a servidores de Moonshot AI en China. O sea: un desarrollador que tuviera esa clave exportada por cualquier otro motivo, ejecutando graphify extract sin --backend sobre documentación de un cliente, la estaría enviando fuera sin haber tomado una sola decisión consciente.

No es una vulnerabilidad. Es un default. Y ese es justo el punto: el orden de una lista de fallback no es una decisión de ingeniería, es una decisión de política de datos, y casi nadie la revisa como tal. Vale para Graphify y vale para cualquier herramienta que resuelva proveedores por variables de entorno.

El control es trivial, eso sí: prohibir la invocación sin --backend explícito y fijarlo en la configuración del proyecto. Para cualquier cosa que toque datos de cliente, --code-only o --backend openai apuntado a tu propia inferencia (llama.cpp, LM Studio, vLLM — el backend acepta cualquier endpoint compatible con OpenAI, y esa pieza es la que hace viable un despliegue íntegramente propio).

El problema que nadie había atacado: el asistente ignora el grafo

Hay un detalle en Graphify que revela que el equipo ha usado esto de verdad.

Puedes construir el mejor grafo del mundo, exponerlo por MCP con siete herramientas perfectamente tipadas… y ver cómo el asistente lo ignora y abre ficheros igualmente. Alex Rusin lo documentó en julio: Claude Code se saltaba el grafo de forma repetida y volvía a leer fuentes.

La respuesta de Graphify no es documentación ni un prompt más largo. Es coerción: hooks PreToolUse específicos por plataforma y un modo --strict que bloquea la primera lectura de fuente de la sesión y la redirige al grafo. Después de esa primera vez, vuelve al modo suave.

Es una solución fea y es la correcta. Si tu herramienta compite contra un hábito arraigado del modelo, la disponibilidad no basta; hace falta una interrupción. Me parece un patrón exportable a cualquiera que construya herramientas para agentes: la adopción por parte del modelo es un problema de diseño, no de documentación.

Lo que un AST no puede ver

Conviene ser preciso sobre el alcance, porque «grafo de conocimiento del código» suena a más de lo que es.

Graphify modela estructura estática: llamadas, importaciones, herencia, referencias entre capas. El índice es una instantánea del árbol sintáctico en un commit. Lo que no modela son flujos de ejecución reales: qué se ejecuta de verdad, en qué orden, con qué frecuencia, bajo qué condiciones. El mapa de llamadas estáticas y la topología de ejecución son dos problemas distintos, y el segundo no se deduce del primero.

Para lo que sirve el mapa estático: orientarte rápido en un sistema que no conoce nadie del equipo, unir código de aplicación con esquema de base de datos e infraestructura Terraform en un espacio consultable —la pregunta «qué conecta esta ruta de API con esta tabla y este recurso desplegado» no tiene alternativa barata—, y estimar el radio de impacto antes de una migración.

Para lo que no sirve: sustituir la verificación contra fuente. La etiqueta de confianza no valida el nombre de la relación, ya lo hemos visto.

La deriva documental funciona en las dos direcciones

Un apunte de rigor, porque suele contarse solo en un sentido.

La documentación de Graphify tiene deriva medible: el número de lenguajes soportados varía entre 25, 37 y «~40» según la página que abras —la cifra buena y trazable es 37 gramáticas de tree-sitter, la de la tabla de formatos del README—, y SECURITY.md sigue nombrando la serie 0.3.x y afirmando que el análisis del grafo no hace llamadas de red. No lo uses como inventario de flujos de datos.

Pero aquí viene lo interesante, y es algo que casi todo el análisis crítico que he leído cuenta al revés. El README dice en un sitio que toda consulta se registra en ~/.cache/graphify-queries.log con opción de exclusión. Eso ya no es cierto: desde el issue #1797 el registro es opt-in vía GRAPHIFY_QUERY_LOG_ENABLE=1, está desactivado por defecto, y la frase del README es texto obsoleto. Es decir, la deriva documental de este proyecto también corre en contra del proyecto: la documentación le atribuye un comportamiento peor del que tiene.

Que conste. Verificar en disco sigue siendo la respuesta correcta, pero repetir la versión antigua sería un error en la dirección contraria a la habitual.

Lo que sí está bien, sin peros

Para no dejar la impresión equivocada, porque hay cosas de las que otros proyectos deberían copiar directamente:

  • Cadena de suministro. Publicación con Trusted Publishing y atestaciones Sigstore verificables por tag. El certificado de la 0.9.48 lleva el SAN del workflow que la construyó. Poder verificar criptográficamente la procedencia de un artefacto sigue siendo raro.
  • Licencia permisiva y sin fricción, aunque con una ambigüedad que hay que resolver por escrito antes de meter código en un entregable: el repo lleva LICENSE (Apache-2.0) y LICENSE-MIT, y PyPI declara Apache-2.0.
  • El grafo versionado en git. Que el artefacto viva en el repositorio y se revise en un PR es la decisión que convierte esto en infraestructura en vez de en un juguete de escritorio.

Y las señales de alarma, sin adornos: pre-1.0 con cuatro meses de vida, cadencia de release casi diaria, 497 issues y 537 PRs abiertos, un único mantenedor de paquete en PyPI, y Bandit y pip-audit corriendo con continue-on-error en CI —es decir, los hallazgos de seguridad estática son consultivos, no una puerta de calidad—. No asumas que hay un gate donde no lo hay.

Mi veredicto

El patrón sí. La dependencia, todavía no. Otra vez.

Hay cuatro ideas aquí que te puedes llevar hoy a tu propio sistema sin heredar nada: procedencia por arista con rúbrica discreta en vez de puntuación continua; hiperaristas de primera clase para relaciones n-arias; reflexión a nivel de paso con superposición de memoria de trabajo e invalidación automática cuando la fuente cambia; y coerción del agente vía hook cuando la disponibilidad no basta. Ninguna necesita a Graphify para existir. En un proyecto propio de grafo de conocimiento me encontré con las tres primeras como huecos abiertos, y ver un diseño concreto que las resuelve a escala de 48.000 aristas vale más que el software.

Como herramienta interna, --code-only es un piloto de coste cero, red cero y exposición de datos cero. uv tool install "graphifyy==0.9.48", fija la versión, corre graphify extract . --code-only sobre un repositorio real y mide contra un conjunto ciego de veinte preguntas cuya respuesta ya conozcas y puedas verificar contra fuente. Esa medición vale infinitamente más que cualquier benchmark ajeno.

Como dependencia en algo que entregas a un cliente: no, todavía no. Y la autodetección de backend por defecto, prohibida en cualquier contexto con compromisos de residencia de datos.

Lo que no me quito de la cabeza es la fila de las cuatro milésimas. Un proyecto publica un benchmark honesto, con su metodología, su validación de juez y su libro de gastos —y elige titular contra el competidor más débil de la tabla, teniendo la línea base seria tres filas más arriba. La conclusión no es que Graphify haga trampa: es que el benchmark es tan bueno que se puede leer en contra de quien lo publicó, y llegar a una conclusión distinta sin salir del documento. Ojalá el problema del resto del sector fuera ese.

Ideas por encima de código; evidencia por encima de humo.


Fuentes

Pablo Formoso
autor

Pablo Formoso

Notas de campo desde la intersección de datos, IA, y filosofía aplicada.

entradas
58
desde
2024

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *