MÓDULO 13 · CONCEITO 14 DE 15

Tipos de agentes de IA — taxonomia, arquiteturas e quando usar cada um

Agente reativo, agente com memória, agente planejador, orquestrador e multi-agent systems: o que cada arquitetura oferece, quando usá-la, e os trade-offs de cada escolha

Tempo de leitura ~20 min Pré-requisito 03 · Agentic Coding · 13 · MCP — Model Context Protocol Próximo 15 · Configurando Agentes de IA

"Agente de IA" é um dos termos mais sobrecarregados do vocabulário de engenharia atual. Dependendo de quem usa, pode significar desde uma única chamada de LLM com tools disponíveis até um sistema com dezenas de modelos colaborando em tempo real. Para tomar decisões de arquitetura informadas, é necessário vocabulário preciso: o que distingue um tipo de agente do outro, quando cada arquitetura faz sentido, e quais são os trade-offs reais de cada escolha.

A definição funcional de agente de IA é: qualquer sistema onde um LLM toma decisões iterativas usando ferramentas e feedback do ambiente para alcançar um objetivo. O que distingue um agente de uma chamada de LLM simples é o loop — o modelo não responde uma vez e termina, mas age, observa o resultado, e decide o próximo passo com base nessa observação. Esse loop é o coração de qualquer arquitetura de agente.

A anatomia do loop de agência

O loop de agência segue o padrão perceive → reason → act, repetido até que a condição de parada seja atingida:

Perceive — o agente recebe o estado atual: a tarefa original, resultados de ações anteriores, e contexto acumulado. Tecnicamente, isso é a lista de mensagens (messages) crescendo a cada ciclo.

Reason — o LLM processa o estado atual e gera um raciocínio + decisão de próxima ação. Pode ser uma tool call, uma resposta final, ou (em agentes planejadores) um passo de raciocínio explícito antes de qualquer ação.

Act — a ação é executada: tool call, escrita de arquivo, chamada de API. O resultado é injetado de volta no contexto como mensagem do usuário, e o ciclo recomeça.

a primitiva técnica

O loop de agência não é abstração — é uma lista de mensagens que cresce a cada ciclo. Cada turno adiciona: a resposta do assistente (com tool calls) e os resultados das tools (como mensagem do usuário). Quanto mais ciclos, maior o contexto; quanto maior o contexto, maior o custo e o risco de degradação do raciocínio. Arquitetura de agente é, em grande parte, gestão de contexto: o que entra, o que fica, o que é descartado.

Taxonomia prática: cinco arquiteturas

1. Agente reativo

O agente reativo é o mais simples: recebe uma tarefa, usa tools para completá-la em uma sessão, e não mantém estado entre sessões. Cada execução começa do zero. O histórico de mensagens existe durante a execução, mas é descartado quando a tarefa termina.

Casos de uso adequados: geração de código para uma especificação bem definida, resposta a perguntas sobre um repositório, refatoração de um arquivo com regras explícitas, análise de um diff. Qualquer tarefa autocontida que não precise de contexto de sessões anteriores.

Vantagens: menor overhead — latência previsível, custo por token controlado, comportamento determinístico (para a mesma entrada, o mesmo processo). Limitação: a ausência de aprendizado entre sessões. Cada execução é independente, e o agente não se beneficia de interações anteriores.

2. Agente com memória

O agente com memória mantém alguma forma de estado persistente entre sessões. Existem três mecanismos principais com características distintas:

Scratchpad textual — arquivo de texto que o agente lê no início de cada sessão e escreve ao final. Simples de implementar, sem infraestrutura adicional. Limitado por tamanho e falta de busca eficiente. Ideal para contexto pequeno e estruturado.

Banco de dados vetorial — embeddings de interações e decisões anteriores são armazenados e recuperados por similaridade semântica. Escala para grandes volumes de histórico, mas adiciona complexidade de infraestrutura (vector DB, pipeline de embedding) e custo adicional.

