Engenharia de Contexto para Agentes de IA: Guia Prático com Python

Aprenda engenharia de contexto para agentes de IA com Python. Guia prático com código para memória em camadas, roteamento de contexto, compressão e validação — padrões prontos para produção em 2026.

O Que É Engenharia de Contexto e Por Que Ela Importa em 2026

Se você trabalha com IA em 2026, provavelmente já percebeu algo que pouca gente admite em público: a maioria das falhas em agentes de IA avançados não são falhas do modelo — são falhas de contexto. O LLM é capaz, sim. Mas recebe informações erradas, incompletas ou desorganizadas. E aí o resultado é previsível: alucinações, respostas genéricas e agentes que "quebram" na primeira situação real.

É aqui que entra a Engenharia de Contexto (Context Engineering) — a disciplina de projetar sistemas dinâmicos que fornecem as informações certas, no formato certo, no momento certo. Como Andrej Karpathy definiu: "Context engineering is the delicate art and science of filling the context window with just the right information for the next step."

Pensa assim: se a engenharia de prompt é a arte de escrever a pergunta certa, a engenharia de contexto é a disciplina de construir o ambiente inteiro onde essa pergunta será interpretada. O prompt é uma carta; o contexto é toda a biblioteca onde a IA estuda antes de responder.

E honestamente, em 2026, com janelas de contexto de milhões de tokens, o problema não é mais caber informação — é curar, estruturar e priorizar o que chega ao modelo. Cada token na janela de contexto compete por atenção. Informação irrelevante dilui o sinal, e pesquisas já demonstram que o desempenho do LLM cai mesmo quando a tarefa não muda, bastando encher o contexto com conteúdo desnecessário (o famoso efeito "lost-in-the-middle").

Neste guia, vamos da teoria à prática. Você vai entender os componentes da engenharia de contexto, implementar cada um com Python e sair com padrões prontos para produção.

Engenharia de Contexto vs. Engenharia de Prompt: A Diferença Fundamental

Antes de mergulhar na implementação, vale cristalizar a diferença entre as duas disciplinas — porque confundi-las é, de longe, o erro mais comum em equipes que estão começando a construir agentes.

A Engenharia de Prompt foca em o que dizer ao modelo. Técnicas como few-shot, chain-of-thought e role prompting são ferramentas de prompt engineering. O escopo? Uma única interação.

Já a Engenharia de Contexto foca em o que o modelo sabe quando você diz algo a ele. Ela decide o que preenche a janela de contexto: quais documentos são recuperados, quais ferramentas estão disponíveis, o que é lembrado de interações anteriores, como o histórico é comprimido. O escopo aqui é o sistema inteiro.

Na prática, a engenharia de prompt é um subconjunto da engenharia de contexto. O prompt opera dentro do contêiner que a engenharia de contexto construiu. Sem uma boa estrutura de contexto, mesmo o prompt mais brilhante pode se perder em meio a informações irrelevantes.

CritérioEngenharia de PromptEngenharia de Contexto
FocoInstrução pontualAmbiente informacional completo
EscopoUma interaçãoMúltiplas interações e sistemas
TécnicasFew-shot, CoT, role promptingRAG, memória, compressão, roteamento
ComplexidadeArtesanal e iterativaArquitetônica e sistêmica
Quem implementaQualquer desenvolvedorEngenheiro de IA / Arquiteto de sistemas

Os 6 Componentes da Janela de Contexto

Todo contexto enviado a um LLM pode ser decomposto em seis componentes. Dominar a engenharia de contexto significa saber quando, como e quanto de cada um incluir. Vamos a eles.

1. Instruções do Sistema (System Prompt)

Define o comportamento, personalidade, restrições e formato de saída do agente. É o componente mais estável — quase nunca muda entre interações.

2. Definições de Ferramentas (Tool Definitions)

JSON Schema que informa ao modelo quais funções ele pode chamar, com parâmetros e descrições. Em agentes complexos, o número de ferramentas pode ser enorme, e carregar todas a cada chamada desperdiça um contexto precioso.

3. Informação Recuperada (RAG)

Documentos, trechos de código, dados de APIs — tudo que é buscado dinamicamente para enriquecer o contexto. A qualidade da recuperação define diretamente a qualidade da resposta. Simples assim.

4. Histórico da Conversa (State)

