No Azure Pipelines, uma approval que não é concluída dentro do seu timeout marca o stage como skipped, não como falho. Um stage skipped aparece verde na maioria dos dashboards e na maioria dos templates de notificação, então uma promoção de modelo que ninguém olhou pode concluir uma execução de pipeline que reporta sucesso enquanto a mudança nunca saiu, ou pior, enquanto um stage anterior já fez metade dela. A Parte 8 colocou as aplicações em cima do gateway. Esta parte leva o próprio gateway à produção de forma repetível, e transforma o email de aposentadoria de modelo da Parte 1 em uma query de banco de dados.

O que de fato está sendo deployado

Quatro artefatos, mudando em três velocidades diferentes, e é por isso que são três pipelines em vez de um.

  • Infraestrutura da plataforma do 10-platform da Parte 2: API Management, AKS, rede, observabilidade. Muda algumas vezes por ano, é deployada com uma approval humana, e ninguém está com pressa.
  • Model deployments e configuração do gateway, a camada 20-models mais a policy XML e o mapa de aliases. Muda toda semana. Esse é o pipeline que precisa do eval gate.
  • Configuração de tenants, a camada 30-tenants. Muda no onboarding, e é o único lugar onde um pull request self-service de um time consumidor faz sentido.
  • O container do control plane no AKS. CI/CD comum de aplicação, e o menos interessante dos quatro precisamente por ser comum.

A policy XML merece uma nota própria. Ela é executada em cada requisição, então é código de produção, e chega à produção pelo mesmo pipeline com o mesmo review do Terraform. O portal é para leitura, não para edição, e o jeito de fazer isso valer é um drift check no pipeline que falha quando a policy deployada difere do repositório.

A service connection, e o timer que ninguém configura

Pipelines se autenticam no Azure com uma service connection do tipo Azure Resource Manager, e a configuração recomendada é workload identity federation, com um app registration ou uma managed identity, o que tira segredos e gestão de segredos completamente do quadro. Para uma plataforma cujo argumento inteiro da Parte 5 era o ciclo de vida de credenciais, usar um segredo armazenado aqui seria difícil de defender.

Dois detalhes operacionais merecem entrar no runbook em vez de serem descobertos. O Azure Pipelines desabilita automaticamente service connections que não são usadas por 100 dias, e um administrador da service connection ou um Project Administrator precisa reabilitá-las. Um pipeline que só roda em mudanças trimestrais de infraestrutura é exatamente o pipeline que cai nisso, e a falha chega na pior hora, quando algo urgente precisa ser deployado. E uma service connection convertida de segredo para federation pode ser revertida por sete dias; depois disso, um novo segredo tem que ser criado manualmente, e conexões que você converteu e depois alterou manualmente não podem ser revertidas pela ferramenta de jeito nenhum.

O eval gate pertence ao environment, não ao job

O instinto é adicionar um step de eval no fim do job de deploy. Isso faz do eval uma coisa que o pipeline faz, o que significa que o autor do pipeline pode reordená-lo, pulá-lo com uma condição ou movê-lo para depois do deployment que ele deveria proteger.

O Azure Pipelines tem a primitiva certa: checks no environment ou na service connection que o stage consome. Antes de um stage rodar, todos os checks em todos os recursos que ele usa precisam ser satisfeitos, e uma única decisão negativa nega o stage. Configure o eval como um check do tipo Invoke REST API que chama o serviço de eval do control plane, e o gate deixa de pertencer à definição do pipeline e passa a pertencer ao environment.

As regras de finalidade importam aqui. Decisões de approvals e da maioria dos checks são finais. Os checks Invoke Azure Function e Invoke REST API são a exceção, eles podem ser reexecutados, e se você configurar um time between evaluations diferente de zero, a decisão do check deixa de ser final e ele será reavaliado. Para um eval gate, isso é uma escolha de design, não um detalhe: um check não final que continua rodando é o certo para "o eval ainda precisa estar passando quando este stage finalmente começar", e um final é o certo para "este build passou nos seus evals, pode enviar". Escolha um, e saiba que a forma assíncrona configurada do jeito recomendado é final.

Adicione uma approval humana em cima do eval, no environment de produção, com a opção de impedir que aprovadores aprovem suas próprias execuções habilitada. E defina o timeout deliberadamente, por causa do lead deste post: uma approval que expira marca o stage como skipped, e skipped é lido como verde. A mitigação é um timeout curto, alertas especificamente sobre stages skipped, e nunca tratar um pipeline verde como evidência de que um deployment aconteceu.