Armazenamento estruturado — banco de dados relacional ou chave-valor com esquema explícito. Ideal para contexto tipado: convenções de código, decisões de arquitetura, estado de projetos em andamento. Requer mais código para gerenciar, mas oferece consultas precisas e sem ambiguidade semântica.

O sistema de memória do Claude Code — arquivos Markdown em .claude/memory/ lidos no início de cada sessão — é um exemplo de scratchpad estruturado. A memória define o contexto persistente; a execução da tarefa consome e pode enriquecer esse contexto.

3. Agente planejador

O agente planejador decompõe o objetivo em subtarefas antes de executar. Em vez de agir imediatamente, ele primeiro raciocina sobre o plano e só então começa a usar tools. O raciocínio explícito antes da ação é o que define essa arquitetura.

O padrão mais influente para agentes planejadores é o ReAct (Reasoning + Acting), de Yao et al. (2022): o modelo alterna entre passos de raciocínio explícito ("preciso ler o arquivo X para entender Y antes de modificar Z") e passos de ação. O raciocínio é gerado como texto visível — não é um processo interno opaco, mas um output estruturado que pode ser verificado.

A diferença entre agente reativo e planejador não é binária — é uma questão de system prompt e de como o planejamento é enforced. Um agente reativo com instrução "antes de usar qualquer ferramenta, escreva um plano detalhado" se comporta como um agente planejador. A distinção arquitetural robusta é quando esse planejamento é enforced por mecanismo — paradas obrigatórias, verificação de artefatos — não apenas por instrução. Esse é o tema central do próximo conceito.

por que planejamento explícito importa

Um agente que age sem planejar é como um desenvolvedor que começa a digitar código sem ler o ticket. Funciona para tarefas triviais; falha sistematicamente em tarefas com dependências não óbvias. O benefício do planejamento explícito não é só qualidade do output — é rastreabilidade: quando o plano está escrito, o humano pode verificar e corrigir antes que o agente execute ações difíceis de reverter. Planejamento é o mecanismo primário de controle em agentes autônomos.

4. Agente orquestrador

O agente orquestrador não executa tarefas diretamente — ele coordena outros agentes. Suas "ações" são: delegar subtarefas para agentes especializados, agregar resultados, decidir o próximo passo com base nos outputs recebidos, e gerenciar o estado global do pipeline.

O orquestrador resolve o problema de complexidade que emerge em tarefas longas: um único agente acumulando contexto excessivo perde foco e degrada a qualidade das decisões progressivamente. Ao dividir a tarefa em subtarefas e distribuir para agentes especializados com contextos menores e bem definidos, o orquestrador mantém cada agente focado em uma responsabilidade clara com um window de contexto gerenciável.

O custo é real: o orquestrador introduz latência (chamadas sequenciais de agentes), custo multiplicado (cada sub-agente é uma sessão de LLM separada), e o risco crítico de erro em cascata — se um sub-agente produz output incorreto e o orquestrador não verifica explicitamente, esse erro se propaga para todas as etapas subsequentes sem nenhum agente individual ter "falhado" pelo seu próprio critério.

5. Multi-agent systems

Sistemas multi-agente têm múltiplos agentes com papéis distintos colaborando via troca de mensagens. Diferente do orquestrador (que tem hierarquia clara e controle centralizado), sistemas multi-agente podem ter topologias variadas: hierarquia, par-a-par, ou especialização paralela.

Exemplos concretos: um agente de pesquisa que identifica perguntas abertas, um agente de implementação que responde cada pergunta com código, um agente de síntese que integra os resultados — três agentes com interações definidas. Ou múltiplos agentes trabalhando em paralelo em subproblemas independentes, com um agente de integração que agrega os resultados.

A complexidade de sistemas multi-agente é substancial: orquestração das interações, gerenciamento do estado global compartilhado, tratamento de falhas parciais (o que fazer quando um de cinco agentes falha?), e debugging de comportamentos emergentes que não eram observáveis em nenhum agente individual. São adequados para problemas que genuinamente não cabem em um único agente — não como ponto de partida.

a complexidade não é virtude