Mensagens anteriores do usuário e do modelo na sessão atual. É o componente que mais cresce ao longo do tempo e o principal candidato a compressão.

5. Memória de Longo Prazo

Informações que persistem entre sessões: preferências do usuário, fatos aprendidos, padrões detectados. Geralmente armazenada em banco de dados vetorial e recuperada por similaridade semântica.

6. Saída Estruturada (Structured Output)

Definições do formato esperado da resposta — JSON Schema, templates, exemplos de saída. Garante que o modelo gere dados que o código consiga parsear sem surpresas.

Implementação Prática: Sistema de Contexto com Python

Chega de teoria. Vamos construir um sistema completo de engenharia de contexto, componente por componente. Usaremos a API da OpenAI como base, mas os padrões se aplicam a qualquer LLM.

Estrutura Base: O Gerenciador de Contexto

O primeiro passo é criar uma classe que trata a janela de contexto como recurso finito — porque é exatamente isso que ela é:

import tiktoken
from dataclasses import dataclass, field
from typing import Optional


@dataclass
class ContextBudget:
    """Gerencia o orçamento de tokens da janela de contexto."""
    max_tokens: int = 128_000
    reserved_for_output: int = 4_096
    system_prompt_tokens: int = 0
    tool_definitions_tokens: int = 0
    rag_tokens: int = 0
    history_tokens: int = 0
    memory_tokens: int = 0

    @property
    def available_tokens(self) -> int:
        used = (
            self.system_prompt_tokens
            + self.tool_definitions_tokens
            + self.rag_tokens
            + self.history_tokens
            + self.memory_tokens
            + self.reserved_for_output
        )
        return max(0, self.max_tokens - used)

    @property
    def utilization_pct(self) -> float:
        used = self.max_tokens - self.available_tokens
        return (used / self.max_tokens) * 100


class ContextEngine:
    """Motor de engenharia de contexto para agentes de IA."""

    def __init__(self, model: str = "gpt-4o", max_context: int = 128_000):
        self.model = model
        self.encoder = tiktoken.encoding_for_model(model)
        self.budget = ContextBudget(max_tokens=max_context)

    def count_tokens(self, text: str) -> int:
        return len(self.encoder.encode(text))

    def build_context(
        self,
        system_prompt: str,
        tools: list[dict],
        rag_documents: list[str],
        history: list[dict],
        memories: list[str],
    ) -> dict:
        """Monta o contexto completo respeitando o orçamento de tokens."""
        import json

        # 1. System prompt (prioridade máxima — sempre incluído)
        self.budget.system_prompt_tokens = self.count_tokens(system_prompt)

        # 2. Definições de ferramentas
        tools_text = json.dumps(tools)
        self.budget.tool_definitions_tokens = self.count_tokens(tools_text)

        # 3. Memórias de longo prazo (alta prioridade)
        memory_text = "\n".join(memories)
        self.budget.memory_tokens = self.count_tokens(memory_text)

        # 4. Documentos RAG (prioridade média — truncar se necessário)
        rag_text = "\n---\n".join(rag_documents)
        rag_token_count = self.count_tokens(rag_text)
        if rag_token_count > self.budget.available_tokens * 0.4:
            rag_documents = self._truncate_rag(
                rag_documents,
                max_tokens=int(self.budget.available_tokens * 0.4)
            )
            rag_text = "\n---\n".join(rag_documents)
        self.budget.rag_tokens = self.count_tokens(rag_text)

        # 5. Histórico (prioridade adaptável — comprimir se necessário)
        history = self._compress_history(
            history, max_tokens=self.budget.available_tokens
        )
        history_text = json.dumps(history)
        self.budget.history_tokens = self.count_tokens(history_text)

        return {
            "system_prompt": system_prompt,
            "tools": tools,
            "rag_context": rag_text,
            "history": history,
            "memories": memory_text,
            "budget": {
                "total": self.budget.max_tokens,
                "used_pct": f"{self.budget.utilization_pct:.1f}%",
                "available": self.budget.available_tokens,
            },
        }

    def _truncate_rag(
        self, documents: list[str], max_tokens: int
    ) -> list[str]:
        """Mantém documentos por ordem de relevância até o limite."""
        result = []
        current_tokens = 0
        for doc in documents:
            doc_tokens = self.count_tokens(doc)
            if current_tokens + doc_tokens > max_tokens:
                break
            result.append(doc)
            current_tokens += doc_tokens
        return result

    def _compress_history(
        self, history: list[dict], max_tokens: int
    ) -> list[dict]:
        """Mantém mensagens recentes, descarta as mais antigas."""
        import json
        result = []
        current_tokens = 0
        for msg in reversed(history):
            msg_tokens = self.count_tokens(json.dumps(msg))
            if current_tokens + msg_tokens > max_tokens:
                break
            result.insert(0, msg)
            current_tokens += msg_tokens
        return result

