Phase 05 - Lesson 17

Chatbots — De baseados em regras a neurais a agentes de LLM

A ELIZA respondia com correspondência de padrões. O DialogFlow mapeava intenções. O GPT respondia a partir dos pesos. O Claude executa ferramentas e verifica. Cada era resolveu a pior falha da anterior.

Tipo: Aprender Linguagens: Python Pré-requisitos: Fase 5 · 13 (Question Answering), Fase 5 · 14 (Information Retrieval) Tempo: ~75 minutos

O problema

Um usuário diz "Quero trocar meu voo." O sistema precisa descobrir o que ele quer, qual informação está faltando, como obtê-la e como concluir a ação. Então o usuário diz "espera, e se eu cancelar em vez disso?" e o sistema precisa lembrar o contexto, trocar de tarefa e preservar o estado.

Conversação é difícil para um sistema de ML. A entrada é aberta. A saída tem que ser coerente ao longo de muitos turnos. O sistema pode precisar agir sobre o mundo (trocar um voo, cobrar um cartão). Cada passo errado é visível para o usuário.

As arquiteturas de chatbot passaram por quatro paradigmas, cada um introduzido porque o anterior falhava de forma visível demais. Esta lição percorre todos em ordem. O cenário de produção de 2026 é um híbrido dos dois últimos.

O conceito

Evolução dos chatbots: baseado em regras → recuperação → neural → agente

Baseado em regras (ELIZA, AIML, DialogFlow). Padrões escritos à mão correspondem à entrada do usuário e produzem respostas. Classificadores de intenção roteiam para fluxos predefinidos. Máquinas de estado de preenchimento de slots coletam as informações necessárias. Funcionam brilhantemente dentro do escopo estreito para o qual foram projetadas. Falham imediatamente fora dele. Ainda são usadas em domínios críticos de segurança (autenticação bancária, reserva de passagens aéreas) onde a alucinação não é tolerada.

Baseado em recuperação. Um sistema no estilo de FAQ. Codifique cada par de (enunciado, resposta). Em tempo de execução, codifique a mensagem do usuário e recupere a resposta armazenada mais próxima. Pense no clássico recurso de "artigos similares" do Zendesk. Lida com paráfrases melhor do que regras. Sem geração, então sem alucinação.

Neural (seq2seq). Codificador-decodificador treinado em logs de conversa. Gera respostas do zero. Fluente, mas propenso a saídas genéricas ("Não sei") e desvios factuais. Nunca fica confiavelmente no tópico. A razão pela qual Google, Facebook e Microsoft tiveram chatbots decepcionantes em 2016-2019.

Agentes de LLM. Um modelo de linguagem envolvido em um laço que planeja, chama ferramentas e verifica resultados. Não é um chatbot com um prompt longo. É um laço de agente: planejar → chamar ferramenta → observar resultado → decidir próximo passo. O aterramento priorizando recuperação (RAG) impede que ele alucine. As chamadas de ferramenta permitem que ele realmente faça coisas. Esta é a arquitetura de 2026.

Os quatro paradigmas não são substituições sequenciais. Um chatbot de produção de 2026 roteia através de todos os quatro: baseado em regras para autenticação e ações destrutivas, recuperação para FAQ, geração neural para fraseado natural, agente de LLM para consultas ambíguas e abertas.

Construa

Passo 1: correspondência de padrões baseada em regras

import re


class RulePattern:
    def __init__(self, pattern, response_template):
        self.regex = re.compile(pattern, re.IGNORECASE)
        self.template = response_template


PATTERNS = [
    RulePattern(r"my name is (\w+)", "Nice to meet you, {0}."),
    RulePattern(r"i (need|want) (.+)", "Why do you {0} {1}?"),
    RulePattern(r"i feel (.+)", "Why do you feel {0}?"),
    RulePattern(r"(.*)", "Tell me more about that."),
]


def rule_based_respond(user_input):
    for pattern in PATTERNS:
        m = pattern.regex.match(user_input.strip())
        if m:
            return pattern.template.format(*m.groups())
    return "I don't understand."

A ELIZA em 20 linhas. O truque do reflexo ("I feel sad" → "Why do you feel sad") é a demonstração canônica do psicoterapeuta de Weizenbaum 1966. Ainda instrutivo.

Passo 2: baseado em recuperação (FAQ)

Este trecho ilustrativo requer pip install sentence-transformers (que traz o torch junto). O code/main.py executável desta lição usa, em vez disso, uma similaridade de Jaccard da biblioteca padrão, de modo que a lição roda sem dependências externas.

from sentence_transformers import SentenceTransformer
import numpy as np


FAQ = [
    ("how do i reset my password", "Go to Settings > Security > Reset Password."),
    ("how do i cancel my order", "Go to Orders, find the order, click Cancel."),
    ("what is your return policy", "30-day returns on unused items, original packaging."),
]


