Graph engineering vs harness engineering: no son alternativas
Hace unas semanas alguien me enseñó el diagrama de su sistema multi-agente. Doce nodos, aristas condicionales, un router en el centro y el estado compartido dibujado en un lateral con su leyenda de colores. Bonito de verdad.
Le hice una sola pregunta: cuando el nodo que implementa escribe el código, ¿qué comprueba que ese código compila antes de que el grafo avance al siguiente nodo?
Silencio.
Ese silencio es toda la diferencia entre graph engineering y harness engineering. Y explica por qué la pregunta que me llega cada semana —"¿por cuál apuesto?"— está mal planteada desde el principio.
No son alternativas. Son ejes ortogonales. Puedes tener mucho de uno y nada del otro, y la mayoría de equipos está exactamente en ese caso.
Qué "graph engineering" estamos comparando: recuperación u orquestación
El término se usa para dos cosas distintas, y mezclarlas hace daño.
El primer uso es de recuperación de contexto: tratar tu código como un grafo de dependencias para que el agente navegue por él en lugar de tragarse el repositorio entero en cada petición. De eso escribí en qué es graph engineering, y no es de lo que va este post.
El segundo uso —el que se popularizó en 2026— es de orquestación: modelar la ejecución multi-agente como un grafo. Nodos que son agentes o pasos, aristas que son routing, un estado compartido que fluye por esas aristas. Este post va de ese.
Graph engineering, en su sentido de orquestación, es la disciplina de modelar la ejecución de un sistema multi-agente como un grafo dirigido: cada nodo es un agente o un paso, cada arista es una decisión de routing, y el estado compartido viaja por esas aristas. Responde a una pregunta concreta: qué se ejecuta, en qué orden y con qué estado.
Qué es harness engineering
Harness engineering es la disciplina de diseñar todo lo que rodea al modelo —las herramientas que puede llamar, los permisos que tiene y los comandos que deciden si su salida es válida— para que el agente falle rápido y en voz alta en lugar de entregar código plausible que no funciona.
El término lo acuñó Mitchell Hashimoto —cofundador de HashiCorp, el que creó Terraform— el 5 de febrero de 2026, y lo hizo admitiendo que ni siquiera sabía si ya existía un nombre para esto: "I don't know if there is a broad industry-accepted term for this yet, but I've grown to calling this 'harness engineering'". Dos meses después, el 2 de abril de 2026, Birgitta Böckeler le dedicó un artículo entero en martinfowler.com, que suele ser la señal de que un término ha venido para quedarse.
La ecuación que lo resume la formuló LangChain en The Anatomy of an Agent Harness:
Agente = Modelo + Harness
El modelo lo ponen Anthropic, OpenAI o Google. Tú no lo controlas, y cambia cada pocos meses sin pedirte permiso.
Lo único que construyes de verdad es el harness: las herramientas que expones, los permisos que concedes, el linter, los tests, el pipeline de CI, el AGENTS.md, los hooks que se disparan antes y después de cada edición.
Harness engineering responde a otra pregunta distinta: qué se le permite hacer y cómo compruebas que lo hizo bien.
Ninguna de las dos preguntas es un subconjunto de la otra. Por eso son ejes.
Graph engineering vs harness engineering: tabla comparativa
La diferencia en una frase: graph engineering modela la ejecución; harness engineering modela las restricciones y la verificación. Uno decide qué corre y en qué orden. El otro decide qué se le permite tocar y qué comando declara que ha terminado.
| Graph engineering | Harness engineering | |
|---|---|---|
| Qué modela | La ejecución | Las restricciones y la verificación |
| Pregunta que responde | ¿Qué corre, en qué orden, con qué estado? | ¿Qué puede tocar y cómo sé que funcionó? |
| Artefactos | Nodos, aristas, routing condicional, estado compartido | Tools, permisos, tests, linters, CI, AGENTS.md, hooks |
| Dónde vive | En el framework de orquestación | En tu repo y en tu pipeline |
| Herramientas típicas | LangGraph, Google ADK 2.0, Microsoft Agent Framework | tsc, ESLint, Vitest, git worktrees, GitHub Actions |
| Falla cuando… | La tarea necesita más de un rol y no hay estructura | El agente entrega algo plausible que no compila |
| Cómo se ve el fallo | Un loop que da vueltas sin converger | Un PR limpio, ordenado y equivocado |
| Visibilidad | Alta: se dibuja en una slide | Baja: vive en un script de package.json |
Y este es el cuadrante que sale de cruzar los dos ejes, que es donde duele:
| Harness pobre | Harness sólido | |
|---|---|---|
| Sin grafo | Un loop suelto que acaba rompiendo main |
Un agente lento pero fiable para una tarea acotada |
| Con grafo | Basura ordenada, y además a escala | Un sistema que puedes dejar corriendo sin mirarlo |
Un grafo perfecto con un harness pésimo produce basura ordenada. Con trazas preciosas, eso sí. Cada paso registrado, cada transición visible, y un resultado que no funciona.
Un harness excelente sin grafo se atasca en cuanto la tarea necesita más de un rol —investigar, implementar, revisar— y todo intenta caber en un único bucle que se queda sin contexto a mitad de camino.
La mayoría de los equipos invierte en el eje del grafo. Es lo visible, lo que se dibuja, lo que impresiona en una demo. Y descuida el harness, que es lo que de verdad mueve la aguja.
Nada de esto es nuevo, y conviene decirlo
Ninguna capacidad de graph engineering apareció en 2026.
LangGraph, AutoGen y ADK ya orquestaban por grafo antes de que el término existiera. Lo que cambió fue el vocabulario, no la tecnología.
Google publicó ADK 2.0 para Python el 19 de mayo de 2026 —el ADK ya había llegado a disponibilidad general un año antes, con la 1.0 de mayo de 2025— y ahí consolidó la idea en su arquitectura: los agentes se modelan como nodos de un grafo de workflow. Go recibió su 2.0 el 30 de junio de 2026 y TypeScript el 21 de agosto de 2026, así que desde entonces construyes workflows basados en grafo de forma nativa también desde Node, sin salir del lenguaje en el que están todos los ejemplos de este post.
Cuando un término se pone de moda, la reacción sana no es migrar. Es preguntarte qué problema tuyo resuelve hoy.
Si tu agente de un solo loop se atasca porque la tarea necesita roles separados, el grafo te ayuda: sobre cuándo dar ese salto escribí en LangGraph con TypeScript: grafo de estados vs. loop.
Si tu agente entrega cosas que no compilan, el grafo no te va a salvar. Vas a tener el mismo problema, solo que mejor enrutado.
La diferencia, en código
Un nodo de grafo es una función que recibe el estado compartido y devuelve el trozo de estado que cambia. Nada más.
// EJE 1: GRAPH — qué se ejecuta y con qué estado
// state.ts
export type Verdict = { ok: boolean; failures: { name: string; output: string }[] }
export type BuildState = {
spec: string
plan?: string
patch?: string
verdict?: Verdict
attempts: number
}
export type GraphNode = (state: BuildState) => Promise<Partial<BuildState>>
// Tu cliente de LLM: la SDK de Anthropic, la de OpenAI, el AI SDK de Vercel…
declare const model: {
generate(input: { system: string; prompt: string }): Promise<string>
}
export const implement: GraphNode = async (state) => {
const errores = state.verdict?.failures
.map((f) => `[${f.name}]\n${f.output}`)
.join(39;\n\n39;)
const patch = await model.generate({
system: 39;Implementa la tarea. Devuelve un diff unificado.39;,
prompt: [
state.spec,
`Plan:\n${state.plan ?? '(sin plan)'}`,
errores ? `Intento anterior fallido:\n${errores}` : 39;39;,
].join(39;\n\n39;),
})
return { patch, attempts: state.attempts + 1 }
}
Fíjate en lo que este nodo no sabe: si lo que ha escrito sirve para algo. Devuelve un diff y se queda tan tranquilo. El grafo enrutará al siguiente nodo con la misma confianza tanto si el parche compila como si es una invención con buena sintaxis.
El otro eje es este:
// EJE 2: HARNESS — qué se permite y cómo se comprueba
// harness.ts
import { execa } from 39;execa39;
import type { Verdict } from 39;./state39;
type Check = { name: string; cmd: string; args: string[] }
const CHECKS: Check[] = [
{ name: 39;types39;, cmd: 39;npx39;, args: [39;tsc39;, 39;--noEmit39;] },
{ name: 39;lint39;, cmd: 39;npx39;, args: [39;eslint39;, 39;.39;, 39;--max-warnings=039;] },
{ name: 39;tests39;, cmd: 39;npx39;, args: [39;vitest39;, 39;run39;] },
]
export async function verify(cwd: string): Promise<Verdict> {
const failures: Verdict[39;failures39;] = []
for (const check of CHECKS) {
const result = await execa(check.cmd, check.args, { cwd, reject: false })
if (result.exitCode !== 0) {
failures.push({
name: check.name,
output: `${result.stdout}\n${result.stderr}`.trim().slice(-4000),
})
}
}
return { ok: failures.length === 0, failures }
}
Y así es como se cruzan los dos ejes. El harness envuelve al nodo: el grafo decide quién trabaja, el harness decide qué cuenta como "terminado".
// wire.ts
import { verify } from 39;./harness39;
import type { BuildState, GraphNode } from 39;./state39;
declare const WORKTREE: string
declare function applyPatch(cwd: string, patch: string): Promise<void>
const withHarness =
(node: GraphNode): GraphNode =>
async (state) => {
const update = await node(state)
if (update.patch === undefined) return update
// Siempre sobre un worktree aislado, nunca sobre tu rama de trabajo
await applyPatch(WORKTREE, update.patch)
const verdict = await verify(WORKTREE)
return { ...update, verdict }
}
// El routing condicional ahora decide con evidencia, no con optimismo
const routeAfterImplement = (state: BuildState): 39;review39; | 39;implement39; | 39;giveUp39; => {
if (state.verdict?.ok) return 39;review39;
return state.attempts >= 3 ? 39;giveUp39; : 39;implement39;
}
El detalle que lo cambia todo está en routeAfterImplement. Sin verdict, esa función solo puede enrutar por número de intentos o por lo que el propio modelo diga de sí mismo. Con verdict, enruta por hechos.
Y el array failures que devuelve verify no es para tu log: va de vuelta al prompt del siguiente intento. Un harness que detecta el fallo pero no se lo cuenta al agente es media pieza.
Quita el grafo y te queda un agente que, al menos, sabe cuándo ha fallado. Quita el harness y te queda un grafo que enruta con total seguridad hacia una conclusión falsa.
En cuál invertir primero: harness antes que grafo
Gástala entera en el harness. Esta es mi opinión y la defiendo: el grafo es un problema de estructura que puedes resolver más tarde, cuando sepas qué roles necesitas de verdad. El harness es un problema de confianza, y sin confianza no vas a dejar corriendo nada.
Cuatro pasos concretos, en este orden:
- Escribe qué significa "terminado" como comandos. Un único script
verifyque devuelva exit code. Si no existe, no tienes harness: tienes esperanza. - Cierra los permisos. Lista blanca de herramientas y un git worktree aislado. El agente no escribe en tu rama. Ojo con un detalle que te va a morder: un worktree recién creado no trae
node_modules, así que instala antes de verificar o el harness te dará falsos rojos que no tienen nada que ver con el parche. - Mueve el verificador a CI. Lo que solo corre en tu máquina no protege a nadie; el montaje completo está en test harness para agentes de IA.
- Convierte la revisión en un contrato en lugar de en una lectura a ojo. El método está en revisión por contrato y, desarrollado paso a paso, en el ebook gratuito Revisión por Contrato (30 páginas, sin coste).
Cuando los cuatro estén en su sitio, añade el grafo. Vas a notar la diferencia en la primera semana, porque los nodos empezarán a fallar rápido y en voz alta en lugar de fallar en silencio.
La única conclusión que te llevas
Abre hoy tu repo y responde por escrito a una pregunta: ¿qué comando decide que el agente ha terminado?
Si la respuesta es "lo miro yo en el PR", tu cuello de botella no es la orquestación. Es que no tienes harness, y ningún diagrama lo va a arreglar.
Ese comando es tu trabajo de esta semana. El grafo puede esperar.
Definir el "terminado" antes de escribir código es exactamente de lo que va el libro de Spec-Driven Development: la especificación es la parte del harness que decide si el resultado vale, y se escribe antes que nada. Y si quieres ver los dos ejes montados sobre un producto real, de la idea al deploy, eso es lo que construimos paso a paso en Construye con IA.
Preguntas frecuentes
¿Necesito un framework de grafos para montar un sistema multi-agente?
No. Un grafo es un diccionario de nodos y una función de routing: unas cuantas decenas de líneas de TypeScript, no mucho más que los ejemplos de este post.
Los frameworks —LangGraph, ADK 2.0, Microsoft Agent Framework— te dan persistencia del estado, checkpoints, reanudación tras un fallo y trazabilidad. Eso es lo que estás comprando, no el concepto de grafo. Si tu proceso cabe en memoria y dura dos minutos, escríbelo a mano y ahórrate la dependencia.
¿El harness no es simplemente tener tests?
Los tests son una pieza del harness, la más obvia. Pero el harness también decide qué herramientas ve el agente, qué ficheros puede tocar, qué comandos puede ejecutar y qué pasa cuando un check falla.
Un agente con una suite de tests excelente y acceso de escritura a producción no tiene un buen harness. Tiene un buen día, hasta que deje de tenerlo.
¿"Graph engineering" no era lo del grafo de dependencias del código?
También, y por eso genera tanta confusión: el término se usa para dos cosas.
En su sentido de recuperación de contexto, graph engineering es indexar tu código como grafo de dependencias para que el agente navegue por ahí en vez de por embeddings sueltos. En su sentido de orquestación —el de este post— es modelar la ejecución multi-agente como grafo. Cuando alguien lo use, pregunta a cuál de los dos se refiere antes de discutir. La versión larga del sentido de recuperación —cómo se indexa, con qué herramientas y cuándo no compensa— está en qué es graph engineering.
Si ya uso Claude Code o Codex, ¿tengo harness?
Tienes el harness que trae la herramienta: permisos, hooks, ejecución de comandos, lectura del AGENTS.md. Es un buen punto de partida y está mejor pensado que lo que la mayoría montaría desde cero.
Lo que no trae es la parte específica de tu proyecto: qué comando verifica tu código, qué invariantes de tu dominio no se pueden romper, qué rutas están prohibidas. Esa parte la escribes tú, y es la que separa un agente útil de un generador de PRs.
¿Cómo sé si mi problema es del grafo o del harness?
Mira el modo de fallo. Si el agente da vueltas, repite trabajo, pierde el hilo a mitad o mezcla roles que deberían estar separados, tu problema es de estructura: te falta grafo.
Si el agente termina rápido, entrega algo que parece correcto y luego no compila, no pasa los tests o rompe un caso que nadie había mirado, tu problema es de verificación: te falta harness. Ese segundo caso es el habitual, y además es el caro, porque el tiempo se te va entero en revisar a mano lo que la máquina genera. De ese cuello de botella hablé en por qué verificar es el nuevo cuello de botella.
¿Puedo aplicar harness engineering sin agentes autónomos?
Sí, y es donde más rápido se nota. Si usas la IA solo como autocompletado avanzado en el editor, el harness sigue siendo tu red: tsc en modo estricto, linter sin warnings, tests que se ejecutan al guardar.
La diferencia es el margen de error. Con un humano al mando, un harness flojo produce fricción. Con un agente corriendo solo durante veinte minutos, produce un desastre repartido en veinte commits.
Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.
¿Te resultó útil este artículo?
Compártelo con tu comunidad y ayuda a otros desarrolladores.