Esse ContextEngine trata a janela de contexto como um orçamento. Cada componente recebe uma fatia, e o sistema prioriza automaticamente: system prompt primeiro, depois ferramentas e memória, e por último RAG e histórico — que são os mais dispensáveis quando o espaço aperta.

Gerenciamento de Memória: Curta, Trabalho e Longo Prazo

Agentes de produção precisam de um sistema de memória em camadas, meio parecido com a memória humana. Em 2026, a arquitetura dominante usa três camadas — e faz bastante sentido quando você vê na prática.

Memória de Curto Prazo (Janela Deslizante)

Essa é a mais simples. Mantém as N mensagens mais recentes da conversa, intactas e sem compressão:

class SlidingWindowMemory:
    """Memória de curto prazo com janela deslizante."""

    def __init__(self, max_messages: int = 20):
        self.max_messages = max_messages
        self.messages: list[dict] = []

    def add(self, role: str, content: str) -> None:
        self.messages.append({"role": role, "content": content})
        if len(self.messages) > self.max_messages:
            self.messages = self.messages[-self.max_messages:]

    def get_messages(self) -> list[dict]:
        return self.messages.copy()

Memória de Trabalho (Sumarização)

Quando a conversa ultrapassa a janela de curto prazo, as mensagens mais antigas são comprimidas em um resumo. Esta é a abordagem híbrida que domina em 2026 — janela deslizante + sumarização. Na minha experiência, é o melhor custo-benefício para a maioria dos casos:

from openai import OpenAI


class SummarizationMemory:
    """Memória com sumarização automática de histórico antigo."""

    def __init__(
        self,
        client: OpenAI,
        recent_window: int = 10,
        model: str = "gpt-4o-mini",
    ):
        self.client = client
        self.recent_window = recent_window
        self.model = model
        self.messages: list[dict] = []
        self.summary: str = ""

    def add(self, role: str, content: str) -> None:
        self.messages.append({"role": role, "content": content})
        if len(self.messages) > self.recent_window * 2:
            self._compress()

    def _compress(self) -> None:
        """Sumariza mensagens antigas e mantém as recentes."""
        old_messages = self.messages[:-self.recent_window]
        recent_messages = self.messages[-self.recent_window:]

        old_text = "\n".join(
            f"{m['role']}: {m['content']}" for m in old_messages
        )

        prompt = f"""Resumo anterior da conversa:
{self.summary}

Novas mensagens para incorporar ao resumo:
{old_text}

Gere um resumo conciso que preserve:
- Decisões tomadas e seus motivos
- Fatos importantes mencionados
- Tarefas pendentes ou em andamento
- Preferências do usuário identificadas

Responda APENAS com o resumo atualizado, sem preâmbulos."""

        response = self.client.chat.completions.create(
            model=self.model,
            messages=[{"role": "user", "content": prompt}],
            max_tokens=500,
        )
        self.summary = response.choices[0].message.content
        self.messages = recent_messages

    def get_context(self) -> list[dict]:
        """Retorna o contexto completo: resumo + mensagens recentes."""
        context = []
        if self.summary:
            context.append({
                "role": "system",
                "content": (
                    f"Resumo da conversa anterior: {self.summary}"
                ),
            })
        context.extend(self.messages)
        return context

Memória de Longo Prazo (Vetorial)

Para informações que precisam persistir entre sessões — preferências, fatos aprendidos, padrões — usamos um banco de dados vetorial. Aqui vai um exemplo com ChromaDB, que é bem direto de implementar:

import chromadb
import uuid
from datetime import datetime, timezone