encoder = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
faq_questions = [q for q, _ in FAQ]
faq_embeddings = encoder.encode(faq_questions, normalize_embeddings=True)


def faq_respond(user_input, threshold=0.5):
    q_emb = encoder.encode([user_input], normalize_embeddings=True)[0]
    sims = faq_embeddings @ q_emb
    best = int(np.argmax(sims))
    if sims[best] < threshold:
        return None
    return FAQ[best][1]

A recusa baseada em limiar é a escolha de projeto chave. Se a melhor correspondência não estiver próxima o suficiente, retorne None e deixe o sistema escalar.

Passo 3: geração neural (baseline)

Use um pequeno codificador-decodificador ajustado por instrução (FLAN-T5) ou um modelo conversacional fine-tuned. Inutilizável em produção por si só em 2026 (contradição, desvio do tópico, absurdos factuais), mas é usado dentro de sistemas híbridos para fraseado natural. Modelos somente-decodificador no estilo DialoGPT precisam de separadores de turno explícitos e tratamento de EOS para produzir respostas coerentes; um pipeline text2text do FLAN-T5 funciona de imediato para um exemplo didático.

from transformers import pipeline

chatbot = pipeline("text2text-generation", model="google/flan-t5-small")

response = chatbot("Respond politely to: Hi there!", max_new_tokens=40)
print(response[0]["generated_text"])

Passo 4: laço do agente de LLM

A forma de produção de 2026:

def agent_loop(user_message, tools, llm, max_steps=5):
    history = [{"role": "user", "content": user_message}]
    for _ in range(max_steps):
        response = llm(history, tools=tools)
        tool_call = response.get("tool_call")
        if tool_call:
            tool_name = tool_call.get("name")
            args = tool_call.get("arguments")
            if not isinstance(tool_name, str) or tool_name not in tools:
                history.append({"role": "assistant", "tool_call": tool_call})
                history.append({"role": "tool", "name": str(tool_name), "content": f"error: unknown tool {tool_name!r}"})
                continue
            if not isinstance(args, dict):
                history.append({"role": "assistant", "tool_call": tool_call})
                history.append({"role": "tool", "name": tool_name, "content": f"error: arguments must be a dict, got {type(args).__name__}"})
                continue
            result = tools[tool_name](**args)
            history.append({"role": "assistant", "tool_call": tool_call})
            history.append({"role": "tool", "name": tool_name, "content": result})
        else:
            return response["content"]
    return "I could not complete the task in the step budget."

Três coisas a nomear. Ferramentas são funções chamáveis que o LLM pode invocar. O laço termina quando o LLM retorna uma resposta final em vez de uma chamada de ferramenta. O orçamento de passos impede laços infinitos em tarefas ambíguas.

A produção real acrescenta: aterramento priorizando recuperação (injetar documentos relevantes antes de cada chamada de LLM), guardrails (recusar ações destrutivas sem confirmação), observabilidade (registrar cada passo) e avaliações (verificações automatizadas de que o comportamento do agente permanece dentro da especificação).

Passo 5: roteamento híbrido

def hybrid_chat(user_input):
    if is_destructive_action(user_input):
        return structured_flow(user_input)

    faq_answer = faq_respond(user_input, threshold=0.6)
    if faq_answer:
        return faq_answer

    return agent_loop(user_input, tools, llm)


def is_destructive_action(text):
    danger_words = ["delete", "cancel", "charge", "refund", "transfer"]
    return any(w in text.lower() for w in danger_words)

O padrão: regras determinísticas para qualquer coisa destrutiva, recuperação para FAQs prontas, agentes de LLM para todo o resto. É isso que entra em produção nos sistemas de atendimento ao cliente de 2026.

Use

A pilha de 2026:

Caso de uso Arquitetura
Reserva, pagamento, autenticação Máquinas de estado baseadas em regras + preenchimento de slots
FAQs de atendimento ao cliente Recuperação sobre respostas curadas
Chat de ajuda aberto Agente de LLM com RAG + chamadas de ferramenta
Ferramentas internas / assistentes de IDE Agente de LLM com chamadas de ferramenta (buscar, ler, escrever)
Chatbots de companhia / personagem LLM ajustado com prompt de sistema de persona, recuperação sobre o conhecimento

Sempre use roteamento híbrido em produção. Nenhuma arquitetura isolada lida bem com toda requisição. A própria camada de roteamento é tipicamente um pequeno classificador de intenção.