Sistemas multi-agente são a solução certa para um conjunto pequeno de problemas. Para a maioria das tarefas de engenharia, um agente planejador bem configurado supera um sistema multi-agente mal arquitetado — e é dramaticamente mais fácil de debugar. A regra: use a arquitetura mais simples que resolve o problema. Adicione complexidade apenas quando há evidência de que a solução mais simples não é suficiente, não por expectativa de que mais sofisticação implique melhor resultado.

Skills no Claude Code

Skills são comandos slash customizados que encapsulam workflows reutilizáveis. Quando invocada com /nome-da-skill, o Claude Code carrega um arquivo Markdown de definição de skill e executa o workflow nele descrito — essencialmente um agente planejador especializado com propósito e processo fixos.

Uma skill é um arquivo Markdown em .claude/skills/ (por projeto) ou ~/.claude/skills/ (global). A estrutura mínima define: contexto da skill (o que ela faz e quando usar), o processo a ser seguido (a sequência de passos), e o formato do output esperado. O agente interpreta esse arquivo como instrução de workflow a cada invocação.

Skills úteis em times de engenharia:

/spec — dado uma descrição de feature em linguagem natural, gera um documento de especificação estruturado: objetivo, comportamento esperado, edge cases, não-objetivos, critérios de aceite.

/review — dado um diff ou arquivo modificado, executa code review com critérios predefinidos: segurança, performance, legibilidade, cobertura de casos de erro, aderência às convenções do projeto.

/adr — dado um problema de design, gera um Architecture Decision Record no formato do time: contexto, opções consideradas, decisão, consequências.

/incident — dado um erro em produção, conduz análise de root cause com template específico: timeline, impacto, causa raiz, ações imediatas, ações preventivas.

A distinção entre skills e MCP servers é importante: uma skill define um processo de interação (sequência de passos que o agente executa); um MCP server define capacidades (ferramentas que o agente pode usar). São complementares — uma skill /review pode usar um MCP server de GitHub para buscar dados da PR, e usar outro MCP server para postar o comentário de revisão.

A distinção entre skills e código direto: skills são interpretadas pelo LLM a cada execução — flexíveis, mas com variabilidade de output. Código direto é determinístico e verificável. Para operações críticas ou de alta frequência, prefira código. Para workflows exploratórios ou que requerem julgamento contextual variável, skills são mais adequadas.

Escolhendo a arquitetura certa

A decisão segue uma hierarquia de complexidade crescente. Parta sempre do mais simples e adicione complexidade apenas quando há justificativa concreta:

A tarefa é autocontida e termina em uma sessão? → Agente reativo. Sem necessidade de persistência entre execuções, sem estado compartilhado, sem coordenação.

Precisa de consistência entre sessões? → Adicione memória. Escolha o mecanismo pelo volume e estrutura do contexto: scratchpad para pouco contexto estruturado, vector DB para muito histórico não estruturado, banco de dados para contexto tipado e consultável.

A tarefa tem dependências não óbvias ou risco de ações prematuras? → Adicione planejamento explícito. O plano deve ser um artefato verificável, não apenas texto descartável antes da execução.

O contexto necessário para completar a tarefa excede o que um único agente pode gerenciar sem degradar? → Considere orquestrador. Divida por responsabilidade, não por conveniência.

Há subtarefas genuinamente independentes que se beneficiariam de paralelismo ou de contextos completamente isolados? → Multi-agent. E documente explicitamente por que as abordagens anteriores não eram suficientes.

Trade-offs de arquitetura

Latência cresce com a complexidade: um agente reativo tem latência proporcional ao número de ciclos de tool use; um orquestrador com quatro sub-agentes sequenciais tem latência que é a soma de quatro sessões de LLM mais o tempo de orquestração. Sistemas multi-agente paralelos podem ser mais rápidos que sequenciais, mas ainda têm overhead de coordenação.

Custo é multiplicativo em arquiteturas complexas. Cada sub-agente consome tokens independentemente — o contexto não é compartilhado. Um orquestrador que passa o output completo de um sub-agente para o próximo duplica o custo de tokens desse output (aparece tanto como output do produtor quanto como input do consumidor).

