💡 Como ler este módulo
Assumo que você já terminou a Trilha 1 — sabe o que é skill, sub-agent e memória. Agora vamos descer ao esqueleto. Se uma seção parecer teórica demais, pule para o Lab no tópico 6 e volte depois; o trace no LangSmith torna tudo concreto em minutos.
🧭 O que é LangGraph no DeerFlow
LangGraph é a biblioteca da LangChain que o DeerFlow usa como substrato do lead_agent. O modelo mental é simples: um grafo direcionado onde cada node é uma função Python que recebe um state compartilhado e devolve um patch desse state; cada edge decide o próximo node com base no state resultante. Não há mágica — é um loop que lê o state, executa um node, aplica o patch, consulta as edges e avança.
🎯 Conceito Central
O LangGraph dá três coisas que o DeerFlow explora ao máximo: state tipado, edges condicionais e checkpointing. Juntas, transformam um loop de LLM em algo que se pode pausar, inspecionar, retomar e versionar.
- •State — TypedDict/Pydantic com reducers que definem como cada campo é merjado (append em listas, replace em escalares).
- •Nodes — funções puras
(state) -> partial_state. Sem efeito colateral fora do state, toda IO vai por tool. - •Edges condicionais — roteamento decidido por uma função que olha o state. É aqui que o agente "decide".
- •Checkpointer — salva o state a cada passo num backend (SQLite/Postgres). Crash recovery sai de graça.
💡 Por que grafo e não pipeline
Pipeline assume que você sabe a ordem dos passos antes de começar. Um agente não sabe — precisa decidir "chamo a skill de research ou pergunto de volta?" olhando o state atual. Grafo com edge condicional representa isso nativamente; pipeline exige um dispatcher por fora e você reinventa metade do LangGraph até o fim do mês.
🧠 O grafo do lead_agent
O lead_agent do DeerFlow é, no fundo, um grafo minúsculo com cinco nodes e duas edges condicionais. Tudo que você vê acontecer numa sessão — plano, chamada de skill, execução de tool, reflexão, resposta final — passa por eles. Entender essa topologia é o que torna trivial depurar qualquer comportamento estranho.
entry — recepção do turno
Normaliza input, carrega memórias relevantes, injeta guardrails
Recebe a mensagem do usuário, roda retrievers de memória, monta o system prompt com as instruções obrigatórias e publica tudo no state. Nenhuma decisão de negócio acontece aqui — só montagem.
plan — chamada ao LLM
O modelo decide: responder direto, chamar skill, despachar custom agent
Única chamada "cara". O output vem como tool_calls estruturados. Aqui é onde trigger accuracy de skill vive ou morre — se a descrição da skill não bateu, o modelo vai ignorá-la.
route — edge condicional
Inspeciona tool_calls e decide o próximo node
Se veio tool_call, vai para act. Se veio mensagem final, vai para respond. Se veio despacho para custom agent, troca o subgrafo inteiro. É 20 linhas de Python e mudar isso é reescrever o harness.
act — execução de tool/skill
Roda a tool, aplica middleware, grava resultado no state
Aqui entra o sandbox, o MCP client, o search provider, o filesystem. Errors são capturados e viram mensagens do sistema no state — o modelo lê e decide se tenta de novo. Depois volta para plan.
respond — saída do turno
Formata resposta final, persiste memórias novas, fecha trace
Passa pela camada de saída (formatação por canal: Slack, web, API), grava memórias "worth remembering" detectadas no turno, marca o checkpoint final e devolve ao chamador.
💡 Loop natural: plan ↔ act
Entre plan e act existe um ciclo. Enquanto o modelo produzir tool_calls, o grafo oscila entre os dois. Quando o modelo produz só texto, a edge condicional manda para respond e fecha o turno. A profundidade desse ciclo é o "max iterations" do agente — um guardrail que evita loops infinitos por mais que o modelo insista.
🧩 Split harness ↔ app
O documento HARNESS_APP_SPLIT.md define uma fronteira que parece burocrática e não é: onde termina o harness (a engine do agente) e onde começa a app (o produto que usa essa engine). Errar esse recorte é o motivo número um de forks que quebram em toda atualização.
🦴 Harness — o esqueleto
- •Grafo LangGraph do lead_agent
- •Loader de skills/tools/MCP
- •Middleware pipeline e ciclo de tool
- •Memory store e context engine
- •Sandbox adapters (local/docker/k8s)
- •Observabilidade (LangSmith hooks)
📦 App — o produto rodando dentro
- •Catálogo de skills específicas do cliente
- •Prompts e personalidade (SOUL)
- •Canais de entrada (Slack, web, API)
- •Credenciais, flags e config por ambiente
- •UI, autenticação, billing
- •Logs de negócio e métricas de produto
⚠️ Regra de ouro da fronteira
A app depende do harness. O harness nunca depende da app. Se um arquivo dentro de deerflow/harness/ importa algo de deerflow/app/, é bug. Esse é o único teste objetivo que separa um fork saudável de um fork que vai doer para manter.
📐 Context engineering na prática
Agora na Trilha 2 você precisa saber como o DeerFlow faz context engineering, não só que faz. São três políticas plugadas dentro do node entry: janela ativa, compactação e summarization por sub-agent. Cada uma resolve um problema diferente.
📊 Dados do context engine
- Janela ativa — mantém as N últimas mensagens inteiras (default 20) + turno atual. Tudo mais antigo cai na pilha fria.
- Threshold de compactação — quando tokens ativos > 70% da janela do modelo, dispara summarization do trecho mais antigo da pilha fria.
- Pin de guardrails — system prompt e instruções obrigatórias são ancorados no topo e nunca são compactados.
- Memórias seletivas — retriever só traz as 5 memórias mais relevantes por turno, com score mínimo de similaridade.
- Tool outputs grandes — resultados acima de ~4k tokens são sumarizados por sub-agent antes de voltar ao loop.
💡 Por que isso importa na depuração
Quando um agente "esquece" uma instrução a meio do turno, 9 em 10 vezes não é falha do modelo — é que o texto caiu na compactação. No LangSmith trace isso aparece claro: o node entry mostra o prompt montado, e você vê o que sobreviveu. Sempre olhe o prompt final antes de culpar o modelo.
🚧 Guardrails
Guardrail, no DeerFlow, é uma instrução ancorada no system prompt que não pode ser sobrescrita nem pelo usuário nem por skill. Ele vive num slot protegido do context engine; compactação, memória e prompts de skills não o tocam. É o único lugar onde "instrução obrigatória" realmente significa obrigatório.
🎯 Categorias típicas
- Safety— regras de conteúdo, PII, tópicos proibidos.
- Escopo— "só responda sobre produto X", "sempre cite fonte".
- Tool allow/deny— lista branca de tools que podem ser chamadas por turno.
- Output shape— formato obrigatório de resposta quando há contrato com o canal.
🚨 Guardrail não é prompt "normal"
Muitos times tentam implementar guardrail colando texto no prompt de uma skill. Não funciona. Na primeira compactação ele vira resumo genérico; na primeira conversa longa some; e nenhum deny-list de tool é aplicado de verdade.
No DeerFlow, guardrails passam pelo subsistema certo: config no harness, injeção no node entry e enforcement no middleware de tools. Commits recentes da ByteDance mexeram aqui justamente para blindar contra prompt injection em memórias carregadas por retriever.
🧪 Lab: traçar o grafo via LangSmith
Chega de teoria. Vamos ligar o LangSmith, rodar uma query complexa e abrir o trace inteiro do grafo. O objetivo é identificar, em minutos, onde o tempo foi gasto e quais tool calls falharam.
Passos do lab
- 1.Criar conta no LangSmith e gerar um API key em
Settings → API Keys. O tier free é suficiente para o lab. - 2.Exportar variáveis antes de subir o DeerFlow:
LANGCHAIN_TRACING_V2=trueLANGCHAIN_API_KEY=lsv2_...LANGCHAIN_PROJECT=deerflow-lab-2.1 - 3.Reiniciar o harness:
docker compose restart deerflow. Confirme no log a linha "LangSmith tracing enabled". - 4.Rodar uma query pesada: peça um deep-research cruzando duas fontes diferentes, algo como "compare crescimento do PIB brasileiro e argentino nos últimos 10 anos, cite IBGE e INDEC, gere um gráfico". Isso garante ao menos 3 tool calls.
- 5.Abrir o trace em
smith.langchain.comno projetodeerflow-lab-2.1. Você vê a árvore:entry → plan → act(tool) → plan → act(tool) → ... → respond. - 6.Identificar o gargalo: ordene por Latency. Quase sempre um único node domina — geralmente uma tool de busca externa. Esse é o alvo a otimizar.
- 7.Exportar o trace como JSON (botão Export no canto superior direito do run). Guarde — vai ser sua evidência quando abrir ticket interno ou issue no repositório do DeerFlow.
💡 Entregável do lab
Um screenshot ou JSON do trace com: (a) número de iterações plan↔act, (b) tempo total, (c) tool mais lenta, (d) tokens estimados no prompt final. Se conseguir responder essas quatro em 2 minutos olhando o LangSmith, o lab serviu.
📝 Resumo do Módulo
Próximo Módulo:
2.2 — ⚙️ Middleware e execução
Como o DeerFlow intercepta, transforma e recupera tool calls. Inclui lab hands-on de middleware em Python.