Voltar ao Blog
AI & Development26 de abril de 202611 min de leitura

Como Configurei Guardrails de IA no BMO

DR

Doug Ribeiro

11 min de leitura

Em um banco, a primeira pergunta sobre um assistente de programação com IA raramente é 'o que essa ferramenta pode fazer?' É 'o que podemos garantir que ela nunca faça?' Essa abordagem moldou tudo quando configurei o assistente do meu time no BMO, o GitHub Copilot Business. Guardrails em um ambiente bancário não são sobre limitar a ferramenta. São sobre tornar a ferramenta confiável o suficiente para realmente usar em uma base de código regulada.

O que segue é a abordagem: as camadas em que me apoio, as categorias de preocupação que valem a pena cobrir e como uma configuração assim funciona na prática. Os arquivos deste post são exemplos ilustrativos que escrevi para ele, não a configuração do banco, que continua interna. O que se transfere para a sua própria base de código é a estrutura e o raciocínio.

O Que Guardrails Realmente Significam em um Ambiente Regulamentado

Em uma startup de tecnologia para consumidor, guardrails de IA podem significar 'não deixar o Copilot sugerir geração insegura de números aleatórios.' Em um banco, o escopo é consideravelmente mais amplo. Um banco canadense opera sob diretrizes do OSFI, requisitos do FINTRAC, PCI DSS para dados de cartão de pagamento e políticas de classificação de dados que determinam o que o código pode fazer com quais categorias de informação.

O modelo de ameaças para programação assistida por IA em bancos tem três camadas. Primeiro, risco de exfiltração de dados: código que inadvertidamente envia dados de clientes para serviços externos. Segundo, anti-padrões de segurança: sugestões que introduzem vulnerabilidades conhecidas, como credenciais hardcoded, validação insuficiente de entrada ou referências diretas inseguras a objetos. Terceiro, violações de compliance: código que ignora logging de auditoria, ignora flags de consentimento ou processa dados de formas que violam requisitos de residência de dados.

Nada disso é hipotético. Sem instruções, um assistente de IA sugere com facilidade localStorage para armazenar um token de sessão de usuário (um anti-padrão de segurança em contexto bancário), uma chamada HTTP direta para uma API de terceiros sem roteamento por proxy (um problema de governança de dados) ou um console.log que imprime um objeto de cliente inteiro (uma violação de logging de PII). O code review pega esses casos. Guardrails são sobre pegá-los antes que cheguem a um revisor humano.

Camada de instruções de IA.github/copilot-instructions.md e arquivos de instrução por caminho. Ev… 1 Camada de análise estáticaRegras customizadas de ESLint. Pegam as violações que passaram pelas in… 2 Camada de code reviewGates de revisão humana. Pegam divergências de intenção que nenhuma fer… 3 Quality gate de CIESLint, o limiar de cobertura do Jest e os quality gates do SonarQube e… 4

Instruções Customizadas de Repositório no Copilot

O Copilot lê instruções customizadas válidas para o repositório inteiro a partir do .github/copilot-instructions.md, um arquivo Markdown simples com instruções em linguagem natural versionado junto com o código. Pense nele como um system prompt que viaja com o repositório e é adicionado a cada requisição de chat. O GitHub documenta isso para o Copilot Chat, para o code review e para o agente na nuvem, e a documentação do VS Code diz que instruções customizadas não são levadas em conta nas sugestões inline enquanto você digita, o que é uma das razões pelas quais a camada de lint abaixo importa. Ainda assim, é aqui que os guardrails de maior impacto vivem.

Um arquivo desses funciona melhor dividido em seis frentes: contexto do projeto, regras de segurança, tratamento de dados, padrões específicos do Angular, padrões de qualidade de código e coisas que a IA nunca deve fazer. A seção 'nunca fazer' é a mais importante. É explícita e imperativa. O exemplo abaixo segue essa estrutura. Tudo nele, incluindo cada nome de serviço, foi inventado para este post, então trate como uma forma a preencher e não como um conjunto de regras para copiar.

markdown
<!-- .github/copilot-instructions.md -->
<!-- ILLUSTRATIVE. Written for this blog post, invented end to end. -->
<!-- Every service name below is made up. This is not BMO's file, and the -->
<!-- rules are the kind a bank would write, not the ones any bank did write. -->

# Project context
This is an enterprise Angular application for a regulated financial institution.
- Angular version: <your exact major version>
- State management: NgRx with strict action hygiene.
- Styling: SCSS with design tokens via CSS custom properties.
- Testing: Jest for unit tests.
- All API calls go through ApiClientService, never directly via HttpClient.

