Files
nemo_guardrails_ci_cd/README_BKP.md
2026-04-29 20:45:00 -03:00

12 KiB

CI/CD NeMo Guardrails no Azure DevOps com Deploy em OCI OKE

Objetivo

Esta documentação tem por objetivo mostrar como implantar a estratégia de guardrails via Nemo Guardrails. A idéia principal é que o projeto está baseado nesta especificação:

Tutorial: NeMo Guardrails com Python, Proxy OpenAI-Compatible e Tracing

A arquitetura desta solução visa se integrar com soluções de agentes de IA com os guardrails do Nemo Guardrails.

User Input
  ↓
Input Rails
  ├─ Regex: PII Masking
  ├─ LLM: Toxicidade
  └─ LLM: Out-of-Scope
  ↓
LLM principal via NeMo Guardrails
  ↓
Output Rails
  ├─ Compliance Anatel
  ├─ Verbalização Prematura
  └─ Groundedness
  ↓
Python Rules
  ├─ Alçada de Ajuste
  └─ Consistência Histórica
  ↓
Execução de API / Backend
  ↓
Supervisor VAS Avulso
  ↓
Curadoria / Métricas
  ├─ TCR
  ├─ Fallback
  ├─ Tokens
  ├─ Tamanho de mensagem
  └─ Eficiência RAG
  ↓
Resposta final

Os detalhes da arquitetura podem ser vistas no material citado acima.

As aplicações agentes devem ser integradas a esta estrutura do projeto:

ai_agent_project
├── src/
│   ...
nemo_guardrails_tracing_project/
├── config/
│   ├── config.yml : Arquivo de configuração para o Nemo Guardrail (Modelos, definições de rails de input e output)
│   ├── config.py : Registro das ações para os guardrails
│   ├── guardrails.yaml : Arquivo de mapeamento dos rails conforme definição da Planilha inicial (não utilizado pelo código)
│   ├── rails/
│   ├─────input.co : configuração de entrada dos fluxos Nemo 
│   ├─────output.co : configuração de saída dos fluxos Nemo 
│   └── guardrails_catalog.json
├── src/
│   ├── app.py : demo para consumir rails sem uso de Nemo Guardrails
│   ├── app_nemo.py : demo para consumir rails com uso de Nemo Guardrails
│   ├── actions.py : Arquivo de Actions expondo deterministic_rails e llm_rails
│   ├── deterministic_rails.py : rails deterministicos para serem utilizados como modelo na estrutura
│   ├── judges.py : estrutura demo para ilustrar judge
│   ├── llm_client.py : mockup para demo llm
│   ├── llm_rails.py : rails llm para serem utilizados como modelo na estrutura
│   ├── models.py : classe do modelo de resposta dos rails
│   ├── registry.py : modelo de registry do guardrail
│   └── prompts/ : pasta com os prompts para rails que utilizam llm
├── tests/
│   └── test_guardrails.py : código utilizado para pytest
├── scripts/
│   └── run_tests.sh : bash script para testar os rails individualmente via pytest
├── requirements.txt : bibliotecas necessárias para o projeto
├── .env.example : variáveis de ambiente para configuração
└── README.md

Portanto, trataremos aqui neste material a implantação da estrutura de nemo_guardrails_tracing_project juntamente com o projeto de Agente de IA.

Conceitualmente, teríamos este pipeline de deployment:

Pipeline do Agente de IA
↓
aciona/consome
↓
Pipeline do projeto NeMo Guardrails
↓
publica pacote/artefato aprovado
↓
Pipeline do Agente usa somente o resultado

O pipeline do agente não vê nem controla os steps internos do NeMo. Ele só consome um artefato versionado/aprovado.

Modelo recomendado

1. Pipeline separado do NeMo

No repositório nemo_guardrails_configuration, você cria um pipeline próprio que:

checkout nemo
↓
instala dependências
↓
roda testes de guardrails
↓
gera pacote/artefato
↓
publica artifact ou package
  • O NeMo NÃO publica uma imagem final para produção
  • Ele publica um artefato reutilizável

Exemplo conceitual:

O alvo do deployment é um container dentro de um cluster Kubernetes. Portanto, a idéia é:

Pod
 └── container único
        ├── código do agente
        ├── library do NeMo
        └── configs de guardrails

No modelo de library, teremos o seguinte:

Pipeline NeMo
   ↓
gera artifact (ou package)
   ↓
-----------------------------------
Pipeline Agente
   ↓
baixa artifact do NeMo
   ↓
EMBUTE no build
   ↓
gera imagem FINAL
   ↓
deploy no OKE

azure-pipelines-nemo.yml

trigger:
- main

pool:
vmImage: ubuntu-latest

steps:
- checkout: self

- task: UsePythonVersion@0
  inputs:
  versionSpec: '3.11'

- script: |
  python -m pip install --upgrade pip
  pip install -r requirements.txt
  pytest -v
  displayName: Test NeMo Guardrails

- script: |
  mkdir -p dist/nemo_guardrails_configuration
  cp -r config dist/nemo_guardrails_configuration/
  cp -r src dist/nemo_guardrails_configuration/
  cp requirements.txt dist/nemo_guardrails_configuration/
  displayName: Package NeMo artifacts

- publish: dist/nemo_guardrails_configuration
  artifact: nemo-guardrails-package
  displayName: Publish NeMo package

Esse pipeline é controlado pelo time responsável pelo NeMo.

A arquitetura foi pensada para um projeto de guardrails que não é uma aplicação final isolada, mas uma camada reutilizável consumida por várias soluções, agentes, bots, APIs e motores conversacionais.


Arquitetura

Azure DevOps
   |
   |-- checkout do repositório
   |-- instala Python 3.11
   |-- instala dependências
   |-- executa pytest
   |-- build Docker
   |-- push para OCIR
   |-- kubectl apply no OKE
   |
OCI OKE
   |
   |-- Deployment guardrails-api
   |-- Service guardrails-service
   |
Aplicações consumidoras
   |
   |-- HTTP POST /chat

Estrutura de arquivos

.
├── app/
│   ├── __init__.py
│   └── main.py
├── config/
│   ├── config.yml
│   ├── input.co
│   └── output.co
├── src/
│   ├── __init__.py
│   ├── actions.py
│   ├── deterministic_rails.py
│   └── llm_rails.py
├── tests/
│   ├── conftest.py
│   ├── test_api_contract.py
│   ├── test_e2e.py
│   └── test_structural.py
├── k8s/
│   ├── deployment.yaml
│   ├── service.yaml
│   └── kustomization.yaml
├── scripts/
│   ├── build_local.sh
│   ├── deploy_local.sh
│   └── test_local.sh
├── azure-pipelines.yml
├── Dockerfile
├── requirements.txt
└── README.md

O que é o ./config no CI/CD?

No código NeMo Guardrails normalmente existe:

from nemoguardrails import LLMRails, RailsConfig

config = RailsConfig.from_path("./config")
rails = LLMRails(config)

No Azure DevOps, o ./config é o diretório config/ versionado no repositório.

Quando o pipeline executa:

- checkout: self

o repositório é baixado no agente do Azure DevOps. A partir daí, o diretório config/ passa a existir no workspace do pipeline.

Para evitar problemas com diretório corrente, este projeto usa path absoluto relativo ao arquivo Python:

BASE_DIR = Path(__file__).resolve().parent.parent
CONFIG_PATH = BASE_DIR / "config"

Assim, funciona localmente, no pytest, no Docker e no OKE.


Artefatos principais

config/config.yml

Define o modelo principal usado pelo NeMo Guardrails.

Neste exemplo usamos engine: openai porque muitos frameworks esperam a interface OpenAI. Em ambientes reais, esse endpoint pode apontar para um proxy OpenAI-compatible, inclusive um proxy para OCI Generative AI.