Debuggability é a dimensão mais subestimada. Um agente reativo pode ser traced linearmente: mensagem 1, tool call, resultado, mensagem 2, resposta final. Um orquestrador com quatro sub-agentes tem quatro históricos de conversa separados, cada um com seu próprio contexto e raciocínio. Quando o resultado final está errado, identificar qual sub-agente produziu o output incorreto que causou a falha cascateada requer instrumentação explícita.

Erro em cascata é o risco diferencial de arquiteturas com múltiplos agentes. Em um sistema sequencial, um erro em qualquer fase contamina todas as fases subsequentes. Um agente de pesquisa que alucina um fato passa essa alucinação para o agente de planejamento, que a incorpora no spec, que o agente de implementação usa como verdade. Nenhum agente "falhou" pelo seu próprio critério — mas o sistema produziu output incorreto. Mitigação requer verificações explícitas entre fases, não apenas confiança no modelo.

Comparação por linguagem

C# — Agente Planejador com Loop ReAct (Anthropic.SDK)
// PlanningAgent.cs — Loop ReAct: planeja → age → observa → repete
using Anthropic.SDK;
using Anthropic.SDK.Messaging;
using System.Text.Json;

public class PlanningAgent(AnthropicClient client)
{
    private const string SystemPrompt = """
        Antes de usar qualquer ferramenta, escreva:
        PLANO: [lista numerada dos passos que você vai executar]

        Só então execute os passos na ordem planejada.
        Se o objetivo ficar ambíguo durante a execução, reavalie o plano antes de continuar.
        """;

    private static readonly List<Tool> Tools =
    [
        new()
        {
            Name = "read_file",
            Description = "Lê o conteúdo de um arquivo no projeto",
            InputSchema = new InputSchema
            {
                Type = "object",
                Properties = new Dictionary<string, Property>
                {
                    ["path"] = new() { Type = "string", Description = "Caminho relativo ao projeto" }
                },
                Required = ["path"]
            }
        },
        new()
        {
            Name = "list_files",
            Description = "Lista arquivos em um diretório",
            InputSchema = new InputSchema
            {
                Type = "object",
                Properties = new Dictionary<string, Property>
                {
                    ["dir"] = new() { Type = "string", Description = "Diretório a listar" }
                },
                Required = ["dir"]
            }
        }
    ];

    public async Task<string> RunAsync(string goal, CancellationToken ct = default)
    {
        var messages = new List<Message>
        {
            new() { Role = RoleType.User, Content = [new TextContent { Text = goal }] }
        };

        // Loop ReAct: continua até stop_reason = "end_turn"
        while (true)
        {
            var response = await client.Messages.GetClaudeMessageAsync(new()
            {
                Model = "claude-opus-4-7",
                MaxTokens = 8192,
                System = SystemPrompt,
                Messages = messages,
                Tools = Tools,
            }, ct);

            // Registra resposta do assistente no histórico
            messages.Add(new() { Role = RoleType.Assistant, Content = response.Content });

            if (response.StopReason == "end_turn")
                return string.Join("\n", response.Content.OfType<TextContent>().Select(t => t.Text));

            // Executa cada tool solicitada e injeta resultados no próximo turno
            var toolResults = new List<IMessageContent>();
            foreach (var block in response.Content.OfType<ToolUseContent>())
            {
                var result = await RunToolAsync(block.Name, block.Input, ct);
                toolResults.Add(new ToolResultContent { ToolUseId = block.Id, Content = result });
            }
            messages.Add(new() { Role = RoleType.User, Content = toolResults });
        }
    }

    private static async Task<string> RunToolAsync(
        string name, JsonElement input, CancellationToken ct) => name switch
    {
        "read_file" => await File.ReadAllTextAsync(
            input.GetProperty("path").GetString()!, ct),
        "list_files" => string.Join("\n", Directory.GetFiles(
            input.GetProperty("dir").GetString()!, "*", SearchOption.AllDirectories)),
        _ => $"Tool desconhecida: {name}"
    };
}