class LongTermMemory:
    """Memória de longo prazo com armazenamento vetorial."""

    def __init__(self, collection_name: str = "agent_memory"):
        self.client = chromadb.PersistentClient(path="./memory_db")
        self.collection = self.client.get_or_create_collection(
            name=collection_name,
            metadata={"hnsw:space": "cosine"},
        )

    def store(
        self,
        content: str,
        metadata: dict | None = None,
    ) -> str:
        """Armazena um fato ou preferência na memória de longo prazo."""
        memory_id = str(uuid.uuid4())
        meta = metadata or {}
        meta["created_at"] = datetime.now(timezone.utc).isoformat()

        self.collection.add(
            documents=[content],
            metadatas=[meta],
            ids=[memory_id],
        )
        return memory_id

    def recall(
        self, query: str, n_results: int = 5
    ) -> list[dict]:
        """Recupera memórias relevantes por similaridade semântica."""
        results = self.collection.query(
            query_texts=[query],
            n_results=n_results,
        )
        memories = []
        for i, doc in enumerate(results["documents"][0]):
            memories.append({
                "content": doc,
                "metadata": results["metadatas"][0][i],
                "distance": results["distances"][0][i],
            })
        return memories

    def forget(self, memory_id: str) -> None:
        """Remove uma memória específica."""
        self.collection.delete(ids=[memory_id])

A combinação dessas três camadas dá ao agente uma memória funcional de verdade: curto prazo para a conversa atual, sumarização para comprimir histórico que vai crescendo, e memória vetorial para recuperação semântica entre sessões.

Roteamento de Contexto: Carregando Apenas o Necessário

Aqui está um ponto que muita gente subestima. Um agente multi-domínio com acesso a 30 ferramentas, 5 bases de conhecimento e instruções específicas para cada área não deve carregar tudo em toda chamada. Isso desperdiça contexto e degrada a precisão. Uma pergunta sobre faturamento não precisa da base de conhecimento de onboarding — faz sentido, certo?

O roteamento de contexto resolve isso classificando a intenção do usuário antes de montar o contexto completo:

from openai import OpenAI
import json


DOMAIN_CONFIG = {
    "suporte_tecnico": {
        "tools": ["diagnosticar_erro", "consultar_logs", "reiniciar_servico"],
        "rag_collection": "docs_tecnicos",
        "system_addendum": (
            "Você é um especialista em suporte técnico. "
            "Sempre peça logs antes de sugerir soluções."
        ),
    },
    "financeiro": {
        "tools": ["consultar_fatura", "gerar_boleto", "aplicar_desconto"],
        "rag_collection": "politicas_financeiras",
        "system_addendum": (
            "Você é um assistente financeiro. "
            "Sempre confirme valores antes de processar."
        ),
    },
    "vendas": {
        "tools": ["consultar_estoque", "calcular_frete", "criar_pedido"],
        "rag_collection": "catalogo_produtos",
        "system_addendum": (
            "Você é um consultor de vendas. "
            "Sugira produtos complementares quando relevante."
        ),
    },
}


class ContextRouter:
    """Roteia o contexto com base na intenção do usuário."""

    def __init__(self, client: OpenAI):
        self.client = client
        self.domains = DOMAIN_CONFIG

    def classify_intent(self, user_message: str) -> str:
        """Classifica a intenção usando um modelo rápido."""
        domain_list = ", ".join(self.domains.keys())
        response = self.client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {
                    "role": "system",
                    "content": (
                        f"Classifique a mensagem em um dos domínios: "
                        f"{domain_list}. "
                        "Responda APENAS com o nome do domínio."
                    ),
                },
                {"role": "user", "content": user_message},
            ],
            max_tokens=20,
        )
        domain = response.choices[0].message.content.strip().lower()
        return domain if domain in self.domains else "suporte_tecnico"

    def get_domain_context(self, domain: str) -> dict:
        """Retorna a configuração de contexto para o domínio."""
        config = self.domains.get(domain, self.domains["suporte_tecnico"])
        return {
            "tools": config["tools"],
            "rag_collection": config["rag_collection"],
            "system_addendum": config["system_addendum"],
        }

O roteamento usa um modelo pequeno e rápido (gpt-4o-mini) para classificar a intenção e, com base nisso, carrega apenas as ferramentas e documentos relevantes. Em produção, essa técnica costuma reduzir o consumo de tokens em 40-60% e melhora a precisão das respostas. É daquelas otimizações que parecem óbvias depois que você implementa.

Validação de Contexto: Prevenindo Context Poisoning

Um dos riscos mais sérios em sistemas de produção — e que pouca gente fala — é o context poisoning. É quando informações incorretas, desatualizadas ou até maliciosas entram no contexto e contaminam as respostas do agente. Em agentes que operam em loop, esses erros se propagam e amplificam. Já vi isso acontecer em produção e não é bonito.

