Todo sistema RAG começa simples: um prompt de sistema, alguns documentos recuperados e uma chamada ao modelo. Mas conforme os casos de uso crescem — extrair valores monetários, datas, listas, tabelas, booleanos — o prompt de sistema vira uma monstruosidade. Cada novo tipo de resposta ganha uma cláusula condicional (“se a resposta for uma data, use ISO 8601; se for um valor, use ISO 4217…”), e dois meses depois ninguém lembra qual cláusula resolve qual caso.
Kezhan Shi, na série Enterprise Document Intelligence do Towards Data Science, propõe uma alternativa elegante: o padrão dispatcher para montagem de prompts. Em vez de um mega-prompt monolítico, você mantém um BASE fixo com regras universais (citar fontes, tipar respostas, falhar honestamente) e um conjunto de fragmentos que são acionados conforme o tipo de pergunta.
Como funciona o dispatcher
O coração do sistema é um objeto ParsedQuestion — uma pergunta do usuário já classificada por tipo e forma de resposta. Esse objeto carrega:
- expected_answer_shape: o formato esperado (data, valor monetário, lista, tabela, boolean)
- generation brief: restrições de formato e regras de desambiguação
- keywords: termos extraídos da pergunta original
- structural_hints: pistas como “na página 5” ou “páginas 3 a 7”
Quando a pergunta chega, o dispatcher consulta um ANSWER_REGISTRY que mapeia cada expected_answer_shape para um schema Pydantic e para os fragmentos de prompt relevantes. A composição é determinística: BASE + fragmentos da vez, sem cláusulas condicionais que crescem indefinidamente. Adicionar um novo formato de resposta significa adicionar um fragmento — e nada mais.
Prompt de sistema: BASE + fragmentos
O BASE é agnóstico ao formato e codifica o contrato que vale para toda chamada:
- Toda afirmação precisa de uma linha de origem (
GLOBAL_LINE) - Se a informação não estiver nos documentos, retorne
answer_found=False - Nunca invente dados — o modelo não está “consultando” nada, está prevendo tokens
Os fragmentos são específicos por formato: um para valores monetários (ISO 4217), outro para datas (ISO 8601), outro para listas (“retorne um AnswerItem por elemento”). O dispatcher monta apenas o que a pergunta pede. O schema Pydantic garante a estrutura; o fragmento orienta a estratégia de extração.
Prompt do usuário: pergunta + palavras-chave + passagens
O prompt do usuário é uma casca fina: a pergunta original, as palavras-chave extraídas (para o modelo sinalizar quais apareceram nas passagens) e as linhas dos documentos recuperados, com colunas explicitamente rotuladas (GLOBAL_LINE | page | line_in_page | text). O cabeçalho das colunas aparece duas vezes — no BASE e no prompt do usuário — porque isso resolveu um bug recorrente em que o modelo confundia número de linha global com número de linha por página.
Evidência por campo: FieldExtraction
Quando a resposta tem múltiplos campos tipados (ex: extrair nome, email, telefone e cargo de um currículo), o padrão escala com FieldExtraction[T]: cada campo carrega seu próprio valor tipado mais a página, linha inicial e linha final de onde foi lido. Em vez de um bloco de evidências genérico no topo da resposta, cada campo responde individualmente: “o email veio da linha 47, página 2”.
O verificador pós-LLM percorre o schema, confere se cada citação realmente existe no documento original e sinaliza campos com citações alucinadas. É barato e resolve o medo número um de empresas que adotam RAG: o modelo inventar uma justificativa que parece plausível mas não existe.
Few-shot dinâmico: exemplos que chegam na hora
Um banco de exemplos validados (pergunta + resposta JSON correta) é indexado com o mesmo embedding do corpus. Quando chega uma pergunta nova, o sistema recupera os 3-5 exemplos mais próximos e os insere no prompt como worked examples. Se um exemplo anterior já resolveu a normalização de “$” para “USD”, o modelo copia o padrão por demonstração, sem precisar rederivar de um parágrafo de regras que ele acabou de ignorar.
O custo é real — cada exemplo gasta tokens —, então o mecanismo é condicional: só é ativado quando o banco existe, quando a pergunta é similar a exemplos do banco e quando os dados do banco não são sensíveis.
O que levar para o seu projeto
- Mega-prompts não escalam. Separe regras universais (BASE) de regras por formato (fragmentos).
- Rotule as colunas do prompt do usuário. O modelo confunde número de linha global com número de linha por página se você não disser qual é qual — e isso acontece com frequência.
- Evidência por campo, não por resposta. Quando um campo está errado, você quer saber exatamente qual linha o modelo leu — não um bloco genérico de “páginas consultadas”.
- Guarde o payload bruto da API.
output_textnão basta. Salvar o JSON completo da resposta (tokens, modelo, request ID, system fingerprint) custa alguns KB por chamada e salva horas de investigação forense depois. - Few-shot dinâmico resolve casos difíceis. Um banco de exemplos validados, indexado com embeddings e puxado por similaridade, corrige erros de formatação que regras textuais não conseguem evitar.
O código completo com notebooks executáveis está disponível no GitHub do projeto doc-intel. O dispatcher é parte de uma série maior sobre os quatro blocos de um sistema RAG empresarial: parsing de documentos, parsing de perguntas, recuperação e geração — cada um com contrato, auditoria e verificação.
Descubra mais sobre noticiAI
Assine para receber nossas notícias mais recentes por e-mail.