# Security rules (mandatory)
- Never suggest storing sensitive data (tokens, credentials, PII, account numbers) in localStorage or sessionStorage.
- Never generate code that logs objects containing user data, account information, or transaction details.
- Never suggest direct third-party API calls. All external communication goes through ApiClientService.
- Never generate hardcoded credentials, API keys, environment-specific URLs, or secrets of any kind.
- Never suggest eval(), Function(), or other dynamic code execution.
- Validate and sanitize input on the client, and never assume the client check replaces the server one.
- Tokens live in httpOnly cookies managed by AuthService. Do not suggest alternatives.

# Data handling
- Display PII fields (SIN, date of birth, account numbers, name plus address combinations) through PiiMaskingService.
- Audit-sensitive operations call AuditService.record() before the operation runs.
- Check consent through ConsentService before processing marketing-related data.
- Never suggest patterns that send customer data outside approved service boundaries.

# Angular patterns
- Match the component style already used in the surrounding code. Do not mix styles in one feature.
- OnPush change detection is the default. Justify any use of the Default strategy.
- Every subscription needs a teardown. Prefer AsyncPipe, or the teardown pattern the codebase already uses.
- Route errors through the global ErrorHandler, not ad-hoc try/catch with console.error.
- Feature flags go through FeatureFlagService.isEnabled(). Never hardcode feature conditionals.
- Avoid any. If you cannot type something precisely, use unknown and narrow it.

# Code quality
- Public methods have JSDoc comments.
- Flag functions longer than 30 lines for decomposition.
- Magic numbers and strings become named constants.
- New services come with a *.spec.ts that meets the coverage threshold enforced in CI.

# Never do
- Do not suggest code that bypasses the established service layer.
- Do not generate commented-out code blocks.
- Do not add TODO comments without a ticket reference.
- Do not propose architectural changes without flagging them as architectural suggestions that need review.

Instruções por Caminho e Exclusão de Conteúdo

Regras que só importam para uma parte da base de código vão em arquivos por caminho dentro de .github/instructions/, com o nome NOME.instructions.md e um glob applyTo no frontmatter. Quando um arquivo corresponde, o Copilot combina essas instruções com as do repositório inteiro. Convenções de teste são uma boa candidata: elas importam em cada arquivo spec e em nenhum outro lugar. Aqui vai um .github/instructions/testing.instructions.md a título de exemplo:

markdown
---
applyTo: "**/*.spec.ts"
---

<!-- ILLUSTRATIVE. Written for this blog post, not a real bank configuration. -->

- Unit tests use Jest. Follow the describe/it structure of the neighboring spec files.
- Cover the happy path, boundary values, null and undefined inputs, and the error branch.
- Never put realistic customer data in fixtures. Use values that are obviously fake.
- Mock HTTP at the service boundary. A unit test never calls a real endpoint.
- Every assertion checks behavior. A test that only proves the component was created does not count as coverage.

Instruções moldam o que o Copilot escreve. A exclusão de conteúdo controla o que ele consegue ver. Nos planos Business e Enterprise, administradores de repositório e owners da organização podem excluir arquivos e diretórios para que o Copilot não os use como contexto. As exclusões são cadastradas na UI do GitHub (Settings do repositório, depois Copilot, depois Content exclusion, ou o equivalente no nível da organização), não em um arquivo versionado. É isso que as torna a ferramenta certa para os arquivos de implementação mais sensíveis. Uma lista ilustrativa no nível do repositório:

yaml
# Illustrative repository-level content exclusion, not a real configuration.
# Entered in the GitHub UI under Settings > Copilot > Content exclusion.
# Mirrored here for documentation only. Copilot does not read this file.
- "/src/environments/**"   # environment configuration
- "**/secrets/**"          # secret management utilities
- "*.pem"
- "*.key"
- "*.env*"

As Camadas que Realmente Aplicam: ESLint e o Gate de CI

Guardrails não podem depender da IA respeitar instruções, porque uma instrução é um pedido e um modelo é livre para ignorar um pedido. A camada de lint captura os anti-padrões no nível do código, independente de quem ou do que digitou. Um detalhe que importa mais do que parece: escreva a regra contra a AST, não contra uma string. Uma versão ingênua que só inspeciona um identificador solto deixa passar window.localStorage e acessos computados como window['localStorage'], que é exatamente a forma que um modelo procura quando disseram para ele não usar a forma simples.

