Lab 82 — Prompt em produção não é configuração
O problema, e a empresa que o tem
A Cadência publicou o cliente .NET do L81 há duas semanas: um catálogo de 30 lojas passou a gerar a descrição de cada produto novo chamando o Bedrock, com o texto do prompt de sistema guardado como recurso de Prompt Management. Funcionou bem — até a manhã em que alguém do time de marketing, tentando deixar as descrições "mais persuasivas", abriu o console do Bedrock, editou o texto do prompt diretamente na aba Prompt management, e salvou.
Não houve deploy. Não houve pull request. A próxima chamada do catálogo já usou o texto novo, porque o código só guarda o ID do prompt — nunca pediu uma versão específica. Em três dias, o time de vendas notou que descrições de produto vinham saindo estranhas: promessas que o produto não cumpria, tom fora do padrão da marca, uma vez até um adjetivo que soava como propaganda enganosa. Ninguém sabia dizer QUANDO o texto mudou, porque a mudança nunca foi um commit.
O L54 já resolveu como publicar CÓDIGO sem chave estática e sem pular etapa de teste. O prompt nunca passou por esse mesmo caminho — porque, na cabeça de quem editou, prompt não é código, é só um texto de configuração. É essa hipótese que este laboratório desfaz: o prompt decide o comportamento de produção tanto quanto uma linha de C#, e merece exatamente a mesma disciplina — versão, revisão e teste antes de qualquer loja ver a mudança.
O que este laboratório NÃO é
Não é sobre RAG nem sobre agente com ferramenta — isso é o L83 e o L87, adiante nesta banda. Não é sobre streaming nem sobre o cliente .NET que fala com o Bedrock pela primeira vez — isso é o L81, e este laboratório assume que ele já existe e reaproveita o mesmo cliente. E não é sobre avaliar qualidade com outro LLM como juiz: o golden set aqui é determinístico (palavra obrigatória, palavra proibida, faixa de tamanho), não um veredito de outro modelo — isso fica para o L88.
O que você vai conseguir fazer
Objetivos verificáveis: cada um se prova com um comando ou uma medição na seção de implantação, não com a sensação de ter entendido versionamento de prompt.
- Explicar por que Bedrock Prompt Management sem versão publicada deixa produção mutável para qualquer pessoa com acesso ao console.
- Tratar o texto do prompt como arquivo no Git, revisado por pull request, igual a qualquer outra mudança de comportamento de produção.
- Escrever um golden set pequeno e determinístico que reprova uma mudança de prompt antes dela chegar a qualquer loja.
- Configurar um CodePipeline que só chama CreatePromptVersion depois do golden set aprovar.
- Apontar a produção para um número de versão fixo no Parameter Store, nunca para o draft mutável.
- Medir, com números, quantos casos do golden set uma mudança ruim reprova, e confirmar que o pipeline bloqueia a promoção.
- Distinguir o que este laboratório resolve (regressão determinística e barata) do que o L88 resolve (avaliação por LLM-juiz, mais ampla e mais cara).
O que a certificação cobra disto
| Conceito | Certificação | Como aparece aqui | O que dominar |
|---|---|---|---|
| Versionamento de prompt como artefato | AIF-C01 | o texto do prompt vira arquivo no repositório, com PR e histórico | que "prompt" não é dado de runtime — é comportamento de produção, e merece o mesmo controle de mudança que código |
| Bedrock Prompt Management: draft vs. versão publicada | AIF-C01 | CreatePromptVersion grava um número imutável; a API de produção nunca lê o draft | a diferença entre EDITAR (mutável, sem rastro) e PUBLICAR (imutável, com número) |
| Teste de regressão bloqueante em pipeline | AIF-C01, DOP-C02 | o estágio de teste do CodePipeline impede a promoção se o golden set reprovar | que "funcionou uma vez no console" não é o mesmo que "passou no teste automatizado" |
| Least privilege em CreatePromptVersion | SAA-C03, AIF-C01 | policy restrita ao ARN do prompt específico, e só a role do pipeline pode chamá-la | por que ninguém além do pipeline deveria ter essa permissão, nem para "só testar rápido" |
| Configuração externa vs. redeploy | DVA-C02 | Parameter Store guarda a versão ativa; trocar de versão não exige novo deploy da API | quando um valor pertence à configuração de runtime e quando pertence ao código |
Onde isto costuma ser cobrado errado
A pergunta clássica descreve um time que edita o prompt de um assistente diretamente no console para corrigir um problema urgente, e pergunta o que fazer a seguir. A resposta esperada NÃO é "nada, o problema já foi resolvido" — é publicar uma versão (CreatePromptVersion) da correção testada e trazer a mudança de volta para o controle de versão, porque uma edição de emergência no console, sem isso, deixa a produção apontando para um draft que a próxima pessoa pode sobrescrever sem querer.
Requisitos, e como cada um muda o desenho
Requisito não funcional que não aparece numa linha de Terraform ou numa condição de IAM é intenção. A coluna da direita é onde cada um deixou marca.
| Requisito | Valor declarado | O que ele decide no desenho |
|---|---|---|
| Nenhuma mudança de prompt chega à produção sem teste | obrigatório, bloqueante | CodePipeline com estágio de teste ANTES do estágio de promoção — não em paralelo, não depois |
| Mudança de prompt é revisada por outra pessoa | obrigatório | o texto do prompt vira arquivo versionado no Git, com PR — não um valor colado num formulário |
| Detectar regressão sem depender de um LLM-juiz caro | obrigatório, nesta fase | golden set determinístico (palavra obrigatória, palavra proibida, faixa de tamanho) — barato, rápido, cobre o caso deste laboratório |
| Versão em produção tem que ser rastreável a um commit | obrigatório | número de versão do CreatePromptVersion registrado junto ao SHA do commit que a originou |
| Trocar versão em produção não pode exigir novo deploy | requisito operacional | valor lido do Parameter Store, resolvido a cada lote de chamadas, nunca hardcoded na imagem |
| Só o pipeline pode publicar versão nova | segurança | IAM: CreatePromptVersion restrito à role do pipeline, nenhuma role humana tem essa permissão |
| Rollback tem que ser tão rápido quanto promover | obrigatório | reapontar PROMPT_VERSION para o número anterior é a mesma operação de escrita, sem re-rodar o pipeline |
Arquitetura mínima: o prompt que qualquer edição de console reescreve
Este é exatamente o desenho que sobrou do L81 — não um desenho ingênuo escrito de propósito para este laboratório, mas o estado real em que aquele módulo deixou o cliente. Ele é legítimo como ponto de partida: funciona perfeitamente todo dia. O laboratório começa medindo o que acontece quando alguém, sem má intenção, usa a porta que ele deixa aberta.
- → edita e salva o texto do prompt, direto no console
- → template resolvido, usado na próxima invocação
- → GetPrompt sem versão — sempre o draft mais recente
- → InvokeModel com o texto do prompt resolvido
- → descrição gerada, sem baseline para comparar
- → descrição publicada, sem revisão
- Fora da AWS
- IA e machine learning
- Compute
Este é o desenho que a Cadência tinha no ar quando o incidente aconteceu — e ele funciona todo dia, o que é justamente por que ninguém percebeu o problema antes. O defeito não está numa configuração errada: está em uma ausência. Percorra os passos e repare onde a mudança deixa de ser rastreável.
- Editar parece mais rápido que abrir um pull request. Alguém do time de catálogo quer "melhorar" a descrição gerada, abre o console do Bedrock, encontra o prompt em Prompt management, edita o texto e clica em salvar. Não existe branch, não existe revisor, não existe log de "quem mudou o quê".
- O draft não é versão — é o estado atual, sobrescrito. Bedrock Prompt Management tem versionamento real (CreatePromptVersion), mas é opt-in. Editar e salvar sem publicar versão deixa o recurso em DRAFT — e DRAFT é mutável: a próxima edição apaga o texto anterior sem deixar histórico para comparar.
- A API sempre lê o texto mais recente, porque não pede versão nenhuma. O código de produção chama GetPrompt (ou GetPromptVersion sem parâmetro) passando só o ID do prompt. Sem uma versão publicada fixada, qualquer edição no console entra em produção na PRÓXIMA chamada — sem deploy, sem pipeline, sem aviso.
- InvokeModel roda com o texto resolvido, e ninguém compara com o de ontem. A API monta o payload com o prompt já resolvido e chama o modelo. Não há golden set, não há teste de regressão — a única forma de notar diferença é ler a descrição gerada e desconfiar.
- A resposta muda, e a mudança não aparece em log nenhum. O Bedrock devolve texto plausível sempre — inclusive quando o prompt piorou. Sem uma baseline registrada, "ficou pior" é opinião de quem notou, não uma medição.
- O cliente vê a descrição nova, e ninguém sabe que ela mudou. A página do produto publica o texto sem revisão humana. Dias depois alguém nota queda de conversão, mas não há como apontar QUANDO o prompt mudou, porque a mudança nunca foi um commit.
# Antes de mudar qualquer coisa, meca quantas versoes existem de verdade.
aws bedrock-agent list-prompts --query "promptSummaries[?name=='descricao-produto']"
aws bedrock-agent list-prompt-versions --prompt-identifier "$PROMPT_ID" \
--query "promptVersionSummaries[].{Versao:version,Criada:createdAt}"
# Na Cadencia: list-prompt-versions devolve lista VAZIA. O prompt existe
# ha meses, foi editado dezenas de vezes, e nunca teve uma unica versao
# publicada -- toda a historia do texto viveu e morreu dentro do DRAFT.Editar sem versão não é "testar rápido" — é publicar sem rede de segurança
Não existe ambiente de teste separado aqui: o texto que está no DRAFT É o texto que a próxima chamada de produção vai usar, porque a API não fixa nenhuma versão. `list-prompt-versions` vazio não significa "prompt nunca usado" — significa "toda mudança até agora foi direto para produção, sem exceção".
Arquitetura para produção
Cada peça nova abaixo rastreia a uma linha da tabela de requisitos. Se você não conseguir apontar o requisito, a peça é adorno — e este desenho não tem nenhuma.
- → push após aprovação do PR dispara o pipeline
- → estágio de teste roda antes de qualquer publicação
- → InvokeModel com o texto do PR, um caso do golden set por vez
- → publica quantos casos do golden set passaram nesta execução
- → se o golden set aprovar, CreatePromptVersion grava a versão N, imutável
- → aponta PROMPT_VERSION para a versão nova, só depois de publicada
- → lê a versão de produção atual antes de cada lote de chamadas
- → InvokeModel com o texto da versão fixada em PROMPT_VERSION
- → descrição gerada com o prompt que passou no teste
- → descrição publicada, rastreável até o commit e a versão que a gerou
- Fora da AWS
- Gestão e governança
- Compute
- IA e machine learning
O modelo e a API são os MESMOS do desenho anterior — o que muda é a existência de um caminho inteiro entre "alguém propõe uma mudança" e "produção usa essa mudança", com um teste bloqueante no meio. A peça mais importante não é o Git: é o pipeline decidindo, antes de qualquer loja ver o texto novo, se ele passa. Percorra os passos: cada peça nova rastreia a um requisito da tabela ao lado.
- O prompt muda por pull request, revisado por outra pessoa. Quem quer mudar o texto abre um PR no repositório que guarda o prompt como arquivo versionado, com os casos de teste do golden set ao lado. Outra pessoa revisa antes do merge — a mesma disciplina que qualquer mudança de código já tinha, agora aplicada ao prompt.
- Nenhuma publicação acontece antes do teste. O merge dispara o CodePipeline, e o primeiro estágio é o teste — não a publicação. É a mesma ordem do L54: nada promove sem passar por uma etapa bloqueante antes.
- O golden set roda contra o texto do PR, não contra o que já está em produção. O CodeBuild invoca o modelo uma vez por caso do golden set, usando o texto NOVO do prompt (ainda não publicado). Cada resposta é comparada com o critério esperado do caso — não um "parece bom", um teste com veredito.
- O resultado do teste vira número, não sensação. A cada execução, o pipeline publica quantos casos passaram e quantos falharam. É esse número, não a opinião de quem revisou o PR, que decide se o próximo estágio roda.
- Só quem passou no teste ganha um número de versão imutável. Com o golden set aprovado, o pipeline chama CreatePromptVersion. A versão criada não muda mais — ao contrário do DRAFT do desenho anterior, ela é um carimbo fixo que qualquer pessoa pode apontar depois e saber exatamente o que estava em produção naquele dia.
- Produção aponta para um número, e só depois disso a API o lê. O último estágio do pipeline atualiza o Parameter Store com o número da versão nova — só faz isso DEPOIS da versão existir. A cada lote de chamadas, a API consulta esse mesmo parâmetro e chama o Bedrock pedindo especificamente aquela versão — nunca o texto "mais recente" que alguém possa ter editado no console sem passar pelo pipeline.
- A descrição publicada é rastreável até o commit que a gerou. A resposta do modelo chega à API, que publica a descrição na página do produto. Se algo piorar, dá para responder em um comando: qual versão está ativa, qual commit a criou, e quem aprovou o PR.
A diferença estrutural em relação ao desenho anterior não é "a mesma coisa com uma caixa a mais": é a existência de um CAMINHO inteiro — Git, CodeBuild, Parameter Store — que não tinha equivalente antes. No desenho mínimo, editar e publicar eram a MESMA ação, feita por qualquer pessoa com acesso ao console. Aqui, editar é só o primeiro passo de um processo que pode reprovar.
O que fica exatamente igual, de propósito
O modelo, o cliente .NET que chama o Bedrock e a forma como a descrição chega até a página do produto — tudo isso é o L81, e não aparece redesenhado aqui porque não muda. Trocar COMO o prompt vira produção é uma mudança de governança, não de arquitetura de inferência. Redesenhar os dois ao mesmo tempo é o jeito mais comum de gastar uma tarde resolvendo o problema errado.
Uma execução do pipeline, ponta a ponta
O nome de cada estágio não é decoração: é o que decide se a próxima loja vê o texto novo ou não. PROMPT_VERSION só muda no ÚLTIMO passo, e só se todos os anteriores tiverem passado.
{
"evento": "DESCRICAO_GERADA",
"timestamp": "2026-08-08T09:14:02.331Z",
"produtoId": "PRD-88213",
"promptId": "descricao-produto",
"promptVersion": "7",
"modelId": "us.anthropic.claude-3-5-haiku-20241022-v1:0",
"_comentario": "promptVersion vem do valor lido do Parameter Store no início do lote, não é recalculado por chamada — é isso que garante que uma unica execução nunca mistura duas versões."
}As decisões, e o que se perde em cada uma
📋 A Cadência precisa impedir que uma edição de prompt malfeita chegue às 30 lojas sem que ninguém perceba a queda de qualidade — sem introduzir uma equipe de revisão manual full-time, nem depender de um segundo LLM caro para julgar toda mudança pequena.
Resolve o requisito com o menor custo e a menor superfície nova: o golden set é barato (regras determinísticas, não outro modelo), o pipeline reaproveita a disciplina que o L54 já implantou (teste bloqueante antes de promover), e o versionamento do Bedrock (CreatePromptVersion) já existe — só precisava deixar de ser opcional.
Alt: Deixar a edição só no console, com um 'pedir revisão por Slack' antes de salvar — depende de disciplina humana sem nenhuma barreira técnica; a primeira urgência pula o processo, e foi exatamente isso que já aconteceu na Cadência
Alt: Julgar cada mudança com outro LLM (LLM-as-judge) desde já — resolve um problema mais amplo do que o declarado aqui, com custo de token maior e resultado menos determinístico; é o L88, e usar antes de ter o versionamento básico é otimizar a parte errada primeiro
Alt: Aprovação manual de duas pessoas para toda mudança de prompt, sem teste automatizado — não escala e não é repetível: duas pessoas aprovando "parece bom" não pega o mesmo tipo de regressão que um golden set com critério fixo pega toda vez
Alt: Congelar o prompt e proibir qualquer edição depois do primeiro deploy — resolve regressão eliminando a capacidade de melhorar; a Cadência quer mudar o prompt com segurança, não parar de mudar
| Decisão | Escolha | Alternativas | Motivo | O que se perde |
|---|---|---|---|---|
| Onde o prompt mora | arquivo de texto no repositório Git | campo editável só no console do Bedrock | ganha PR, diff, histórico e revisão — tudo que "editar no console" não tem | mais um arquivo para manter sincronizado com o que está publicado no Bedrock |
| Tipo de teste de regressão | golden set determinístico (palavra obrigatória/proibida, faixa de tamanho) | LLM-as-judge (L88) | mais barato, mais rápido, e resultado sempre igual para a mesma entrada — decidível sem ambiguidade | não pega regressão de TOM ou de qualidade percebida, só quebra de regra explícita |
| Quando publicar versão | só depois do golden set aprovar, dentro do pipeline | publicar versão a cada edição no console, testar depois | elimina a janela em que uma versão ruim fica ativa mesmo que por minutos | exige que toda mudança passe pelo Git — não dá para "só testar rápido" no console |
| Onde produção lê a versão ativa | Parameter Store, resolvido em runtime | valor fixo na imagem do container, exigindo rebuild | trocar versão (inclusive rollback) não exige novo deploy | mais uma dependência de runtime que a API precisa tratar como algo que pode falhar |
| Ferramenta de pipeline | CodePipeline + CodeBuild | GitHub Actions com OIDC (padrão do L54) | integra nativamente com o SDK/CLI da AWS sem sair do ecossistema; ganha aprovação manual e notificação via EventBridge de graça | perde a simplicidade de manter tudo num único arquivo YAML no repositório, como o L54 fez |
A dívida que este laboratório não paga
O golden set é pequeno e determinístico — pega instrução obrigatória removida, palavra proibida reintroduzida, tamanho fora da faixa. Ele NÃO pega regressão sutil de tom, de persuasão ou de qualidade percebida, porque essas dimensões não cabem numa regra fixa de "contém"/"não contém". Isso é o L88: avaliar com um LLM como juiz, sobre a mesma base de versionamento que este módulo constrói.
Construir: o prompt como arquivo, e o golden set que o testa
O texto do prompt deixa de ser um valor colado no console e vira um arquivo como outro qualquer — revisável, com histórico, e testável pelo mesmo golden set toda vez que muda.
Você escreve a descrição de um produto para a loja online da Cadência.
Regras obrigatórias:
- Cite pelo menos um material ou característica física do produto.
- Nunca prometa prazo de entrega — isso é decidido em outro sistema.
- Nunca use as palavras "garantido", "imperdível" ou "exclusivo" — a área
jurídica já reprovou essas três por soarem como propaganda enganosa.
- Escreva entre 80 e 220 caracteres.
Produto: {{produto}}
Atributos: {{atributos}}{
"promptId": "descricao-produto",
"limiarAprovacao": 0.8,
"casos": [
{
"id": "c01",
"produto": "Tênis de corrida modelo Vento",
"atributos": "entressola de EVA, cabedal em tela respirável",
"palavrasObrigatorias": [
"EVA"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c02",
"produto": "Cadeira de escritório Studio",
"atributos": "base giratória de aço, apoio lombar ajustável",
"palavrasObrigatorias": [
"aço"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c03",
"produto": "Garrafa térmica Alpina 750ml",
"atributos": "parede dupla em aço inox, mantém temperatura por 12h",
"palavrasObrigatorias": [
"inox"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo",
"entrega em"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c04",
"produto": "Mochila urbana Trilha 22L",
"atributos": "tecido ripstop impermeável, compartimento para notebook",
"palavrasObrigatorias": [
"ripstop"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c05",
"produto": "Luminária de mesa Foco LED",
"atributos": "corpo em alumínio escovado, três níveis de intensidade",
"palavrasObrigatorias": [
"alumínio"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c06",
"produto": "Fone sem fio Onda Pro",
"atributos": "cancelamento de ruído ativo, estojo com carregamento sem fio",
"palavrasObrigatorias": [
"cancelamento de ruído"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c07",
"produto": "Panela de ferro fundido 24cm",
"atributos": "fundo espesso para retenção de calor, cabo lateral",
"palavrasObrigatorias": [
"ferro fundido"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c08",
"produto": "Relógio esportivo Ritmo GPS",
"atributos": "caixa em policarbonato, bateria de 7 dias em uso normal",
"palavrasObrigatorias": [
"policarbonato"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo",
"entrega em"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c09",
"produto": "Kit de facas de cozinha Corte 5 peças",
"atributos": "lâminas em aço inox de alto carbono, bloco em madeira",
"palavrasObrigatorias": [
"aço inox"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
},
{
"id": "c10",
"produto": "Cobertor casal Nuvem",
"atributos": "microfibra dupla face, 220x240cm",
"palavrasObrigatorias": [
"microfibra"
],
"palavrasProibidas": [
"garantido",
"imperdível",
"exclusivo"
],
"tamanhoMin": 80,
"tamanhoMax": 220
}
]
}// TesteRegressaoPrompt/Program.cs -- roda o golden set contra o texto do PR,
// bloqueia a promocao se a taxa de acerto cair abaixo do limiar. Reaproveita
// o cliente Bedrock do L81 (tema: streaming, retry, custo logado) -- aqui so
// a chamada e sincrona porque o teste precisa do texto completo para julgar.
using System.Text.Json;
using Amazon.BedrockRuntime;
using Amazon.BedrockRuntime.Model;
var caminhoPrompt = args.Length > 0 ? args[0] : "prompts/descricao-produto.txt";
var textoPrompt = await File.ReadAllTextAsync(caminhoPrompt);
var goldenSet = JsonSerializer.Deserialize<GoldenSet>(
await File.ReadAllTextAsync("prompts/golden-set-descricao-produto.json"),
new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;
using var bedrock = new AmazonBedrockRuntimeClient();
var resultados = new List<(string id, bool passou, string motivo)>();
foreach (var caso in goldenSet.Casos)
{
var prompt = textoPrompt.Replace("{{produto}}", caso.Produto)
.Replace("{{atributos}}", caso.Atributos);
// ID com prefixo de provedor, o mesmo padrao do L81 -- Haiku aqui porque
// descricao de produto e uma tarefa barata, nao precisa do modelo mais caro.
var resposta = await bedrock.InvokeModelAsync(new InvokeModelRequest
{
ModelId = "us.anthropic.claude-3-5-haiku-20241022-v1:0",
ContentType = "application/json",
Body = MontarCorpo(prompt),
});
var texto = ExtrairTexto(resposta);
var faltouObrigatoria = caso.PalavrasObrigatorias
.Where(p => !texto.Contains(p, StringComparison.OrdinalIgnoreCase)).ToList();
var usouProibida = caso.PalavrasProibidas
.Where(p => texto.Contains(p, StringComparison.OrdinalIgnoreCase)).ToList();
var tamanhoOk = texto.Length >= caso.TamanhoMin && texto.Length <= caso.TamanhoMax;
var passou = faltouObrigatoria.Count == 0 && usouProibida.Count == 0 && tamanhoOk;
var motivos = new List<string>();
if (faltouObrigatoria.Count > 0) motivos.Add($"faltou: {string.Join(',', faltouObrigatoria)}");
if (usouProibida.Count > 0) motivos.Add($"usou proibida: {string.Join(',', usouProibida)}");
if (!tamanhoOk) motivos.Add($"tamanho {texto.Length} fora de [{caso.TamanhoMin},{caso.TamanhoMax}]");
resultados.Add((caso.Id, passou, passou ? "ok" : string.Join("; ", motivos)));
}
var passaram = resultados.Count(r => r.passou);
var taxa = passaram / (double)resultados.Count;
Console.WriteLine($"golden set: {passaram} de {resultados.Count} casos passaram ({taxa:P0})");
foreach (var r in resultados.Where(r => !r.passou))
Console.WriteLine($" FALHOU [{r.id}]: {r.motivo}");
if (taxa < goldenSet.LimiarAprovacao)
{
Console.WriteLine($"taxa {taxa:P0} abaixo do limiar de {goldenSet.LimiarAprovacao:P0} -- pipeline bloqueado");
Environment.Exit(1); // CodeBuild le este codigo de saida e falha o estagio
}
Por que o golden set é determinístico, não um LLM julgando
Cada caso testa três coisas objetivas: uma palavra obrigatória apareceu, uma palavra proibida não apareceu, o tamanho ficou na faixa. A mesma entrada sempre produz o mesmo veredito — sem isso, um pipeline de CI/CD ficaria instável, "passando" numa execução e "falhando" na próxima sem nenhuma mudança de código ou de prompt. Julgamento de qualidade mais amplo, que depende de nuance de tom, é o problema que o L88 resolve com um LLM-juiz — sobre esta mesma base de versionamento.
Construir: o pipeline que bloqueia promoção ruim
O pipeline é construído com TRÊS estágios, e a ordem entre eles é a parte que importa: fonte, depois teste, depois — e só depois — promoção.
# pipeline-prompt.tf -- CodePipeline com fonte no GitHub via conexao, um
# estagio de teste bloqueante, e promocao condicionada ao resultado dele.
resource "aws_codestarconnections_connection" "github" {
name = "${var.projeto}-github"
provider_type = "GitHub"
# Apos aplicar, a conexao fica PENDING ate alguem autorizar no console --
# o Terraform nao completa esse passo, e o pipeline nao dispara sem ele.
}
resource "aws_s3_bucket" "artefatos_pipeline" {
bucket = "${var.projeto}-artefatos-prompt-pipeline"
}
resource "aws_cloudwatch_log_group" "teste_golden_set" {
name = "/aws/codebuild/${var.projeto}-teste-golden-set"
retention_in_days = 30
}
resource "aws_codebuild_project" "teste_golden_set" {
name = "${var.projeto}-teste-golden-set"
service_role = aws_iam_role.pipeline_prompt.arn
environment {
compute_type = "BUILD_GENERAL1_SMALL"
image = "aws/codebuild/standard:7.0"
type = "LINUX_CONTAINER"
environment_variable {
name = "PROMPT_ID"
value = aws_bedrockagent_prompt.descricao_produto.id
}
}
logs_config {
cloudwatch_logs {
group_name = aws_cloudwatch_log_group.teste_golden_set.name
}
}
source {
type = "CODEPIPELINE"
buildspec = "buildspec.yml"
}
artifacts { type = "CODEPIPELINE" }
}
resource "aws_codepipeline" "prompt_descricao_produto" {
name = "${var.projeto}-prompt-descricao-produto"
role_arn = aws_iam_role.pipeline_prompt.arn
artifact_store {
location = aws_s3_bucket.artefatos_pipeline.bucket
type = "S3"
}
stage {
name = "Fonte"
action {
name = "Github"
category = "Source"
owner = "AWS"
provider = "CodeStarSourceConnection"
version = "1"
output_artifacts = ["codigo_fonte"]
configuration = {
ConnectionArn = aws_codestarconnections_connection.github.arn
FullRepositoryId = "cadencia/catalogo-prompts"
BranchName = "main"
}
}
}
# UNICO estagio que pode falhar sem consequencia visivel para produção: se
# falhar aqui, PROMPT_VERSION nunca muda, e a versao anterior segue ativa.
stage {
name = "TestarGoldenSet"
action {
name = "RodarGoldenSet"
category = "Build"
owner = "AWS"
provider = "CodeBuild"
version = "1"
input_artifacts = ["codigo_fonte"]
output_artifacts = ["resultado_teste"]
configuration = { ProjectName = aws_codebuild_project.teste_golden_set.name }
}
}
stage {
name = "PromoverVersao"
action {
name = "PublicarEApontar"
category = "Build"
owner = "AWS"
provider = "CodeBuild"
version = "1"
input_artifacts = ["resultado_teste"]
configuration = { ProjectName = aws_codebuild_project.promover_versao.name }
# So executa se o estagio anterior (TestarGoldenSet) tiver sucesso --
# e o comportamento PADRAO do CodePipeline entre estagios sequenciais,
# nao uma condicao extra que alguem precisa lembrar de configurar.
}
}
}
# buildspec.yml -- estagio TestarGoldenSet
version: 0.2
phases:
install:
runtime-versions:
dotnet: 8.0
build:
commands:
- echo "Rodando golden set contra o prompt do PR..."
- dotnet run --project TesteRegressaoPrompt -- prompts/descricao-produto.txt
# dotnet run sai com codigo != 0 se a taxa de acerto ficar abaixo do
# limiar -- CodeBuild interpreta isso como falha de fase, e o
# CodePipeline nunca chama o estagio PromoverVersao.
reports:
golden-set:
files: ["resultado-golden-set.json"]
file-format: "JSON"
A condição que, sozinha, autorizaria mais do que parece
Se a policy da role `pipeline_prompt` conceder `"Action": "bedrock:CreatePromptVersion", "Resource": "*"` em vez do ARN do prompt `descricao-produto`, a MESMA role passa a poder publicar versão em qualquer prompt gerenciado pelo Bedrock na conta — inclusive um prompt de outro sistema que nada tem a ver com descrição de produto. O `Resource` restrito, na seção de segurança adiante, é o que fecha essa lacuna.
Construir: Parameter Store e o cliente de produção
A produção nunca fala com o draft. Toda chamada resolve primeiro qual versão está ativa, e só depois pede o texto DAQUELA versão específica.
# parameter-store.tf -- ponte entre "versao testada" e "producao usa"
resource "aws_ssm_parameter" "prompt_version_producao" {
name = "/${var.projeto}/prompt/descricao-produto/versao-producao"
type = "String"
value = "0" # placeholder -- o pipeline escreve o valor real apos a primeira promocao
lifecycle {
ignore_changes = [value] # o PIPELINE atualiza isto, nao o terraform apply
}
}
// ResolvedorVersaoPrompt.cs -- le a versao ativa do Parameter Store com
// cache curto, e monta a chamada ao Bedrock fixada naquela versao. Reaproveita
// o ClienteBedrockStreaming do L81 (tema: primeira chamada com streaming e
// custo logado) -- so a resolucao da versao do prompt muda aqui.
public class ResolvedorVersaoPrompt
{
private readonly IAmazonSimpleSystemsManagement _ssm;
private string? _versaoEmCache;
private DateTime _expiraEm = DateTime.MinValue;
private static readonly TimeSpan TTL_CACHE = TimeSpan.FromSeconds(30);
public async Task<string> VersaoAtivaAsync()
{
if (_versaoEmCache is not null && DateTime.UtcNow < _expiraEm)
return _versaoEmCache;
var resposta = await _ssm.GetParameterAsync(new GetParameterRequest
{
Name = "/cadencia/prompt/descricao-produto/versao-producao",
});
_versaoEmCache = resposta.Parameter.Value; // NUNCA "$LATEST" -- sempre um numero
_expiraEm = DateTime.UtcNow.Add(TTL_CACHE);
return _versaoEmCache;
}
}
public class GeradorDescricao
{
private readonly ResolvedorVersaoPrompt _versao;
private readonly IAmazonBedrockAgent _bedrockAgent; // Prompt Management
private readonly AmazonBedrockRuntimeClient _bedrockRuntime; // InvokeModel, do L81
public async Task<string> GerarAsync(string produto, string atributos)
{
var versao = await _versao.VersaoAtivaAsync();
// GetPromptVersion com numero FIXO -- e o que impede a API de herdar
// uma edicao feita no console sem passar pelo pipeline.
var promptResolvido = await _bedrockAgent.GetPromptAsync(new GetPromptRequest
{
PromptIdentifier = "descricao-produto",
PromptVersion = versao,
});
var texto = promptResolvido.Variants[0].TemplateConfiguration.Text.Text
.Replace("{{produto}}", produto)
.Replace("{{atributos}}", atributos);
return await _bedrockRuntime.InvokeModelEColetarTextoAsync(texto, versao);
}
}
Por que 30 segundos de cache, e não zero
Ler o Parameter Store a cada chamada funcionaria, mas somaria uma chamada de rede extra ao orçamento de latência que o L81 já cronometra. Trinta segundos de cache significa que, no pior caso, uma promoção nova demora até meio minuto para valer para todas as instâncias da API — atraso pequeno, e sem custo de uma leitura por requisição.
Implantar, e provar que uma mudança ruim é barrada
Cinco medições. Nenhuma conclusão vem de "o pipeline ficou verde" sem um número atrelado — é a diferença entre demonstrar e afirmar.
- Prova 1 — o prompt bom passa por inteiro: rode `dotnet run --project TesteRegressaoPrompt -- prompts/descricao-produto.txt` no ambiente de exemplo. Esperado: "golden set: 10 de 10 casos passaram (100%)".
- Prova 2 — uma mudança ruim é reproduzida e pega: remova do texto do prompt a instrução "Cite pelo menos um material ou característica física do produto" e rode o teste de novo. Esperado, medido no ambiente de exemplo da Cadência: "golden set: 7 de 10 casos passaram (70%)" — 3 casos falharam por faltar a palavra obrigatória, e a taxa fica abaixo do limiar de 80%.
- Prova 3 — o pipeline de verdade bloqueia, não só o teste local: abra um PR com essa mesma mudança e confira o console do CodePipeline. Esperado: estágio "TestarGoldenSet" com status Failed, estágio "PromoverVersao" nunca inicia — CreatePromptVersion não é chamado nenhuma vez.
- Prova 4 — a promoção só acontece depois do número existir: reverta a mudança ruim, confirme que o golden set volta a 10 de 10, e acompanhe o pipeline até o fim. Esperado: `aws bedrock-agent list-prompt-versions` mostra uma nova versão (por exemplo, a 7), e só DEPOIS `aws ssm get-parameter --name /cadencia/prompt/descricao-produto/versao-producao` passa a devolver "7".
- Prova 5 — rollback é reescrita de um parâmetro, não um novo pipeline: aponte o parâmetro manualmente de volta para o número anterior (por exemplo, "6") e confirme, com uma chamada de teste à API, que o texto da descrição volta a ser o da versão 6 em até 30 segundos — o TTL do cache do resolvedor.
O que não foi medido em produção real
A taxa de 7 de 10 (70%) e o limiar de 80% vêm de uma execução de exemplo contra o golden set deste laboratório, calibrado para o catálogo da Cadência — não são constantes universais do Bedrock. Um catálogo com produtos de categorias muito diferentes provavelmente precisa de mais casos no golden set, e o limiar deve refletir o quanto de risco de regressão sua operação tolera antes de bloquear.
Quebrar de propósito: três falhas e o diagnóstico
| Falha provocada | Sintoma | Onde olhar | Correção |
|---|---|---|---|
| Remover o estágio "TestarGoldenSet" do CodePipeline "para agilizar um hotfix" | qualquer texto vira versão publicada, inclusive um que reprovaria o golden set | histórico do pipeline mostra promoção sem estágio de teste no meio | reintroduzir o estágio bloqueante; um hotfix passa pelo MESMO caminho, só com revisão mais rápida |
| Baixar o limiar do golden set de 80% para 30% "porque o pipeline está sempre falhando" | prompts com quase 70% dos casos quebrados ainda são promovidos | taxa de acerto reportada no CloudWatch cai, mas o pipeline continua verde | investigar POR QUE o prompt está falhando, não abaixar a régua que detecta isso |
| Editar o prompt direto no console "só para testar uma ideia rápido" | produção não muda (ela lê o Parameter Store), mas o DRAFT fica divergente da última versão publicada, e a próxima pessoa que olhar o console vê um texto que não é o de produção | CloudTrail mostra UpdatePrompt fora de qualquer execução do CodePipeline | restringir UpdatePrompt/CreatePromptVersion só à role do pipeline, para que essa edição nem seja possível |
| Apontar PROMPT_VERSION manualmente para um número que não existe (erro de digitação num rollback) | GetPromptVersion falha para a API, que passa a lançar exceção em toda chamada de geração de descrição | logs de erro do cliente Bedrock reportando "versão não encontrada" | validar o número contra `list-prompt-versions` antes de escrever no Parameter Store |
Baixar o limiar do golden set é pior do que não ter golden set
Um pipeline sem teste nenhum pelo menos deixa claro, para quem olha o desenho mínimo, que não há proteção. Um pipeline com golden set e limiar artificialmente baixo passa a impressão contrária — "isto foi testado" — enquanto aprova quase qualquer mudança. É a mesma armadilha do fallback que devolve valor fixo: parece funcionando, e esconde exatamente o defeito que deveria expor.
O Bedrock Prompt Management já suporta versionamento. Um engenheiro da Cadência argumenta que editar o prompt direto no console "já é seguro, porque dá para criar uma versão depois se precisar". Qual é o defeito nesse argumento?
Segurança: o que a permissão de publicar prompt expõe
| Risco | Probabilidade | Impacto | Prevenção | Detecção | Resposta |
|---|---|---|---|---|---|
| Role do pipeline com `Resource: "*"` em CreatePromptVersion, podendo publicar versão de QUALQUER prompt da conta | média | alto — uma pipeline mal configurada pode alterar o prompt de outro sistema sem querer | Resource com o ARN do prompt específico `descricao-produto`, nunca `bedrock:*` | CloudTrail mostrando CreatePromptVersion em prompt fora do esperado | revogar a policy ampla e reemitir com o ARN restrito |
| Pessoa com acesso ao console do Bedrock ainda consegue editar o draft, mesmo com o pipeline no lugar | alta — a permissão de console não é removida só porque o pipeline existe | médio — o draft muda, mas produção lê o Parameter Store e continua servindo a versão fixa | remover UpdatePrompt/CreatePromptVersion de qualquer role humana, deixando só a role do pipeline | alarme de CloudTrail para UpdatePrompt fora de uma execução do CodePipeline | investigar quem editou e por quê; o draft "sujo" não afeta produção, mas é sinal de processo sendo contornado |
| `PROMPT_VERSION` gravável por qualquer role com acesso ao Parameter Store | baixa | alto — reescrever o parâmetro aponta produção para qualquer versão, inclusive uma nunca testada | ssm:PutParameter restrito ao path exato e só à role do pipeline | CloudTrail em PutParameter para o path `/cadencia/prompt/*` | reverter para a última versão publicada pelo pipeline e revisar quem tinha a permissão |
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicarSomenteEsteVersaoDePrompt",
"Effect": "Allow",
"Action": "bedrock:CreatePromptVersion",
"Resource": "arn:aws:bedrock:*:*:prompt/descricao-produto"
},
{
"Sid": "InvocarModeloParaTestarOGoldenSet",
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": "arn:aws:bedrock:*::foundation-model/anthropic.claude-3-5-haiku-20241022-v1:0"
},
{
"Sid": "AtualizarSomenteEsteParametro",
"Effect": "Allow",
"Action": "ssm:PutParameter",
"Resource": "arn:aws:ssm:*:*:parameter/cadencia/prompt/descricao-produto/versao-producao"
}
]
}Observabilidade: as perguntas que o painel tem de responder
- A última promoção aconteceu quando, e qual PR a causou?
- Qual é a taxa de acerto do golden set na última execução do pipeline?
- Quantas vezes o pipeline bloqueou uma promoção nos últimos 30 dias?
- PROMPT_VERSION em produção corresponde à última versão publicada, ou alguém reescreveu manualmente?
- Alguém editou o draft no console fora de uma execução do pipeline?
| Alarme | Métrica | Limiar inicial | Por que esse limiar |
|---|---|---|---|
| Taxa de acerto do golden set abaixo do limiar | GoldenSetTaxaDeAcerto (custom) | < 80% | abaixo disso a promoção já é bloqueada pelo pipeline; o alarme existe para AVISAR alguém, não só bloquear em silêncio |
| UpdatePrompt fora de execução do pipeline | contagem de CloudTrail filtrada por role != role-do-pipeline | ≥ 1 | qualquer edição fora do pipeline é sinal de processo sendo contornado, mesmo que não afete produção ainda |
| PROMPT_VERSION divergente da última versão publicada | comparação periódica entre o parâmetro e list-prompt-versions | qualquer divergência | detecta rollback manual não documentado ou erro de digitação |
| Pipeline bloqueando promoção repetidamente | contagem de execuções com status Failed no estágio de teste | ≥ 3 em 7 dias | uma falha isolada é o pipeline funcionando; falhas repetidas sinalizam golden set desatualizado ou revisão de PR fraca |
Escala: 10, 10 mil, 1 milhão, e falha de região
| Cenário | O que muda | Onde a arquitetura sente primeiro |
|---|---|---|
| 10 chamadas de InvokeModel por dia (uma loja piloto) | pipeline roda raramente; golden set de 10 casos é suficiente | quase nenhuma pressão em nenhuma peça |
| 10 mil chamadas por dia (30 lojas, geração sob demanda) | o volume de InvokeModel em PRODUÇÃO cresce, mas o pipeline só roda quando o prompt muda, não a cada chamada | custo de token em produção, não do teste — o golden set roda dezenas de vezes por semana, não milhões |
| 1 milhão de chamadas por dia (hipotético, catálogo bem maior) | cache de resposta por produto vira relevante, e cobrança por token domina a fatura — tema do L89, não deste módulo | a decisão deixa de ser "como testar o prompt" e vira "como não pagar token repetido para o mesmo produto" |
| Falha de região do Bedrock | InvokeModel falha para TODAS as versões, não só a mais recente — não é um problema de versionamento | o cliente do L81 (retry, timeout, fallback) é quem trata isso, não este laboratório |
Custo: o que este laboratório acrescenta à fatura
CodeBuild cobra por minuto de execução; Bedrock cobra por token, tanto no golden set quanto em produção; Parameter Store Standard não cobra. Use o AWS Pricing Calculator para o seu volume; nenhum valor absoluto aqui envelhece bem.
| Cenário | Dimensões que pesam | Custo oculto |
|---|---|---|
| Protótipo (1 loja, golden set de 10 casos) | CodeBuild por minuto de execução; Bedrock cobra por token do golden set a cada execução do pipeline | desprezível, mas ainda existe — golden set grande demais na frequência errada soma |
| Produção pequena (30 lojas da Cadência) | InvokeModel em produção domina a fatura, não o pipeline; o pipeline roda só quando o prompt muda | tempo de CodeBuild ocioso esperando resposta do Bedrock ainda cobra por minuto |
| Alta escala (catálogo bem maior, golden set crescendo junto com o L88) | golden set maior soma mais tokens por execução do pipeline; o LLM-juiz do L88 adiciona um segundo modelo cobrando | golden set nunca podado, com casos redundantes, cresce a fatura do PIPELINE sem melhorar a detecção |
O custo que ninguém mede: regressão publicada e descoberta tarde
Antes deste laboratório, uma mudança de prompt ruim ficava no ar até alguém notar queda de conversão — dias, às vezes semanas, como aconteceu na Cadência. Esse tempo não aparece em nenhuma fatura da AWS, mas é o custo real que o teste bloqueante elimina.
Well-Architected nos seis pilares
| Pilar | Situação | Risco | Melhoria | Prioridade |
|---|---|---|---|---|
| Excelência operacional | prompt editado sem log, sem revisão, sem versão na mínima | ninguém sabe que mudou até a queda de conversão aparecer | pipeline com teste bloqueante, log estruturado por execução, número de versão rastreável | alta |
| Segurança | qualquer pessoa com acesso ao console pode publicar mudança em produção | superfície de mudança não controlada | permissão de publicar restrita à role do pipeline; console vira só leitura para humanos | alta |
| Confiabilidade | mudança ruim de prompt derruba qualidade sem nenhum teste automatizado pegando | regressão silenciosa, detectada só por reclamação | golden set bloqueante antes de qualquer promoção | alta |
| Eficiência de performance | golden set roda todo caso a cada execução, mesmo casos redundantes | pipeline mais lento do que precisa, sem ganhar cobertura real | revisar e podar o golden set periodicamente, mantendo só casos que pegam regressão real | média |
| Otimização de custo | pipeline pode rodar a cada commit trivial, como um typo em comentário | tokens gastos testando mudança que nem é de prompt | disparar o pipeline só quando o arquivo do prompt (ou do golden set) muda | média |
| Sustentabilidade | golden set determinístico evita chamar um segundo LLM para toda mudança pequena | menos chamadas de inferência do que a alternativa de LLM-as-judge para tudo | manter o golden set determinístico como primeira linha, reservando o juiz do L88 para o que ele realmente precisa julgar | baixa |
Evolução em níveis: o que muda, e o que passa a doer
A terceira arquitetura não é um desenho: é a resposta a QUANDO trocar de desenho. Cada nível resolve um risco deste módulo e compra outro — até o ponto em que "testar regressão" deixa de ser regra fixa e passa a ser julgamento de modelo.
Qualquer pessoa com acesso ao Bedrock edita o prompt e salva. Sem versão, sem revisão, sem teste.Prompt versionado como arquivo, revisado por PR, testado por um golden set determinístico antes de CreatePromptVersion; produção lê a versão fixa do Parameter Store.Um estágio de aprovação manual do CodePipeline entra entre o teste e a promoção, para mudanças sensíveis (ex.: prompt de categoria regulada).Casos reais sinalizados como regressão (por reclamação ou pelo juiz do Nível 6) entram no golden set, que cresce a cada incidente real.A versão nova serve só uma fração das lojas antes de ir para 100%, reaproveitando a disciplina de canário do L39.O golden set determinístico deixa de ser a única barreira; um segundo modelo julga qualidade de tom e persuasão em escala — é o L88, avaliar sistema com LLM: golden set e juiz.A ordem não é negociável, e o motivo é concreto
Adicionar o LLM-juiz do Nível 6 antes de ter o versionamento do Nível 2 resolveria a parte mais cara e mais incerta do problema, deixando a parte mais barata e mais óbvia — "toda mudança passa por um teste antes de virar produção" — sem solução nenhuma. Este laboratório constrói a fundação; o L88 constrói em cima dela.
Onde IA entra nesta arquitetura, e onde não entra
O modelo já é a peça de IA aqui — o que este laboratório NÃO faz é usar IA para decidir se o prompt está bom
A geração da descrição em si já é o ponto de entrada de IA nesta arquitetura, herdada do L81. O que este módulo resolve é puramente mecânico: versionamento e teste de regra fixa são disciplinas auditáveis, iguais ao L54 aplicado a código. Julgar a QUALIDADE de uma resposta com outro LLM desde já substituiria um critério simples e determinístico por uma caixa-preta com custo maior, sem que a Cadência tenha esgotado o que a regra fixa já pega.
O ponto de entrada legítimo de MAIS IA nesta cadeia é o Nível 6 da escada de evolução acima, e é o L88: quando o golden set determinístico não consegue mais expressar o que "boa descrição" significa — tom, persuasão, adequação à marca — um segundo modelo julgando a resposta do primeiro passa a valer o custo extra, sobre a mesma base de versionamento que este módulo constrói.
Anti-padrões deste laboratório
| Antipadrão | Por que alguém faz | Sintoma em produção | Forma correta |
|---|---|---|---|
| Editar o prompt direto no console "só para um ajuste rápido" | parece mais rápido do que abrir PR, esperar revisão e rodar pipeline — e ninguém pensa em prompt como código até a primeira regressão | mudança em produção sem PR, sem teste, sem número de versão para reverter | tratar o texto do prompt como arquivo no Git, com PR e pipeline, mesmo para ajuste "pequeno" |
| Golden set com 2-3 casos óbvios, nunca atualizado | escrever o golden set completo dá trabalho, e os primeiros casos já pegam os erros mais gritantes | o prompt passa no teste raso e ainda assim degrada em casos reais que ninguém cobriu | crescer o golden set com casos reais que já causaram problema, tratando-o como suíte viva |
| Baixar o limiar de aprovação quando o pipeline "está sempre falhando" | a frustração com pipeline vermelho é real, e mudar o número resolve o sintoma sem investigar a causa | prompts ruins passam a ser promovidos, e o limiar deixa de significar qualquer coisa | investigar por que o prompt está reprovando antes de mexer no limiar |
| Deixar humano com permissão de CreatePromptVersion "por segurança, caso o pipeline quebre" | parece prudente ter um caminho manual de emergência | a emergência vira rotina, e a versão de emergência nunca passou pelo golden set | corrigir o pipeline quando ele quebra, não contornar com permissão manual permanente |
| Testar o prompt manualmente no console antes de abrir o PR, e considerar isso "já testado" | dá uma sensação de confiança rápida, e o console realmente mostra uma resposta | o teste manual usa só um ou dois exemplos que a pessoa lembrou, não os casos que já causaram regressão antes | o golden set é o teste — o console serve para explorar uma ideia, não para aprovar uma mudança |
Quando algo não funciona
| Sintoma | Causa provável | Como investigar | Log/métrica | Correção |
|---|---|---|---|---|
| Produção continua servindo a versão antiga mesmo depois do pipeline reportar sucesso | o cache do resolvedor de versão ainda não expirou | comparar o timestamp da última leitura do parâmetro pela API com o timestamp da atualização | ausência de nova entrada de log com o `promptVersion` esperado | aguardar o TTL de 30s ou reduzi-lo se a operação exigir corte mais rápido |
| Pipeline falha no estágio de teste, mas ninguém sabe qual caso reprovou | o log do CodeBuild não está mostrando a saída detalhada do teste | saída do `dotnet run` do TesteRegressaoPrompt no CloudWatch Logs do build | linhas "FALHOU [id]: motivo" no log do estágio | garantir que o teste imprima cada falha individualmente, não só a taxa agregada |
| Alguém editou o prompt no console e a mudança nunca aparece em produção | comportamento correto — produção lê o Parameter Store, não o draft | comparar UpdatePrompt no CloudTrail com o número em PROMPT_VERSION | ausência de CreatePromptVersion correspondente | nenhuma — é o requisito funcionando; a mudança precisa passar pelo PR e pelo pipeline |
| CreatePromptVersion falha com erro de permissão mesmo dentro do pipeline | a policy da role do pipeline está restrita a um ARN de prompt diferente do que o build está testando | comparar o Resource da policy com o PROMPT_ID usado no buildspec | AccessDenied no CloudTrail para a chamada CreatePromptVersion | corrigir o ARN na policy — nunca ampliar para `bedrock:*` |
Limpeza: o que o destroy não leva
#!/usr/bin/env bash
set -euo pipefail
# CodePipeline e CodeBuild nao tem dependente fora deste modulo, mas o log
# group e as versoes publicadas do prompt sobrevivem ao destroy.
terraform destroy \
-target=aws_codepipeline.prompt_descricao_produto \
-target=aws_codebuild_project.teste_golden_set \
-target=aws_codestarconnections_connection.github \
-auto-approve
terraform destroy -auto-approve
echo "Confira manualmente:"
echo " - o log group /aws/codebuild/<projeto>-teste-golden-set nao e removido por este destroy"
echo " - versoes publicadas do prompt (CreatePromptVersion) continuam existindo no"
echo " Bedrock; Prompt Management nao cobra por versao armazenada, mas acumula"
echo " lixo se ninguem limpar versoes antigas nunca mais usadas"
echo " - o parametro /cadencia/prompt/descricao-produto/versao-producao no"
echo " Parameter Store Standard nao cobra, mas fica orfao apontando para uma"
echo " versao que pode nao existir mais"
O que o destroy NÃO leva
O CodePipeline e o projeto CodeBuild são removidos pelo `destroy` acima, e são os únicos recursos deste laboratório que cobram por execução pendurada. O log group `/aws/codebuild/<projeto>-teste-golden-set` NÃO é removido — tem retenção de 30 dias configurada, mas continua existindo até expirar ou ser apagado manualmente. As versões publicadas do prompt no Bedrock também sobrevivem: `terraform destroy` não sabe que elas existem, porque foram criadas pelo PIPELINE, não pelo Terraform.
Resumo: problema, peça e motivo
| Problema | Peça | Motivo |
|---|---|---|
| Edição direta no console muda produção sem rastro | prompt como arquivo no Git, com PR | qualquer mudança de comportamento passa por revisão, igual a código |
| Mudança ruim pode chegar a todas as lojas sem ninguém perceber | golden set determinístico bloqueando o pipeline | reprova mecanicamente antes de qualquer loja ver a versão nova |
| Produção pode apontar para o draft mutável sem querer | Parameter Store com número de versão fixo | só uma versão IMUTÁVEL e testada pode ser produção |
| Só quem passou no teste deveria poder publicar | IAM restrito a CreatePromptVersion só pela role do pipeline | elimina o atalho "editar rápido, testar depois" |
| Rollback tem que ser tão rápido quanto promover | reescrever PROMPT_VERSION para o número anterior | não exige novo pipeline nem novo deploy |
- Alguém propõe mudar o prompt por pull request, com casos de teste ao lado.
- Outra pessoa revisa e aprova o merge.
- O CodePipeline roda o golden set contra o texto novo, antes de qualquer publicação.
- Abaixo do limiar, o pipeline para — a versão anterior continua ativa.
- Acima do limiar, CreatePromptVersion grava um número imutável.
- O Parameter Store passa a apontar para esse número.
- A API lê a versão fixa a cada lote de chamadas, e a loja vê o texto testado.
Perguntas frequentes
❓ Editar o prompt no console do Bedrock já não é versionado automaticamente?
❓ Por que o golden set não usa outro LLM para julgar a resposta?
❓ Quem pode publicar uma nova versão do prompt em produção?
❓ O que acontece se o golden set reprovar a mudança do PR?
❓ Por que a versão em produção fica no Parameter Store, não fixa no código?
❓ Um golden set de 10 casos é suficiente para pegar toda regressão de prompt?
❓ Por que este laboratório usa CodePipeline em vez do GitHub Actions do L54?
Fixando
O golden set de 10 casos roda contra uma mudança de prompt que removeu a instrução obrigatória de citar o material do produto. 3 casos falham por faltar essa informação. O que o CodePipeline faz a seguir?
A role do CodePipeline tem uma policy com `"Action": "bedrock:CreatePromptVersion", "Resource": "*"` em vez do ARN do prompt específico. Qual é o risco concreto disso?
Conhecimentos, próximo módulo e documentação
| Item | Conteúdo |
|---|---|
| Conhecimentos anteriores necessários | L54 (pipeline sem chave estática, CodePipeline ou GitHub Actions), L81 (cliente .NET streaming contra o Bedrock, tema: primeira chamada com custo logado) |
| Conhecimentos adquiridos | prompt como artefato versionado; DRAFT vs. versão publicada do Bedrock; golden set determinístico como teste de regressão bloqueante; Parameter Store como ponte entre versão testada e produção sem redeploy; least privilege em CreatePromptVersion |
| Limitação que fica | o golden set é determinístico e não pega regressão de tom ou qualidade percebida — só quebra de regra explícita (palavra obrigatória, palavra proibida, faixa de tamanho) |
| Próximo exemplo recomendado | L88 — Avaliar sistema com LLM: golden set e juiz. Troca o critério fixo por um LLM julgando qualidade em escala, sobre a mesma base de versionamento construída aqui |
| Também habilitado por este módulo | o padrão "configuração como código, testada antes de promover" se aplica a qualquer outro parâmetro de comportamento que a Cadência trate hoje como configuração solta — feature flag, texto de notificação, regra de negócio simples |
| Data da última validação técnica | 8 de agosto de 2026 |
Documentação oficial consultada: Amazon Bedrock — Prompt management (CreatePromptVersion, GetPrompt, GetPromptVersion, ListPromptVersions); AWS CodePipeline e AWS CodeBuild (estágios sequenciais, buildspec, reports); e AWS Systems Manager — Parameter Store. Nenhum valor em dólar aparece neste módulo por decisão: os números de taxa de acerto e limiar são medições do cenário de exemplo da Cadência, e o ID de modelo citado muda com o catálogo do Bedrock — confira o console para o ID e o preço atuais.
O que não foi verificado, e você deve conferir na sua conta
O limiar de 80% e os 10 casos do golden set são calibrações de exemplo para o catálogo da Cadência, não um número universal — um catálogo maior ou mais heterogêneo provavelmente precisa de mais casos e de um limiar revisado. O ID de modelo `us.anthropic.claude-3-5-haiku-20241022-v1:0` é ilustrativo do padrão de prefixo de provedor citado pelo L81; confira no console do Bedrock qual modelo e qual perfil de inferência estão disponíveis na sua região antes de fixar um valor em código.
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…