Implementação de um agente planejador em C# com o loop ReAct completo: acumulação de mensagens, execução de tools, injeção de resultados, e condição de parada. O system prompt força planejamento explícito antes de qualquer ação.

Python — Agente Reativo vs. Agente com Memória Persistente
# agents.py — Agente reativo vs. agente com memória persistente
import json
from pathlib import Path
from dataclasses import dataclass, field
import anthropic

client = anthropic.Anthropic()

TOOLS = [{
    "name": "read_file",
    "description": "Lê o conteúdo de um arquivo",
    "input_schema": {
        "type": "object",
        "properties": {"path": {"type": "string"}},
        "required": ["path"]
    }
}]

def _run_tool(name: str, inp: dict) -> str:
    if name == "read_file":
        p = Path(inp["path"])
        return p.read_text(encoding="utf-8") if p.exists() else f"Não encontrado: {p}"
    return f"Tool desconhecida: {name}"

def _agent_loop(system: str, task: str) -> str:
    messages = [{"role": "user", "content": task}]
    while True:
        r = client.messages.create(
            model="claude-opus-4-7", max_tokens=8192,
            system=system, messages=messages, tools=TOOLS,
        )
        messages.append({"role": "assistant", "content": r.content})
        if r.stop_reason == "end_turn":
            return next(b.text for b in r.content if b.type == "text")
        results = [
            {"type": "tool_result", "tool_use_id": b.id, "content": _run_tool(b.name, b.input)}
            for b in r.content if b.type == "tool_use"
        ]
        messages.append({"role": "user", "content": results})


# --- Agente Reativo: sem estado entre chamadas ---
def reactive_agent(task: str) -> str:
    """Cada chamada é independente. Nenhum contexto persiste."""
    return _agent_loop(
        "Você é um assistente de engenharia. Complete a tarefa usando as ferramentas disponíveis.",
        task
    )


# --- Agente com Memória: contexto persiste entre sessões ---
@dataclass
class MemoryAgent:
    memory_path: Path = Path(".agent_memory.json")
    _mem: dict = field(default_factory=dict, init=False)

    def __post_init__(self):
        if self.memory_path.exists():
            self._mem = json.loads(self.memory_path.read_text(encoding="utf-8"))

    def remember(self, key: str, value: str) -> None:
        self._mem[key] = value
        self.memory_path.write_text(
            json.dumps(self._mem, indent=2, ensure_ascii=False), encoding="utf-8"
        )

    def forget(self, key: str) -> None:
        self._mem.pop(key, None)
        self.memory_path.write_text(
            json.dumps(self._mem, indent=2, ensure_ascii=False), encoding="utf-8"
        )

    def run(self, task: str) -> str:
        if self._mem:
            ctx = "\n".join(f"- {k}: {v}" for k, v in self._mem.items())
            system = f"Você é um assistente de engenharia.\n\nCONTEXTO PERSISTIDO:\n{ctx}\n\nMantenha consistência com essas convenções."
        else:
            system = "Você é um assistente de engenharia. Nenhum contexto anterior disponível."

        result = _agent_loop(system, task)
        self.remember("ultima_tarefa", task[:200])
        return result


# --- Uso comparativo ---
if __name__ == "__main__":
    # Reativo: cada chamada começa sem contexto
    reactive_agent("Liste os arquivos do projeto e descreva a estrutura")

    # Com memória: tem contexto de sessões anteriores
    agent = MemoryAgent()
    agent.remember("linguagem", "Python 3.12+ com type hints obrigatórios")
    agent.remember("padrao_testes", "pytest com fixtures — sem mock da camada de dados")
    agent.remember("convencao_commits", "conventional commits: feat/fix/docs/refactor/test")
    agent.run("Crie uma função para calcular o percentil 95 de uma lista de latências")

Contraste entre as duas arquiteturas: o agente reativo é uma função pura (sem estado entre chamadas); o MemoryAgent persiste contexto em JSON entre sessões e o injeta no system prompt de cada nova execução.

Go — Orquestrador com Pipeline de Agentes Especializados
// orchestrator.go — Pipeline multi-agente com especialização por fase
package agent