typescript
// eslint-rules/no-web-storage.js (illustrative example)
// Disallow localStorage/sessionStorage. Session tokens live in httpOnly
// cookies managed by AuthService, which is what the instructions file says too.
module.exports = {
  meta: {
    type: 'problem',
    docs: {
      description: 'Disallow direct localStorage/sessionStorage access',
      category: 'Security'
    },
    messages: {
      noWebStorage:
        'Do not use {{ storage }} directly. Tokens live in httpOnly cookies managed by AuthService. See: <link to your storage guidelines>'
    }
  },
  create(context) {
    const STORAGES = ['localStorage', 'sessionStorage'];

    // Matches localStorage.x, window.localStorage.x and window['localStorage'].x
    const storageName = (object) => {
      if (object.type === 'Identifier' && STORAGES.includes(object.name)) {
        return object.name;
      }
      if (object.type === 'MemberExpression') {
        const key = object.computed
          ? (object.property.type === 'Literal' ? object.property.value : null)
          : object.property.name;
        return STORAGES.includes(key) ? key : null;
      }
      return null;
    };

    return {
      MemberExpression(node) {
        const storage = storageName(node.object);
        if (!storage) return;
        context.report({ node, messageId: 'noWebStorage', data: { storage } });
      }
    };
  }
};

ESLint é só metade da aplicação das regras, e a metade menor. A outra metade é o gate de CI, e é essa a camada que decide o que entra no merge. O lint e o limiar de cobertura do Jest rodam em cada pull request, e os quality gates do SonarQube rodam junto. Um gate que falha bloqueia o merge, e ele não está nem aí se foi uma pessoa ou um modelo que escreveu a linha. A distinção toda está aí: o arquivo de instruções é orientação, e orientação pode ser ignorada, enquanto o pipeline não é negociável. Se o seu time só tem fôlego para construir uma camada neste trimestre, construa essa. Instruções sem um gate atrás delas são um guia de estilo.

O Que Regras Assim Previnem

As categorias são previsíveis. Em código front-end escrito com ajuda de IA, as mesmas quatro aparecem sempre: uso direto de localStorage para estado de autenticação, chamadas console.log que serializam payloads de resposta inteiros, uso direto de HttpClient que ignora a camada de serviços e anotações tipadas como any, que enfraquecem a segurança de tipos.

Esses não são bugs de segurança hipotéticos. São os padrões que as ferramentas de IA adotam por padrão, porque tutoriais genéricos de Angular estão cheios deles. Guardrails não tornam as ferramentas menos úteis. Tornam o resultado confiável o suficiente para incluir em code review em vez de tratar cada sugestão de IA como suspeita por padrão.

Os Limites dos Guardrails

Quero ser claro sobre o que guardrails não podem fazer. Eles não podem prevenir erros de arquitetura. Não podem capturar erros de lógica de negócio em regras de domínio complexas. Não podem verificar se um algoritmo gerado por IA produz resultados corretos para casos extremos. Não podem substituir code review. O que eles fazem é estabelecer um piso: um conjunto de padrões inegociáveis que nunca devem aparecer independente da tarefa.

  • Guardrails são um piso, não um teto. Previnem os piores resultados, não todos os subótimos
  • Instruções customizadas são sugestões para a IA, não restrições rígidas, e elas não chegam às sugestões inline. O pipeline, lint mais testes mais quality gates, é a aplicação real
  • Guardrails precisam ser atualizados conforme o codebase evolui. Regras desatualizadas criam confusão e depois são ignoradas
  • Engenheiros precisam saber por que cada regra existe. Regras sem justificativa são contornadas
  • A exclusão de conteúdo é o controle mais confiável sobre o que o Copilot enxerga, que é uma pergunta diferente do que vai para produção. Conteúdo excluído não é usado como contexto. O GitHub documenta que o modo Agent do Copilot Chat não suporta exclusão de conteúdo, então verifique quais modos o seu time realmente usa

O objetivo não é prender a IA. O objetivo é tornar as sugestões da IA confiáveis o suficiente para que os desenvolvedores possam focar sua energia de revisão em lógica e corretude de negócio, não em capturar anti-padrões de segurança que o linting deveria capturar automaticamente.

Se você está configurando ferramentas de IA em um ambiente regulamentado, comece pela lista do 'nunca fazer'. Quais são as três categorias de código que, se geradas e mergeadas sem revisão, causariam um incidente? Escreva essas regras primeiro, aplique-as com linting e construa a orientação positiva a partir daí. Montar isso não é um projeto de um trimestre, e também nunca fica pronto. Eu continuei refinando conforme a base de código e as ferramentas mudavam. Está limitado a um time só, não é um template para toda a organização. Isso é suficiente para mudar como um time de cinco desenvolvedores trabalha, e é um ponto de partida melhor do que tentar padronizar a organização inteira antes de alguém ter usado as regras para valer.

Referências

AIEnterpriseBankingDeveloper ToolsAngular