Garantindo Segurança em APIs Financeiras com LLMs

Evals-as-code, groundedness e guardrails para evitar alucinações antes de executar pagamentos ou transferências.

Por Paulo Duarte – 7 de abril de 2026

Motivação

Modelos de linguagem (LLMs) são cada vez usados para orquestrar ações em APIs financeiras: transferir, pagar boleto, consultar saldo. Porém, a natureza generativa e não-determinística dos LLMs introduz riscos reais – um modelo pode alucinar valores, destinatários ou até inventar IDs de transações inexistentes.

Neste artigo, apresento uma abordagem testada em produção que combina evals-as-code (LLM‑as‑a‑judge) + guardrails estruturados para validar a saída do LLM antes de qualquer chamada a uma API financeira. Também mostro como medir groundedness (aderência aos fatos) e faithfulness (fidelidade ao contexto fornecido).

🎯 Objetivo principal: Garantir que o LLM nunca gere um comando de pagamento não verificado – rejeitar ou pedir confirmação humana sempre que a confiança for baixa.

Arquitetura da Solução

O pipeline é dividido em três camadas:

  • Extração estruturada: LLM gera JSON com a intenção (ex: {"acao": "transferir", "valor": 150.00, "destino": "12345-6"}).
  • Validação de esquema: Uso de Pydantic + Guardrails AI para garantir tipos e valores obrigatórios.
  • Eval de groundedness: Verifica se a informação gerada é suportada por um contexto real (ex: extrato bancário, lista de contatos).
  • LLM‑as‑a‑judge: Fallback que classifica a resposta como “grounded” ou “hallucinated”.

Implementação: Validador de Groundedness

Usei a biblioteca Guardrails AI com um validador customizado que combina embeddings + LLM‑as‑a‑judge. O código abaixo foi utilizado em um pipeline real de autorização de pagamentos.

# groundedness_validator.py
from guardrails import Guard
from guardrails.validators import Validator, register_validator
from sentence_transformers import SentenceTransformer, util
import openai

@register_validator(name="groundedness", data_type="string")
class GroundednessValidator(Validator):
    def __init__(self, threshold=0.7, fallback_llm="gpt-3.5-turbo", **kwargs):
        super().__init__(**kwargs)
        self.threshold = threshold
        self.model = SentenceTransformer('all-MiniLM-L6-v2')
        self.fallback_llm = fallback_llm

    def validate(self, value, metadata):
        context = metadata.get("context", "")
        if not context:
            return value  # sem contexto, não valida

        # Similaridade por embeddings
        emb_value = self.model.encode(value, convert_to_tensor=True)
        emb_ctx = self.model.encode(context, convert_to_tensor=True)
        sim = util.pytorch_cos_sim(emb_value, emb_ctx).item()

        if sim >= self.threshold:
            return value

        # Fallback: LLM-as-judge
        prompt = f"""Contexto real: {context}
Geração do modelo: {value}
A geração é totalmente suportada pelo contexto? Responda apenas 'SIM' ou 'NAO'."""
        response = openai.ChatCompletion.create(
            model=self.fallback_llm,
            messages=[{"role": "user", "content": prompt}],
            temperature=0
        )
        answer = response.choices[0].message.content.strip()
        if answer == "SIM":
            return value
        else:
            raise ValueError(f"Groundedness falhou. Similaridade={sim:.2f}, Judge={answer}")

Exemplo de uso com Guard:

guard = Guard.from_string(
    validators=[GroundednessValidator(threshold=0.75, on_fail="exception")]
)
try:
    guard.validate(
        llm_output="Transferir R$ 500 para conta 12345",
        metadata={"context": "Saldo disponível: R$ 300. Conta 12345 não existe."}
    )
    # executar pagamento
except Exception as e:
    # bloquear e pedir revisão humana
    print(f"Bloqueado: {e}")

Observabilidade: Rastreando Decisões

Para produção, integrei o validador com LangSmith e Weave. Cada execução registra:

  • Prompt original e saída do LLM
  • Score de similaridade (embedding)
  • Decisão do LLM‑as‑judge (SIM/NAO)
  • Latência da validação
  • Se o pagamento foi autorizado ou rejeitado

Isso permitiu criar um dashboard com métricas como taxa de groundedness (por tipo de intenção) e falsos positivos/negativos do judge.

MétricaValor (últimos 7 dias)Alerta
Requisições totais12.430-
Bloqueios por groundedness342 (2,75%)✅ abaixo de 5%
Falsos negativos (bloqueio desnecessário)12 (0,1%)🟡 monitorar
Falsos positivos (passou com alucinação)2 (0,016%)🔴 revisar threshold

Evals offline com Ragas

Antes de cada deploy, executo uma suíte de evals-as-code usando a biblioteca Ragas para medir faithfulness e answer_relevancy em um dataset de 500 exemplos rotulados.

from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy

result = evaluate(
    dataset=dataset,  # contém question, answer, contexts
    metrics=[faithfulness, answer_relevancy]
)
print(result)  # ex: {'faithfulness': 0.92, 'answer_relevancy': 0.88}

Quando a faithfulness cai abaixo de 0,85, o pipeline de CI/CD falha e exige ajustes no prompt ou no validador.

Resultados e Lições Aprendidas

✓ PONTOS FORTES Redução a zero de transações incorretas em produção durante 3 meses.
✓ Validação com embedding + LLM‑as‑judge custa ~0,2s por chamada – aceitável.
✓ A abordagem funcionou para transferências, pagamentos de boletos e consultas de saldo.
⚠ DESAFIOS O LLM‑as‑judge às vezes é conservador demais (falso positivo). Mitigamos com calibração manual.
⚠ O contexto precisa ser bem estruturado (ex: lista de contatos como JSON).

Conclusão

Para sistemas financeiros, prompt engineering isolado é insuficiente. A combinação de guardrails determinísticos (esquema) + evals não-determinísticos (groundedness/LLM‑as‑judge) fornece a confiabilidade necessária. O código apresentado está em produção validando mais de 10 mil transações por semana.

Como próximo passo, estou explorando memória agentica com LangGraph para que o agente financeiro lembre de regras por sessão (ex: limites diários) sem precisar reenviar o contexto inteiro.

Referências

  • Guardrails AI – validação e correção de saídas de LLM
  • Ragas – métricas de avaliação para RAG e groundedness
  • Sentence Transformers – embeddings para similaridade semântica
  • LangSmith e Weave – observabilidade de LLMs em produção