Modos de falha que ainda chegam à produção

  • Fabricação confiante. O agente de LLM afirma ter concluído uma ação que não concluiu. Mitigação: verificar resultados, registrar chamadas de ferramenta, nunca deixar o LLM afirmar ter feito algo sem um retorno de ferramenta bem-sucedido.

  • Injeção de prompt. O usuário insere texto que sobrescreve o prompt de sistema. Classificada como LLM01 no OWASP Top 10 for LLM Applications 2025. Dois sabores: injeção direta (colada no chat) e injeção indireta (escondida em documentos, e-mails ou saídas de ferramentas que o agente lê).

    As taxas de ataque variam por cenário. As taxas de sucesso medidas vão de ~0,5-8,5% entre modelos de fronteira em benchmarks gerais de uso de ferramentas e de programação. Configurações específicas de alto risco (ataques adaptativos contra agentes de programação de IA, orquestração vulnerável) chegaram a ~84%. CVEs de produção incluem o EchoLeak (CVE-2025-32711, CVSS 9.3) — uma falha de exfiltração de dados zero-click no Microsoft 365 Copilot disparada por um e-mail controlado pelo atacante.

    Mitigações: tratar a entrada do usuário como não confiável ao longo de todo o laço; sanitizar antes das chamadas de ferramenta; isolar as saídas de ferramentas do prompt principal; usar o padrão Plan-Verify-Execute (PVE) em que o agente planeja primeiro, depois verifica cada ação contra esse plano antes de executar (isso impede que resultados de ferramentas injetem novas ações não planejadas); exigir confirmação do usuário para ações destrutivas; aplicar privilégio mínimo aos escopos das ferramentas.

    Nenhuma quantidade de engenharia de prompt elimina totalmente esse risco. Camadas externas de defesa em tempo de execução (LLM Guard, validação por allowlist, detecção de anomalias semânticas) são necessárias.

  • Desvio de escopo. O agente sai da tarefa porque uma chamada de ferramenta retornou informação tangencialmente relacionada. Mitigação: contratos de ferramenta estreitos; manter o prompt de sistema focado; adicionar avaliações para a taxa de desvio de tarefa.

  • Laços infinitos. O agente fica chamando a mesma ferramenta. Mitigação: orçamento de passos, deduplicação de chamadas de ferramenta, juiz LLM sobre "estamos progredindo".

  • Esgotamento da janela de contexto. Conversas longas empurram os turnos mais antigos para fora do contexto. Mitigação: resumir turnos antigos, recuperar turnos passados relevantes por similaridade, ou usar um modelo de contexto longo.

Entregue

Salve como outputs/skill-chatbot-architect.md:

---
name: chatbot-architect
description: Design a chatbot stack for a given use case.
version: 1.0.0
phase: 5
lesson: 17
tags: [nlp, agents, chatbot]
---

Given a product context (user need, compliance constraints, available tools, data volume), output:

1. Architecture. Rule-based, retrieval, neural, LLM agent, or hybrid (specify which paths go where).
2. LLM choice if applicable. Name the model family (Claude, GPT-4, Llama-3.1, Mixtral). Match to tool-use quality and cost.
3. Grounding strategy. RAG sources, retrieval method (see lesson 14), tool contracts.
4. Evaluation plan. Task success rate, tool-call correctness, off-task rate, hallucination rate on held-out dialogs.

Refuse to recommend a pure-LLM agent for any destructive action (payments, account deletion, data modification) without a structured confirmation flow. Refuse to skip the prompt-injection audit if the agent has write access to anything.

Exercícios

  1. Fácil. Implemente o respond baseado em regras acima com 10 padrões para um bot de pedidos de uma cafeteria. Teste casos de borda: pedidos duplicados, modificações, cancelamento, intenção pouco clara.
  2. Médio. Construa um híbrido de FAQ + fallback de LLM. 50 entradas de FAQ prontas para um produto SaaS, fallback de LLM com recuperação sobre o site de documentação. Meça a taxa de recusa e a acurácia em 100 perguntas reais de suporte.
  3. Difícil. Implemente o laço do agente acima com três ferramentas (buscar, ler-dados-do-usuário, enviar-e-mail). Execute uma avaliação com 50 cenários de teste, incluindo tentativas de injeção de prompt. Reporte a taxa de desvio de tarefa, a taxa de tarefas falhadas e qualquer sucesso de injeção.

Termos-chave

Termo O que as pessoas dizem O que realmente significa
Intenção O que o usuário quer Rótulo categórico (book_flight, reset_password). Roteado para um handler.
Slot Um pedaço de informação Parâmetro de que o bot precisa (data, destino). Preenchimento de slots é a sequência de perguntas.
RAG Recuperação mais geração Recuperar documentos relevantes, depois aterrar a resposta do LLM.
Chamada de ferramenta Invocação de função O LLM emite uma chamada estruturada com nome + args. O runtime executa, retorna o resultado.
Laço do agente Planejar, agir, verificar Controlador que executa chamadas de LLM intercaladas com chamadas de ferramenta até a tarefa terminar.
Injeção de prompt Usuário ataca o prompt Entrada maliciosa que tenta sobrescrever o prompt de sistema.

Leitura adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).