MÓDULO 2.1

🏗️ Arquitetura profunda

Abrir a caixa-preta. Como o lead_agent é um grafo LangGraph, onde mora a fronteira entre harness e app, e que políticas de contexto impedem um agente de 60 minutos de colapsar no meio do caminho.

6
Tópicos
60
Minutos
Avançado
Nível
Teoria + Lab
Tipo

💡 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.

1

🧭 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.

2

🧠 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.

1

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.

2

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.

3

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.

4

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.

5

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.

3

🧩 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.

4

📐 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.

5

🚧 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.

6

🧪 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. 1.
    Criar conta no LangSmith e gerar um API key em Settings → API Keys. O tier free é suficiente para o lab.
  2. 2.
    Exportar variáveis antes de subir o DeerFlow:
    LANGCHAIN_TRACING_V2=true
    LANGCHAIN_API_KEY=lsv2_...
    LANGCHAIN_PROJECT=deerflow-lab-2.1
  3. 3.
    Reiniciar o harness: docker compose restart deerflow. Confirme no log a linha "LangSmith tracing enabled".
  4. 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. 5.
    Abrir o trace em smith.langchain.com no projeto deerflow-lab-2.1. Você vê a árvore: entry → plan → act(tool) → plan → act(tool) → ... → respond.
  6. 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. 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

LangGraph = state tipado + edges condicionais + checkpointer — três recursos que transformam um loop de LLM em algo pausável, inspecionável e versionável.
lead_agent = 5 nodes, 2 edges — entry → plan ⇄ act → respond. O ciclo plan↔act é o que faz o agente "agir".
Harness nunca depende da app — é o único teste objetivo de um fork saudável.
Context engine faz janela + compactação + summarization — quando o agente "esquece", o culpado costuma ser a compactação, não o modelo.
Guardrails vivem em slot protegido — prompt colado numa skill não é guardrail.
LangSmith torna o grafo concreto — liga as três variáveis, roda uma query pesada, e em minutos você lê o comportamento real.

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.