Conectando

As mesmas verificações, onde quer que o seu agente rode. Em todos os casos, o seu código executa a ferramenta, nunca o guardrail, e só depois de o guardrail permitir.

Em Python puro

from stalwart_agents.guardrails import Guardrail

rail = Guardrail(tools=tools, rules="compiled", tool_kinds="tool_kinds.yaml")
verdict = rail.check_call(call, {"turns": turns, "results": results, "session": sid})

call pode ser o nosso formato ({"tool", "arguments"}), uma chamada de ferramenta da OpenAI, um bloco tool_use da Anthropic ou um ToolCall do LangChain. Execute só onde verdict.proceed. Veja Construindo um guardrail para a ida e volta da confirmação.

O contexto que as verificações leem:

Chave O que é
turns os turnos da pessoa, o último por último: a rastreabilidade e as verificações do que foi dito os leem
results o que as ferramentas devolveram, por chamada ou ferramenta, para que um valor numa escrita possa ser rastreado até elas
session mantém o histórico de uma conversa separado: as regras de ordem e o registro da sessão o leem
confirmation o token que approve deu para o sim da pessoa
language o idioma dos turnos, en ou pt-BR, onde não é o do próprio guardrail (turns_in)
labels, origins, plan, plan_token, usage rótulos de confiança, de onde vieram os valores, um plano aprovado e contagens de uso

Na frente de ferramentas MCP: o gateway

O stalwart gateway fica entre o seu agente e os seus servidores MCP, e verifica cada tools/call antes de chegar a um servidor. snippet("gateway", upstream=...) no stalwart mcp grava catalogue.json (as suas ferramentas, os tipos delas e as regras sobrepostas) e um gateway.yaml para começar:

$ stalwart gateway --config gateway.yaml

Uma chamada que precisa de uma pessoa vira uma elicitação MCP com a frase da confirmação; um cliente que não pode ser perguntado recebe uma recusa, nunca uma escrita sem confirmação.

Dentro do Guardrails AI

$ pip install "stalwart-agents[guardrails]"
from guardrails import Guard
from stalwart_agents.guardrails import gai

guard = Guard().use(gai.ToolCall(tools=tools, rules="compiled", on_fail=gai.ask))

for call in response.tool_calls:                    # formato da OpenAI ou da Anthropic, do LangChain, ou um dict
    outcome = guard.validate(gai.as_json(call), metadata={"stalwart": {"turns": turns, "results": results,
                                                                       "session": sid}})
    if outcome.validation_passed:
        results[call.id] = run_tool(call)
    elif (payload := gai.asked(guard)) is not None:  # mostre payload["sentence"]; com o sim da pessoa:
        outcome = guard.validate(gai.as_json(call), metadata={"stalwart": {
            "turns": turns, "results": results, "session": sid, "confirmation": gai.approve(payload)}})

Valide cada chamada; não embrulhe o modelo. O Guardrails AI entrega aos validadores o texto que um modelo escreveu. Quando embrulha uma chamada ao modelo, um validador recebe só os argumentos da última chamada de ferramenta, sem o nome da ferramenta, em toda versão da faixa que suportamos (0.7 a 0.11). Por isso cada chamada que o modelo propõe é validada sozinha, antes de o seu código executá-la.

Os validadores:

Validador Nome Roda
ToolCall stalwart/tool_call todas as verificações
GroundedArguments stalwart/grounded_arguments a rastreabilidade
PolicyRules stalwart/policy_rules a sua planilha de regras
AllowedTool stalwart/allowed_tool as ferramentas permitidas

As verificações que não podem ser desligadas rodam em cada um, então uma escrita espera por uma pessoa em todos.

O que cada resposta faz:

O contexto vem de metadata["stalwart"] (as chaves acima). Uma escrita verificada sem turns falha, nomeando a chave que falta: fechado, nunca uma passagem silenciosa. Os nossos validadores nunca alteram uma chamada e nunca chamam um modelo, então fix e reask não mudam nada: uma chamada recusada continua recusada.

Vários validadores num guard: Guard().use_many(deles, nosso) até a 0.10, Guard().use(deles, nosso) a partir da 0.11.

O próprio Guardrails AI envia métricas de uso, a menos que ~/.guardrailsrc diga enable_metrics=false. Nada nosso envia.

No NeMo Guardrails

$ pip install "stalwart-agents[nemo]"
from nemoguardrails import LLMRails, RailsConfig
from stalwart_agents.guardrails import Guardrail
from stalwart_agents.guardrails.nemo import register, snippet

rails = LLMRails(RailsConfig.from_path("config"))
register(rails, Guardrail(tools=tools, rules="compiled"))
print(snippet("2.x"))          # ou "1.0"

register acrescenta uma ação, stalwart_check_tool (StalwartCheckToolAction em Colang 2). O trecho é a forma de guarda que o verificador prova: a ação da ferramenta só roda dentro do ramo onde a verificação a permitiu; uma chamada que espera por uma pessoa pergunta a ela e, com o sim, é verificada de novo com o token; todo outro ramo termina na frase de recusa. Troque a intenção e a ferramenta pelas suas; mantenha a forma.

O que a ação lê do contexto do NeMo, onde os próprios argumentos dela não o dão:

Chave Lida como
stalwart_arguments os argumentos da chamada (defina-a a partir da ação que propõe a chamada)
user_message, senão last_user_message o último turno da pessoa
stalwart_turns os turnos da pessoa, o último por último, no lugar só do último
stalwart_results o que as ferramentas devolveram
stalwart_session a conversa (nemo onde não definida)
stalwart_confirmation o token do sim da pessoa

Uma escrita verificada sem turno é recusada, nunca permitida. Em Colang 2, bot say espera o seu cliente informar que a fala terminou antes de o fluxo continuar, como um laço de chat faz.

A própria integração do NeMo com o Guardrails AI valida só as mensagens do usuário e do bot, nunca chamadas de ferramenta, então os nossos validadores são usados por register acima, não por ela.