Uma camada de validação antes da montagem do contexto previne os cenários mais comuns:

from datetime import datetime, timezone
from dataclasses import dataclass


@dataclass
class ValidationResult:
    is_valid: bool
    issues: list[str]
    filtered_content: str | None = None


class ContextValidator:
    """Valida e sanitiza componentes do contexto."""

    def __init__(self, max_doc_age_days: int = 30):
        self.max_doc_age_days = max_doc_age_days

    def validate_rag_document(
        self, content: str, metadata: dict
    ) -> ValidationResult:
        """Valida um documento recuperado antes de incluí-lo."""
        issues = []

        # Verificar se o documento não está vazio
        if not content or len(content.strip()) < 50:
            issues.append("Documento muito curto ou vazio")

        # Verificar idade do documento
        if "updated_at" in metadata:
            doc_date = datetime.fromisoformat(metadata["updated_at"])
            age_days = (
                datetime.now(timezone.utc) - doc_date
            ).days
            if age_days > self.max_doc_age_days:
                issues.append(
                    f"Documento desatualizado ({age_days} dias)"
                )

        # Verificar tentativas de injeção de prompt
        injection_patterns = [
            "ignore previous instructions",
            "ignore all previous",
            "disregard your instructions",
            "you are now",
            "new instructions:",
            "system prompt override",
        ]
        content_lower = content.lower()
        for pattern in injection_patterns:
            if pattern in content_lower:
                issues.append(
                    f"Possível prompt injection detectado: '{pattern}'"
                )
                return ValidationResult(
                    is_valid=False,
                    issues=issues,
                    filtered_content=None,
                )

        return ValidationResult(
            is_valid=len(issues) == 0,
            issues=issues,
            filtered_content=content,
        )

    def validate_tool_output(
        self, tool_name: str, output: str, max_tokens: int = 2000
    ) -> ValidationResult:
        """Valida a saída de uma ferramenta antes de reinseri-la."""
        issues = []

        if len(output) > max_tokens * 4:
            output = output[:max_tokens * 4]
            issues.append("Saída truncada por exceder limite de tokens")

        if not output.strip():
            issues.append("Saída da ferramenta vazia")

        return ValidationResult(
            is_valid=True,
            issues=issues,
            filtered_content=output,
        )

Essa camada verifica três riscos principais: documentos desatualizados que podem conter informações obsoletas, tentativas de prompt injection via documentos recuperados e saídas de ferramentas longas demais que consumiriam tokens à toa.

Compressão de Contexto: Fazendo Mais com Menos Tokens

Tokens custam dinheiro e atenção. Não tem como fugir disso. A compressão de contexto é a técnica de reduzir o volume de informação sem perder o sinal. Em 2026, existem três abordagens principais que vale a pena conhecer.

1. Extração de Fatos (Fact Extraction)

Em vez de manter a conversa inteira no contexto, extraia apenas os fatos relevantes. É surpreendente quanta informação redundante existe numa conversa típica:

class FactExtractor:
    """Extrai fatos estruturados do histórico da conversa."""

    def __init__(self, client: OpenAI):
        self.client = client
        self.facts: list[dict] = []

    def extract_from_messages(
        self, messages: list[dict]
    ) -> list[dict]:
        """Extrai fatos novos de mensagens recentes."""
        messages_text = "\n".join(
            f"{m['role']}: {m['content']}" for m in messages
        )

        response = self.client.chat.completions.create(
            model="gpt-4o-mini",
            messages=[
                {
                    "role": "system",
                    "content": """Extraia fatos concretos das mensagens.
Para cada fato, retorne um JSON com:
- "fact": o fato em uma frase
- "category": "preference" | "decision" | "requirement" | "context"
- "confidence": 0.0 a 1.0

Retorne APENAS um array JSON, sem texto adicional.""",
                },
                {"role": "user", "content": messages_text},
            ],
            max_tokens=500,
            response_format={"type": "json_object"},
        )

        import json
        result = json.loads(response.choices[0].message.content)
        new_facts = result.get("facts", [])
        self.facts.extend(new_facts)
        return new_facts

    def get_relevant_facts(
        self, query: str, max_facts: int = 10
    ) -> str:
        """Retorna fatos relevantes formatados para inclusão."""
        relevant = self.facts[-max_facts:]
        if not relevant:
            return ""
        lines = [f"- {f['fact']}" for f in relevant]
        return "Fatos conhecidos:\n" + "\n".join(lines)

