🚶 Escolha: varrer em largura ou descer fundo
Percorrer (ou “fazer travessia”) e visitar os nos de um grafo seguindo as arestas. Existem duas maneiras basicas, e a escolha entre elas nao e detalhe tecnico: e uma decisao de estrategia que voce vai repetir com agentes.
🆕 Novo aqui? BFS e DFS
- BFS (breadth-first search, busca em largura): visita todos os vizinhos de um no antes de descer mais fundo. Imagine explorar um predio andar por andar.
- DFS (depth-first search, busca em profundidade): escolhe um caminho e desce ate o fim antes de voltar e tentar outro. Imagine entrar num corredor ate a ultima porta antes de voltar.
O que olhar: e o mesmo grafo dos dois lados — so muda a ordem dos numeros. Na largura, o 4 e 5 so aparecem depois de todo o segundo andar. Na profundidade, o 3 e o 4 aparecem antes do ramo da direita sequer ser tocado. Nenhuma das duas e “melhor”: largura acha o mais proximo primeiro, profundidade acha uma resposta rapido.
✓ Largura serve quando
- ✓Voce quer o mais proximo / mais curto (graus de separacao)
- ✓Vale mais varrer muitas opcoes raso do que aprofundar uma
- ✓Com agentes: “me da 8 abordagens diferentes, sem se aprofundar”
✓ Profundidade serve quando
- ✓Qualquer solucao valida ja resolve, e voce quer chegar rapido
- ✓O caminho so faz sentido inteiro (nao da pra avaliar pela metade)
- ✓Com agentes: “persiga esta hipotese ate o fim antes de trocar”
⚠️ A regra que evita o travamento
Marque quem ja foi visitado. Sem isso, qualquer ciclo transforma sua travessia em loop infinito — o algoritmo fica indo e voltando entre os mesmos dois nos pra sempre. E o bug numero 1 de quem escreve travessia na mao.
Conceitos-chave
Andar por andar
Corredor ate o fim
Sem marcar, ciclo trava
Largura ou fundo, com agentes
🛣️ Ache o caminho barato (nao o mais curto)
“Caminho minimo” quase sempre significa menor soma de pesos, nao menor numero de saltos. Sao coisas diferentes e confundi-las custa dinheiro. Um caminho de 2 arestas caras pode ser pior que um de 5 arestas baratas.
Na rotina da manha do modulo anterior isso ficou obvio: o atalho tinha menos nos e tambem custava menos. Mas troque o peso por “risco” e o desenho se inverte — o atalho pode ser o caminho mais curto e o mais perigoso ao mesmo tempo. O peso que voce escolhe define o que e “melhor”.
Escolha o peso antes de otimizar
Token? Latencia? Risco? Confianca? Cada escolha produz uma rota otima diferente. Otimizar sem declarar o peso e otimizar no escuro.
Peso pode mudar durante a execucao
A latencia de uma API sobe, um modelo fica congestionado, o preco muda. Rota otima calculada uma vez e chute depois de um tempo.
Mais de um peso ao mesmo tempo
Quando voce quer barato e confiavel, nao existe “o” otimo — existem trocas. E ai voce ja esta no problema que a Trilha 2 chama de “uma metrica nunca basta”.
Conceitos-chave
Conte peso, nao saltos
Declare antes
Rota envelhece
Vira trade-off, nao otimo
📋 Descubra a ordem: topologica
Ordem topologica e uma fila de execucao valida num DAG: ninguem aparece antes
de alguem de quem ele depende. E o que o make calcula, e o que o seu CI calcula —
e e o que o orquestrador do seu grafo de agentes vai calcular.
🎯 Duas consequencias praticas
- •Nao existe uma ordem so. Varias filas podem ser validas. Isso e bom: significa que ha liberdade — e essa liberdade e exatamente o espaco do paralelismo.
- •Se nao da pra ordenar, ha ciclo. O algoritmo trava e isso e um diagnostico, nao um bug: ele acabou de te mostrar uma dependencia circular que voce nao tinha visto.
🧪 Exemplo pratico: ordene o seu grafo e detecte ciclo
Objetivo: dado um grafo de dependencias, obter a ordem de execucao — e receber um erro claro se houver ciclo. Cole no console do navegador (F12) ou rode no 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(" → "));
Como verificar: a primeira linha deve dizer
rodam em PARALELO agora: pesquisar, coletar_dados — as duas sem dependencia.
Depois escrever, depois revisar.
Agora force um ciclo: acrescente "revisar": ["pesquisar"] e rode de novo — tem que estourar o erro de CICLO.
Se estourar, voce acabou de implementar deteccao de dependencia circular.
Troque: os nomes por <as etapas do seu processo>. As linhas “rodam em PARALELO” sao o seu plano de alocacao.
Conceitos-chave
Ninguem antes da sua dependencia
Varias ordens servem
Zero = pronto pra rodar
Diagnostico, nao bug
⚡ Conte o paralelismo real
O script anterior ja imprimiu a resposta: cada linha “rodam em PARALELO agora” e um nivel do grafo, e o tamanho dessa linha e quantos trabalhadores cabem naquele momento. Isso vale igual pra pessoas e pra agentes. Disparar 20 agentes num grafo que so libera 3 nos por vez nao acelera nada — so gasta.
O que olhar: as tres faixas tem larguras diferentes — 2, 3 e 1. O ultimo nivel (“juntar”) e um gargalo: por mais agentes que voce tenha, ali passa um de cada vez. Esse padrao — abrir em varios, depois estreitar num so — e o fan-out / fan-in que a Trilha 4 vai chamar pelo nome.
Conceitos-chave
Quem esta pronto junto
Teto de trabalhadores
Nivel de largura 1
Abre e depois junta
🌀 Escape do ciclo: teto, progresso, humano
Ciclo nao e defeito — e o que permite repetir trabalho ate ficar bom. O defeito e ciclo sem saida. Existem exatamente tres tipos de saida, e todo ciclo que voce desenhar precisa de pelo menos um deles escrito ao lado.
✓ Saidas legitimas
- ✓Teto: “no maximo 5 voltas” — grosseiro, mas sempre para
- ✓Criterio de progresso: “se duas voltas seguidas nao melhoraram, desiste”
- ✓Humano: “depois de N voltas, pergunta pra alguem”
✗ Saidas falsas
- ✗“Ele para quando estiver bom” — quem decide o que e bom?
- ✗“O modelo sabe a hora de parar” — nao sabe, e a conta chega
- ✗Deadlock mutuo: A espera B, B espera A, ninguem anda
💡 Dica pratica
Use os tres juntos, em camadas: criterio de progresso como saida normal, teto como rede de seguranca, e o humano como ultima instancia. O teto sozinho e desperdicio (roda 5 vezes mesmo quando 2 bastavam); o criterio sozinho e arriscado (se a medicao quebrar, ele nunca dispara).
Conceitos-chave
Repetir ate ficar bom
Rede de seguranca
Saida normal
Espera mutua trava tudo
🔍 Escreva em JSON: o formato que o orquestrador le
Na pratica, um grafo e um dicionario: para cada no, a lista de vizinhos. Isso chama lista de adjacencia e resolve 95% dos casos. O pulo do gato pra agentes e simples: alem dos vizinhos, guarde os dados do no — modelo, ferramentas, prompt, teto de custo. E isso que transforma um grafo comum num grafo agentico.
🧪 Exemplo pratico: o esqueleto que voce vai reusar ate o fim do curso
Objetivo: ter um arquivo grafo.json que descreve nos (com config de agente) e arestas (com condicao). Salve como grafo.json na pasta do seu projeto.
{
"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" }
]
}
Como verificar: quatro checagens, todas visuais —
(1) o JSON e valido (cole em qualquer validador ou rode JSON.parse);
(2) existe pelo menos uma aresta com quando diferente de "sempre" — senao voce escreveu uma fila, nao um grafo;
(3) toda aresta de volta tem max_voltas — e a condicao de parada do topico 5;
(4) existe caminho ate "FIM". Se faltar qualquer um dos quatro, o grafo tem um defeito real.
Troque: tudo entre < >. Guarde este arquivo — a Trilha 5 volta nele pra transformar em codigo que roda.
Checagem rapida: seu algoritmo de ordem topologica trava e nao consegue produzir a fila. O que isso significa?
Conceitos-chave
No → vizinhos
Modelo, ferramentas, teto
Sem ela e so uma fila
JSON ja basta pra comecar
📌 Resumo do Modulo
Proxima trilha:
Trilha 2 — Loop Engineering: a anatomia do ciclo, por que o verificador e o gargalo e os quatro jeitos estruturais de um loop te trair.