import (
    "context"
    "fmt"

    "github.com/anthropics/anthropic-sdk-go"
)

type Pipeline struct{ client anthropic.Client }

func NewPipeline() *Pipeline {
    return &Pipeline{client: anthropic.NewClient()}
}

type TaskResult struct {
    Context        string
    Spec           string
    Implementation string
    Review         string
}

// Execute — pipeline sequencial de quatro agentes especializados
func (p *Pipeline) Execute(ctx context.Context, task string) (*TaskResult, error) {
    r := &TaskResult{}
    var err error

    // Fase 1: pesquisa de contexto — NÃO implementa, apenas lê e reporta
    r.Context, err = p.run(ctx,
        "Você pesquisa contexto. Leia o código existente, identifique padrões e convenções. Produza um relatório estruturado. NÃO implemente nada.",
        fmt.Sprintf("Pesquise o contexto para: %s", task))
    if err != nil {
        return nil, fmt.Errorf("fase de contexto: %w", err)
    }

    // Fase 2: spec detalhado — NÃO escreve código de implementação
    r.Spec, err = p.run(ctx,
        "Você escreve specs. Dado objetivo e contexto, produza: comportamento esperado, edge cases, arquivos afetados, critérios de aceite. NÃO escreva código.",
        fmt.Sprintf("Tarefa: %s\n\nContexto:\n%s", task, r.Context))
    if err != nil {
        return nil, fmt.Errorf("fase de spec: %w", err)
    }

    // Fase 3: implementação — segue o spec, NÃO muda escopo
    r.Implementation, err = p.run(ctx,
        "Você implementa specs. Dado spec e contexto, escreva código seguindo rigorosamente o spec. NÃO mude o escopo.",
        fmt.Sprintf("Spec:\n%s\n\nContexto:\n%s", r.Spec, r.Context))
    if err != nil {
        return nil, fmt.Errorf("fase de implementação: %w", err)
    }

    // Fase 4: revisão — lista problemas, NÃO reescreve
    r.Review, err = p.run(ctx,
        "Você revisa implementações. Dado spec + código, liste discrepâncias, bugs potenciais e violações de convenção. NÃO reescreva.",
        fmt.Sprintf("Spec original:\n%s\n\nImplementação:\n%s", r.Spec, r.Implementation))
    if err != nil {
        return nil, fmt.Errorf("fase de revisão: %w", err)
    }

    return r, nil
}

func (p *Pipeline) run(ctx context.Context, role, prompt string) (string, error) {
    messages := []anthropic.MessageParam{
        anthropic.NewUserMessage(anthropic.NewTextBlock(prompt)),
    }
    for {
        msg, err := p.client.Messages.New(ctx, anthropic.MessageNewParams{
            Model:     anthropic.ModelClaudeOpus4_7,
            MaxTokens: anthropic.Int(8192),
            System:    []anthropic.TextBlockParam{{Type: "text", Text: role}},
            Messages:  messages,
        })
        if err != nil {
            return "", fmt.Errorf("LLM: %w", err)
        }
        messages = append(messages, msg.ToParam())
        if msg.StopReason == "end_turn" {
            for _, b := range msg.Content {
                if b.Type == "text" {
                    return b.Text, nil
                }
            }
            return "", fmt.Errorf("resposta sem bloco de texto")
        }
    }
}

Pipeline de quatro fases onde cada fase é um agente especializado com system prompt restrito ao seu papel: pesquisador coleta contexto, planejador gera spec, implementador escreve código, revisor valida contra o spec original.