2. Compressão Seletiva por Relevância

Nem todas as mensagens do histórico têm o mesmo peso. Mensagens com decisões, erros e instruções são muito mais valiosas que saudações e confirmações genéricas. Essa classe implementa essa ideia:

class RelevanceCompressor:
    """Comprime histórico mantendo mensagens de alto valor."""

    # Palavras-chave que indicam mensagens de alto valor
    HIGH_VALUE_KEYWORDS = [
        "decidi", "erro", "bug", "requisito", "importante",
        "não", "nunca", "sempre", "preciso", "urgente",
        "mudou", "atualiz", "corrig", "problema",
    ]

    def score_message(self, message: dict) -> float:
        """Atribui uma pontuação de relevância à mensagem."""
        content = message.get("content", "").lower()
        score = 0.5  # base

        # Mensagens do usuário são mais valiosas
        if message.get("role") == "user":
            score += 0.2

        # Mensagens com tool_calls são decisões do agente
        if message.get("tool_calls"):
            score += 0.3

        # Palavras-chave de alto valor
        for keyword in self.HIGH_VALUE_KEYWORDS:
            if keyword in content:
                score += 0.1

        return min(score, 1.0)

    def compress(
        self,
        messages: list[dict],
        keep_ratio: float = 0.6,
    ) -> list[dict]:
        """Mantém as mensagens mais relevantes."""
        if len(messages) <= 5:
            return messages

        scored = [
            (self.score_message(msg), i, msg)
            for i, msg in enumerate(messages)
        ]

        # Sempre manter a primeira e as últimas 3 mensagens
        keep_indices = {0, len(messages) - 1}
        keep_indices.update(
            range(max(0, len(messages) - 3), len(messages))
        )

        # Ordenar por relevância e manter top N
        n_keep = max(
            len(keep_indices),
            int(len(messages) * keep_ratio),
        )
        scored.sort(key=lambda x: x[0], reverse=True)
        for _, idx, _ in scored[:n_keep]:
            keep_indices.add(idx)

        return [
            messages[i]
            for i in sorted(keep_indices)
            if i < len(messages)
        ]

Integrando Tudo: Agente com Engenharia de Contexto Completa

Bom, agora vem a parte divertida. Vamos montar um agente que integra todos os componentes — roteamento, memória em camadas, validação e compressão — em um loop de execução coerente:

from openai import OpenAI
import json


class ContextAwareAgent:
    """Agente de IA com engenharia de contexto completa."""

    def __init__(self, system_prompt: str):
        self.client = OpenAI()
        self.system_prompt = system_prompt
        self.context_engine = ContextEngine(
            model="gpt-4o", max_context=128_000
        )
        self.short_memory = SlidingWindowMemory(max_messages=20)
        self.summarization = SummarizationMemory(
            client=self.client, recent_window=10
        )
        self.long_memory = LongTermMemory()
        self.router = ContextRouter(client=self.client)
        self.validator = ContextValidator()
        self.compressor = RelevanceCompressor()

    def process_message(self, user_message: str) -> str:
        """Processa uma mensagem do usuário com contexto otimizado."""

        # 1. Roteamento: identificar domínio
        domain = self.router.classify_intent(user_message)
        domain_ctx = self.router.get_domain_context(domain)

        # 2. Memória de longo prazo: recuperar fatos relevantes
        memories = self.long_memory.recall(user_message, n_results=5)
        memory_texts = [m["content"] for m in memories]

        # 3. Adicionar mensagem ao histórico
        self.short_memory.add("user", user_message)
        self.summarization.add("user", user_message)

        # 4. Montar system prompt com addendum do domínio
        full_system = (
            f"{self.system_prompt}\n\n{domain_ctx['system_addendum']}"
        )

        # 5. Histórico com compressão
        history = self.compressor.compress(
            self.summarization.get_context()
        )

        # 6. Montar contexto completo com orçamento
        context = self.context_engine.build_context(
            system_prompt=full_system,
            tools=[],  # Ferramentas do domínio seriam carregadas aqui
            rag_documents=[],  # Documentos RAG do domínio
            history=history,
            memories=memory_texts,
        )

        # 7. Chamar o LLM
        messages = [
            {"role": "system", "content": context["system_prompt"]},
        ]
        if context["memories"]:
            messages.append({
                "role": "system",
                "content": (
                    f"Memórias relevantes:\n{context['memories']}"
                ),
            })
        messages.extend(context["history"])

        response = self.client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
        )

        assistant_msg = response.choices[0].message.content

        # 8. Atualizar memórias
        self.short_memory.add("assistant", assistant_msg)
        self.summarization.add("assistant", assistant_msg)

        return assistant_msg