O que o eval de fato verifica

Um golden set de casos que importam para os consumidores da plataforma, versionado no repositório ao lado da policy XML, rodando contra a configuração candidata através do gateway, não contra um endpoint de modelo. Esse último ponto é o que faz dele um eval de plataforma, não um benchmark de modelo: ele exercita a resolução de alias, as policies, o filtro de content safety e o caminho de retrieval, e qualquer um deles pode quebrar uma resposta sem que o modelo mude em nada.

Três thresholds, e o terceiro é o que as pessoas pulam. Um score agregado de qualidade contra o golden set. Um orçamento de latência, porque um modelo marginalmente melhor e significativamente mais lento não é um upgrade para um assistente interativo. E uma lista de regressão: casos específicos que não podem piorar, independentemente da média. Uma nova versão de modelo que melhora a média enquanto quebra os três prompts dos quais o time de atendimento depende é a forma normal de uma promoção ruim, e só a lista de regressão a pega.

Promoção, canary e rollback são um mecanismo só

Como a Parte 3 colocou um alias na frente de um backend pool, uma mudança de versão de modelo não toca nenhuma aplicação. É uma mudança de configuração com três formas:

  • Canary: adicione o novo deployment ao pool do alias com um peso baixo no mesmo grupo de prioridade. Uma fatia pequena do tráfego vai para ele, as métricas da Parte 5 mostram diferenças de latência e de tokens por alias de modelo, e nada precisou ser redeployado.
  • Promoção: desloque os pesos, depois remova o deployment antigo do pool.
  • Rollback: a mudança de peso inversa. Segundos, não um redeploy, que é o argumento inteiro para o alias existir.

A única coisa que isso não faz é tornar as respostas idênticas dos dois lados da fronteira do canary. Duas versões de modelo servindo o mesmo alias produzem saídas diferentes para o mesmo prompt, então qualquer coisa que cacheia, compara ou reexecuta respostas precisa saber qual versão respondeu. Isso é um campo de log, adicionado junto com o canary, não depois.

Aposentadoria, que agora é uma query

O email de aposentadoria da Parte 1 custou dois dias de grep em repositórios. Agora deveria custar uma query, porque três decisões anteriores tornaram a resposta conhecível. A Parte 2 fixou as versões de modelo e definiu version_upgrade_option deliberadamente, então nenhum deployment se move sozinho. A Parte 3 colocou toda aplicação atrás de um alias, então o mapeamento de alias para deployment é dado, não código. A Parte 5 registrou cada requisição com seu tenant e seu modelo resolvido, então o conjunto de tenants que de fato usou um deployment nos últimos 30 dias é uma query de log, não uma suposição.

O runbook de aposentadoria vira então quatro passos que cabem em um ticket: consultar quais tenants usaram o deployment recentemente; subir o deployment substituto e rodar o eval gate contra ele; fazer canary do alias e observar a lista de regressão; virar e remover. As aplicações nunca ficam sabendo que nada disso aconteceu, que é o resultado para o qual a série inteira existiu.

Modos de falha para observar

  • Skipped lido como sucesso. Uma approval expirada pula o stage. Alerte explicitamente sobre o estado skipped, na notificação do pipeline e em qualquer dashboard que o time de fato olha.
  • O eval como step de job. Se ele pode ser reordenado ou removido por uma condição, é documentação. Como check de environment, é um gate.
  • Bypass sem visibilidade. Fazer bypass de um check exige permissão de administrador no recurso e fica registrado com quem o fez, o que é bom, e vale trazer para uma revisão semanal em vez de deixar no painel de checks.
  • Uma service connection desabilitada aos 100 dias. O pipeline trimestral de infraestrutura descobre isso durante um incidente.
  • Edições de policy pelo portal. Sem um drift check, um hotfix feito no portal sobrevive até o próximo deploy revertê-lo silenciosamente, que é o pior timing possível.
  • Um conjunto de eval que ninguém atualiza. Um golden set que não muda há um ano está medindo o produto do ano passado.

O que a Parte 10 herda

Um gateway que chega à produção por pipelines, com evals como gates e mudanças de modelo como configuração. Todo mecanismo da série está agora no lugar, e cada um deles emite alguma coisa. A última parte é sobre o que fazer com isso: traces, evals em produção em vez de no pipeline, as perguntas de governança que o EU AI Act faz, e os números que a Parte 1 prometeu.

Leia isso a seguir

Para o lado de infraestrutura e plataforma de rodar isso em escala, as notas de campo estão em ercan.cloud, e o hub fica em ercanermis.com.

Referências