Exercícios práticos

  1. Trace um agente existente: Habilite o verbose mode no Claude Code (--verbose) e execute uma tarefa não trivial — como "adicione validação de entrada nessa função". Observe o loop completo: quais ferramentas foram usadas, em que ordem, quantas vezes o loop repetiu, e o que o agente fez que você não teria previsto. Identifique: a arquitetura foi reativa ou planejadora? O agente fez planejamento explícito antes de agir? Em que ponto o contexto cresceu mais?
  2. Implemente o MemoryAgent e use por uma semana: Implemente a versão Python (ou adapte para C# ou Go) e use-o para tarefas de engenharia reais por cinco dias. A cada dia, adicione pelo menos uma entrada de memória relevante (decisão tomada, convenção do projeto, contexto descoberto). No quinto dia: quais entradas de memória foram mais úteis? Quais eram ruído? O que você colocaria em um CLAUDE.md vs. no JSON de memória?
  3. Construa uma skill: Identifique um workflow repetitivo no seu trabalho que envolva julgamento (não apenas automação mecânica) — code review, geração de changelog, análise de performance de query. Escreva uma skill em .claude/skills/ que encapsule o processo. Execute-a em cinco situações reais. Documente: onde o processo definido na skill produziu bom output? Onde o julgamento do LLM divergiu do processo esperado? O que você ajustaria no arquivo de definição?

Referências e leitura complementar

  1. paper ReAct: Synergizing Reasoning and Acting in Language Models — Yao et al. (2022). arxiv.org/abs/2210.03629 — O paper original do padrão ReAct. Demonstra que alternar entre raciocínio explícito e ação melhora significativamente tanto a qualidade do resultado quanto a rastreabilidade do processo. Leitura obrigatória para entender a base teórica de agentes planejadores.
  2. artigo Building Effective Agents — Anthropic (2024). anthropic.com/research/building-effective-agents — Guia prático da Anthropic sobre como construir agentes confiáveis: quando usar agentes vs. pipelines diretos, como estruturar sistemas multi-agente, e os erros mais comuns. Referência primária para decisões de arquitetura.
  3. paper Chain-of-Thought Prompting Elicits Reasoning in LLMs — Wei et al. (2022). arxiv.org/abs/2201.11903 — O paper que demonstrou que pedir ao modelo para "pensar passo a passo" melhora raciocínio em tarefas complexas. Base para entender por que planejamento explícito funciona em agentes.
  4. paper Reflexion: Language Agents with Verbal Reinforcement Learning — Shinn et al. (2023). arxiv.org/abs/2303.11366 — Propõe agentes que refletem sobre seus erros em linguagem natural e usam essa reflexão para melhorar nas tentativas subsequentes. Relevante para entender agentes com memória de sesão.
  5. livro An Introduction to MultiAgent Systems — Michael Wooldridge (2009). Wiley — Referência clássica em sistemas multi-agente da perspectiva de IA distribuída. Cobre fundamentos teóricos (agentes reativos, BDI, sistemas de normas) que precedem a era dos LLMs mas são conceitualmente válidos. Leitura para quem quer entender os trade-offs em profundidade.
  6. docs Claude Code — Skills e Slash Commands — Anthropic (2025). docs.anthropic.com/claude-code/slash-commands — Documentação de como criar skills customizadas no Claude Code: estrutura do arquivo Markdown, como o agente interpreta a definição, e exemplos de skills para workflows de engenharia.
  7. docs Anthropic SDK — Tool Use — Anthropic (2025). docs.anthropic.com/claude/docs/tool-use — Referência completa do mecanismo de tool use da API. Inclui definição de tools, tratamento de tool_use blocks, e padrões para loops de tool use como o implementado nos exemplos deste conceito.
  8. artigo The Anatomy of Autonomy: What Makes an Agentic System Work — Latent Space (2024). latent.space — Análise do que diferencia sistemas agentivos confiáveis dos que falham em produção: o papel do planejamento, memória, e verificação de output. Perspectiva prática de times que operam agentes em produção.
  9. docs LangGraph — Building Stateful Multi-Actor Applications — LangChain (2024). langchain-ai.github.io/langgraph — Framework para construir sistemas multi-agente com grafos de estado explícitos. Útil como referência de padrões de orquestração — mesmo que você não use o framework, os conceitos de nós, arestas, e estado compartilhado são relevantes.
  10. artigo Evaluating Multi-Agent Systems: The Cascade Problem — Simon Willison (2024). simonwillison.net — Análise do problema de erro em cascata em sistemas multi-agente: por que é difícil detectar, por que as métricas tradicionais não capturam, e abordagens para instrumentação e verificação entre fases.