# Uso
agent = ContextAwareAgent(
    system_prompt=(
        "Você é um assistente inteligente de e-commerce. "
        "Responda de forma objetiva e útil."
    )
)

resposta = agent.process_message(
    "Quero saber se o notebook Dell XPS 15 está disponível em SP"
)
print(resposta)

Esse agente executa o ciclo completo de engenharia de contexto a cada interação: classifica a intenção, recupera memórias relevantes, comprime o histórico, monta o contexto dentro do orçamento de tokens e chama o LLM com apenas as informações necessárias. É muita coisa acontecendo, mas cada peça tem seu papel.

Padrões de Produção: Context Caching e Monitoramento

Até aqui, cobrimos a arquitetura do sistema. Mas em produção, dois padrões adicionais fazem uma diferença enorme no custo e na confiabilidade do dia a dia.

Context Caching

Quando o system prompt e as definições de ferramentas são pesados (pense em documentação de APIs, guias de marca e afins), recomputar tudo a cada chamada é desperdício puro. Em 2026, as principais APIs oferecem context caching — a capacidade de "fixar" partes estáticas do contexto para reutilização:

# Com a API da Anthropic (Claude), o caching é explícito
# Blocos marcados com cache_control são reutilizados entre chamadas

import anthropic

client = anthropic.Anthropic()

# O system prompt pesado é cacheado automaticamente
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    max_tokens=1024,
    system=[
        {
            "type": "text",
            "text": "Você é um assistente especializado...",
        },
        {
            "type": "text",
            "text": documentacao_completa_api,  # 10k+ tokens
            "cache_control": {"type": "ephemeral"},
        },
    ],
    messages=[{"role": "user", "content": user_message}],
)
# Chamadas subsequentes reutilizam o cache,
# reduzindo custo em até 90% nos tokens cacheados

Monitoramento de Contexto

Rastrear a saúde do contexto é tão importante quanto monitorar latência e erros — mas é algo que muitos times negligenciam. Aqui estão as métricas essenciais que você deveria estar acompanhando:

import logging
from dataclasses import dataclass, field
from datetime import datetime, timezone


@dataclass
class ContextMetrics:
    """Métricas de uso da janela de contexto."""
    timestamp: str = ""
    total_tokens: int = 0
    system_tokens: int = 0
    history_tokens: int = 0
    rag_tokens: int = 0
    memory_tokens: int = 0
    utilization_pct: float = 0.0
    compression_applied: bool = False
    domain_routed: str = ""
    validation_issues: list[str] = field(default_factory=list)


class ContextMonitor:
    """Monitor de saúde do contexto para produção."""

    def __init__(self):
        self.logger = logging.getLogger("context_monitor")
        self.metrics_history: list[ContextMetrics] = []

    def record(self, budget: ContextBudget, **kwargs) -> None:
        metrics = ContextMetrics(
            timestamp=datetime.now(timezone.utc).isoformat(),
            total_tokens=budget.max_tokens,
            system_tokens=budget.system_prompt_tokens,
            history_tokens=budget.history_tokens,
            rag_tokens=budget.rag_tokens,
            memory_tokens=budget.memory_tokens,
            utilization_pct=budget.utilization_pct,
            **kwargs,
        )
        self.metrics_history.append(metrics)

        # Alertar se utilização acima de 80%
        if metrics.utilization_pct > 80:
            self.logger.warning(
                "Utilização de contexto alta: %.1f%% - "
                "considere aumentar compressão",
                metrics.utilization_pct,
            )

        # Alertar se há problemas de validação
        if metrics.validation_issues:
            self.logger.warning(
                "Problemas de validação: %s",
                metrics.validation_issues,
            )

Antipadrões: O Que Evitar na Engenharia de Contexto

