Construindo um guardrail

O guardrail verifica cada chamada de ferramenta que um agente de LLM propõe, antes de ela ser executada, e responde permitir, confirmar (perguntar à pessoa) ou recusar, citando a regra que decidiu. Nada numa decisão é adivinhado por um modelo de linguagem: a mesma chamada, conversa e regras sempre dão a mesma resposta e o mesmo decision_id.

Dá para construir um de dois jeitos, pelos mesmos passos: em Python, ou pedindo ao seu próprio assistente de IA pelo stalwart mcp.

O que é garantido, e o que não é

Garantido:

Não garantido:

Passo a passo, em Python

  1. Instale. pip install stalwart-agents, e ative a licença (stalwart licence activate). As verificações são respondidas pelo runtime licenciado na sua máquina; se ele não responder, toda chamada é bloqueada, nunca permitida.
  2. Dê a ele suas ferramentas. A lista que você entrega ao modelo basta: definições de ferramentas da OpenAI ou da Anthropic, o tools/list de um servidor MCP, ou ferramentas do LangChain. Um LLM sem ferramentas não faz chamadas de ferramenta, então não há o que verificar.

    from stalwart_agents.guardrails import Guardrail
    
    rail = Guardrail(tools=tools)
    
  3. Diga quais ferramentas escrevem. Cada ferramenta lê, escreve ou envia para fora. A maioria é deduzida pelo nome, pela descrição e pelos argumentos; uma ferramenta que ninguém consegue dizer é tratada como escrita. Escreva as que você conhece em tool_kinds.yaml (Tipos de ferramenta) e passe tool_kinds="tool_kinds.yaml".

  4. Escreva suas regras. Uma planilha de regras em YAML simples (A planilha de regras): rules="rules.yaml".
  5. Escolha as verificações. Toda verificação vem ligada; checks=[...] liga só as listadas (As verificações). As que não podem ser desligadas sempre rodam.
  6. Verifique cada chamada antes de executá-la.

    from stalwart_agents.guardrails import CallRefused
    
    context = {"turns": turns, "results": results, "session": sid}
    try:
        verdict = rail.check_call(call, context)          # uma recusa levanta CallRefused
        if verdict.decision == "confirm" and ask_person(verdict.confirmation["sentence"]):
            verdict = rail.check_call(call, {**context, "confirmation": rail.approve(verdict.confirmation)})
        if verdict.proceed:
            results[call["id"]] = run_tool(call)          # seu código executa a ferramenta, nunca o guardrail
    except CallRefused as refused:
        tell(refused.verdict.reason)
    

    Uma chamada alterada depois do sim da pessoa é perguntada de novo.

  7. Teste. Faça chamadas de ensaio que você espera que passem e que sejam barradas, busque as formulações que o leitor verifica (A busca de formulações) e repita as chamadas antigas do seu agente (Avaliando).

  8. Conecte onde seu agente roda: Python puro, o gateway MCP, Guardrails AI ou NeMo Guardrails (Conectando).

Construa com seu assistente de IA

O stalwart mcp é um servidor MCP na sua máquina. Adicione-o ao seu cliente MCP (Claude Code, Cursor, VS Code) e peça ao seu assistente para construir o guardrail: o prompt build-guardrail o conduz, nesta ordem:

  1. ler suas ferramentas: catalogue_from_server;
  2. os tipos delas: writes_file, e o prompt classify-tools, que deixa seu assistente propor tipos por set_tool_kinds (registrados como source: assistant, listados à parte);
  3. rascunhar uma planilha de regras a partir da sua política em palavras simples, com a referência da planilha como recurso;
  4. compile_rules e lint_rules;
  5. ensaios: check_call;
  6. search_wordings e hunt;
  7. o trecho de código para onde ele vai rodar: snippet.

Quem rascunha as regras é o seu próprio assistente; nosso servidor nunca chama um modelo. O que ele rascunha é compilado, verificado e caçado, e você lê a planilha antes de ela ser usada.

$ claude mcp add stalwart -- stalwart mcp

As respostas e o que fazem

Resposta O que acontece Python Guardrails AI
permitir a chamada pode rodar verdict.proceed é verdadeiro PassResult
confirmar a pessoa é perguntada; com o sim, a mesma chamada passa ConfirmationNeeded, ou action == "ask" FailResult com stalwart_confirm
recusar nada roda; a frase da regra diz por quê CallRefused, ou action == "refrain" FailResult com a frase

on_fail escolhe o que uma falha faz: {"refuse": "exception" | "refrain" | "noop", "confirm": "ask" | "exception" | "refrain"}. noop registra a falha e deixa a chamada rodar, só para uma verificação que pode ser desligada.

Por que uma chamada foi barrada

Cada decisão é guardada, por padrão, num arquivo JSON lines por dia em ~/.stalwart/decisions/ (record=False não guarda nada; STALWART_DECISIONS muda o lugar): o decision_id, a resposta, a verificação e sua regra, a ferramenta, os nomes dos argumentos e seus digests, de onde veio o tipo da ferramenta e o que a verificação leu. Nunca o valor de um argumento e nunca uma palavra do que a pessoa disse. explain_decision(decision_id) no stalwart mcp lê de volta.