← Voltar aos artigosTRILHA 05 · Construindo um assistente conversacional: LangGraph, DSPy e Redis · PARTE 10 DE 10
ObservabilidadeEngenharia

O pipeline completo: do texto do usuário à resposta formatada

Nos últimos meses publiquei posts separados sobre cada componente do assistente conversacional de e-commerce: memória episódica, roteadores, DSPy, anáforas, Redis Stack, guardrails, observabilidade, QueryBuilder.

Esse post fecha o ciclo, mostrando o caminho completo de uma query: do texto que o usuário digitou até a resposta que ele recebe, passando por cada componente na ordem em que é executado.

Novamente vou usar como exemplo o domínio e-commerce. A arquitetura é a mesma que documentei ao longo da série.


A query de exemplo#

Ampliar ↗

Turno 2, numa sessão que já teve uma pergunta sobre tênis feminino no turno anterior.


Etapa 1 Supervisor: macro roteamento sem LLM#

A mensagem chega no supervisor. Antes de qualquer LLM, o semantic router por embeddings classifica a macro-intent:

Ampliar ↗

Score 0.81, acima do threshold de 0.72 para busca_catalogo, não ambíguo. O supervisor roteia para o subgrafo de busca de catálogo sem chamar nenhum LLM.

Latência desta etapa: ~35ms.


Etapa 2 Setup node: contexto de negócio em paralelo#

O subgrafo começa com o setup_node. Ele carrega em paralelo com asyncio.gather tudo que o subgrafo vai precisar:

Ampliar ↗

Num sistema sequencial, essas duas chamadas seriam ~300ms cada. Em paralelo, ficam no tempo da mais lenta: ~320ms no P50.

O estado do grafo sai do setup com user_preferences, browsing_history_summary e account_context preenchidos. Se o cliente já comprou tênis antes, isso está no contexto quando o LLM gerar a resposta.

Latência desta etapa: ~320ms.


Etapa 3 Router node: anáfora + DSPy#

O router_node é onde a decisão cognitiva acontece. Mas antes de chegar no DSPy, o resolver de anáforas verifica se a mensagem tem referências implícitas:

Ampliar ↗

"tênis masculino Nike até R$ 300 com frete grátis e boa avaliação" não tem pronomes, não tem continuação implícita. O resolver não modifica a mensagem.

Se o usuário tivesse dito "e esse com frete grátis?", o resolver teria expandido para "e o Nike Air Force 1 Feminino com frete grátis?" antes de passar para o DSPy.

Agora o DSPy:

Ampliar ↗

O scope é busca_catalogo, o router_node seleciona a CatalogSearchTool e constrói os argumentos a partir do RouterOutput:

Ampliar ↗

Latência desta etapa: ~2.1s (embedding da query + LLM DSPy).


Etapa 4 Tools node: FT.SEARCH no Redis Stack#

A tool recebe os argumentos do RouterOutput e chama o QueryBuilder:

Ampliar ↗

O Redis retorna 8 produtos (9 encontrados, 1 indica has_more=True). Cada documento chega com 5 campos (projeção Slim):

Ampliar ↗

A tool formata o output para o LLM:

Ampliar ↗

O token [HAS_MORE_PAGES=true] no output da tool vai ser lido pelo generate_node para saber se deve preparar a UX de paginação.

Latência desta etapa: ~45ms.


Etapa 5 Generate node: DSPy Advisor com Refine#

O generate_node monta o contexto e chama o CatalogAdvisorModule:

Ampliar ↗

O CatalogAdvisorModule usa dspy.Refine(N=3, threshold=1.0). Na primeira tentativa, o LLM gera:

Ampliar ↗

A função de recompensa detecta "ótima escolha", que violação da regra de opinião qualitativa. Score: 0.75. O Refine gera novamente.

Segunda tentativa:

Ampliar ↗

Score: 1.0. O Refine encerra.

Ampliar ↗

Latência desta etapa: ~3.6s (2 chamadas ao LLM).


Etapa 6 Critique node: validação final#

O critique_node recebe a resposta gerada e valida:

Ampliar ↗

Fast path ativo para scope="busca_catalogo". Checks síncronos em <5ms:

  1. Strip de oferta de PDF prematura → não encontrou

  2. Quick regex (imperativo) → passou

  3. Disclaimer ausente → não há disclaimer obrigatório, não aplica

  4. Perspective check → passou

Ampliar ↗

Latência desta etapa: <5ms.


O trace completo#

Ampliar ↗

O que cada componente decidiu#

  • O semantic router decidiu que é busca de catálogo, sem LLM, em 35ms.

  • O resolver de anáforas decidiu que a mensagem não tem referência implícita em <1ms.

  • O DSPy router decidiu scope, ferramenta e 8 parâmetros de filtro em 2.1s.

  • O QueryBuilder transformou esses parâmetros numa query FT.SEARCH segura em <1ms.

  • O Redis Stack executou a busca e retornou 5 campos × 8 produtos em 45ms.

  • O DSPy Refine gerou a resposta, detectou uma violação e se corrigiu em 3.6s.

  • O critique_node confirmou que nada passou em 5ms.

Cada componente tem uma responsabilidade única, nenhum sabe o que o anterior fez. A arquitetura de grafo garante que eles se conectam pelo estado.


O que custou mais e onde há espaço de melhoria#

O DSPy domina a latência: 2.1s no router + 3.6s no generate = 5.7s dos 6.1s totais.

O router DSPy só pode ser acelerado com um modelo mais rápido ou com o fast-path de paginação (que bypassa o DSPy completamente quando o usuário pede "mais" resultados). O generate pode ser acelerado reduzindo a taxa de re-geração do Refine, pois se a Signature for mais explícita sobre a regra de opinião, a primeira tentativa passa e economizamos ~1.8s.

O Redis Stack é irrelevante na latência total: 45ms para busca e retorno de 8 documentos paginados. É onde a modelagem Fat/Slim/Híbrido pagou o custo de construção.


Fechando a série#

Esta série cobriu o sistema de ponta a ponta:

→ Fat/Slim/Híbrido no Redis Stack: como modelar dados para o LLM não se perder

→ DSPy: prompts como código compilável: os 4 conceitos fundamentais

→ Migração Agno → DSPy: o que muda quando o roteador já é LLM

→ Anáforas: resolver antes do LLM, com EntityStack e regex

→ Três roteadores: DSPy, customizado e Aurélio AI comparados

→ Guardrails regulatórios: critique_node com três camadas de defesa

→ Observabilidade com Langfuse: traces, metadata e detecção de degradação

→ dspy.Refine: autocorreção em runtime com função de recompensa

→ QueryBuilder: Pydantic → FT.SEARCH seguro

→ Pipeline completo: a visão de síntese

Cada artigo existe independentemente. Juntos, formam a documentação de uma arquitetura real em produção, com os trade-offs, os bugs silenciosos e as decisões que não ficam visíveis no código.

Publicado originalmente no LinkedIn. Ver publicação ↗