Tão importante quanto saber o que fazer é saber o que não fazer. Estes são os antipadrões que mais aparecem em times que estão colocando agentes em produção:

  • Contexto "tudo ou nada": Carregar todas as ferramentas, todos os documentos e todo o histórico em cada chamada. Isso dilui o sinal e infla o custo. Use roteamento.
  • Ignorar a compressão: Conversas longas sem compressão degradam a qualidade aos poucos. O modelo se "distrai" com mensagens antigas que já não importam.
  • Confiar cegamente no RAG: Documentos recuperados podem estar desatualizados, fora de contexto ou até conter prompt injection. Sempre valide antes de injetar no contexto.
  • Subestimar o system prompt: Instruções vagas como "seja útil" geram respostas genéricas. System prompts específicos, com restrições claras e formato esperado, fazem uma diferença absurda.
  • Sem monitoramento: Se você não mede a utilização de contexto, não tem como saber quando está desperdiçando tokens ou quando a compressão está descartando informação que era crítica.
  • Memória de longo prazo sem expiração: Fatos antigos podem se tornar incorretos com o tempo. Implemente TTL (time-to-live) e revalidação periódica.

O Futuro: De Engenharia de Contexto a Engenharia de Agentes

Em março de 2026, o campo está se expandindo da engenharia de contexto para a engenharia de agentes. A diferença? A engenharia de contexto cuida do que entra na janela de contexto. A engenharia de agentes cuida de todo o runtime: planos, subagentes, checkpoints, aprovações humanas, recuperação de falhas e execução de longa duração.

Alguns marcos importantes desse avanço:

  • Model Context Protocol (MCP) — agora governado pela Agentic AI Foundation sob a Linux Foundation — se tornou o padrão universal para conectar agentes a ferramentas, com mais de 97 milhões de downloads mensais de SDK.
  • Frameworks de orquestração como LangGraph e CrewAI tratam fluxos de contexto como código, onde prompts, ferramentas e memória são compostos em cadeias programáticas.
  • Memória hierárquica com camadas de curto prazo, trabalho e longo prazo está virando padrão em frameworks de produção como LlamaIndex.
  • O Gartner prevê que 40% das aplicações empresariais contarão com agentes de IA até o final de 2026.

A engenharia de contexto não vai desaparecer — ela se torna uma camada fundamental dentro da engenharia de agentes. Dominar esses conceitos hoje é, sem exagero, a base para construir os sistemas autônomos de amanhã.

FAQ — Perguntas Frequentes

Qual a diferença entre engenharia de contexto e engenharia de prompt?

A engenharia de prompt foca em como formular a instrução ao modelo — é a arte de escrever a pergunta certa. A engenharia de contexto é mais ampla: trata de todo o ambiente informacional que acompanha o prompt, incluindo documentos recuperados (RAG), memória, ferramentas e histórico da conversa. Na prática, a engenharia de prompt é um subconjunto da engenharia de contexto.

Quanto contexto devo incluir na janela de contexto do LLM?

Menos do que você imagina. Pesquisas mostram que o desempenho do LLM cai mesmo quando a tarefa não muda, bastando aumentar o contexto com conteúdo irrelevante. A regra é simples: inclua apenas informações diretamente relevantes para a tarefa atual. Use roteamento de contexto para filtrar por domínio e compressão para reduzir o histórico.

Preciso de um banco de dados vetorial para implementar engenharia de contexto?

Não necessariamente. Para aplicações simples com poucas interações, a janela deslizante de histórico pode dar conta. O banco de dados vetorial (como ChromaDB, Pinecone ou Weaviate) se torna necessário quando você precisa de memória de longo prazo entre sessões ou de recuperação semântica de grandes volumes de documentos.

O que é context poisoning e como me proteger?

Context poisoning ocorre quando informações incorretas, desatualizadas ou maliciosas entram no contexto e contaminam as respostas do agente. Para se proteger, implemente uma camada de validação que verifica a idade dos documentos, detecta tentativas de prompt injection e trunca saídas excessivas de ferramentas antes de inseri-las no contexto.

Context caching vale a pena em produção?

Com certeza, especialmente quando seu system prompt ou definições de ferramentas passam de 5.000 tokens. APIs como a da Anthropic permitem fixar partes estáticas do contexto com cache_control, reduzindo custos em até 90% nos tokens cacheados. Para aplicações de alto volume, a economia pode chegar a milhares de dólares por mês.

Sobre o Autor Editorial Team

Our team of expert writers and editors.