Ir para o conteúdo

Emerson Amorim

AI Agents

Como construir agentes de IA para produção: arquitetura, Agent Harness, segurança e observabilidade

Diferença entre protótipo e agente em produção: Agent Harness, permissões, ferramentas tipadas, memória, avaliações e intervenção humana.

Por Emerson Amorim17 min de leitura

Um agente de demonstração chama um modelo e executa ferramentas. Um agente de produção precisa de harness: fronteiras de permissão, validação, estado, falhas previsíveis, telemetria e critério de parada.

Protótipo versus produção

DimensãoProtótipoProdução
FerramentasFunções soltasCatálogo tipado + gateway
PermissõesImplícitasExplicitas por escopo e risco
EstadoChat na memóriaPersistência versionada
FalhaRetry cegoPolíticas e compensação
QualidadeAvaliação manualEval contínuo + traces
AutonomiaMáximaGraduada + human-in-the-loop

O que é um Agent Harness

Agent Harness é a camada que envolve o loop do agente. O modelo decide o próximo passo; o harness decide o que é permitido executar, como validar, o que registrar e quando interromper.

User Goal
   │
   ▼
┌──────────────┐
│ Planner/LLM  │
└──────┬───────┘
       │ proposed action
       ▼
┌──────────────┐     deny / ask human
│ Policy Gate  │─────────────────────►
└──────┬───────┘
       │ allow
       ▼
┌──────────────┐
│ Tool Runtime │──► side effects
└──────┬───────┘
       │ observation
       ▼
┌──────────────┐
│ Trace + Eval │──► stop | continue
└──────────────┘
Loop supervisionado: planejar → validar → executar ferramenta → observar → avaliar → continuar ou parar.

Contrato mínimo do harness

Tipos centrais do harness
type Risk = "read" | "write" | "irreversible";

type ToolSpec<TIn, TOut> = {
  name: string;
  risk: Risk;
  input: (raw: unknown) => TIn;
  execute: (input: TIn, ctx: RunContext) => Promise<TOut>;
};

type ProposedAction = {
  tool: string;
  args: unknown;
  rationale: string;
};

type HarnessResult =
  | { status: "continued"; observation: unknown }
  | { status: "blocked"; reason: string }
  | { status: "needs_human"; ticketId: string }
  | { status: "done"; output: unknown };

async function step(
  action: ProposedAction,
  tools: Map<string, ToolSpec<unknown, unknown>>,
  ctx: RunContext,
): Promise<HarnessResult> {
  const tool = tools.get(action.tool);
  if (!tool) return { status: "blocked", reason: "unknown_tool" };

  let input: unknown;
  try {
    input = tool.input(action.args);
  } catch {
    return { status: "blocked", reason: "invalid_args" };
  }

  if (tool.risk === "irreversible" && !ctx.approvals.has(action.tool)) {
    return {
      status: "needs_human",
      ticketId: await requestApproval(action, ctx),
    };
  }

  const observation = await tool.execute(input, ctx);
  await recordTrace(ctx.runId, action, observation);
  return { status: "continued", observation };
}

Permissões e ferramentas tipadas

O modelo não deve receber carte blanche. Cada ferramenta declara risco, schema e teto de custo. Credenciais ficam no runtime; o agente recebe apenas handles ou resultados redigidos.

  • Allowlist de ferramentas por agente e por tenant
  • Validação de argumentos antes de qualquer side effect
  • Timeouts e circuit breakers por ferramenta
  • Aprovação humana obrigatória para ações irreversíveis

Memória e gerenciamento de contexto

Contexto infinito é ilusão cara. Separe: (1) estado de execução do run, (2) memória de sessão, (3) memória de longo prazo com política de retenção. Compactação e sumarização precisam ser versionadas para não perder fatos críticos.

Observabilidade e avaliações

Sem traces por step, não há depuração. Sem avaliações, não há regressão controlável. Meça taxa de bloqueio por política, taxa de aprovação humana, falhas de ferramenta, custo por run e qualidade de tarefa com casos de ouro.

Esqueleto de avaliação offline
from dataclasses import dataclass

@dataclass
class Case:
    goal: str
    expected_tools: list[str]
    must_not_call: list[str]

def evaluate(run_trace: list[dict], case: Case) -> dict:
    called = [step["tool"] for step in run_trace if "tool" in step]
    return {
        "tool_precision": len(set(called) & set(case.expected_tools))
        / max(len(case.expected_tools), 1),
        "policy_violations": [
            tool for tool in called if tool in case.must_not_call
        ],
        "steps": len(run_trace),
    }

Quando não usar um agente

  • Fluxo determinístico com regras claras: use workflow/serviço
  • Consulta simples a base de conhecimento: RAG ou endpoint pode bastar
  • Latência rígida e orçamento mínimo: o loop multi-step custa caro
  • Ambiente sem auditoria: não exponha side effects

Referência de implementação

No KingFy, o harness materializa a diferença entre conversar com um modelo e operar um sistema agentic. Comece pelo gateway de ferramentas e pela política de risco; só depois otimize o planner.