config/input.co

Define rails de entrada. Em um projeto real, aqui entram fluxos de validação de entrada, por exemplo:

  • toxicidade
  • prompt injection
  • dados sensíveis
  • fora de escopo

config/output.co

Define rails de saída. Em um projeto real, aqui entram validações como:

  • resposta segura
  • resposta aderente ao contexto
  • resposta sem vazamento de dados
  • resposta sem alucinação

src/deterministic_rails.py

Contém regras determinísticas. Exemplos:

  • máscara de CPF
  • validação de alçada
  • checagem de protocolo
  • normalização de texto

src/llm_rails.py

Contém validações que podem depender de LLM ou judge. Exemplos:

  • qualidade de resposta
  • groundedness
  • classificação semântica
  • verificação de tom

src/actions.py

Local para registrar actions usadas pelo NeMo Guardrails.

app/main.py

Expõe a solução como uma API HTTP:

GET  /health
POST /chat

Isso permite que várias soluções consumam a arquitetura central de guardrails.


Execução local

python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
export OPENAI_API_KEY=sk-fake
pytest -v
uvicorn app.main:app --host 0.0.0.0 --port 8080

Teste:

curl -X POST http://localhost:8080/chat \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"quero cancelar plano"}]}'

Docker local

docker build -t guardrails-api:local .
docker run --rm -p 8080:8080 -e OPENAI_API_KEY=sk-fake guardrails-api:local

Variáveis necessárias no Azure DevOps

Configure estas variáveis no Pipeline ou em Variable Group:

Variável Exemplo Observação
OCIR_REGION_KEY gru ou sa-saopaulo-1 usado para montar registry
OCIR_REGISTRY gru.ocir.io endpoint OCIR
OCI_NAMESPACE meunamespace namespace Object Storage/OCIR
OCIR_REPOSITORY guardrails/guardrails-api repositório da imagem
OCIR_USER tenancy/user usuário OCIR
OCIR_PASSWORD *** auth token OCI
OKE_KUBECONFIG_B64 *** kubeconfig em base64
OPENAI_API_KEY sk-fake pode ser fake quando usar proxy

Como gerar o kubeconfig base64

Na sua máquina:

oci ce cluster create-kubeconfig \
  --cluster-id <OCID_DO_CLUSTER> \
  --file kubeconfig \
  --region sa-saopaulo-1 \
  --token-version 2.0.0

base64 -i kubeconfig | pbcopy

Cole o valor em OKE_KUBECONFIG_B64 no Azure DevOps.


Pipeline Azure DevOps

O arquivo azure-pipelines.yml faz:

  1. Checkout.
  2. Setup Python 3.11.
  3. Instala dependências.
  4. Executa testes.
  5. Build da imagem Docker.
  6. Login no OCIR.
  7. Push da imagem.
  8. Aplica manifests Kubernetes no OKE.
  9. Aguarda rollout.

Deploy no OKE

Os manifests estão em k8s/.

O pipeline substitui a imagem no deployment usando:

kubectl set image deployment/guardrails-api guardrails-api=$IMAGE_URI -n guardrails

Testes

Os testes são versionados no repositório. O CI/CD não cria os testes; ele apenas executa.

Este pacote inclui:

  • teste estrutural: valida carregamento do RailsConfig.
  • teste de contrato da API: valida formato da requisição/resposta.
  • teste E2E: simula entrada passando por API e rails.

Considerações de produção

Para produção, recomenda-se evoluir:

  • Secrets via Kubernetes Secret ou OCI Vault.
  • Ingress Controller em vez de LoadBalancer direto.
  • HPA para autoscaling.
  • Logs estruturados.
  • Tracing com OpenTelemetry.
  • Separação de testes determinísticos e testes com LLM.
  • Política de versionamento de imagem por commit SHA.