🚶 Elige: recorrer en anchura o profundizar
Recorrer (o «hacer el recorrido») y visitar los nodos de un grafo siguiendo las aristas. Hay dos maneras básicas, y la elección entre ellas no es un detalle técnico: es una decisión estratégica que vas a repetir con agentes.
🆕 ¿Nuevo aquí? BFS y DFS
- BFS (breadth-first search, búsqueda en anchura): visita a todos los vecinos de un nodo antes de profundizar. Imagina explorar un edificio piso por piso.
- DFS (depth-first search, búsqueda en profundidad): elige un camino y lo sigue hasta el final antes de volver e intentar otro. Imagina entrar en un pasillo hasta la última puerta antes de regresar.
Qué observar: e o mismo grafo de ambos lados — solo cambia el orden de los números. En anchura, el 4 y el 5 solo aparecen después de todo el segundo nivel. En profundidad, el 3 y el 4 aparecen antes de que siquiera se toque la rama de la derecha. Ninguno de los dos es “mejor”: la anchura encuentra primero lo más cercano, la profundidad encuentra una responde rápido.
✓ La amplitud sirve cuando
- ✓Quieres el más cercano / el más corto (grados de separación)
- ✓Vale más revisar muchas opciones superficialmente que profundizar en una
- ✓Con agentes: «dame 8 enfoques diferentes, sin profundizar»
✓ La profundidad sirve cuando
- ✓Cualquier solución válida ya resuelve el problema y quieres llegar rápido
- ✓El camino solo tiene sentido completo (no se puede evaluar a medias)
- ✓Con agentes: «sigue esta hipótesis hasta el final antes de cambiar»
⚠️ La regla que evita que se bloquee
Marca quién ya fue visitado. Sin eso, cualquier ciclo convierte tu recorrido en un bucle infinito: el algoritmo va y viene entre los mismos dos nodos para siempre. Es el error número 1 de quien escribe recorridos a mano.
Conceptos clave
Paso a paso
Corredor hasta el final
Sin marcar, el ciclo se atasca
Anchura o profundidad, con agentes
🛣️ Encuentra el camino económico (no el más corto)
«Camino mínimo» casi siempre significa menor suma de pesos, no menos saltos. Son cosas diferentes y confundirlas cuesta dinero. Un camino de 2 aristas caras puede ser peor que uno de 5 aristas baratas.
En la rutina matutina del módulo anterior quedó claro: el atajo tenía menos nodos y también costaba menos. Pero cambia el peso por «riesgo» y el diseño se invierte: el atajo puede ser el camino más corto y el más peligroso al mismo tiempo. El peso que eliges define qué es “mejor”.
Elige el peso antes de optimizar
¿Tokens? ¿Latencia? ¿Riesgo? ¿Confianza? Cada elección produce una ruta óptima diferente. Optimizar sin declarar el peso es optimizar a ciegas.
El peso puede cambiar durante la ejecución
La latencia de una API aumenta, un modelo se congestiona, el precio cambia. Una ruta óptima calculada una vez se convierte en una apuesta al cabo de un tiempo.
Más de un peso al mismo tiempo
Cuando quieres algo barato e confiable, no existe «el» óptimo: hay concesiones. Y ahí ya estás en el problema que la Trilha 2 llama «una métrica nunca basta».
Conceptos clave
Cuenta el peso, no los saltos
Declara antes
La ruta envejece
Se convierte en trade-off, no en óptimo
📋 Descubre el orden: topológico
Orden topológico y es una fila de ejecución válida en un DAG: nadie aparece antes
que alguien de quien depende. Es lo que el make calcula, es lo que calcula tu CI —
y es lo que calculará el orquestador de tu grafo de agentes.
🎯 Dos consecuencias prácticas
- •No existe un único orden. Varias colas pueden ser válidas. Eso es bueno: significa que hay libertad, y esa libertad es exactamente el espacio para el paralelismo.
- •Si no se puede ordenar, hay un ciclo. El algoritmo se bloquea y eso es un diagnóstico, no es un bug: acaba de mostrarte una dependencia circular que no habías visto.
🧪 Ejemplo práctico: ordena tu grafo y detecta ciclos
Objetivo: dado un grafo de dependencias, obtener el orden de ejecución y recibir un error claro si hay un ciclo. Pégalo en la consola del navegador (F12) o ejecútalo en Node.
// grafo: no -> lista de quem DEPENDE dele (roda depois)
const dep = {
"pesquisar": ["escrever"],
"escrever": ["revisar"],
"coletar_dados": ["escrever"],
"revisar": []
};
function ordenar(g) {
const grau = {}, saida = [];
Object.keys(g).forEach(n => grau[n] = 0);
Object.values(g).flat().forEach(n => grau[n] = (grau[n] || 0) + 1);
let prontos = Object.keys(grau).filter(n => grau[n] === 0);
while (prontos.length) {
console.log("rodam em PARALELO agora:", prontos.join(", "));
const proximos = [];
for (const n of prontos) {
saida.push(n);
for (const viz of (g[n] || [])) if (--grau[viz] === 0) proximos.push(viz);
}
prontos = proximos;
}
if (saida.length !== Object.keys(grau).length)
throw new Error("CICLO detectado — nao existe ordem valida");
return saida;
}
console.log("ordem final:", ordenar(dep).join(" → "));
Cómo verificar: la primera línea debe decir
rodam em PARALELO agora: pesquisar, coletar_dados — las dos sin dependencias.
Después escrever, después revisar.
Ahora fuerza un ciclo: agrega "revisar": ["pesquisar"] y ejecútalo de nuevo: tiene que lanzar el error de CICLO.
Si lo lanza, acabas de implementar la detección de dependencia circular.
Cambia: los nombres por <as etapas do seu processo>. Las líneas «se ejecutan EN PARALELO» son tu plan de asignación.
Conceptos clave
Nadie antes de tu dependencia
Varias órdenes sirven
Cero = listo para ejecutarse
Diagnóstico, no bug
⚡ Cuenta el paralelismo real
El script anterior ya imprimió la respuesta: cada línea “se ejecuta EN PARALELO ahora” es un nivel del grafo, y el tamaño de esa línea es cuántos trabajadores caben en ese momento. Esto vale igual para personas y agentes. Lanzar 20 agentes en un grafo que solo libera 3 nodos a la vez no acelera nada: solo gasta.
Qué observar: las tres franjas tienen anchos diferentes — 2, 3 y 1. El último nivel (“unir”) es un cuello de botella: por muchos agentes que tengas, por ahí pasa uno a la vez. Este patrón — abrir en varios y luego converger en uno solo — es el fan-out / fan-in que la Trilha 4 nombrará.
Conceptos clave
Quién está listo al mismo tiempo
Límite de trabajadores
Nivel de ancho 1
Se abre y después se une
🌀 Escapa del ciclo: límite, progreso, intervención humana
El ciclo no es un defecto: es lo que permite repetir el trabajo hasta que quede bien. El defecto es un ciclo sin salida. Hay exactamente tres tipos de salida, y todo ciclo que dibujes necesita tener al lado escrito al menos uno de ellos.
✓ Salidas legítimas
- ✓Límite: «como máximo 5 vueltas» — rudimentario, pero siempre se detiene
- ✓Criterio de progreso: «si dos vueltas seguidas no mejoraron, abandona»
- ✓Humano: «después de N vueltas, pregúntale a alguien»
✗ Salidas falsas
- ✗«Se detiene cuando esté bien» — ¿quién decide qué está bien?
- ✗«El modelo sabe cuándo detenerse» — no lo sabe, y la cuenta llega
- ✗Deadlock mutuo: A espera a B, B espera a A, nadie avanza
💡 Consejo práctico
Usa los tres juntos, por capas: el criterio de progreso como salida normal, un límite como red de seguridad y una persona como última instancia. El límite por sí solo es un desperdicio (se ejecuta 5 veces aunque 2 bastaran); el criterio por sí solo es arriesgado (si la medición falla, nunca se activa).
Conceptos clave
Repetir hasta que salga bien
Red de seguridad
Salida normal
La espera mutua lo bloquea todo
🔍 Escribe en JSON: el formato que lee el orquestador
En la práctica, un grafo es un diccionario: para cada nodo, la lista de vecinos. Eso se llama lista de adyacencia y resuelve el 95% de los casos. El truco para los agentes es sencillo: además de los vecinos, guarda los datos del nodo — modelo, herramientas, prompt, límite de costo. Eso es lo que transforma un grafo común en un grafo agéntico.
🧪 Ejemplo práctico: el esqueleto que vas a reutilizar hasta el final del curso
Objetivo: tener un archivo grafo.json que describe nodos (con configuración de agente) y aristas (con condición). Guárdalo como grafo.json en la carpeta de tu proyecto.
{
"nos": {
"pesquisar": {
"papel": "Levanta fontes sobre <o tema> e resume em notas curtas",
"modelo": "<modelo rapido e barato>",
"ferramentas": ["busca_web", "ler_url"],
"teto_custo_usd": 0.50
},
"escrever": {
"papel": "Escreve o texto usando SO as notas recebidas",
"modelo": "<modelo bom de escrita>",
"ferramentas": [],
"teto_custo_usd": 1.00
},
"revisar": {
"papel": "Confere fatos e clareza. Responde aprovado: true|false",
"modelo": "<modelo forte de raciocinio>",
"ferramentas": ["busca_web"],
"contexto_limpo": true,
"teto_custo_usd": 1.00
}
},
"arestas": [
{ "de": "pesquisar", "para": "escrever", "quando": "sempre", "passa": "notas" },
{ "de": "escrever", "para": "revisar", "quando": "sempre", "passa": "rascunho" },
{ "de": "revisar", "para": "escrever", "quando": "aprovado == false", "passa": "criticas",
"max_voltas": 2 },
{ "de": "revisar", "para": "FIM", "quando": "aprovado == true" }
]
}
Cómo verificar: cuatro comprobaciones, todas visuales —
(1) el JSON es válido (pégalo en cualquier validador o ejecuta JSON.parse);
(2) existe al menos una arista con quando diferente de "sempre" — si no, escribiste una cola, no un grafo;
(3) toda arista de regreso tiene max_voltas — y es la condición de parada del tema 5;
(4) existe un camino hasta "FIM". Si falta cualquiera de los cuatro, el grafo tiene un defecto real.
Cambia: todo entre < >. Guarda este archivo — la Ruta 5 volverá a él para convertirlo en código ejecutable.
Verificación rápida: tu algoritmo de ordenamiento topológico se bloquea y no puede producir la fila. ¿Qué significa eso?
Conceptos clave
Nodo → vecinos
Modelo, herramientas, límite
Sin ella, solo es una fila
JSON ya basta para empezar
📌 Resumen del módulo
Próxima ruta:
Trilha 2 — Loop Engineering: la anatomía del ciclo, por qué el verificador es el cuello de botella y las cuatro formas estructurales en que un loop te traiciona.