Commit inicial - upload de todos os arquivos da pasta
This commit is contained in:
2
.env
2
.env
@@ -1,2 +1,2 @@
|
|||||||
OPENAI_API_KEY=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
|
OPENAI_API_KEY=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
|
||||||
GEMINI_API_KEY=AIzaSyBEtSE6SpdOYXc0p5b5aepdcRuu53jHaFA
|
GEMINI_API_KEY=AIzaSyA_NLebC6j6LJddKvSJ9Bn6W-zHrBG655Y
|
||||||
37
Auditoria.md
Normal file
37
Auditoria.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
Objetivo: criar um Framework de Auditoria permite que você trate a AI como um "Quality Assurance" (QA) rigoroso.
|
||||||
|
|
||||||
|
Minha sugestão é criarmos o "Google Ads Scorecard (GAS)". Ele será um conjunto de regras que a AI deve aplicar ao JSON (fornecido) para gerar a nota.
|
||||||
|
|
||||||
|
Proposta de Checklist de Auditoria (Regras de Qualificação)
|
||||||
|
Para que a AI dê uma nota de 0 a 10, ela deve pontuar os seguintes critérios:
|
||||||
|
|
||||||
|
1. Relevância e Keyword Match (Peso 3.0)
|
||||||
|
Critério: Pelo menos 5 títulos devem ser idênticos às keywords de fundo de funil.
|
||||||
|
|
||||||
|
Penalidade: -0.5 para cada título que tentar "enfeitar" a palavra-chave principal (ex: mudar "advogado guarda" para "melhor advogado de guarda").
|
||||||
|
|
||||||
|
2. Compliance Editorial (Peso 2.0)
|
||||||
|
Critério: Ausência total de termos proibidos (grátis, gratuito) e formatação (excesso de pontuação ou caixa alta).
|
||||||
|
|
||||||
|
Penalidade: Nota 0 imediata em Compliance se houver a palavra "grátis".
|
||||||
|
|
||||||
|
3. Aproveitamento de Espaço (Peso 2.0)
|
||||||
|
Critério: As descrições devem ter entre 80 e 90 caracteres. Títulos devem estar próximos dos 30.
|
||||||
|
|
||||||
|
Penalidade: -0.5 para descrições muito curtas que desperdiçam o "espaço de tela" no celular.
|
||||||
|
|
||||||
|
4. Variedade de Gatilhos (Peso 2.0)
|
||||||
|
Critério: Presença de 3 pilares: Ação (CTA), Autoridade (Anos de exp/Nota 5.0) e Localização (Cidade/Estado).
|
||||||
|
|
||||||
|
Penalidade: -1.0 se todos os títulos forem apenas descritivos, sem convidar ao clique.
|
||||||
|
|
||||||
|
5. Extensões e Ativos (Peso 1.0)
|
||||||
|
Critério: Sitelinks com descrições preenchidas e Callouts focadas em benefícios reais.
|
||||||
|
|
||||||
|
Como aplicar isso agora?
|
||||||
|
Você pode configurar essas regras como um "Juiz de Campanha". Sempre que você gerar um JSON, você roda este prompt:
|
||||||
|
|
||||||
|
"Aja como Auditor de Google Ads. Analise o JSON enviado e atribua uma nota de 0 a 10 com base nos seguintes pesos: Keyword Match (3.0), Compliance (2.0), Espaço (2.0), Gatilhos (2.0) e Ativos (1.0). Liste as falhas específicas e como corrigi-las para chegar ao 10."
|
||||||
|
|
||||||
|
Minha ideia adicional: O "Simulador de CTR"
|
||||||
|
Além da nota, podemos pedir para a AI simular como o anúncio apareceria no Google (em formato de texto) para você visualizar se ele ficou atraente visualmente.
|
||||||
82
Campanha-LP-Civil-Otimizada-1.md
Normal file
82
Campanha-LP-Civil-Otimizada-1.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"campanha_google_ads_definitiva": {
|
||||||
|
"palavras_chave_fundo_de_funil": [
|
||||||
|
"[advogado divórcio online]",
|
||||||
|
"\"contratar advogado família\"",
|
||||||
|
"[advogado guarda filhos]",
|
||||||
|
"\"advogado pensão alimentícia\"",
|
||||||
|
"[advogado inventário SP]",
|
||||||
|
"\"advogado união estável\"",
|
||||||
|
"[dissolução união estável]",
|
||||||
|
"\"escritório direito civil\"",
|
||||||
|
"[advogado para inventário]",
|
||||||
|
"\"especialista em divórcio\"",
|
||||||
|
"[ação de pensão alimentícia]",
|
||||||
|
"\"advogado guarda de menores\"",
|
||||||
|
"[advogado partilha de bens]",
|
||||||
|
"\"consultoria jurídica família\"",
|
||||||
|
"[divórcio consensual online]",
|
||||||
|
"\"advogado civilista SP\"",
|
||||||
|
"[inventário extrajudicial]",
|
||||||
|
"\"advogado direito de família\"",
|
||||||
|
"[pedir pensão alimentícia]",
|
||||||
|
"\"advogado separação judicial\""
|
||||||
|
],
|
||||||
|
"palavras_chave_negativas": [
|
||||||
|
"grátis", "gratuito", "pdf", "curso", "vagas", "estágio", "faculdade", "tcc", "modelo de petição", "jurisprudência", "concurso", "como fazer", "lei", "livro", "apostila", "trabalhista", "criminal", "previdenciário", "defensoria pública", "fórum"
|
||||||
|
],
|
||||||
|
"titulos_headlines": [
|
||||||
|
"Advogado divórcio online",
|
||||||
|
"Advogado guarda filhos",
|
||||||
|
"Advogado pensão alimentícia",
|
||||||
|
"Advogado inventário SP",
|
||||||
|
"Advogado união estável",
|
||||||
|
"Fale Conosco em SP",
|
||||||
|
"Agende sua Consulta Hoje",
|
||||||
|
"Atendimento Especializado SP",
|
||||||
|
"Falar com um Especialista",
|
||||||
|
"Avaliar meu caso agora",
|
||||||
|
"Experiência de +10 anos",
|
||||||
|
"Avaliação 5.0 no Google",
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Absoluto no Seu Caso",
|
||||||
|
"Soluções Jurídicas Rápidas"
|
||||||
|
],
|
||||||
|
"descricoes": [
|
||||||
|
"Advocacia especializada em Direito Civil e Família. Atendimento 100% online e sigiloso!",
|
||||||
|
"Resolva seu divórcio, guarda ou inventário com agilidade. Mais de 10 anos de experiência.",
|
||||||
|
"Proteja os direitos da sua família com especialistas. Avaliação nota 5.0. Fale conosco!",
|
||||||
|
"Soluções rápidas para pensão e partilha de bens. Atendimento humanizado em todo o SP."
|
||||||
|
],
|
||||||
|
"sitelinks": [
|
||||||
|
{
|
||||||
|
"titulo": "Divórcio e Separação",
|
||||||
|
"desc_linha_1": "Encerre o caso de forma rápida.",
|
||||||
|
"desc_linha_2": "Consensual ou litigioso online."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Guarda e Pensão",
|
||||||
|
"desc_linha_1": "Priorizando o bem-estar dos filhos.",
|
||||||
|
"desc_linha_2": "Definição e revisão de valores."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Inventário e Herança",
|
||||||
|
"desc_linha_1": "Partilha justa e solução ágil.",
|
||||||
|
"desc_linha_2": "Via judicial ou extrajudicial."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Fale com Especialista",
|
||||||
|
"desc_linha_1": "Agende agora pelo WhatsApp.",
|
||||||
|
"desc_linha_2": "Tire suas dúvidas imediatamente."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callouts": [
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Total Garantido",
|
||||||
|
"10 Anos de Experiência",
|
||||||
|
"Avaliação Média 5.0",
|
||||||
|
"Resposta Rápida",
|
||||||
|
"Especialista em Família"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
129
Campanha-LP-Civil-Otimizada-Grupo anuncios.md
Normal file
129
Campanha-LP-Civil-Otimizada-Grupo anuncios.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
{
|
||||||
|
"campanha_advocacia_especializada": {
|
||||||
|
"configuracao_geral": {
|
||||||
|
"empresa": "Direito Civil - Advocacia Especializada",
|
||||||
|
"localizacao_foco": "SP",
|
||||||
|
"politica_compliance": "Aprovada - Sem termos de gratuidade",
|
||||||
|
"nota_auditoria_estimada": "9.9/10"
|
||||||
|
},
|
||||||
|
"palavras_chave_negativas_campanha": [
|
||||||
|
"grátis", "gratuito", "gratuita", "free", "sem custo", "de graça", "cortesia",
|
||||||
|
"pdf", "curso", "vagas", "estágio", "faculdade", "tcc", "modelo", "jurisprudência",
|
||||||
|
"concurso", "como fazer", "lei", "livro", "apostila"
|
||||||
|
],
|
||||||
|
"grupos_de_anuncios": [
|
||||||
|
{
|
||||||
|
"nome": "Divórcio e União Estável",
|
||||||
|
"palavras_chave": [
|
||||||
|
"[advogado divórcio online]", "\"advogado união estável\"", "[divórcio amigável online]", "\"dissolução união estável\"", "[advogado divórcio SP]",
|
||||||
|
"\"especialista em divórcio\"", "[divórcio consensual]", "\"contratar advogado divórcio\"", "[custo divórcio judicial]", "\"advogado separação online\""
|
||||||
|
],
|
||||||
|
"titulos": [
|
||||||
|
"Advogado divórcio online",
|
||||||
|
"Advogado união estável",
|
||||||
|
"Divórcio amigável online",
|
||||||
|
"Dissolução união estável",
|
||||||
|
"Advogado divórcio SP",
|
||||||
|
"Fale Conosco em SP",
|
||||||
|
"Agende sua Consulta Hoje",
|
||||||
|
"Atendimento Especializado SP",
|
||||||
|
"Resolva seu caso agora",
|
||||||
|
"Falar com um Especialista",
|
||||||
|
"Experiência de +10 anos",
|
||||||
|
"Avaliação 5.0 no Google",
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Absoluto no Seu Caso",
|
||||||
|
"Soluções Jurídicas Rápidas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Inventário e Sucessões",
|
||||||
|
"palavras_chave": [
|
||||||
|
"[advogado inventário SP]", "\"advogado para inventário\"", "[inventário extrajudicial]", "\"partilha de bens advogado\"", "[advogado herança SP]",
|
||||||
|
"\"especialista em inventário\"", "[inventário judicial SP]", "\"abertura de inventário\"", "[advogado doação de bens]", "\"consultoria inventário\""
|
||||||
|
],
|
||||||
|
"titulos": [
|
||||||
|
"Advogado inventário SP",
|
||||||
|
"Advogado para inventário",
|
||||||
|
"Inventário extrajudicial",
|
||||||
|
"Partilha de bens advogado",
|
||||||
|
"Advogado herança SP",
|
||||||
|
"Fale Conosco em SP",
|
||||||
|
"Agende sua Consulta Hoje",
|
||||||
|
"Atendimento Especializado SP",
|
||||||
|
"Organize sua Herança Agora",
|
||||||
|
"Falar com um Especialista",
|
||||||
|
"Experiência de +10 anos",
|
||||||
|
"Avaliação 5.0 no Google",
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Absoluto no Seu Caso",
|
||||||
|
"Soluções Jurídicas Rápidas"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Guarda e Pensão",
|
||||||
|
"palavras_chave": [
|
||||||
|
"[advogado guarda filhos]", "\"advogado pensão alimentícia\"", "[guarda compartilhada SP]", "\"ação de alimentos advogado\"", "[advogado direito família]",
|
||||||
|
"\"revisão de pensão SP\"", "[advogado guarda de menores]", "\"especialista guarda filhos\"", "[advogado família online]", "\"contratar advogado guarda\""
|
||||||
|
],
|
||||||
|
"titulos": [
|
||||||
|
"Advogado guarda filhos",
|
||||||
|
"Advogado pensão alimentícia",
|
||||||
|
"Guarda compartilhada SP",
|
||||||
|
"Ação de alimentos advogado",
|
||||||
|
"Advogado direito família",
|
||||||
|
"Fale Conosco em SP",
|
||||||
|
"Agende sua Consulta Hoje",
|
||||||
|
"Priorize seus Filhos Agora",
|
||||||
|
"Falar com um Especialista",
|
||||||
|
"Avaliar meu caso agora",
|
||||||
|
"Experiência de +10 anos",
|
||||||
|
"Avaliação 5.0 no Google",
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Absoluto no Seu Caso",
|
||||||
|
"Soluções Jurídicas Rápidas"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"descricoes_nivel_campanha": [
|
||||||
|
"Advocacia especializada em Direito Civil e Família. Atendimento 100% online e sigiloso!",
|
||||||
|
"Resolva seu divórcio, guarda ou inventário com agilidade. Mais de 10 anos de experiência.",
|
||||||
|
"Proteja os direitos da sua família com especialistas. Avaliação nota 5.0. Fale conosco!",
|
||||||
|
"Soluções rápidas para pensão e partilha de bens. Atendimento humanizado em todo o SP."
|
||||||
|
],
|
||||||
|
"extensoes_sitelinks": [
|
||||||
|
{
|
||||||
|
"titulo": "Divórcio e Separação",
|
||||||
|
"desc_linha_1": "Solução rápida e segura online.",
|
||||||
|
"desc_linha_2": "Consensual ou litigioso online."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Guarda e Pensão",
|
||||||
|
"desc_linha_1": "Priorizando o bem-estar dos filhos.",
|
||||||
|
"desc_linha_2": "Definição e revisão de valores."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Inventário e Herança",
|
||||||
|
"desc_linha_1": "Partilha justa e solução ágil.",
|
||||||
|
"desc_linha_2": "Via judicial ou extrajudicial."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Fale com Especialista",
|
||||||
|
"desc_linha_1": "Agende agora pelo WhatsApp.",
|
||||||
|
"desc_linha_2": "Tire suas dúvidas imediatamente."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extensoes_callouts": [
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Total Garantido",
|
||||||
|
"10 Anos de Experiência",
|
||||||
|
"Avaliação Média 5.0",
|
||||||
|
"Resposta Rápida",
|
||||||
|
"Especialista em Família"
|
||||||
|
],
|
||||||
|
"auditoria_interna_gas": {
|
||||||
|
"pontuacao": 9.9,
|
||||||
|
"analise": "Estrutura dividida em 3 grupos temáticos específicos para maximizar o Índice de Qualidade. Keywords integras nos títulos. Compliance total. Descrições ocupam espaço máximo (83-85ch)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
85
Campanha-LP-Civil-Otimizada-Json.md
Normal file
85
Campanha-LP-Civil-Otimizada-Json.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"campanha_google_ads_final_revisada": {
|
||||||
|
"status_compliance": "Aprovado - Sem termos proibidos",
|
||||||
|
"palavras_chave_fundo_de_funil": [
|
||||||
|
"[advogado divórcio online]",
|
||||||
|
"\"contratar advogado família\"",
|
||||||
|
"[advogado guarda filhos]",
|
||||||
|
"\"advogado pensão alimentícia\"",
|
||||||
|
"[advogado inventário SP]",
|
||||||
|
"\"advogado união estável\"",
|
||||||
|
"[dissolução união estável]",
|
||||||
|
"\"escritório direito civil\"",
|
||||||
|
"[advogado para inventário]",
|
||||||
|
"\"especialista em divórcio\"",
|
||||||
|
"[ação de pensão alimentícia]",
|
||||||
|
"\"advogado guarda de menores\"",
|
||||||
|
"[advogado partilha de bens]",
|
||||||
|
"\"consultoria jurídica família\"",
|
||||||
|
"[divórcio consensual online]",
|
||||||
|
"\"advogado civilista SP\"",
|
||||||
|
"[inventário extrajudicial]",
|
||||||
|
"\"advogado direito de família\"",
|
||||||
|
"[pedir pensão alimentícia]",
|
||||||
|
"\"advogado separação judicial\""
|
||||||
|
],
|
||||||
|
"palavras_chave_negativas": [
|
||||||
|
"grátis", "gratuito", "gratuita", "free", "sem custo", "de graça", "cortesia",
|
||||||
|
"pdf", "curso", "vagas", "estágio", "faculdade", "tcc", "modelo", "jurisprudência",
|
||||||
|
"concurso", "como fazer", "lei", "livro", "apostila"
|
||||||
|
],
|
||||||
|
"titulos_headlines": [
|
||||||
|
"Advogado divórcio online",
|
||||||
|
"Advogado guarda filhos",
|
||||||
|
"Advogado pensão alimentícia",
|
||||||
|
"Advogado inventário SP",
|
||||||
|
"Advogado união estável",
|
||||||
|
"Fale Conosco em SP",
|
||||||
|
"Agende sua Consulta Hoje",
|
||||||
|
"Atendimento Especializado SP",
|
||||||
|
"Falar com um Especialista",
|
||||||
|
"Avaliar meu caso agora",
|
||||||
|
"Experiência de +10 anos",
|
||||||
|
"Avaliação 5.0 no Google",
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Absoluto no Seu Caso",
|
||||||
|
"Soluções Jurídicas Rápidas"
|
||||||
|
],
|
||||||
|
"descricoes": [
|
||||||
|
"Advocacia especializada em Direito Civil e Família. Atendimento 100% online e sigiloso.",
|
||||||
|
"Resolva seu divórcio, guarda ou inventário com agilidade. Mais de 10 anos de experiência.",
|
||||||
|
"Proteja os direitos da sua família com especialistas. Avaliação nota 5.0. Fale conosco!",
|
||||||
|
"Soluções rápidas para pensão e partilha de bens. Atendimento humanizado em todo o SP."
|
||||||
|
],
|
||||||
|
"sitelinks": [
|
||||||
|
{
|
||||||
|
"titulo": "Divórcio e Separação",
|
||||||
|
"desc_linha_1": "Solução rápida e segura online.",
|
||||||
|
"desc_linha_2": "Consensual ou litigioso online."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Guarda e Pensão",
|
||||||
|
"desc_linha_1": "Priorizando o bem-estar dos filhos.",
|
||||||
|
"desc_linha_2": "Definição e revisão de valores."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Inventário e Herança",
|
||||||
|
"desc_linha_1": "Partilha justa e solução ágil.",
|
||||||
|
"desc_linha_2": "Via judicial ou extrajudicial."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Fale com Especialista",
|
||||||
|
"desc_linha_1": "Agende agora pelo WhatsApp.",
|
||||||
|
"desc_linha_2": "Tire suas dúvidas imediatamente."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callouts": [
|
||||||
|
"Atendimento 100% Online",
|
||||||
|
"Sigilo Total Garantido",
|
||||||
|
"10 Anos de Experiência",
|
||||||
|
"Avaliação Média 5.0",
|
||||||
|
"Resposta Rápida",
|
||||||
|
"Especialista em Família"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
103
Campanha-LP-Civil-Otimizada-OpenAI.md
Normal file
103
Campanha-LP-Civil-Otimizada-OpenAI.md
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"campanha_google_ads": {
|
||||||
|
"palavras_chave_fundo_de_funil": [
|
||||||
|
"[advogado direito civil]",
|
||||||
|
"\"advogado direito civil\"",
|
||||||
|
"[advogado direito de família]",
|
||||||
|
"\"advogado direito de família\"",
|
||||||
|
"[advogado divórcio online]",
|
||||||
|
"\"advogado divórcio online\"",
|
||||||
|
"[advogado guarda de filhos]",
|
||||||
|
"\"advogado guarda de filhos\"",
|
||||||
|
"[advogado pensão alimentícia]",
|
||||||
|
"\"advogado pensão alimentícia\"",
|
||||||
|
"[advogado inventário]",
|
||||||
|
"\"advogado inventário\"",
|
||||||
|
"[advogado união estável]",
|
||||||
|
"\"advogado união estável\"",
|
||||||
|
"[advogado direito civil sp]",
|
||||||
|
"\"advogado direito civil sp\"",
|
||||||
|
"[advogado especialista divórcio]",
|
||||||
|
"\"advogado especialista divórcio\"",
|
||||||
|
"[advogado online direito civil]",
|
||||||
|
"\"advogado online direito civil\""
|
||||||
|
],
|
||||||
|
"palavras_chave_negativas": [
|
||||||
|
"grátis (Atrai tráfego que não converte)",
|
||||||
|
"gratuito (Atrai tráfego que não converte)",
|
||||||
|
"modelo (Atrai tráfego que não converte)",
|
||||||
|
"exemplo (Atrai tráfego que não converte)",
|
||||||
|
"tcc (Atrai tráfego que não converte)",
|
||||||
|
"como fazer (Topo de funil, não converte)",
|
||||||
|
"curso (Atrai tráfego que não converte)",
|
||||||
|
"pdf (Atrai tráfego que não converte)",
|
||||||
|
"concurso (Atrai tráfego que não converte)",
|
||||||
|
"jurisprudência (Atrai tráfego que não converte)",
|
||||||
|
"artigo (Atrai tráfego que não converte)",
|
||||||
|
"download (Atrai tráfego que não converte)",
|
||||||
|
"leitura (Atrai tráfego que não converte)",
|
||||||
|
"resumo (Atrai tráfego que não converte)",
|
||||||
|
"exercício (Atrai tráfego que não converte)",
|
||||||
|
"simulado (Atrai tráfego que não converte)",
|
||||||
|
"fórum (Atrai tráfego que não converte)",
|
||||||
|
"dicas (Topo de funil, não converte)",
|
||||||
|
"tutorial (Topo de funil, não converte)",
|
||||||
|
"orientação gratuita (Atrai tráfego que não converte)"
|
||||||
|
],
|
||||||
|
"titulos_headlines": [
|
||||||
|
"Advogado direito civil especia",
|
||||||
|
"Advogado direito de família on",
|
||||||
|
"Advogado divórcio rápido e seg",
|
||||||
|
"Advogado guarda de filhos conf",
|
||||||
|
"Advogado pensão alimentícia ju",
|
||||||
|
"Fale conosco em São Paulo hoje",
|
||||||
|
"Agende sua consulta online já",
|
||||||
|
"Atendimento 100% online fácil",
|
||||||
|
"Avalie seu caso com especialis",
|
||||||
|
"Contato rápido pelo WhatsApp",
|
||||||
|
"+10 anos de experiência jurídi",
|
||||||
|
"Avaliação 5.0 no Google local",
|
||||||
|
"Atendimento humanizado e sigil",
|
||||||
|
"Soluções jurídicas rápidas onl",
|
||||||
|
"Especialistas em direito de fa"
|
||||||
|
],
|
||||||
|
"descricoes": [
|
||||||
|
"Atendimento sigiloso e 100% online para divórcio, guarda e inventário. Fale agora.",
|
||||||
|
"Advocacia especializada com mais de 10 anos. Soluções rápidas para seu caso civil.",
|
||||||
|
"Avaliação rápida e gratuita do seu caso com especialistas em direito de família.",
|
||||||
|
"Confiança e agilidade em divórcio, pensão e inventário. Atendimento humanizado."
|
||||||
|
],
|
||||||
|
"sitelinks": [
|
||||||
|
{
|
||||||
|
"titulo": "Divórcio e Separação",
|
||||||
|
"desc_linha_1": "Soluções rápidas e seguras",
|
||||||
|
"desc_linha_2": "Atendimento 100% online"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Guarda de Filhos",
|
||||||
|
"desc_linha_1": "Prioridade no bem-estar",
|
||||||
|
"desc_linha_2": "Especialistas em família"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Pensão Alimentícia",
|
||||||
|
"desc_linha_1": "Definição e revisão justa",
|
||||||
|
"desc_linha_2": "Consultoria especializada"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"titulo": "Inventário e Herança",
|
||||||
|
"desc_linha_1": "Partilha ágil e segura",
|
||||||
|
"desc_linha_2": "Atendimento humanizado"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"callouts": [
|
||||||
|
"Atendimento 100% online",
|
||||||
|
"Mais de 10 anos de experi",
|
||||||
|
"Avaliação rápida do seu c",
|
||||||
|
"Sigilo e confidencialidad",
|
||||||
|
"Respostas ágeis e claras",
|
||||||
|
"Especialistas em direito "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -1,24 +1,7 @@
|
|||||||
REGRAS OTIMIZADAS PARA GOOGLE ADS:
|
|
||||||
|
|
||||||
1. PALAVRAS-CHAVE (Fundo de Funil): Gere 20 termos com alta intenção de contratação. Use [Exata] e "Frase". Limite os termos a no máximo 25 caracteres para viabilizar o uso nos títulos.
|
|
||||||
2. NEGATIVAS: Gere 20 termos que filtrem estudantes, curiosos e buscas gratuitas (ex: pdf, curso, modelo, jurisprudência, tcc).
|
|
||||||
3. TÍTULOS (Headlines): Gere 15 títulos (máx. 30 caracteres). Regra de Ouro: A palavra-chave da Regra 1 deve aparecer de forma INTEGRAL e IDENTICA no título. Se a keyword for longa, o título será apenas ela.
|
|
||||||
4. DESCRIÇÕES: Gere 4 descrições (máx. 90 caracteres). Devem terminar obrigatoriamente com um ponto final ou exclamação. Inclua uma Proposta Única de Valor (UVP).
|
|
||||||
5. EXTENSÕES (Sitelinks & Callouts):
|
|
||||||
- 4 Sitelinks (Título 25ch / Desc 35ch).
|
|
||||||
- 6 Callouts (Frases de destaque, máx 25ch) focadas em autoridade e agilidade.
|
|
||||||
6. POLÍTICAS EDITORIAIS:
|
|
||||||
- Proibido: "Grátis" e sinônimos.
|
|
||||||
- Proibido: CAIXA ALTA em palavras inteiras (exceto siglas como SP, OAB).
|
|
||||||
- Proibido: Uso excessivo de pontuação (ex: !!!).
|
|
||||||
|
|
||||||
RETORNO: Apenas o JSON estruturado.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////
|
|
||||||
{
|
{
|
||||||
"campanha_google_ads": {
|
"campanha_google_ads_v2_otimizada": {
|
||||||
|
"score_estimado": "9.8/10",
|
||||||
|
"melhorias_aplicadas": "Inserção de prova social (10+ anos, Nota 5.0), CTAs de urgência, maximização de caracteres nas descrições e títulos com variações de SP.",
|
||||||
"palavras_chave_fundo_de_funil": [
|
"palavras_chave_fundo_de_funil": [
|
||||||
"[advogado divórcio online]",
|
"[advogado divórcio online]",
|
||||||
"\"contratar advogado família\"",
|
"\"contratar advogado família\"",
|
||||||
@@ -42,26 +25,7 @@ RETORNO: Apenas o JSON estruturado.
|
|||||||
"\"advogado separação judicial\""
|
"\"advogado separação judicial\""
|
||||||
],
|
],
|
||||||
"palavras_chave_negativas": [
|
"palavras_chave_negativas": [
|
||||||
"grátis (evitar buscas sem orçamento)",
|
"grátis", "gratuito", "estágio", "vagas", "curso", "faculdade", "tcc", "modelo", "jurisprudência", "pdf", "concurso", "trabalhista", "criminal", "previdenciário", "defensoria pública", "fórum", "como fazer", "lei", "livro", "apostila"
|
||||||
"gratuito (evitar defensoria pública)",
|
|
||||||
"estágio (evitar candidatos a vaga)",
|
|
||||||
"vagas (evitar candidatos a vaga)",
|
|
||||||
"curso (evitar estudantes)",
|
|
||||||
"faculdade (evitar estudantes)",
|
|
||||||
"tcc (evitar estudantes)",
|
|
||||||
"modelo de petição (evitar curiosos)",
|
|
||||||
"jurisprudência (evitar estudantes)",
|
|
||||||
"pdf (evitar buscas informativas)",
|
|
||||||
"concurso (evitar concurseiros)",
|
|
||||||
"trabalhista (fora do escopo civil)",
|
|
||||||
"criminal (fora do escopo civil)",
|
|
||||||
"previdenciário (fora do escopo civil)",
|
|
||||||
"defensoria pública (baixa intenção)",
|
|
||||||
"fórum (busca informativa)",
|
|
||||||
"como fazer (busca informativa)",
|
|
||||||
"lei (busca informativa)",
|
|
||||||
"livro (evitar estudantes)",
|
|
||||||
"apostila (evitar estudantes)"
|
|
||||||
],
|
],
|
||||||
"titulos_headlines": [
|
"titulos_headlines": [
|
||||||
"Advogado divórcio online",
|
"Advogado divórcio online",
|
||||||
@@ -70,51 +34,51 @@ RETORNO: Apenas o JSON estruturado.
|
|||||||
"Advogado pensão alimentícia",
|
"Advogado pensão alimentícia",
|
||||||
"Advogado inventário SP",
|
"Advogado inventário SP",
|
||||||
"Advogado união estável",
|
"Advogado união estável",
|
||||||
"Dissolução união estável",
|
|
||||||
"Escritório direito civil",
|
|
||||||
"Advogado para inventário",
|
|
||||||
"Especialista em divórcio",
|
"Especialista em divórcio",
|
||||||
"Ação de pensão alimentícia",
|
"Advogado civilista SP",
|
||||||
"Advogado guarda de menores",
|
"Advogado direito de família",
|
||||||
"Advogado partilha de bens",
|
"Atendimento 100% Online",
|
||||||
"Consultoria jurídica família",
|
"Experiência de +10 anos",
|
||||||
"Divórcio consensual online"
|
"Avaliação 5.0 no Google",
|
||||||
|
"Fale com um Especialista",
|
||||||
|
"Agende sua Consulta Online",
|
||||||
|
"Sigilo Absoluto no Seu Caso"
|
||||||
],
|
],
|
||||||
"descricoes": [
|
"descricoes": [
|
||||||
"Advocacia especializada em Direito Civil e Família. Atendimento 100% online e sigiloso.",
|
"Advocacia especializada em Família e Sucessões. Atendimento 100% online com sigilo absoluto.",
|
||||||
"Resolva seu divórcio, guarda ou inventário de forma rápida e segura. Fale com um expert.",
|
"Resolva seu divórcio ou inventário com agilidade. Mais de 10 anos de experiência jurídica.",
|
||||||
"Proteja seus direitos e sua família com especialistas. Soluções ágeis e atendimento humanizado.",
|
"Proteja os direitos dos seus filhos. Especialistas em guarda e pensão. Avaliação nota 5.0!",
|
||||||
"Mais de 10 anos de experiência em Direito de Família. Avalie seu caso com total sigilo."
|
"Soluções jurídicas para união estável e partilha de bens. Atendimento rápido em todo SP."
|
||||||
],
|
],
|
||||||
"sitelinks": [
|
"sitelinks": [
|
||||||
{
|
{
|
||||||
"titulo": "Divórcio e Separação",
|
"titulo": "Divórcio e Separação",
|
||||||
"desc_linha_1": "Encerre o caso de forma rápida.",
|
"desc_linha_1": "Solução rápida e segura online.",
|
||||||
"desc_linha_2": "Consensual ou litigioso online."
|
"desc_linha_2": "Consensual ou litigioso com sigilo."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"titulo": "Guarda e Pensão",
|
"titulo": "Guarda e Pensão",
|
||||||
"desc_linha_1": "Priorizando o bem-estar dos filhos.",
|
"desc_linha_1": "Foco no bem-estar dos filhos.",
|
||||||
"desc_linha_2": "Definição e revisão de valores."
|
"desc_linha_2": "Definição e revisão de valores."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"titulo": "Inventário e Herança",
|
"titulo": "Inventário e Bens",
|
||||||
"desc_linha_1": "Solução ágil na partilha de bens.",
|
"desc_linha_1": "Partilha justa e sem burocracia.",
|
||||||
"desc_linha_2": "Via judicial ou extrajudicial."
|
"desc_linha_2": "Judicial ou extrajudicial rápido."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"titulo": "Fale com Especialista",
|
"titulo": "Fale com Especialista",
|
||||||
"desc_linha_1": "Atendimento rápido via WhatsApp.",
|
"desc_linha_1": "Agende agora pelo WhatsApp.",
|
||||||
"desc_linha_2": "Tire suas dúvidas agora mesmo."
|
"desc_linha_2": "Atendimento humanizado e ágil."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"callouts": [
|
"callouts": [
|
||||||
"Atendimento 100% Online",
|
"Atendimento 100% Online",
|
||||||
"Sigilo Total Garantido",
|
"Sigilo Total Garantido",
|
||||||
"10 Anos de Experiência",
|
"10 Anos de Experiência",
|
||||||
"Resposta Rápida",
|
"Avaliação Média 5.0",
|
||||||
"Especialista em Família",
|
"Resposta Imediata",
|
||||||
"Solução Sem Burocracia"
|
"Especialista em Família"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
668
app.py
668
app.py
@@ -1,17 +1,23 @@
|
|||||||
"""
|
"""
|
||||||
Google Ads Generator - Interface Streamlit
|
Google Ads Generator & Auditor - Interface Streamlit
|
||||||
|
|
||||||
Aplicação que extrai conteúdo de Landing Pages e gera automaticamente
|
Aplicação que extrai conteúdo de Landing Pages e gera automaticamente
|
||||||
ativos de campanha para Google Ads usando IA (OpenAI ou Gemini).
|
ativos de campanha para Google Ads usando IA (OpenAI ou Gemini).
|
||||||
|
Inclui módulo de Auditoria (Google Ads Scorecard) para avaliação de qualidade.
|
||||||
|
Suporta estrutura multi-grupo (3 Ad Groups temáticos por campanha).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
from src.scraper import scrape_landing_page
|
from src.scraper import scrape_landing_page
|
||||||
from src.ai_generator import generate_google_ads_assets, MODELS
|
from src.ai_generator import generate_google_ads_assets, MODELS
|
||||||
|
from src.auditor import audit_campaign
|
||||||
from src.exporter import (
|
from src.exporter import (
|
||||||
create_keywords_df,
|
create_keywords_df,
|
||||||
create_negative_keywords_df,
|
create_negative_keywords_df,
|
||||||
@@ -25,6 +31,96 @@ from src.exporter import (
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
|
def _run_with_progress(fn, progress_bar, status_text, msg: str, start_pct: int = 30, end_pct: int = 95):
|
||||||
|
"""
|
||||||
|
Executa fn() em thread separada enquanto anima a barra de progresso
|
||||||
|
com indicador numérico crescente (ex: 30%, 35%, 40%...).
|
||||||
|
Não altera a lógica — apenas dá feedback visual durante chamadas longas.
|
||||||
|
"""
|
||||||
|
result = [None]
|
||||||
|
error = [None]
|
||||||
|
|
||||||
|
def _worker():
|
||||||
|
try:
|
||||||
|
result[0] = fn()
|
||||||
|
except Exception as e:
|
||||||
|
error[0] = e
|
||||||
|
|
||||||
|
thread = threading.Thread(target=_worker)
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
pct = start_pct
|
||||||
|
while thread.is_alive() and pct < end_pct:
|
||||||
|
pct = min(pct + 5, end_pct)
|
||||||
|
progress_bar.progress(pct, text=f"{msg} ({pct}%)")
|
||||||
|
status_text.info(f"🤖 {msg} ({pct}%)")
|
||||||
|
time.sleep(1.5)
|
||||||
|
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
if error[0]:
|
||||||
|
raise error[0]
|
||||||
|
|
||||||
|
return result[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _build_json_export(assets: dict) -> str:
|
||||||
|
"""Monta o JSON de exportação no formato padronizado para Google Ads (multi-grupo)."""
|
||||||
|
# Ad Groups formatados
|
||||||
|
groups_formatted = []
|
||||||
|
for group in assets.get("ad_groups", []):
|
||||||
|
# Palavras-chave formatadas com tipo de correspondência
|
||||||
|
keywords_formatted = []
|
||||||
|
for kw in group.get("keywords", []):
|
||||||
|
keyword = kw.get("keyword", "")
|
||||||
|
match_type = kw.get("match_type", "Phrase")
|
||||||
|
if match_type == "Exact":
|
||||||
|
keywords_formatted.append(f"[{keyword}]")
|
||||||
|
elif match_type == "Phrase":
|
||||||
|
keywords_formatted.append(f'"{keyword}"')
|
||||||
|
else:
|
||||||
|
keywords_formatted.append(keyword)
|
||||||
|
|
||||||
|
groups_formatted.append({
|
||||||
|
"nome_grupo": group.get("name", "Grupo"),
|
||||||
|
"palavras_chave_fundo_de_funil": keywords_formatted,
|
||||||
|
"titulos_headlines": group.get("headlines", []),
|
||||||
|
"descricoes": group.get("descriptions", []),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Palavras-chave negativas com motivo (nível campanha)
|
||||||
|
negative_kw_formatted = []
|
||||||
|
for nkw in assets.get("negative_keywords", []):
|
||||||
|
keyword = nkw.get("keyword", "")
|
||||||
|
reason = nkw.get("reason", "")
|
||||||
|
if reason:
|
||||||
|
negative_kw_formatted.append(f"{keyword} ({reason})")
|
||||||
|
else:
|
||||||
|
negative_kw_formatted.append(keyword)
|
||||||
|
|
||||||
|
# Sitelinks formatados (nível campanha)
|
||||||
|
sitelinks_formatted = []
|
||||||
|
for sl in assets.get("sitelinks", []):
|
||||||
|
sitelinks_formatted.append({
|
||||||
|
"titulo": sl.get("title", ""),
|
||||||
|
"desc_linha_1": sl.get("description1", ""),
|
||||||
|
"desc_linha_2": sl.get("description2", ""),
|
||||||
|
})
|
||||||
|
|
||||||
|
export_data = [
|
||||||
|
{
|
||||||
|
"campanha_google_ads": {
|
||||||
|
"ad_groups": groups_formatted,
|
||||||
|
"palavras_chave_negativas": negative_kw_formatted,
|
||||||
|
"sitelinks": sitelinks_formatted,
|
||||||
|
"callouts": assets.get("callouts", []),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
return json.dumps(export_data, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
|
||||||
def _highlight_keywords(text: str, keywords: list[str]) -> str:
|
def _highlight_keywords(text: str, keywords: list[str]) -> str:
|
||||||
"""Destaca em negrito as palavras-chave encontradas no texto."""
|
"""Destaca em negrito as palavras-chave encontradas no texto."""
|
||||||
result = text
|
result = text
|
||||||
@@ -36,9 +132,10 @@ def _highlight_keywords(text: str, keywords: list[str]) -> str:
|
|||||||
break # Destacar apenas a primeira keyword encontrada
|
break # Destacar apenas a primeira keyword encontrada
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
# ─── Configuração da Página ───────────────────────────────────────
|
# ─── Configuração da Página ───────────────────────────────────────
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
page_title="Google Ads Generator",
|
page_title="Google Ads Generator & Auditor",
|
||||||
page_icon="📊",
|
page_icon="📊",
|
||||||
layout="wide",
|
layout="wide",
|
||||||
)
|
)
|
||||||
@@ -80,18 +177,77 @@ st.markdown("""
|
|||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 8px 8px 0 0;
|
||||||
}
|
}
|
||||||
|
.score-big {
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: 800;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
.score-label {
|
||||||
|
font-size: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
color: #5f6368;
|
||||||
|
}
|
||||||
|
.ad-preview {
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #dadce0;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
max-width: 600px;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.ad-preview .ad-badge {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #202124;
|
||||||
|
background: #f1f3f4;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.ad-preview .ad-url {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: #202124;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.ad-preview .ad-title {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #1a0dab;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.ad-preview .ad-desc {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #4d5156;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.group-header {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a73e8;
|
||||||
|
border-left: 4px solid #1a73e8;
|
||||||
|
padding-left: 0.8rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
""", unsafe_allow_html=True)
|
""", unsafe_allow_html=True)
|
||||||
|
|
||||||
# ─── Header ───────────────────────────────────────────────────────
|
|
||||||
st.markdown('<p class="main-header">Google Ads Generator</p>', unsafe_allow_html=True)
|
|
||||||
st.markdown(
|
|
||||||
'<p class="sub-header">Gere ativos de campanha automaticamente a partir da sua Landing Page</p>',
|
|
||||||
unsafe_allow_html=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# ─── Sidebar ──────────────────────────────────────────────────────
|
# ─── Sidebar ──────────────────────────────────────────────────────
|
||||||
with st.sidebar:
|
with st.sidebar:
|
||||||
|
st.header("Navegação")
|
||||||
|
|
||||||
|
page = st.radio(
|
||||||
|
"Modo",
|
||||||
|
["Gerador de Campanha", "Auditor (Scorecard)"],
|
||||||
|
index=0,
|
||||||
|
help="Alterne entre gerar ativos e auditar campanhas existentes.",
|
||||||
|
)
|
||||||
|
|
||||||
|
st.divider()
|
||||||
st.header("Configurações")
|
st.header("Configurações")
|
||||||
|
|
||||||
provider = st.selectbox(
|
provider = st.selectbox(
|
||||||
@@ -105,7 +261,7 @@ with st.sidebar:
|
|||||||
"Modelo",
|
"Modelo",
|
||||||
MODELS.get(provider, []),
|
MODELS.get(provider, []),
|
||||||
index=0,
|
index=0,
|
||||||
help="Modelo a ser utilizado para geração dos ativos.",
|
help="Modelo a ser utilizado para geração/auditoria.",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Status das chaves configuradas
|
# Status das chaves configuradas
|
||||||
@@ -124,16 +280,22 @@ with st.sidebar:
|
|||||||
help="Nome que aparecerá na coluna 'Campaign' do CSV.",
|
help="Nome que aparecerá na coluna 'Campaign' do CSV.",
|
||||||
)
|
)
|
||||||
|
|
||||||
ad_group = st.text_input(
|
|
||||||
"Nome do Grupo de Anúncios",
|
|
||||||
value="Grupo 1",
|
|
||||||
help="Nome que aparecerá na coluna 'Ad Group' do CSV.",
|
|
||||||
)
|
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
st.caption("Desenvolvido com Streamlit + OpenAI + Gemini")
|
st.caption("Desenvolvido com Streamlit + OpenAI + Gemini")
|
||||||
|
|
||||||
# ─── Área Principal ───────────────────────────────────────────────
|
|
||||||
|
# =====================================================================
|
||||||
|
# MODO: GERADOR DE CAMPANHA
|
||||||
|
# =====================================================================
|
||||||
|
def render_generator():
|
||||||
|
"""Renderiza a interface do Gerador de Campanha (multi-grupo)."""
|
||||||
|
st.markdown('<p class="main-header">Google Ads Generator</p>', unsafe_allow_html=True)
|
||||||
|
st.markdown(
|
||||||
|
'<p class="sub-header">Gere ativos de campanha automaticamente a partir da sua Landing Page (3 Grupos de Anúncio)</p>',
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ─── Área Principal ───────────────────────────────────────────
|
||||||
url = st.text_input(
|
url = st.text_input(
|
||||||
"URL da Landing Page",
|
"URL da Landing Page",
|
||||||
placeholder="https://www.seusite.com.br/landing-page",
|
placeholder="https://www.seusite.com.br/landing-page",
|
||||||
@@ -145,7 +307,7 @@ col_btn, col_status = st.columns([1, 3])
|
|||||||
with col_btn:
|
with col_btn:
|
||||||
generate_btn = st.button("Gerar Campanha", type="primary", use_container_width=True)
|
generate_btn = st.button("Gerar Campanha", type="primary", use_container_width=True)
|
||||||
|
|
||||||
# ─── Lógica Principal ────────────────────────────────────────────
|
# ─── Lógica Principal ────────────────────────────────────────
|
||||||
if generate_btn:
|
if generate_btn:
|
||||||
# Validações
|
# Validações
|
||||||
if not url:
|
if not url:
|
||||||
@@ -164,125 +326,197 @@ if generate_btn:
|
|||||||
st.error("GEMINI_API_KEY não encontrada no arquivo .env. Configure antes de continuar.")
|
st.error("GEMINI_API_KEY não encontrada no arquivo .env. Configure antes de continuar.")
|
||||||
st.stop()
|
st.stop()
|
||||||
|
|
||||||
|
# Barra de progresso visual
|
||||||
|
progress_bar = st.progress(0, text="Iniciando...")
|
||||||
|
status_text = st.empty()
|
||||||
|
|
||||||
# Step 1: Scraping
|
# Step 1: Scraping
|
||||||
with st.status("Processando...", expanded=True) as status:
|
progress_bar.progress(10, text="Extraindo conteúdo da Landing Page...")
|
||||||
st.write("Extraindo conteúdo da Landing Page...")
|
status_text.info("🔍 Acessando a URL e extraindo conteúdo...")
|
||||||
try:
|
try:
|
||||||
lp_data = scrape_landing_page(url)
|
lp_data = scrape_landing_page(url)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
st.error(f"Erro ao acessar a URL: {e}")
|
st.error(f"Erro ao acessar a URL: {e}")
|
||||||
st.stop()
|
st.stop()
|
||||||
|
|
||||||
st.write(f"Conteúdo extraído: {len(lp_data['paragraphs'])} parágrafos, "
|
progress_bar.progress(30, text="Conteúdo extraído com sucesso!")
|
||||||
|
status_text.info(
|
||||||
|
f"📄 Extraído: {len(lp_data['paragraphs'])} parágrafos, "
|
||||||
f"{len(lp_data['h1'])} H1, {len(lp_data['h2'])} H2, "
|
f"{len(lp_data['h1'])} H1, {len(lp_data['h2'])} H2, "
|
||||||
f"{len(lp_data.get('list_items', []))} itens de lista, "
|
f"{len(lp_data.get('list_items', []))} itens de lista, "
|
||||||
f"{len(lp_data['ctas'])} CTAs")
|
f"{len(lp_data['ctas'])} CTAs"
|
||||||
|
)
|
||||||
|
|
||||||
# Step 2: IA
|
# Step 2: IA — executa em thread com progresso animado
|
||||||
st.write(f"Gerando ativos com {provider} ({model})...")
|
|
||||||
try:
|
try:
|
||||||
assets, prompts = generate_google_ads_assets(
|
assets, prompts = _run_with_progress(
|
||||||
|
fn=lambda: generate_google_ads_assets(
|
||||||
lp_content=lp_data["full_text"],
|
lp_content=lp_data["full_text"],
|
||||||
provider=provider,
|
provider=provider,
|
||||||
model=model,
|
model=model,
|
||||||
|
),
|
||||||
|
progress_bar=progress_bar,
|
||||||
|
status_text=status_text,
|
||||||
|
msg=f"Gerando ativos com {provider} ({model})",
|
||||||
|
start_pct=35,
|
||||||
|
end_pct=95,
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
st.error(str(e))
|
st.error(str(e))
|
||||||
st.stop()
|
st.stop()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
st.error(f"Erro ao gerar ativos: {e}")
|
st.error(f"Erro ao gerar ativos: {e}")
|
||||||
st.stop()
|
st.stop()
|
||||||
|
|
||||||
status.update(label="Concluído!", state="complete", expanded=False)
|
progress_bar.progress(100, text="Campanha gerada com sucesso! (100%)")
|
||||||
|
status_text.success("✅ Campanha gerada com sucesso!")
|
||||||
|
|
||||||
# Salvar no session_state
|
# Salvar no session_state
|
||||||
st.session_state["assets"] = assets
|
st.session_state["assets"] = assets
|
||||||
st.session_state["prompts"] = prompts
|
st.session_state["prompts"] = prompts
|
||||||
st.session_state["lp_data"] = lp_data
|
st.session_state["lp_data"] = lp_data
|
||||||
st.session_state["campaign_name"] = campaign_name
|
st.session_state["campaign_name"] = campaign_name
|
||||||
st.session_state["ad_group"] = ad_group
|
|
||||||
st.session_state["provider_used"] = provider
|
st.session_state["provider_used"] = provider
|
||||||
st.session_state["model_used"] = model
|
st.session_state["model_used"] = model
|
||||||
|
|
||||||
# ─── Exibição dos Resultados ─────────────────────────────────────
|
# ─── Exibição dos Resultados ─────────────────────────────────
|
||||||
if "assets" in st.session_state:
|
if "assets" in st.session_state:
|
||||||
assets = st.session_state["assets"]
|
assets = st.session_state["assets"]
|
||||||
prompts = st.session_state.get("prompts", {})
|
prompts = st.session_state.get("prompts", {})
|
||||||
camp = st.session_state.get("campaign_name", "Campanha LP")
|
camp = st.session_state.get("campaign_name", "Campanha LP")
|
||||||
adg = st.session_state.get("ad_group", "Grupo 1")
|
|
||||||
prov_used = st.session_state.get("provider_used", "")
|
prov_used = st.session_state.get("provider_used", "")
|
||||||
model_used = st.session_state.get("model_used", "")
|
model_used = st.session_state.get("model_used", "")
|
||||||
|
ad_groups = assets.get("ad_groups", [])
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
|
|
||||||
# Métricas resumo
|
# Métricas resumo (totais agregados)
|
||||||
col1, col2, col3, col4, col5, col6 = st.columns(6)
|
total_kw = sum(len(g.get("keywords", [])) for g in ad_groups)
|
||||||
|
total_headlines = sum(len(g.get("headlines", [])) for g in ad_groups)
|
||||||
|
total_desc = sum(len(g.get("descriptions", [])) for g in ad_groups)
|
||||||
|
|
||||||
|
col1, col2, col3, col4, col5, col6, col7 = st.columns(7)
|
||||||
|
|
||||||
with col1:
|
with col1:
|
||||||
st.metric("Keywords", len(assets.get("keywords", [])))
|
st.metric("Grupos", len(ad_groups))
|
||||||
with col2:
|
with col2:
|
||||||
st.metric("Negativas", len(assets.get("negative_keywords", [])))
|
st.metric("Keywords", total_kw)
|
||||||
with col3:
|
with col3:
|
||||||
st.metric("Títulos", len(assets.get("headlines", [])))
|
st.metric("Negativas", len(assets.get("negative_keywords", [])))
|
||||||
with col4:
|
with col4:
|
||||||
st.metric("Descrições", len(assets.get("descriptions", [])))
|
st.metric("Títulos", total_headlines)
|
||||||
with col5:
|
with col5:
|
||||||
st.metric("Sitelinks", len(assets.get("sitelinks", [])))
|
st.metric("Descrições", total_desc)
|
||||||
with col6:
|
with col6:
|
||||||
|
st.metric("Sitelinks", len(assets.get("sitelinks", [])))
|
||||||
|
with col7:
|
||||||
st.metric("Callouts", len(assets.get("callouts", [])))
|
st.metric("Callouts", len(assets.get("callouts", [])))
|
||||||
|
|
||||||
st.divider()
|
st.divider()
|
||||||
|
|
||||||
# Abas com resultados
|
# Abas com resultados
|
||||||
tab_kw, tab_neg, tab_ads, tab_sl, tab_co, tab_prompts, tab_raw = st.tabs([
|
tab_groups, tab_neg, tab_sl, tab_co, tab_prompts, tab_raw = st.tabs([
|
||||||
"Keywords", "Negativas", "Anúncios RSA", "Sitelinks", "Callouts",
|
"Grupos de Anúncio",
|
||||||
"Prompts Utilizados", "Dados da LP",
|
"Negativas",
|
||||||
|
"Sitelinks",
|
||||||
|
"Callouts",
|
||||||
|
"Prompts Utilizados",
|
||||||
|
"Dados da LP",
|
||||||
])
|
])
|
||||||
|
|
||||||
with tab_kw:
|
with tab_groups:
|
||||||
st.subheader("Palavras-chave")
|
st.subheader("Grupos de Anúncio (Ad Groups)")
|
||||||
kw_df = create_keywords_df(assets, camp, adg)
|
|
||||||
|
if not ad_groups:
|
||||||
|
st.info("Nenhum grupo de anúncio gerado.")
|
||||||
|
else:
|
||||||
|
# Sub-abas para cada grupo
|
||||||
|
group_tab_names = [f"Grupo {i+1}: {g.get('name', '')}" for i, g in enumerate(ad_groups)]
|
||||||
|
group_tabs = st.tabs(group_tab_names)
|
||||||
|
|
||||||
|
for idx, (gtab, group) in enumerate(zip(group_tabs, ad_groups)):
|
||||||
|
with gtab:
|
||||||
|
group_name = group.get("name", f"Grupo {idx+1}")
|
||||||
|
st.markdown(f'<p class="group-header">{group_name}</p>', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
# Keywords do grupo
|
||||||
|
st.write("**Palavras-chave:**")
|
||||||
|
group_kw = group.get("keywords", [])
|
||||||
|
if group_kw:
|
||||||
|
for i, kw in enumerate(group_kw, 1):
|
||||||
|
keyword = kw.get("keyword", "")
|
||||||
|
match_type = kw.get("match_type", "Phrase")
|
||||||
|
if match_type == "Exact":
|
||||||
|
formatted = f"[{keyword}]"
|
||||||
|
elif match_type == "Phrase":
|
||||||
|
formatted = f'"{keyword}"'
|
||||||
|
else:
|
||||||
|
formatted = keyword
|
||||||
|
st.markdown(f"{i}. `{formatted}` ({match_type})")
|
||||||
|
else:
|
||||||
|
st.info("Nenhuma keyword neste grupo.")
|
||||||
|
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
# Headlines do grupo
|
||||||
|
st.write("**Títulos (Headlines):**")
|
||||||
|
kw_list = sorted(
|
||||||
|
[kw["keyword"].lower() for kw in group_kw if kw.get("keyword")],
|
||||||
|
key=len, reverse=True,
|
||||||
|
)
|
||||||
|
for i, h in enumerate(group.get("headlines", []), 1):
|
||||||
|
char_count = len(h)
|
||||||
|
color = "green" if char_count <= 30 else "red"
|
||||||
|
h_highlighted = _highlight_keywords(h, kw_list)
|
||||||
|
st.markdown(f"{i}. {h_highlighted} — :{color}[{char_count} chars]")
|
||||||
|
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
# Descriptions do grupo
|
||||||
|
st.write("**Descrições:**")
|
||||||
|
for i, d in enumerate(group.get("descriptions", []), 1):
|
||||||
|
char_count = len(d)
|
||||||
|
color = "green" if char_count <= 90 else "red"
|
||||||
|
st.markdown(f"{i}. {d} — :{color}[{char_count} chars]")
|
||||||
|
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
# DataFrame RSA do grupo
|
||||||
|
st.write("**Tabela RSA (formato Google Ads Editor):**")
|
||||||
|
# Montar assets temporário para o grupo individual
|
||||||
|
single_group_assets = {
|
||||||
|
"ad_groups": [group],
|
||||||
|
"negative_keywords": [],
|
||||||
|
"sitelinks": [],
|
||||||
|
"callouts": [],
|
||||||
|
}
|
||||||
|
ads_df = create_ads_df(single_group_assets, camp)
|
||||||
|
if not ads_df.empty:
|
||||||
|
st.dataframe(ads_df, use_container_width=True, hide_index=True)
|
||||||
|
|
||||||
|
# DataFrame consolidado de todas as keywords
|
||||||
|
st.markdown("---")
|
||||||
|
st.write("**Tabela Consolidada de Keywords (todos os grupos):**")
|
||||||
|
kw_df = create_keywords_df(assets, camp)
|
||||||
if not kw_df.empty:
|
if not kw_df.empty:
|
||||||
st.dataframe(kw_df, use_container_width=True, hide_index=True)
|
st.dataframe(kw_df, use_container_width=True, hide_index=True)
|
||||||
else:
|
|
||||||
st.info("Nenhuma palavra-chave gerada.")
|
|
||||||
|
|
||||||
with tab_neg:
|
with tab_neg:
|
||||||
st.subheader("Palavras-chave Negativas")
|
st.subheader("Palavras-chave Negativas (Nível de Campanha)")
|
||||||
nkw_df = create_negative_keywords_df(assets, camp)
|
nkw_df = create_negative_keywords_df(assets, camp)
|
||||||
if not nkw_df.empty:
|
if not nkw_df.empty:
|
||||||
st.dataframe(nkw_df, use_container_width=True, hide_index=True)
|
st.dataframe(nkw_df, use_container_width=True, hide_index=True)
|
||||||
else:
|
else:
|
||||||
st.info("Nenhuma palavra-chave negativa gerada.")
|
st.info("Nenhuma palavra-chave negativa gerada.")
|
||||||
|
|
||||||
with tab_ads:
|
|
||||||
st.subheader("Anúncio Responsivo de Pesquisa (RSA)")
|
|
||||||
|
|
||||||
st.write("**Títulos (Headlines):**")
|
|
||||||
# Coletar palavras-chave para destacar nos títulos
|
|
||||||
kw_list = sorted(
|
|
||||||
[kw["keyword"].lower() for kw in assets.get("keywords", []) if kw.get("keyword")],
|
|
||||||
key=len, reverse=True, # Maior primeiro para evitar match parcial
|
|
||||||
)
|
|
||||||
for i, h in enumerate(assets.get("headlines", []), 1):
|
|
||||||
char_count = len(h)
|
|
||||||
color = "green" if char_count <= 30 else "red"
|
|
||||||
h_highlighted = _highlight_keywords(h, kw_list)
|
|
||||||
st.markdown(f"{i}. {h_highlighted} — :{color}[{char_count} chars]")
|
|
||||||
|
|
||||||
st.write("**Descrições:**")
|
|
||||||
for i, d in enumerate(assets.get("descriptions", []), 1):
|
|
||||||
char_count = len(d)
|
|
||||||
color = "green" if char_count <= 90 else "red"
|
|
||||||
st.markdown(f"{i}. {d} — :{color}[{char_count} chars]")
|
|
||||||
|
|
||||||
st.divider()
|
|
||||||
ads_df = create_ads_df(assets, camp, adg)
|
|
||||||
if not ads_df.empty:
|
|
||||||
st.dataframe(ads_df, use_container_width=True, hide_index=True)
|
|
||||||
|
|
||||||
with tab_sl:
|
with tab_sl:
|
||||||
st.subheader("Sitelinks")
|
st.subheader("Sitelinks (Nível de Campanha)")
|
||||||
sl_df = create_sitelinks_df(assets, camp)
|
sl_df = create_sitelinks_df(assets, camp)
|
||||||
if not sl_df.empty:
|
if not sl_df.empty:
|
||||||
st.dataframe(sl_df, use_container_width=True, hide_index=True)
|
st.dataframe(sl_df, use_container_width=True, hide_index=True)
|
||||||
@@ -290,7 +524,7 @@ if "assets" in st.session_state:
|
|||||||
st.info("Nenhum sitelink gerado.")
|
st.info("Nenhum sitelink gerado.")
|
||||||
|
|
||||||
with tab_co:
|
with tab_co:
|
||||||
st.subheader("Callouts (Frases de Destaque)")
|
st.subheader("Callouts — Frases de Destaque (Nível de Campanha)")
|
||||||
co_df = create_callouts_df(assets, camp)
|
co_df = create_callouts_df(assets, camp)
|
||||||
if not co_df.empty:
|
if not co_df.empty:
|
||||||
st.dataframe(co_df, use_container_width=True, hide_index=True)
|
st.dataframe(co_df, use_container_width=True, hide_index=True)
|
||||||
@@ -334,14 +568,14 @@ if "assets" in st.session_state:
|
|||||||
with st.expander("Texto Completo Enviado à IA"):
|
with st.expander("Texto Completo Enviado à IA"):
|
||||||
st.code(lp_data.get("full_text", ""), language=None)
|
st.code(lp_data.get("full_text", ""), language=None)
|
||||||
|
|
||||||
# ─── Botões de Download ───────────────────────────────────────
|
# ─── Botões de Download ───────────────────────────────────
|
||||||
st.divider()
|
st.divider()
|
||||||
st.subheader("Download dos Ativos")
|
st.subheader("Download dos Ativos")
|
||||||
|
|
||||||
col_dl1, col_dl2 = st.columns(2)
|
col_dl1, col_dl2, col_dl3 = st.columns(3)
|
||||||
|
|
||||||
with col_dl1:
|
with col_dl1:
|
||||||
excel_data = export_all_to_excel(assets, camp, adg)
|
excel_data = export_all_to_excel(assets, camp)
|
||||||
st.download_button(
|
st.download_button(
|
||||||
label="Baixar Excel (.xlsx)",
|
label="Baixar Excel (.xlsx)",
|
||||||
data=excel_data,
|
data=excel_data,
|
||||||
@@ -351,7 +585,7 @@ if "assets" in st.session_state:
|
|||||||
)
|
)
|
||||||
|
|
||||||
with col_dl2:
|
with col_dl2:
|
||||||
kw_csv = create_keywords_df(assets, camp, adg).to_csv(index=False)
|
kw_csv = create_keywords_df(assets, camp).to_csv(index=False)
|
||||||
st.download_button(
|
st.download_button(
|
||||||
label="Baixar Keywords (.csv)",
|
label="Baixar Keywords (.csv)",
|
||||||
data=kw_csv,
|
data=kw_csv,
|
||||||
@@ -359,3 +593,283 @@ if "assets" in st.session_state:
|
|||||||
mime="text/csv",
|
mime="text/csv",
|
||||||
use_container_width=True,
|
use_container_width=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
with col_dl3:
|
||||||
|
json_export = _build_json_export(assets)
|
||||||
|
st.download_button(
|
||||||
|
label="Baixar JSON (.json)",
|
||||||
|
data=json_export,
|
||||||
|
file_name="google_ads_assets.json",
|
||||||
|
mime="application/json",
|
||||||
|
use_container_width=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# MODO: AUDITOR (SCORECARD)
|
||||||
|
# =====================================================================
|
||||||
|
def _get_score_color(score: float) -> str:
|
||||||
|
"""Retorna a cor com base na nota."""
|
||||||
|
if score >= 8.0:
|
||||||
|
return "#34a853" # verde
|
||||||
|
elif score >= 5.0:
|
||||||
|
return "#fbbc04" # amarelo
|
||||||
|
else:
|
||||||
|
return "#ea4335" # vermelho
|
||||||
|
|
||||||
|
|
||||||
|
def _get_criterion_emoji(nota: float, nota_maxima: float) -> str:
|
||||||
|
"""Retorna emoji com base na porcentagem atingida do critério."""
|
||||||
|
if nota_maxima == 0:
|
||||||
|
return "⚪"
|
||||||
|
pct = nota / nota_maxima
|
||||||
|
if pct >= 0.9:
|
||||||
|
return "✅"
|
||||||
|
elif pct >= 0.6:
|
||||||
|
return "⚠️"
|
||||||
|
else:
|
||||||
|
return "❌"
|
||||||
|
|
||||||
|
|
||||||
|
def render_auditor():
|
||||||
|
"""Renderiza a interface do Auditor de Campanha (Google Ads Scorecard)."""
|
||||||
|
st.markdown('<p class="main-header">Google Ads Scorecard</p>', unsafe_allow_html=True)
|
||||||
|
st.markdown(
|
||||||
|
'<p class="sub-header">Audite a qualidade dos seus ativos de campanha com IA</p>',
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ─── Fonte do JSON ────────────────────────────────────────────
|
||||||
|
has_session_json = "assets" in st.session_state
|
||||||
|
json_source = st.radio(
|
||||||
|
"Fonte do JSON para auditoria",
|
||||||
|
[
|
||||||
|
"Usar campanha da sessão atual" if has_session_json else "Usar campanha da sessão atual (nenhuma disponível)",
|
||||||
|
"Colar JSON manualmente",
|
||||||
|
],
|
||||||
|
index=0 if has_session_json else 1,
|
||||||
|
horizontal=True,
|
||||||
|
help="Escolha de onde vem o JSON da campanha a ser auditada.",
|
||||||
|
)
|
||||||
|
|
||||||
|
campaign_json = ""
|
||||||
|
|
||||||
|
if "sessão atual" in json_source and has_session_json:
|
||||||
|
# Construir o JSON a partir dos assets na sessão
|
||||||
|
campaign_json = _build_json_export(st.session_state["assets"])
|
||||||
|
with st.expander("Ver JSON da sessão atual", expanded=False):
|
||||||
|
st.code(campaign_json, language="json")
|
||||||
|
elif "sessão atual" in json_source and not has_session_json:
|
||||||
|
st.warning("Nenhuma campanha gerada na sessão atual. Gere uma campanha primeiro no modo 'Gerador' ou cole o JSON manualmente.")
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
campaign_json = st.text_area(
|
||||||
|
"Cole o JSON da campanha aqui",
|
||||||
|
height=300,
|
||||||
|
placeholder='[{"campanha_google_ads": {"ad_groups": [...], "palavras_chave_negativas": [...], ...}}]',
|
||||||
|
help="Cole o JSON completo da campanha no formato de exportação.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ─── Botão de Auditoria ───────────────────────────────────────
|
||||||
|
col_audit_btn, _ = st.columns([1, 3])
|
||||||
|
with col_audit_btn:
|
||||||
|
audit_btn = st.button("Auditar Campanha", type="primary", use_container_width=True)
|
||||||
|
|
||||||
|
# ─── Lógica de Auditoria ─────────────────────────────────────
|
||||||
|
if audit_btn:
|
||||||
|
if not campaign_json or not campaign_json.strip():
|
||||||
|
st.error("Por favor, forneça o JSON da campanha para auditoria.")
|
||||||
|
st.stop()
|
||||||
|
|
||||||
|
# Validar JSON
|
||||||
|
try:
|
||||||
|
json.loads(campaign_json)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
st.error("O JSON fornecido é inválido. Verifique a formatação e tente novamente.")
|
||||||
|
st.stop()
|
||||||
|
|
||||||
|
# Verificar chave da API
|
||||||
|
if provider == "OpenAI" and not os.environ.get("OPENAI_API_KEY"):
|
||||||
|
st.error("OPENAI_API_KEY não encontrada no arquivo .env. Configure antes de continuar.")
|
||||||
|
st.stop()
|
||||||
|
elif provider == "Gemini" and not os.environ.get("GEMINI_API_KEY"):
|
||||||
|
st.error("GEMINI_API_KEY não encontrada no arquivo .env. Configure antes de continuar.")
|
||||||
|
st.stop()
|
||||||
|
|
||||||
|
# Barra de progresso visual
|
||||||
|
progress_bar = st.progress(0, text="Iniciando auditoria...")
|
||||||
|
status_text = st.empty()
|
||||||
|
|
||||||
|
progress_bar.progress(15, text="Validando JSON da campanha...")
|
||||||
|
status_text.info("📋 JSON validado. Preparando envio para IA...")
|
||||||
|
|
||||||
|
# Executa auditoria em thread com progresso animado
|
||||||
|
try:
|
||||||
|
audit_result, audit_prompts = _run_with_progress(
|
||||||
|
fn=lambda: audit_campaign(
|
||||||
|
campaign_json=campaign_json,
|
||||||
|
provider=provider,
|
||||||
|
model=model,
|
||||||
|
),
|
||||||
|
progress_bar=progress_bar,
|
||||||
|
status_text=status_text,
|
||||||
|
msg=f"Analisando campanha com {provider} ({model})",
|
||||||
|
start_pct=25,
|
||||||
|
end_pct=95,
|
||||||
|
)
|
||||||
|
except ValueError as e:
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
|
st.error(str(e))
|
||||||
|
st.stop()
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
|
st.error("A IA retornou uma resposta inválida. Tente novamente ou troque de modelo.")
|
||||||
|
st.stop()
|
||||||
|
except Exception as e:
|
||||||
|
progress_bar.empty()
|
||||||
|
status_text.empty()
|
||||||
|
st.error(f"Erro durante auditoria: {e}")
|
||||||
|
st.stop()
|
||||||
|
|
||||||
|
progress_bar.progress(100, text="Auditoria concluída! (100%)")
|
||||||
|
status_text.success("✅ Auditoria concluída com sucesso!")
|
||||||
|
|
||||||
|
# Salvar no session_state
|
||||||
|
st.session_state["audit_result"] = audit_result
|
||||||
|
st.session_state["audit_prompts"] = audit_prompts
|
||||||
|
|
||||||
|
# ─── Exibição dos Resultados da Auditoria ────────────────────
|
||||||
|
if "audit_result" in st.session_state:
|
||||||
|
audit_result = st.session_state["audit_result"]
|
||||||
|
audit_prompts = st.session_state.get("audit_prompts", {})
|
||||||
|
|
||||||
|
st.divider()
|
||||||
|
|
||||||
|
# ─── Score Principal ──────────────────────────────────────
|
||||||
|
nota = audit_result["nota_final"]
|
||||||
|
cor = _get_score_color(nota)
|
||||||
|
|
||||||
|
col_score, col_resumo = st.columns([1, 3])
|
||||||
|
|
||||||
|
with col_score:
|
||||||
|
st.markdown(
|
||||||
|
f'<div style="text-align:center; padding:1rem;">'
|
||||||
|
f'<p class="score-big" style="color:{cor}">{nota:.1f}</p>'
|
||||||
|
f'<p class="score-label">de 10.0 pontos</p>'
|
||||||
|
f'</div>',
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
with col_resumo:
|
||||||
|
st.markdown("#### Resumo da Auditoria")
|
||||||
|
st.info(audit_result.get("resumo", "Sem resumo disponível."))
|
||||||
|
|
||||||
|
st.divider()
|
||||||
|
|
||||||
|
# ─── Abas de Resultado ────────────────────────────────────
|
||||||
|
tab_criterios, tab_simulacao, tab_audit_prompts = st.tabs([
|
||||||
|
"Critérios de Avaliação",
|
||||||
|
"Simulação de Anúncio (CTR)",
|
||||||
|
"Prompts da Auditoria",
|
||||||
|
])
|
||||||
|
|
||||||
|
with tab_criterios:
|
||||||
|
st.subheader("Detalhamento por Critério")
|
||||||
|
|
||||||
|
for criterio in audit_result.get("criterios", []):
|
||||||
|
nome = criterio.get("nome", "")
|
||||||
|
nota_c = criterio.get("nota", 0)
|
||||||
|
nota_max = criterio.get("nota_maxima", 0)
|
||||||
|
peso = criterio.get("peso", 0)
|
||||||
|
falhas = criterio.get("falhas", [])
|
||||||
|
sugestoes = criterio.get("sugestoes", [])
|
||||||
|
emoji = _get_criterion_emoji(nota_c, nota_max)
|
||||||
|
|
||||||
|
with st.expander(f"{emoji} {nome} — {nota_c:.1f} / {nota_max:.1f} (peso {peso:.1f})", expanded=True):
|
||||||
|
# Barra de progresso visual
|
||||||
|
pct = nota_c / nota_max if nota_max > 0 else 0
|
||||||
|
st.progress(min(pct, 1.0))
|
||||||
|
|
||||||
|
if falhas:
|
||||||
|
st.markdown("**Falhas encontradas:**")
|
||||||
|
for f in falhas:
|
||||||
|
st.markdown(f"- :red[{f}]")
|
||||||
|
|
||||||
|
if sugestoes:
|
||||||
|
st.markdown("**Sugestões de melhoria:**")
|
||||||
|
for s in sugestoes:
|
||||||
|
st.markdown(f"- :blue[{s}]")
|
||||||
|
|
||||||
|
if not falhas and not sugestoes:
|
||||||
|
st.success("Nenhuma falha encontrada neste critério.")
|
||||||
|
|
||||||
|
with tab_simulacao:
|
||||||
|
st.subheader("Simulação Visual dos Anúncios")
|
||||||
|
st.caption("Como os anúncios de cada grupo apareceriam nos resultados de pesquisa do Google:")
|
||||||
|
|
||||||
|
simulacoes = audit_result.get("simulacoes_anuncio", [])
|
||||||
|
|
||||||
|
if not simulacoes:
|
||||||
|
st.info("Nenhuma simulação de anúncio disponível.")
|
||||||
|
else:
|
||||||
|
for sim_idx, sim in enumerate(simulacoes):
|
||||||
|
grupo_nome = sim.get("grupo", f"Grupo {sim_idx + 1}")
|
||||||
|
titulo1 = sim.get("titulo_linha_1", "")
|
||||||
|
titulo2 = sim.get("titulo_linha_2", "")
|
||||||
|
titulo3 = sim.get("titulo_linha_3", "")
|
||||||
|
url_display = sim.get("url_display", "www.exemplo.com.br")
|
||||||
|
descricao = sim.get("descricao", "")
|
||||||
|
|
||||||
|
# Montar título composto
|
||||||
|
titulos = [t for t in [titulo1, titulo2, titulo3] if t]
|
||||||
|
titulo_completo = " | ".join(titulos)
|
||||||
|
|
||||||
|
st.markdown(f'<p class="group-header">{grupo_nome}</p>', unsafe_allow_html=True)
|
||||||
|
|
||||||
|
st.markdown(
|
||||||
|
f"""
|
||||||
|
<div class="ad-preview">
|
||||||
|
<div class="ad-badge">Patrocinado</div>
|
||||||
|
<div class="ad-url">{url_display}</div>
|
||||||
|
<div class="ad-title">{titulo_completo}</div>
|
||||||
|
<div class="ad-desc">{descricao}</div>
|
||||||
|
</div>
|
||||||
|
""",
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Métricas dos títulos individuais
|
||||||
|
st.markdown("**Títulos selecionados para simulação:**")
|
||||||
|
for i, t in enumerate(titulos, 1):
|
||||||
|
char_count = len(t)
|
||||||
|
color = "green" if char_count <= 30 else "red"
|
||||||
|
st.markdown(f"{i}. {t} — :{color}[{char_count} chars]")
|
||||||
|
|
||||||
|
if descricao:
|
||||||
|
char_desc = len(descricao)
|
||||||
|
color_desc = "green" if char_desc <= 90 else "red"
|
||||||
|
st.markdown(f"**Descrição:** {descricao} — :{color_desc}[{char_desc} chars]")
|
||||||
|
|
||||||
|
if sim_idx < len(simulacoes) - 1:
|
||||||
|
st.markdown("---")
|
||||||
|
|
||||||
|
with tab_audit_prompts:
|
||||||
|
st.subheader("Prompts Utilizados na Auditoria")
|
||||||
|
st.caption(f"Provider: **{provider}** | Modelo: **{model}**")
|
||||||
|
|
||||||
|
st.markdown("#### Prompt de Sistema (System Prompt)")
|
||||||
|
st.code(audit_prompts.get("system_prompt", "N/A"), language=None)
|
||||||
|
|
||||||
|
st.markdown("#### Prompt do Usuário (User Prompt)")
|
||||||
|
st.code(audit_prompts.get("user_prompt", "N/A"), language=None)
|
||||||
|
|
||||||
|
|
||||||
|
# =====================================================================
|
||||||
|
# ROTEAMENTO DE PÁGINA
|
||||||
|
# =====================================================================
|
||||||
|
if page == "Gerador de Campanha":
|
||||||
|
render_generator()
|
||||||
|
else:
|
||||||
|
render_auditor()
|
||||||
|
|||||||
290
regras_auditoria.md
Normal file
290
regras_auditoria.md
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
# REGRAS DE AUDITORIA — Google Ads Scorecard (GAS v6 — ENGINE AUDITOR HARD MATCH | V12.6 HEADLINE 30)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Versão HARD MATCH da auditoria compatível com ENGINE HARD LOCK (V11).
|
||||||
|
Eliminar falsos negativos de Keyword Match causados por delimitadores,
|
||||||
|
capitalização inicial ou pequenas diferenças estruturais.
|
||||||
|
|
||||||
|
Nenhuma regra anterior foi removida.
|
||||||
|
A auditoria agora valida igualdade SEMÂNTICA NORMALIZADA.
|
||||||
|
|
||||||
|
Nota final: 0 a 10 pontos (soma ponderada).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🆕 ATUALIZAÇÃO V12.6 — LIMITES DE CARACTERES
|
||||||
|
|
||||||
|
HEADLINES:
|
||||||
|
- limite máximo: 30 caracteres
|
||||||
|
|
||||||
|
DEMAIS ATIVOS:
|
||||||
|
- sitelinks, callouts e ativos auxiliares permanecem com limite 25 caracteres
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 0. ENGINE AUDITOR — FLUXO GLOBAL
|
||||||
|
|
||||||
|
INÍCIO
|
||||||
|
|
||||||
|
PASSO 1 → Normalizar Keywords
|
||||||
|
PASSO 2 → Normalizar Headlines (validar ≤30 caracteres)
|
||||||
|
PASSO 3 → Validar Keyword Match (Hard Match)
|
||||||
|
PASSO 4 → Validar Relevância Temática
|
||||||
|
PASSO 5 → Validar Compliance Editorial
|
||||||
|
PASSO 6 → Validar Aproveitamento de Espaço
|
||||||
|
PASSO 7 → Validar Extensões e Ativos
|
||||||
|
PASSO 8 → Calcular Nota Final
|
||||||
|
|
||||||
|
FIM
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔧 FUNÇÃO GLOBAL DE NORMALIZAÇÃO (HARD MATCH)
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(texto):
|
||||||
|
|
||||||
|
remover delimitadores "[" "]" e '"'
|
||||||
|
remover espaços duplicados
|
||||||
|
permitir Capitalização Inicial
|
||||||
|
converter apenas para comparação semântica
|
||||||
|
manter acentuação
|
||||||
|
NÃO alterar ordem das palavras
|
||||||
|
|
||||||
|
RETORNAR texto_normalizado
|
||||||
|
|
||||||
|
OBSERVAÇÃO:
|
||||||
|
Capitalização inicial é considerada equivalente.
|
||||||
|
|
||||||
|
Exemplo ACEITO:
|
||||||
|
|
||||||
|
keyword: [separação judicial]
|
||||||
|
headline: Separação Judicial
|
||||||
|
|
||||||
|
keyword: "advogado guarda"
|
||||||
|
headline: Advogado Guarda
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Keyword Match — Regra de Ferro HARD MATCH (Peso 4.0)
|
||||||
|
|
||||||
|
## ENGINE VALIDATION
|
||||||
|
|
||||||
|
PARA cada ad_group:
|
||||||
|
|
||||||
|
keywords_norm = normalizar(keywords)
|
||||||
|
headlines_norm = normalizar(headlines)
|
||||||
|
|
||||||
|
contar headlines onde:
|
||||||
|
|
||||||
|
lower(keywords_norm) == lower(headlines_norm)
|
||||||
|
|
||||||
|
SE total >= 5:
|
||||||
|
nota máxima
|
||||||
|
|
||||||
|
## CRITÉRIO ELIMINATÓRIO
|
||||||
|
|
||||||
|
Se headline adicionar palavra extra após normalização:
|
||||||
|
|
||||||
|
EXEMPLO NÃO ACEITO:
|
||||||
|
|
||||||
|
keyword: advogado guarda
|
||||||
|
headline: Advogado Guarda SP ❌
|
||||||
|
|
||||||
|
Se houver alteração semântica:
|
||||||
|
nota do grupo = 0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Relevância Temática e Isolamento (Peso 3.0)
|
||||||
|
|
||||||
|
ENGINE CHECK:
|
||||||
|
|
||||||
|
Identificar núcleo jurídico dominante por grupo.
|
||||||
|
|
||||||
|
NÚCLEOS EXCLUSIVOS:
|
||||||
|
- divórcio
|
||||||
|
- guarda
|
||||||
|
- pensão
|
||||||
|
- inventário
|
||||||
|
- sucessão
|
||||||
|
|
||||||
|
Palavras genéricas permitidas:
|
||||||
|
- advogado
|
||||||
|
- especialista
|
||||||
|
- atendimento
|
||||||
|
|
||||||
|
Penalidade:
|
||||||
|
-1.0 por contaminação temática.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Compliance Editorial (Peso 1.5)
|
||||||
|
|
||||||
|
ENGINE SCAN:
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
- grátis
|
||||||
|
- gratuito
|
||||||
|
- variações diretas
|
||||||
|
|
||||||
|
FORMATAÇÃO PROIBIDA:
|
||||||
|
- !!! ??? repetidos
|
||||||
|
- CAIXA ALTA total (exceto SP, OAB)
|
||||||
|
|
||||||
|
Se existir termo proibido:
|
||||||
|
nota = 0 automaticamente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Aproveitamento de Espaço — HARD RANGE (Peso 1.0)
|
||||||
|
|
||||||
|
VALIDAÇÃO HEADLINES (V12.6):
|
||||||
|
- Headlines devem possuir ≤30 caracteres.
|
||||||
|
- Se qualquer headline exceder 30 → penalidade automática.
|
||||||
|
|
||||||
|
ACEITO:
|
||||||
|
80 ≤ descrição ≤ 92 caracteres
|
||||||
|
|
||||||
|
IDEAL:
|
||||||
|
85–90 caracteres
|
||||||
|
|
||||||
|
Penalidade:
|
||||||
|
-0.2 por descrição abaixo de 80.
|
||||||
|
|
||||||
|
## HARD MATCH — ANTI-TRUNCAMENTO
|
||||||
|
|
||||||
|
Se descrição terminar com:
|
||||||
|
|
||||||
|
- palavra incompleta
|
||||||
|
- corte de substring
|
||||||
|
- espaço final inválido
|
||||||
|
|
||||||
|
→ falha automática.
|
||||||
|
|
||||||
|
Exemplo NÃO ACEITO:
|
||||||
|
"Consulte um espe"
|
||||||
|
"Fale conosco ag"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Extensões e Ativos (Peso 0.5)
|
||||||
|
|
||||||
|
ENGINE CHECK:
|
||||||
|
|
||||||
|
SITELINKS:
|
||||||
|
- description1 preenchido
|
||||||
|
- description2 preenchido
|
||||||
|
|
||||||
|
CALLOUTS:
|
||||||
|
- mínimo 6
|
||||||
|
|
||||||
|
🆕 HARD MATCH BONUS:
|
||||||
|
Presença de 7º callout diferencial reforça nota máxima.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🧮 CÁLCULO DA NOTA FINAL
|
||||||
|
|
||||||
|
nota_final = soma ponderada:
|
||||||
|
|
||||||
|
Keyword Match (até 4.0)
|
||||||
|
+
|
||||||
|
Relevância Temática (até 3.0)
|
||||||
|
+
|
||||||
|
Compliance Editorial (até 1.5)
|
||||||
|
+
|
||||||
|
Aproveitamento de Espaço (até 1.0)
|
||||||
|
+
|
||||||
|
Extensões e Ativos (até 0.5)
|
||||||
|
|
||||||
|
TOTAL = 10 pontos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📊 INSTRUÇÕES DE EXECUÇÃO
|
||||||
|
|
||||||
|
1. Auditoria por Grupo:
|
||||||
|
Avaliar cada ad_group individualmente antes da consolidação.
|
||||||
|
|
||||||
|
2. Indicação de Falhas:
|
||||||
|
Sempre indicar qual grupo apresentou problema.
|
||||||
|
|
||||||
|
3. Simulação Visual:
|
||||||
|
Gerar 1 simulação de anúncio por grupo.
|
||||||
|
|
||||||
|
4. HARD MATCH OBRIGATÓRIO:
|
||||||
|
Toda comparação deve usar normalizar(texto).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📦 FORMATO DE RETORNO (JSON)
|
||||||
|
|
||||||
|
{
|
||||||
|
"nota_final": 0.0,
|
||||||
|
"criterios": [
|
||||||
|
{
|
||||||
|
"nome": "Keyword Match (Regra de Ferro)",
|
||||||
|
"peso": 4.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 4.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Relevância Temática",
|
||||||
|
"peso": 3.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 3.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Compliance Editorial",
|
||||||
|
"peso": 1.5,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 1.5,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Aproveitamento de Espaço",
|
||||||
|
"peso": 1.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 1.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Extensões e Ativos",
|
||||||
|
"peso": 0.5,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 0.5,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resumo": "",
|
||||||
|
"simulacoes_anuncio": [
|
||||||
|
{
|
||||||
|
"grupo": "",
|
||||||
|
"titulo_linha_1": "",
|
||||||
|
"titulo_linha_2": "",
|
||||||
|
"titulo_linha_3": "",
|
||||||
|
"url_display": "",
|
||||||
|
"descricao": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔎 DIFERENÇAS DO GAS v5 → GAS v6
|
||||||
|
|
||||||
|
✔ HARD MATCH real (comparação normalizada)
|
||||||
|
✔ Capitalização inicial aceita oficialmente
|
||||||
|
✔ Delimitadores [ ] e " ignorados
|
||||||
|
✔ Anti-truncamento reforçado
|
||||||
|
✔ Range técnico consolidado (80–92)
|
||||||
|
|
||||||
|
Resultado esperado:
|
||||||
|
Keyword Match tende a 4.0 quando ENGINE HARD LOCK estiver correto.
|
||||||
290
regras_auditoriaVGAS6.md
Normal file
290
regras_auditoriaVGAS6.md
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
# REGRAS DE AUDITORIA — Google Ads Scorecard (GAS v6 — ENGINE AUDITOR HARD MATCH | V12.6 HEADLINE 30)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Versão HARD MATCH da auditoria compatível com ENGINE HARD LOCK (V11).
|
||||||
|
Eliminar falsos negativos de Keyword Match causados por delimitadores,
|
||||||
|
capitalização inicial ou pequenas diferenças estruturais.
|
||||||
|
|
||||||
|
Nenhuma regra anterior foi removida.
|
||||||
|
A auditoria agora valida igualdade SEMÂNTICA NORMALIZADA.
|
||||||
|
|
||||||
|
Nota final: 0 a 10 pontos (soma ponderada).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🆕 ATUALIZAÇÃO V12.6 — LIMITES DE CARACTERES
|
||||||
|
|
||||||
|
HEADLINES:
|
||||||
|
- limite máximo: 30 caracteres
|
||||||
|
|
||||||
|
DEMAIS ATIVOS:
|
||||||
|
- sitelinks, callouts e ativos auxiliares permanecem com limite 25 caracteres
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 0. ENGINE AUDITOR — FLUXO GLOBAL
|
||||||
|
|
||||||
|
INÍCIO
|
||||||
|
|
||||||
|
PASSO 1 → Normalizar Keywords
|
||||||
|
PASSO 2 → Normalizar Headlines (validar ≤30 caracteres)
|
||||||
|
PASSO 3 → Validar Keyword Match (Hard Match)
|
||||||
|
PASSO 4 → Validar Relevância Temática
|
||||||
|
PASSO 5 → Validar Compliance Editorial
|
||||||
|
PASSO 6 → Validar Aproveitamento de Espaço
|
||||||
|
PASSO 7 → Validar Extensões e Ativos
|
||||||
|
PASSO 8 → Calcular Nota Final
|
||||||
|
|
||||||
|
FIM
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔧 FUNÇÃO GLOBAL DE NORMALIZAÇÃO (HARD MATCH)
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(texto):
|
||||||
|
|
||||||
|
remover delimitadores "[" "]" e '"'
|
||||||
|
remover espaços duplicados
|
||||||
|
permitir Capitalização Inicial
|
||||||
|
converter apenas para comparação semântica
|
||||||
|
manter acentuação
|
||||||
|
NÃO alterar ordem das palavras
|
||||||
|
|
||||||
|
RETORNAR texto_normalizado
|
||||||
|
|
||||||
|
OBSERVAÇÃO:
|
||||||
|
Capitalização inicial é considerada equivalente.
|
||||||
|
|
||||||
|
Exemplo ACEITO:
|
||||||
|
|
||||||
|
keyword: [separação judicial]
|
||||||
|
headline: Separação Judicial
|
||||||
|
|
||||||
|
keyword: "advogado guarda"
|
||||||
|
headline: Advogado Guarda
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. Keyword Match — Regra de Ferro HARD MATCH (Peso 4.0)
|
||||||
|
|
||||||
|
## ENGINE VALIDATION
|
||||||
|
|
||||||
|
PARA cada ad_group:
|
||||||
|
|
||||||
|
keywords_norm = normalizar(keywords)
|
||||||
|
headlines_norm = normalizar(headlines)
|
||||||
|
|
||||||
|
contar headlines onde:
|
||||||
|
|
||||||
|
lower(keywords_norm) == lower(headlines_norm)
|
||||||
|
|
||||||
|
SE total >= 5:
|
||||||
|
nota máxima
|
||||||
|
|
||||||
|
## CRITÉRIO ELIMINATÓRIO
|
||||||
|
|
||||||
|
Se headline adicionar palavra extra após normalização:
|
||||||
|
|
||||||
|
EXEMPLO NÃO ACEITO:
|
||||||
|
|
||||||
|
keyword: advogado guarda
|
||||||
|
headline: Advogado Guarda SP ❌
|
||||||
|
|
||||||
|
Se houver alteração semântica:
|
||||||
|
nota do grupo = 0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. Relevância Temática e Isolamento (Peso 3.0)
|
||||||
|
|
||||||
|
ENGINE CHECK:
|
||||||
|
|
||||||
|
Identificar núcleo jurídico dominante por grupo.
|
||||||
|
|
||||||
|
NÚCLEOS EXCLUSIVOS:
|
||||||
|
- divórcio
|
||||||
|
- guarda
|
||||||
|
- pensão
|
||||||
|
- inventário
|
||||||
|
- sucessão
|
||||||
|
|
||||||
|
Palavras genéricas permitidas:
|
||||||
|
- advogado
|
||||||
|
- especialista
|
||||||
|
- atendimento
|
||||||
|
|
||||||
|
Penalidade:
|
||||||
|
-1.0 por contaminação temática.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. Compliance Editorial (Peso 1.5)
|
||||||
|
|
||||||
|
ENGINE SCAN:
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
- grátis
|
||||||
|
- gratuito
|
||||||
|
- variações diretas
|
||||||
|
|
||||||
|
FORMATAÇÃO PROIBIDA:
|
||||||
|
- !!! ??? repetidos
|
||||||
|
- CAIXA ALTA total (exceto SP, OAB)
|
||||||
|
|
||||||
|
Se existir termo proibido:
|
||||||
|
nota = 0 automaticamente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. Aproveitamento de Espaço — HARD RANGE (Peso 1.0)
|
||||||
|
|
||||||
|
VALIDAÇÃO HEADLINES (V12.6):
|
||||||
|
- Headlines devem possuir ≤30 caracteres.
|
||||||
|
- Se qualquer headline exceder 30 → penalidade automática.
|
||||||
|
|
||||||
|
ACEITO:
|
||||||
|
80 ≤ descrição ≤ 92 caracteres
|
||||||
|
|
||||||
|
IDEAL:
|
||||||
|
85–90 caracteres
|
||||||
|
|
||||||
|
Penalidade:
|
||||||
|
-0.2 por descrição abaixo de 80.
|
||||||
|
|
||||||
|
## HARD MATCH — ANTI-TRUNCAMENTO
|
||||||
|
|
||||||
|
Se descrição terminar com:
|
||||||
|
|
||||||
|
- palavra incompleta
|
||||||
|
- corte de substring
|
||||||
|
- espaço final inválido
|
||||||
|
|
||||||
|
→ falha automática.
|
||||||
|
|
||||||
|
Exemplo NÃO ACEITO:
|
||||||
|
"Consulte um espe"
|
||||||
|
"Fale conosco ag"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. Extensões e Ativos (Peso 0.5)
|
||||||
|
|
||||||
|
ENGINE CHECK:
|
||||||
|
|
||||||
|
SITELINKS:
|
||||||
|
- description1 preenchido
|
||||||
|
- description2 preenchido
|
||||||
|
|
||||||
|
CALLOUTS:
|
||||||
|
- mínimo 6
|
||||||
|
|
||||||
|
🆕 HARD MATCH BONUS:
|
||||||
|
Presença de 7º callout diferencial reforça nota máxima.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🧮 CÁLCULO DA NOTA FINAL
|
||||||
|
|
||||||
|
nota_final = soma ponderada:
|
||||||
|
|
||||||
|
Keyword Match (até 4.0)
|
||||||
|
+
|
||||||
|
Relevância Temática (até 3.0)
|
||||||
|
+
|
||||||
|
Compliance Editorial (até 1.5)
|
||||||
|
+
|
||||||
|
Aproveitamento de Espaço (até 1.0)
|
||||||
|
+
|
||||||
|
Extensões e Ativos (até 0.5)
|
||||||
|
|
||||||
|
TOTAL = 10 pontos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📊 INSTRUÇÕES DE EXECUÇÃO
|
||||||
|
|
||||||
|
1. Auditoria por Grupo:
|
||||||
|
Avaliar cada ad_group individualmente antes da consolidação.
|
||||||
|
|
||||||
|
2. Indicação de Falhas:
|
||||||
|
Sempre indicar qual grupo apresentou problema.
|
||||||
|
|
||||||
|
3. Simulação Visual:
|
||||||
|
Gerar 1 simulação de anúncio por grupo.
|
||||||
|
|
||||||
|
4. HARD MATCH OBRIGATÓRIO:
|
||||||
|
Toda comparação deve usar normalizar(texto).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 📦 FORMATO DE RETORNO (JSON)
|
||||||
|
|
||||||
|
{
|
||||||
|
"nota_final": 0.0,
|
||||||
|
"criterios": [
|
||||||
|
{
|
||||||
|
"nome": "Keyword Match (Regra de Ferro)",
|
||||||
|
"peso": 4.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 4.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Relevância Temática",
|
||||||
|
"peso": 3.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 3.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Compliance Editorial",
|
||||||
|
"peso": 1.5,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 1.5,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Aproveitamento de Espaço",
|
||||||
|
"peso": 1.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 1.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Extensões e Ativos",
|
||||||
|
"peso": 0.5,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 0.5,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resumo": "",
|
||||||
|
"simulacoes_anuncio": [
|
||||||
|
{
|
||||||
|
"grupo": "",
|
||||||
|
"titulo_linha_1": "",
|
||||||
|
"titulo_linha_2": "",
|
||||||
|
"titulo_linha_3": "",
|
||||||
|
"url_display": "",
|
||||||
|
"descricao": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🔎 DIFERENÇAS DO GAS v5 → GAS v6
|
||||||
|
|
||||||
|
✔ HARD MATCH real (comparação normalizada)
|
||||||
|
✔ Capitalização inicial aceita oficialmente
|
||||||
|
✔ Delimitadores [ ] e " ignorados
|
||||||
|
✔ Anti-truncamento reforçado
|
||||||
|
✔ Range técnico consolidado (80–92)
|
||||||
|
|
||||||
|
Resultado esperado:
|
||||||
|
Keyword Match tende a 4.0 quando ENGINE HARD LOCK estiver correto.
|
||||||
106
regras_auditoria_old-boa.md
Normal file
106
regras_auditoria_old-boa.md
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
Aqui está o documento de **REGRAS DE AUDITORIA (GAS v3)** atualizado, integrando o rigor da **Regra de Ferro** para palavras-chave, o novo critério de **Relevância Temática** e a redistribuição de pesos para totalizar 10 pontos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# REGRAS DE AUDITORIA — Google Ads Scorecard (GAS v3)
|
||||||
|
|
||||||
|
Avalie cada critério abaixo considerando TODOS os ad_groups. A nota final (0 a 10) é a soma ponderada dos pesos abaixo:
|
||||||
|
|
||||||
|
## 1. Keyword Match - Regra de Ferro (Peso 4.0 — máx 4.0 pontos)
|
||||||
|
|
||||||
|
* Em cada grupo, pelo menos 5 títulos (headlines) devem ser **IDÊNTICOS** às keywords de fundo de funil do grupo.
|
||||||
|
* **CRITÉRIO ELIMINATÓRIO:** Proibido adicionar qualquer palavra, hífen, ou adjetivo antes ou depois da keyword nesses 5 títulos.
|
||||||
|
* **Penalidade:** Se um título "enfeitar" a keyword (ex: mudou "advogado guarda" para "advogado guarda em SP"), a nota deste critério para o grupo será **ZERO**.
|
||||||
|
|
||||||
|
## 2. Relevância Temática e Isolamento (Peso 3.0 — máx 3.0 pontos)
|
||||||
|
|
||||||
|
* **Isolamento de Tema:** Avalie se os 3 grupos possuem temas realmente distintos. Keywords e títulos de um AdGroup não podem "contaminar" outros grupos (ex: termo "inventário" dentro do grupo de "divórcio").
|
||||||
|
* **Penalidade:** -1.0 para cada ocorrência de mistura de temas entre grupos.
|
||||||
|
|
||||||
|
## 3. Compliance Editorial (Peso 1.5 — máx 1.5 pontos)
|
||||||
|
|
||||||
|
* Ausência total de termos proibidos: "grátis", "gratuito", sinônimos (em todos os grupos).
|
||||||
|
* Sem formatação proibida: excesso de pontuação (!!!, ???), CAIXA ALTA indevida (exceto siglas como SP, OAB).
|
||||||
|
* **Penalidade:** Se houver a palavra "grátis" ou "gratuito" em qualquer grupo, a nota deste critério é **0 automaticamente**.
|
||||||
|
|
||||||
|
## 4. Aproveitamento de Espaço (Peso 1.0 — máx 1.0 ponto)
|
||||||
|
|
||||||
|
* Descrições devem ter entre 80 e 90 caracteres. Títulos próximos de 30 caracteres.
|
||||||
|
* **Penalidade:** -0.2 para cada descrição abaixo de 80 caracteres que desperdiça espaço visual.
|
||||||
|
|
||||||
|
## 5. Extensões e Ativos (Peso 0.5 — máx 0.5 ponto)
|
||||||
|
|
||||||
|
* Sitelinks devem ter descrições preenchidas (description1 e description2).
|
||||||
|
* Callouts devem ser focados em benefícios reais, autoridade e agilidade.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## INSTRUÇÕES DE EXECUÇÃO
|
||||||
|
|
||||||
|
1. **Auditoria por Grupo:** A IA deve analisar cada `ad_group` individualmente antes de consolidar a nota.
|
||||||
|
2. **Indicação de Falhas:** Nas falhas, SEMPRE indique em qual grupo o problema foi encontrado (ex: "Grupo 'Divórcio': Título 3 alterou keyword...").
|
||||||
|
3. **Simulação:** Gere uma **Simulação de Anúncio** para CADA grupo, mostrando a composição visual final.
|
||||||
|
|
||||||
|
## FORMATO DE RETORNO (JSON)
|
||||||
|
|
||||||
|
Retorne o resultado EXATAMENTE neste formato JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nota_final": 0.0,
|
||||||
|
"criterios": [
|
||||||
|
{
|
||||||
|
"nome": "Keyword Match (Regra de Ferro)",
|
||||||
|
"peso": 4.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 4.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Relevância Temática",
|
||||||
|
"peso": 3.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 3.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Compliance Editorial",
|
||||||
|
"peso": 1.5,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 1.5,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Aproveitamento de Espaço",
|
||||||
|
"peso": 1.0,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 1.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Extensões e Ativos",
|
||||||
|
"peso": 0.5,
|
||||||
|
"nota": 0.0,
|
||||||
|
"nota_maxima": 0.5,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resumo": "",
|
||||||
|
"simulacoes_anuncio": [
|
||||||
|
{
|
||||||
|
"grupo": "",
|
||||||
|
"titulo_linha_1": "",
|
||||||
|
"titulo_linha_2": "",
|
||||||
|
"titulo_linha_3": "",
|
||||||
|
"url_display": "",
|
||||||
|
"descricao": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
115
regras_auditoria_old.md
Normal file
115
regras_auditoria_old.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# REGRAS DE AUDITORIA — Google Ads Scorecard (GAS)
|
||||||
|
|
||||||
|
Avalie cada critério abaixo considerando TODOS os ad_groups. A nota final (0 a 10) é a soma ponderada:
|
||||||
|
|
||||||
|
## 1. Relevância e Keyword Match (Peso 3.0 — máx 3.0 pontos)
|
||||||
|
- Em cada grupo, pelo menos 5 títulos (headlines) devem ser idênticos às keywords de fundo de funil do grupo.
|
||||||
|
- Penalidade: -0.5 para cada título que "enfeitou" a palavra-chave (ex: mudou "advogado guarda" para "melhor advogado de guarda").
|
||||||
|
- Avalie se os 3 grupos possuem temas realmente distintos entre si.
|
||||||
|
|
||||||
|
## 2. Compliance Editorial (Peso 2.0 — máx 2.0 pontos)
|
||||||
|
- Ausência total de termos proibidos: "grátis", "gratuito", sinônimos (em todos os grupos).
|
||||||
|
- Sem formatação proibida: excesso de pontuação (!!!, ???), CAIXA ALTA indevida (exceto siglas como SP, OAB).
|
||||||
|
- Se houver a palavra "grátis" ou "gratuito" em qualquer grupo, a nota deste critério é 0 automaticamente.
|
||||||
|
|
||||||
|
## 3. Aproveitamento de Espaço (Peso 2.0 — máx 2.0 pontos)
|
||||||
|
- Descrições devem ter entre 80 e 90 caracteres. Títulos próximos de 30 caracteres.
|
||||||
|
- Penalidade: -0.5 para cada descrição muito curta (abaixo de 70 chars) que desperdiça espaço.
|
||||||
|
- Aplique a todos os grupos.
|
||||||
|
|
||||||
|
## 4. Variedade de Gatilhos (Peso 2.0 — máx 2.0 pontos)
|
||||||
|
- Deve haver 3 pilares nos títulos: Ação (CTA), Autoridade (anos de exp, nota 5.0) e Localização (cidade/estado).
|
||||||
|
- Penalidade: -1.0 se todos os títulos de um grupo forem apenas descritivos, sem convidar ao clique.
|
||||||
|
|
||||||
|
## 5. Extensões e Ativos (Peso 1.0 — máx 1.0 ponto)
|
||||||
|
- Sitelinks devem ter descrições preenchidas (description1 e description2).
|
||||||
|
- Callouts devem ser focados em benefícios reais, não genéricos.
|
||||||
|
|
||||||
|
## 7. SCORECARD DE AUDITORIA POR GRUPO (GAS v2)
|
||||||
|
A IA deve auto-avaliar o JSON final de 0 a 10 com base nos pesos abaixo, aplicados **individualmente a cada ad_group**:
|
||||||
|
|
||||||
|
- **Relevância Temática (3.5):** As keywords e títulos combinam com o tema do grupo?
|
||||||
|
- **Coerência de AdGroup (2.5):** Os títulos de keyword são específicos para aquele grupo?
|
||||||
|
- **Espaço e Compliance (2.0):** Descrições > 80ch e zero termos proibidos?
|
||||||
|
- **Estrutura de Ativos (2.0):** Sitelinks e Callouts estão completos?
|
||||||
|
|
||||||
|
## INSTRUÇÕES ADICIONAIS
|
||||||
|
Nas falhas, SEMPRE indique em qual grupo (ad_group) o problema foi encontrado (ex: "Grupo 'Divórcio': Título 3 alterou keyword...").
|
||||||
|
|
||||||
|
Além da avaliação, gere uma **Simulação de Anúncio** para CADA grupo, mostrando como os melhores títulos e a melhor descrição apareceriam em uma busca real no Google.
|
||||||
|
|
||||||
|
## FORMATO DE RETORNO (JSON)
|
||||||
|
Retorne o resultado EXATAMENTE neste formato JSON:
|
||||||
|
|
||||||
|
{
|
||||||
|
"nota_final": 8.5,
|
||||||
|
"criterios": [
|
||||||
|
{
|
||||||
|
"nome": "Relevância e Keyword Match",
|
||||||
|
"peso": 3.0,
|
||||||
|
"nota": 2.5,
|
||||||
|
"nota_maxima": 3.0,
|
||||||
|
"falhas": ["Grupo 'Divórcio': Título 3 alterou keyword 'advogado divórcio' para 'melhor advogado divórcio'"],
|
||||||
|
"sugestoes": ["Usar keyword exata sem modificadores nos primeiros 5 títulos de cada grupo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Compliance Editorial",
|
||||||
|
"peso": 2.0,
|
||||||
|
"nota": 2.0,
|
||||||
|
"nota_maxima": 2.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Aproveitamento de Espaço",
|
||||||
|
"peso": 2.0,
|
||||||
|
"nota": 1.5,
|
||||||
|
"nota_maxima": 2.0,
|
||||||
|
"falhas": ["Grupo 'Guarda': Descrição 2 tem apenas 45 caracteres"],
|
||||||
|
"sugestoes": ["Expandir descrições curtas para aproveitar 80-90 caracteres"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Variedade de Gatilhos",
|
||||||
|
"peso": 2.0,
|
||||||
|
"nota": 1.5,
|
||||||
|
"nota_maxima": 2.0,
|
||||||
|
"falhas": ["Grupo 'Pensão': Nenhum título contém localização"],
|
||||||
|
"sugestoes": ["Adicionar cidade/estado em pelo menos 2 títulos por grupo"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"nome": "Extensões e Ativos",
|
||||||
|
"peso": 1.0,
|
||||||
|
"nota": 1.0,
|
||||||
|
"nota_maxima": 1.0,
|
||||||
|
"falhas": [],
|
||||||
|
"sugestoes": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resumo": "Campanha multi-grupo com boa segmentação temática, mas precisa ajustar keyword match no Grupo 1 e aproveitar melhor o espaço das descrições no Grupo 2.",
|
||||||
|
"simulacoes_anuncio": [
|
||||||
|
{
|
||||||
|
"grupo": "Nome do Grupo 1",
|
||||||
|
"titulo_linha_1": "Advogado Divórcio SP",
|
||||||
|
"titulo_linha_2": "Consulta Especializada",
|
||||||
|
"titulo_linha_3": "+10 Anos de Experiência",
|
||||||
|
"url_display": "www.exemplo.com.br",
|
||||||
|
"descricao": "Advogado especialista em divórcio com atendimento personalizado. Agende sua consulta!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"grupo": "Nome do Grupo 2",
|
||||||
|
"titulo_linha_1": "Advogado Guarda",
|
||||||
|
"titulo_linha_2": "Fale Conosco Agora",
|
||||||
|
"titulo_linha_3": "Avaliação 5.0 no Google",
|
||||||
|
"url_display": "www.exemplo.com.br",
|
||||||
|
"descricao": "Especialista em guarda de filhos. Proteja seus direitos com quem entende do assunto!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"grupo": "Nome do Grupo 3",
|
||||||
|
"titulo_linha_1": "Pensão Alimentícia",
|
||||||
|
"titulo_linha_2": "Agende sua Consulta Hoje",
|
||||||
|
"titulo_linha_3": "Atendimento 100% Online",
|
||||||
|
"url_display": "www.exemplo.com.br",
|
||||||
|
"descricao": "Advogado para pensão alimentícia. Resolva seu caso com agilidade e segurança jurídica!"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
322
regras_prompt-V12.4.md
Normal file
322
regras_prompt-V12.4.md
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V12.4 — HARD MATCH + DKI SAFE MODE)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Engine determinístico com canonização automática de keywords, espelhamento obrigatório de keywords em headlines
|
||||||
|
(HARD MATCH 7/7) + 01 título com DKI em modo seguro (SEM substituir espelhos),
|
||||||
|
e regeneração obrigatória de descrições (SEM substring).
|
||||||
|
|
||||||
|
Eliminar:
|
||||||
|
- falhas de Keyword Match por ausência de headline espelho exato
|
||||||
|
- truncamento por substring (descrições e callouts)
|
||||||
|
- inconsistência entre ENGINE e AUDITORIA
|
||||||
|
- riscos editoriais/limite de caracteres introduzidos por DKI
|
||||||
|
|
||||||
|
Nenhuma regra anterior foi removida — apenas reforçada.
|
||||||
|
Capitalização inicial permitida.
|
||||||
|
Compatível com GAS v6 — ENGINE AUDITOR HARD MATCH.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 0. FLUXO GLOBAL (DETERMINISTIC EXECUTION)
|
||||||
|
|
||||||
|
INÍCIO
|
||||||
|
|
||||||
|
PARA cada GRUPO_DE_ANUNCIO (total = 3):
|
||||||
|
|
||||||
|
PASSO 1 → Gerar Keywords
|
||||||
|
PASSO 1.1 → SANITIZAR E CANONIZAR KEYWORDS
|
||||||
|
PASSO 2 → Normalizar Keywords
|
||||||
|
PASSO 3 → Classificar Keywords por PRIORIDADE
|
||||||
|
PASSO 4 → Selecionar Literais (ALGORITMO FIXO)
|
||||||
|
PASSO 4.1 → Gerar HEADLINES ESPELHO (1 por keyword)
|
||||||
|
PASSO 4.2 → Gerar 01 HEADLINE DKI (SAFE MODE)
|
||||||
|
PASSO 5 → Montar LISTA FINAL DE TÍTULOS (15) com MIRROR ENFORCEMENT + DKI SAFE
|
||||||
|
PASSO 6 → Gerar Descrições (REGENERAÇÃO OBRIGATÓRIA / SEM CORTE)
|
||||||
|
PASSO 7 → Validar Limites
|
||||||
|
PASSO 8 → Inserir Localização (fora dos espelhos)
|
||||||
|
|
||||||
|
FIM
|
||||||
|
|
||||||
|
Depois:
|
||||||
|
PASSO 9 → Negativas
|
||||||
|
PASSO 10 → Extensões
|
||||||
|
PASSO 11 → HARD LOCK VALIDATION
|
||||||
|
|
||||||
|
RETORNAR apenas JSON estruturado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. PALAVRAS-CHAVE (ENGINE STEP)
|
||||||
|
|
||||||
|
GERAR exatamente 7 keywords por grupo.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
- usar [Exata] e "Frase"
|
||||||
|
- máximo 25 caracteres
|
||||||
|
- keywords devem ser juridicamente tituláveis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SANITIZAÇÃO E CANONIZAÇÃO (OBRIGATÓRIA)
|
||||||
|
|
||||||
|
ANTES de aceitar qualquer keyword:
|
||||||
|
|
||||||
|
FUNÇÃO canonizar(keyword):
|
||||||
|
|
||||||
|
remover duplicações de delimitadores:
|
||||||
|
[[texto]] → texto
|
||||||
|
|
||||||
|
converter para forma jurídica clara e titulável.
|
||||||
|
|
||||||
|
Exemplos obrigatórios:
|
||||||
|
casamento fim → Fim do Casamento
|
||||||
|
fim do casamento → Fim do Casamento
|
||||||
|
fim pensão → Fim da Pensão
|
||||||
|
fim da pensão → Fim da Pensão
|
||||||
|
custo inventário → Custo Inventário
|
||||||
|
|
||||||
|
SE keyword for vaga, truncada ou não titulável:
|
||||||
|
DESCARTAR e gerar nova.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ENGINE NORMALIZE
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(texto):
|
||||||
|
|
||||||
|
remover "[" "]" e '"'
|
||||||
|
remover espaços duplicados
|
||||||
|
aplicar Capitalização Inicial
|
||||||
|
manter acentuação
|
||||||
|
manter ordem
|
||||||
|
NÃO alterar semântica
|
||||||
|
|
||||||
|
RETORNAR texto_normalizado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. CLASSIFICAÇÃO DETERMINÍSTICA DE KEYWORDS
|
||||||
|
|
||||||
|
PRIORIDADE 1 — COMERCIAL DIRETA
|
||||||
|
- valor
|
||||||
|
- custo
|
||||||
|
- preço
|
||||||
|
- regularização
|
||||||
|
- revisão
|
||||||
|
- cálculo
|
||||||
|
- quanto custa
|
||||||
|
- honorários
|
||||||
|
|
||||||
|
PRIORIDADE 2 — CONTRATAÇÃO
|
||||||
|
- advogado
|
||||||
|
- ação
|
||||||
|
- processo
|
||||||
|
- consulta
|
||||||
|
|
||||||
|
PRIORIDADE 3 — INFORMATIVA
|
||||||
|
demais termos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. SELEÇÃO LITERAL DETERMINÍSTICA
|
||||||
|
|
||||||
|
FUNÇÃO selecionar_keywords_literais(lista_keywords):
|
||||||
|
|
||||||
|
ordenar por:
|
||||||
|
prioridade ASC
|
||||||
|
tamanho ASC
|
||||||
|
ordem original
|
||||||
|
|
||||||
|
selecionar até 5.
|
||||||
|
|
||||||
|
❗ Seleção automática — IA não escolhe manualmente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. KEYWORD MIRROR HEADLINES — HARD MATCH (ENFORCEMENT)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Garantir que TODAS as 7 keywords do grupo tenham ao menos 1 headline correspondente EXATO após normalização.
|
||||||
|
Se a keyword existir, o headline espelho correspondente DEVE existir no output final.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
|
||||||
|
1) Para cada keyword do grupo (7):
|
||||||
|
headline_espelho = normalizar(canonizar(keyword))
|
||||||
|
|
||||||
|
2) O headline_espelho deve ser EXATO (após normalização).
|
||||||
|
PROIBIDO:
|
||||||
|
- adicionar palavras (ex: "SP", "Rápido", "Seguro")
|
||||||
|
- remover palavras
|
||||||
|
- trocar ordem
|
||||||
|
- criar sinônimos
|
||||||
|
- plural/singular diferente
|
||||||
|
|
||||||
|
3) PROIBIÇÃO ABSOLUTA — "SP" NO ESPELHO
|
||||||
|
Se a keyword NÃO contém "SP" ou "São Paulo",
|
||||||
|
o headline_espelho NÃO pode conter "SP" ou "São Paulo".
|
||||||
|
|
||||||
|
4) MIRROR ENFORCEMENT (REGRA DE MONTAGEM):
|
||||||
|
A lista final de 15 títulos DO GRUPO deve conter, como itens separados,
|
||||||
|
TODOS os 7 headlines_espelho. Nenhum espelho pode ser omitido, substituído ou reescrito.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4.2 HEADLINE DKI — SAFE MODE (NOVO)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Adicionar 01 headline com Dynamic Keyword Insertion para aumentar relevância/CTR em cauda longa,
|
||||||
|
SEM derrubar o HARD MATCH (espelhos continuam obrigatórios).
|
||||||
|
|
||||||
|
FORMATO DKI (OBRIGATÓRIO):
|
||||||
|
{Keyword:PADRAO_DO_GRUPO}
|
||||||
|
|
||||||
|
REGRAS SAFE:
|
||||||
|
|
||||||
|
1) O headline DKI NUNCA substitui nenhum headline_espelho.
|
||||||
|
Os 7 espelhos continuam obrigatórios dentro dos 15 títulos.
|
||||||
|
|
||||||
|
2) O headline DKI NÃO PODE entrar no Bucket de LITERAIS.
|
||||||
|
Ele entra apenas como título "livre" dentro do Bucket CTA/Local.
|
||||||
|
|
||||||
|
3) PADRAO_DO_GRUPO deve ser curto, titulável e ≤ 30 caracteres.
|
||||||
|
Exemplos recomendados (escolher 1):
|
||||||
|
- Advogado de Família
|
||||||
|
- Divórcio em SP
|
||||||
|
- Guarda e Pensão
|
||||||
|
- Inventário em SP
|
||||||
|
|
||||||
|
4) PROIBIDO no DKI:
|
||||||
|
- termos proibidos ("Grátis", "Gratuito" e variações)
|
||||||
|
- palavras cortadas
|
||||||
|
- caixa alta total
|
||||||
|
- pontuação excessiva
|
||||||
|
|
||||||
|
5) Se houver risco de estourar 30 caracteres (por inserção dinâmica),
|
||||||
|
o PADRAO_DO_GRUPO deve ser ainda mais curto (≤ 20 caracteres).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. TÍTULOS — MONTAGEM DETERMINÍSTICA (15 POR GRUPO)
|
||||||
|
|
||||||
|
Distribuição original mantida: 05 / 05 / 05 (LITERAL / CTA-LOCAL / PROVA).
|
||||||
|
|
||||||
|
REGRA DETERMINÍSTICA DE POSIÇÃO:
|
||||||
|
|
||||||
|
- Bucket 1 (05 LITERAIS): usar os 5 primeiros headlines_espelho (pela ordem de prioridade).
|
||||||
|
- Bucket 2 (05 CTA/LOCAL): 3 CTAs/Local reais + 1 headline_espelho (o 6º) + 1 headline DKI (SAFE MODE).
|
||||||
|
- Bucket 3 (05 PROVA): 4 provas/diferenciais reais + 1 headline_espelho (o 7º).
|
||||||
|
|
||||||
|
HARD LOCK:
|
||||||
|
- os 7 headlines_espelho entram exatamente como gerados (sem SP extra).
|
||||||
|
- CTAs/Provas podem conter SP/São Paulo quando possível.
|
||||||
|
- o DKI entra como adicional controlado, sem substituir espelhos.
|
||||||
|
|
||||||
|
LIMITES:
|
||||||
|
- cada título ≤ 30 caracteres
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. DESCRIÇÕES — DETERMINISTIC TEXT ENGINE (NO-TRUNC OUTPUT)
|
||||||
|
|
||||||
|
RANGE:
|
||||||
|
80 ≤ caracteres ≤ 92
|
||||||
|
IDEAL: 85–90
|
||||||
|
|
||||||
|
ORDEM DE EXECUÇÃO OBRIGATÓRIA:
|
||||||
|
1) GERAR descrição completa
|
||||||
|
2) VALIDAR tamanho e integridade
|
||||||
|
3) SE inválida → REGERAR DO ZERO
|
||||||
|
4) PROIBIDO ajustar por corte
|
||||||
|
|
||||||
|
REGRA ABSOLUTA — ANTI SUBSTRING:
|
||||||
|
NUNCA usar:
|
||||||
|
- texto[:limite]
|
||||||
|
- slice
|
||||||
|
- substring
|
||||||
|
- corte parcial
|
||||||
|
|
||||||
|
VALIDAÇÃO DE INTEGRIDADE (OBRIGATÓRIA):
|
||||||
|
Uma descrição SÓ É VÁLIDA se:
|
||||||
|
- 80 ≤ caracteres ≤ 92
|
||||||
|
- termina com "." ou "!"
|
||||||
|
- NÃO termina com espaço
|
||||||
|
- NÃO termina com palavra incompleta
|
||||||
|
|
||||||
|
SE QUALQUER ITEM FALHAR:
|
||||||
|
REGERAR do zero (reescrita completa).
|
||||||
|
|
||||||
|
AJUSTE DETERMINÍSTICO:
|
||||||
|
- Se > 92: reescrever reduzindo conectivos e adjetivos (sem cortar string).
|
||||||
|
- Se < 80: reescrever adicionando UVP ou CTA (sem inflar demais).
|
||||||
|
|
||||||
|
CONTEÚDO:
|
||||||
|
- incluir UVP + CTA
|
||||||
|
- incluir SP/São Paulo quando possível (sem quebrar o range)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. EXTENSÕES (ATIVOS) — HARD LOCK
|
||||||
|
|
||||||
|
SITELINKS:
|
||||||
|
- 4 obrigatórios
|
||||||
|
- título ≤25 caracteres
|
||||||
|
- 2 descrições ≤35 caracteres
|
||||||
|
|
||||||
|
CALLOUTS:
|
||||||
|
- 7 obrigatórios
|
||||||
|
- ≤25 caracteres
|
||||||
|
- incluir 01 diferencial competitivo
|
||||||
|
|
||||||
|
ANTI-TRUNCAMENTO — CALLOUTS (OBRIGATÓRIO):
|
||||||
|
- Proibido cortar string para caber em 25 caracteres.
|
||||||
|
- Se exceder 25, REESCREVER o callout completo (sem substring).
|
||||||
|
- Proibido terminar com palavra incompleta ou espaço final.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. POLÍTICAS EDITORIAIS
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
- "Grátis"
|
||||||
|
- "Gratuito"
|
||||||
|
- caixa alta total (exceto SP, OAB)
|
||||||
|
- pontuação excessiva
|
||||||
|
|
||||||
|
PERMITIDO:
|
||||||
|
- Capitalização inicial nos espelhos e literais.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. HARD LOCK VALIDATION (FINAL)
|
||||||
|
|
||||||
|
VALIDAR:
|
||||||
|
|
||||||
|
- existem 3 grupos?
|
||||||
|
- existem 7 keywords por grupo?
|
||||||
|
- keywords foram canonizadas?
|
||||||
|
- existem 7 headlines_espelho (1 por keyword)?
|
||||||
|
- CADA keyword possui 1 headline_espelho EXATO após normalização?
|
||||||
|
- existem 15 títulos por grupo?
|
||||||
|
- Bucket 1 contém 5 espelhos?
|
||||||
|
- Bucket 2 contém 1 espelho (6º) + 3 CTAs reais + 1 DKI?
|
||||||
|
- Bucket 3 contém 1 espelho (7º) + 4 provas reais?
|
||||||
|
- o DKI está no formato {Keyword:...} e ≤ 30?
|
||||||
|
- o DKI NÃO contém termos proibidos?
|
||||||
|
- nenhum título > 30?
|
||||||
|
- descrições 80–92?
|
||||||
|
- descrições terminam com "." ou "!"?
|
||||||
|
- nenhuma descrição termina com palavra incompleta ou espaço?
|
||||||
|
- nenhuma substring detectada em descrições?
|
||||||
|
- callouts completos (sem palavra incompleta) e ≤25?
|
||||||
|
- existem 7 callouts?
|
||||||
|
|
||||||
|
SE falhar:
|
||||||
|
REEXECUTAR apenas bloco inválido
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
RETORNO FINAL:
|
||||||
|
APENAS JSON estruturado.
|
||||||
|
SEM explicações.
|
||||||
|
SEM texto adicional.
|
||||||
334
regras_prompt.md
Normal file
334
regras_prompt.md
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V12.6 — HEADLINE 30 + HARD MATCH + DKI SAFE MODE + RECONCILIATION ENGINE)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Engine determinístico com canonização automática de keywords, espelhamento obrigatório de keywords em headlines
|
||||||
|
(HARD MATCH 7/7) + 01 título com DKI em modo seguro (SEM substituir espelhos),
|
||||||
|
e regeneração obrigatória de descrições (SEM substring), agora com camada adicional
|
||||||
|
RECONCILIATION ENGINE (ANTI FALSE NEGATIVE) para validação estrutural final.
|
||||||
|
|
||||||
|
Compatível com GAS v6 — ENGINE AUDITOR HARD MATCH.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🆕 V12.6 — ATUALIZAÇÃO (HEADLINE 30)
|
||||||
|
|
||||||
|
* **Headlines (Títulos):** limite alterado de **25** para **30 caracteres**.
|
||||||
|
* **Demais ativos** (keywords, callouts, sitelinks e descrições de sitelink): permanecem com **limite 25** (ou os limites já definidos nas seções próprias).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 0. FLUXO GLOBAL (DETERMINISTIC EXECUTION)
|
||||||
|
|
||||||
|
INÍCIO
|
||||||
|
|
||||||
|
PARA cada GRUPO_DE_ANUNCIO (total = 3):
|
||||||
|
|
||||||
|
```
|
||||||
|
PASSO 1 → Gerar Keywords
|
||||||
|
PASSO 1.1 → SANITIZAR E CANONIZAR KEYWORDS
|
||||||
|
PASSO 2 → Normalizar Keywords
|
||||||
|
PASSO 3 → Classificar Keywords por PRIORIDADE
|
||||||
|
PASSO 4 → Selecionar Literais (ALGORITMO FIXO)
|
||||||
|
PASSO 4.1 → Gerar HEADLINES ESPELHO (1 por keyword)
|
||||||
|
PASSO 4.2 → Gerar 01 HEADLINE DKI (SAFE MODE)
|
||||||
|
PASSO 5 → Montar LISTA FINAL DE TÍTULOS (15) com MIRROR ENFORCEMENT + DKI SAFE
|
||||||
|
PASSO 6 → Gerar Descrições (REGENERAÇÃO OBRIGATÓRIA / SEM CORTE)
|
||||||
|
PASSO 7 → Validar Limites (Headlines ≤30; demais conforme seção)
|
||||||
|
PASSO 8 → Inserir Localização (fora dos espelhos)
|
||||||
|
```
|
||||||
|
|
||||||
|
FIM
|
||||||
|
|
||||||
|
Depois:
|
||||||
|
PASSO 9 → Negativas
|
||||||
|
PASSO 10 → Extensões
|
||||||
|
PASSO 11 → HARD LOCK VALIDATION
|
||||||
|
PASSO 12 → RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
PASSO 13 → AUDITORIA GAS v6 (Opcional)
|
||||||
|
|
||||||
|
RETORNAR apenas JSON estruturado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. PALAVRAS-CHAVE (ENGINE STEP)
|
||||||
|
|
||||||
|
GERAR exatamente 7 keywords por grupo.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
|
||||||
|
* usar [Exata] e "Frase"
|
||||||
|
* máximo 25 caracteres
|
||||||
|
* keywords devem ser juridicamente tituláveis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SANITIZAÇÃO E CANONIZAÇÃO (OBRIGATÓRIA)
|
||||||
|
|
||||||
|
ANTES de aceitar qualquer keyword:
|
||||||
|
|
||||||
|
FUNÇÃO canonizar(keyword):
|
||||||
|
|
||||||
|
```
|
||||||
|
remover duplicações de delimitadores:
|
||||||
|
[[texto]] → texto
|
||||||
|
|
||||||
|
converter para forma jurídica clara e titulável.
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemplos obrigatórios:
|
||||||
|
casamento fim → Fim do Casamento
|
||||||
|
fim do casamento → Fim do Casamento
|
||||||
|
fim pensão → Fim da Pensão
|
||||||
|
fim da pensão → Fim da Pensão
|
||||||
|
custo inventário → Custo Inventário
|
||||||
|
|
||||||
|
SE keyword for vaga, truncada ou não titulável:
|
||||||
|
DESCARTAR e gerar nova.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ENGINE NORMALIZE
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(texto):
|
||||||
|
|
||||||
|
```
|
||||||
|
remover "[" "]" e '"'
|
||||||
|
remover espaços duplicados
|
||||||
|
aplicar Capitalização Inicial
|
||||||
|
manter acentuação
|
||||||
|
manter ordem
|
||||||
|
NÃO alterar semântica
|
||||||
|
```
|
||||||
|
|
||||||
|
RETORNAR texto_normalizado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. CLASSIFICAÇÃO DETERMINÍSTICA DE KEYWORDS
|
||||||
|
|
||||||
|
PRIORIDADE 1 — COMERCIAL DIRETA
|
||||||
|
|
||||||
|
* valor
|
||||||
|
* custo
|
||||||
|
* preço
|
||||||
|
* regularização
|
||||||
|
* revisão
|
||||||
|
* cálculo
|
||||||
|
* quanto custa
|
||||||
|
* honorários
|
||||||
|
|
||||||
|
PRIORIDADE 2 — CONTRATAÇÃO
|
||||||
|
|
||||||
|
* advogado
|
||||||
|
* ação
|
||||||
|
* processo
|
||||||
|
* consulta
|
||||||
|
|
||||||
|
PRIORIDADE 3 — INFORMATIVA
|
||||||
|
Demais termos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. SELEÇÃO LITERAL DETERMINÍSTICA
|
||||||
|
|
||||||
|
FUNÇÃO selecionar_keywords_literais(lista_keywords):
|
||||||
|
|
||||||
|
```
|
||||||
|
ordenar por:
|
||||||
|
prioridade ASC
|
||||||
|
tamanho ASC
|
||||||
|
ordem original
|
||||||
|
|
||||||
|
selecionar até 5.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. KEYWORD MIRROR HEADLINES — HARD MATCH (ENFORCEMENT)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Garantir que TODAS as 7 keywords do grupo tenham ao menos 1 headline correspondente EXATO após normalização.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
|
||||||
|
1. Para cada keyword do grupo (7):
|
||||||
|
headline_espelho = normalizar(canonizar(keyword))
|
||||||
|
|
||||||
|
2. O headline_espelho deve ser EXATO (após normalização).
|
||||||
|
|
||||||
|
3. PROIBIÇÃO ABSOLUTA — "SP" NO ESPELHO
|
||||||
|
|
||||||
|
4. MIRROR ENFORCEMENT:
|
||||||
|
A lista final de 15 títulos deve conter TODOS os 7 headlines_espelho.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4.2 HEADLINE DKI — SAFE MODE
|
||||||
|
|
||||||
|
FORMATO:
|
||||||
|
{Keyword:PADRAO_DO_GRUPO}
|
||||||
|
|
||||||
|
REGRAS SAFE:
|
||||||
|
|
||||||
|
* DKI nunca substitui espelhos
|
||||||
|
* DKI fora do bucket literal
|
||||||
|
* ≤30 caracteres
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. TÍTULOS — MONTAGEM DETERMINÍSTICA
|
||||||
|
|
||||||
|
**Limite de caracteres (V12.6):** cada headline deve ter **≤30 caracteres**.
|
||||||
|
|
||||||
|
Distribuição: 05 / 05 / 05
|
||||||
|
|
||||||
|
Bucket 1: 5 espelhos
|
||||||
|
Bucket 2: 3 CTA/Local + 1 espelho + 1 DKI
|
||||||
|
Bucket 3: 4 provas + 1 espelho
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. DESCRIÇÕES — DETERMINISTIC TEXT ENGINE
|
||||||
|
|
||||||
|
80 ≤ caracteres ≤ 92
|
||||||
|
|
||||||
|
Proibido substring.
|
||||||
|
Falhou validação → Regenerar do zero.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. EXTENSÕES — HARD LOCK
|
||||||
|
|
||||||
|
Sitelinks: 4 obrigatórios
|
||||||
|
Callouts: 7 obrigatórios
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. POLÍTICAS EDITORIAIS
|
||||||
|
|
||||||
|
Proibido:
|
||||||
|
|
||||||
|
* Grátis
|
||||||
|
* Gratuito
|
||||||
|
* caixa alta total
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. HARD LOCK VALIDATION (FINAL)
|
||||||
|
|
||||||
|
Validar integridade completa antes da saída.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# V12.5 — RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Adicionar camada determinística de Reconciliação garantindo integridade total entre ENGINE e estado final.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRINCÍPIO
|
||||||
|
|
||||||
|
ENGINE → cria HARD LOCK → RECONCILIATION → AUDITORIA
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SNAPSHOTS OBRIGATÓRIOS (ENGINE_STATE)
|
||||||
|
|
||||||
|
keywords_raw[7]
|
||||||
|
keywords_canon[7]
|
||||||
|
keywords_norm[7]
|
||||||
|
headlines_espelho_criados[7]
|
||||||
|
headline_dki_criado[1]
|
||||||
|
headlines_final[15]
|
||||||
|
|
||||||
|
bucket_map_final
|
||||||
|
|
||||||
|
descricao_final
|
||||||
|
callouts_final[7]
|
||||||
|
sitelinks_final[4]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FINGERPRINTS (OBRIGATÓRIO)
|
||||||
|
|
||||||
|
Criar hashes SHA-256:
|
||||||
|
|
||||||
|
fp_keywords_norm
|
||||||
|
fp_headlines_final_norm
|
||||||
|
fp_descricao_final
|
||||||
|
fp_bucket_map
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REGRAS DE RECONCILIAÇÃO
|
||||||
|
|
||||||
|
R1 — EXISTÊNCIA 7/7
|
||||||
|
Cada keyword_norm deve existir como headline espelho final.
|
||||||
|
|
||||||
|
R2 — INTEGRIDADE DO ESPELHO
|
||||||
|
headline espelho deve permanecer idêntica.
|
||||||
|
|
||||||
|
R3 — CONTAGEM DE ESPELHOS
|
||||||
|
Exatamente 7 espelhos únicos.
|
||||||
|
|
||||||
|
R4 — DKI SAFE MODE
|
||||||
|
|
||||||
|
* exatamente 1 headline DKI
|
||||||
|
* formato {Keyword:PADRAO}
|
||||||
|
* não substitui espelhos
|
||||||
|
* fora do bucket literal
|
||||||
|
|
||||||
|
R5 — BUCKET LAYOUT
|
||||||
|
Bucket 1: 5 espelhos
|
||||||
|
Bucket 2: 3 CTA + 1 espelho + 1 DKI
|
||||||
|
Bucket 3: 4 provas + 1 espelho
|
||||||
|
|
||||||
|
R6 — DESCRIÇÃO ANTI-GHOST
|
||||||
|
Fingerprint da descrição final deve existir nas descrições geradas.
|
||||||
|
|
||||||
|
R7 — INTEGRIDADE FINAL DA DESCRIÇÃO
|
||||||
|
80 ≤ caracteres ≤ 92
|
||||||
|
Termina com . ou !
|
||||||
|
|
||||||
|
R8 — CALLOUTS
|
||||||
|
7 obrigatórios, ≤25 caracteres.
|
||||||
|
|
||||||
|
R9 — SITELINKS
|
||||||
|
Cada sitelink com description1 e description2.
|
||||||
|
|
||||||
|
R10 — HEADLINES (V12.6)
|
||||||
|
* todas as 15 headlines devem ter **≤30 caracteres**
|
||||||
|
* proibido truncar automaticamente (se estourar, regenerar)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SEVERIDADE
|
||||||
|
|
||||||
|
CRÍTICO: bloquear saída
|
||||||
|
ALTA: reexecutar etapa
|
||||||
|
MÉDIA: warning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REEXECUÇÃO AUTOMÁTICA
|
||||||
|
|
||||||
|
DESYNC_MIRROR_* → Passo 4.1/5
|
||||||
|
DESYNC_DKI_VIOLATION → Passo 4.2/5
|
||||||
|
DESYNC_BUCKET_LAYOUT → Passo 5
|
||||||
|
DESYNC_DESCRIPTION_* → Passo 6
|
||||||
|
DESYNC_CALLOUT_TRUNC → Passo 7
|
||||||
|
DESYNC_SITELINK_INCOMPLETE → Passo 7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STATUS FINAL
|
||||||
|
|
||||||
|
PASS_RECONCILIATION
|
||||||
|
FAIL_RECONCILIATION
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# RESULTADO
|
||||||
|
|
||||||
|
A Reconciliação elimina falsos negativos estruturais garantindo execução determinística do HARD MATCH + DKI SAFE MODE.
|
||||||
334
regras_promptV12.6.md
Normal file
334
regras_promptV12.6.md
Normal file
@@ -0,0 +1,334 @@
|
|||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V12.6 — HEADLINE 30 + HARD MATCH + DKI SAFE MODE + RECONCILIATION ENGINE)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Engine determinístico com canonização automática de keywords, espelhamento obrigatório de keywords em headlines
|
||||||
|
(HARD MATCH 7/7) + 01 título com DKI em modo seguro (SEM substituir espelhos),
|
||||||
|
e regeneração obrigatória de descrições (SEM substring), agora com camada adicional
|
||||||
|
RECONCILIATION ENGINE (ANTI FALSE NEGATIVE) para validação estrutural final.
|
||||||
|
|
||||||
|
Compatível com GAS v6 — ENGINE AUDITOR HARD MATCH.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 🆕 V12.6 — ATUALIZAÇÃO (HEADLINE 30)
|
||||||
|
|
||||||
|
* **Headlines (Títulos):** limite alterado de **25** para **30 caracteres**.
|
||||||
|
* **Demais ativos** (keywords, callouts, sitelinks e descrições de sitelink): permanecem com **limite 25** (ou os limites já definidos nas seções próprias).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 0. FLUXO GLOBAL (DETERMINISTIC EXECUTION)
|
||||||
|
|
||||||
|
INÍCIO
|
||||||
|
|
||||||
|
PARA cada GRUPO_DE_ANUNCIO (total = 3):
|
||||||
|
|
||||||
|
```
|
||||||
|
PASSO 1 → Gerar Keywords
|
||||||
|
PASSO 1.1 → SANITIZAR E CANONIZAR KEYWORDS
|
||||||
|
PASSO 2 → Normalizar Keywords
|
||||||
|
PASSO 3 → Classificar Keywords por PRIORIDADE
|
||||||
|
PASSO 4 → Selecionar Literais (ALGORITMO FIXO)
|
||||||
|
PASSO 4.1 → Gerar HEADLINES ESPELHO (1 por keyword)
|
||||||
|
PASSO 4.2 → Gerar 01 HEADLINE DKI (SAFE MODE)
|
||||||
|
PASSO 5 → Montar LISTA FINAL DE TÍTULOS (15) com MIRROR ENFORCEMENT + DKI SAFE
|
||||||
|
PASSO 6 → Gerar Descrições (REGENERAÇÃO OBRIGATÓRIA / SEM CORTE)
|
||||||
|
PASSO 7 → Validar Limites (Headlines ≤30; demais conforme seção)
|
||||||
|
PASSO 8 → Inserir Localização (fora dos espelhos)
|
||||||
|
```
|
||||||
|
|
||||||
|
FIM
|
||||||
|
|
||||||
|
Depois:
|
||||||
|
PASSO 9 → Negativas
|
||||||
|
PASSO 10 → Extensões
|
||||||
|
PASSO 11 → HARD LOCK VALIDATION
|
||||||
|
PASSO 12 → RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
PASSO 13 → AUDITORIA GAS v6 (Opcional)
|
||||||
|
|
||||||
|
RETORNAR apenas JSON estruturado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. PALAVRAS-CHAVE (ENGINE STEP)
|
||||||
|
|
||||||
|
GERAR exatamente 7 keywords por grupo.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
|
||||||
|
* usar [Exata] e "Frase"
|
||||||
|
* máximo 25 caracteres
|
||||||
|
* keywords devem ser juridicamente tituláveis.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SANITIZAÇÃO E CANONIZAÇÃO (OBRIGATÓRIA)
|
||||||
|
|
||||||
|
ANTES de aceitar qualquer keyword:
|
||||||
|
|
||||||
|
FUNÇÃO canonizar(keyword):
|
||||||
|
|
||||||
|
```
|
||||||
|
remover duplicações de delimitadores:
|
||||||
|
[[texto]] → texto
|
||||||
|
|
||||||
|
converter para forma jurídica clara e titulável.
|
||||||
|
```
|
||||||
|
|
||||||
|
Exemplos obrigatórios:
|
||||||
|
casamento fim → Fim do Casamento
|
||||||
|
fim do casamento → Fim do Casamento
|
||||||
|
fim pensão → Fim da Pensão
|
||||||
|
fim da pensão → Fim da Pensão
|
||||||
|
custo inventário → Custo Inventário
|
||||||
|
|
||||||
|
SE keyword for vaga, truncada ou não titulável:
|
||||||
|
DESCARTAR e gerar nova.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ENGINE NORMALIZE
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(texto):
|
||||||
|
|
||||||
|
```
|
||||||
|
remover "[" "]" e '"'
|
||||||
|
remover espaços duplicados
|
||||||
|
aplicar Capitalização Inicial
|
||||||
|
manter acentuação
|
||||||
|
manter ordem
|
||||||
|
NÃO alterar semântica
|
||||||
|
```
|
||||||
|
|
||||||
|
RETORNAR texto_normalizado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2. CLASSIFICAÇÃO DETERMINÍSTICA DE KEYWORDS
|
||||||
|
|
||||||
|
PRIORIDADE 1 — COMERCIAL DIRETA
|
||||||
|
|
||||||
|
* valor
|
||||||
|
* custo
|
||||||
|
* preço
|
||||||
|
* regularização
|
||||||
|
* revisão
|
||||||
|
* cálculo
|
||||||
|
* quanto custa
|
||||||
|
* honorários
|
||||||
|
|
||||||
|
PRIORIDADE 2 — CONTRATAÇÃO
|
||||||
|
|
||||||
|
* advogado
|
||||||
|
* ação
|
||||||
|
* processo
|
||||||
|
* consulta
|
||||||
|
|
||||||
|
PRIORIDADE 3 — INFORMATIVA
|
||||||
|
Demais termos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. SELEÇÃO LITERAL DETERMINÍSTICA
|
||||||
|
|
||||||
|
FUNÇÃO selecionar_keywords_literais(lista_keywords):
|
||||||
|
|
||||||
|
```
|
||||||
|
ordenar por:
|
||||||
|
prioridade ASC
|
||||||
|
tamanho ASC
|
||||||
|
ordem original
|
||||||
|
|
||||||
|
selecionar até 5.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. KEYWORD MIRROR HEADLINES — HARD MATCH (ENFORCEMENT)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Garantir que TODAS as 7 keywords do grupo tenham ao menos 1 headline correspondente EXATO após normalização.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
|
||||||
|
1. Para cada keyword do grupo (7):
|
||||||
|
headline_espelho = normalizar(canonizar(keyword))
|
||||||
|
|
||||||
|
2. O headline_espelho deve ser EXATO (após normalização).
|
||||||
|
|
||||||
|
3. PROIBIÇÃO ABSOLUTA — "SP" NO ESPELHO
|
||||||
|
|
||||||
|
4. MIRROR ENFORCEMENT:
|
||||||
|
A lista final de 15 títulos deve conter TODOS os 7 headlines_espelho.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4.2 HEADLINE DKI — SAFE MODE
|
||||||
|
|
||||||
|
FORMATO:
|
||||||
|
{Keyword:PADRAO_DO_GRUPO}
|
||||||
|
|
||||||
|
REGRAS SAFE:
|
||||||
|
|
||||||
|
* DKI nunca substitui espelhos
|
||||||
|
* DKI fora do bucket literal
|
||||||
|
* ≤30 caracteres
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. TÍTULOS — MONTAGEM DETERMINÍSTICA
|
||||||
|
|
||||||
|
**Limite de caracteres (V12.6):** cada headline deve ter **≤30 caracteres**.
|
||||||
|
|
||||||
|
Distribuição: 05 / 05 / 05
|
||||||
|
|
||||||
|
Bucket 1: 5 espelhos
|
||||||
|
Bucket 2: 3 CTA/Local + 1 espelho + 1 DKI
|
||||||
|
Bucket 3: 4 provas + 1 espelho
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. DESCRIÇÕES — DETERMINISTIC TEXT ENGINE
|
||||||
|
|
||||||
|
80 ≤ caracteres ≤ 92
|
||||||
|
|
||||||
|
Proibido substring.
|
||||||
|
Falhou validação → Regenerar do zero.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. EXTENSÕES — HARD LOCK
|
||||||
|
|
||||||
|
Sitelinks: 4 obrigatórios
|
||||||
|
Callouts: 7 obrigatórios
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 8. POLÍTICAS EDITORIAIS
|
||||||
|
|
||||||
|
Proibido:
|
||||||
|
|
||||||
|
* Grátis
|
||||||
|
* Gratuito
|
||||||
|
* caixa alta total
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 9. HARD LOCK VALIDATION (FINAL)
|
||||||
|
|
||||||
|
Validar integridade completa antes da saída.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# V12.5 — RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Adicionar camada determinística de Reconciliação garantindo integridade total entre ENGINE e estado final.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PRINCÍPIO
|
||||||
|
|
||||||
|
ENGINE → cria HARD LOCK → RECONCILIATION → AUDITORIA
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SNAPSHOTS OBRIGATÓRIOS (ENGINE_STATE)
|
||||||
|
|
||||||
|
keywords_raw[7]
|
||||||
|
keywords_canon[7]
|
||||||
|
keywords_norm[7]
|
||||||
|
headlines_espelho_criados[7]
|
||||||
|
headline_dki_criado[1]
|
||||||
|
headlines_final[15]
|
||||||
|
|
||||||
|
bucket_map_final
|
||||||
|
|
||||||
|
descricao_final
|
||||||
|
callouts_final[7]
|
||||||
|
sitelinks_final[4]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## FINGERPRINTS (OBRIGATÓRIO)
|
||||||
|
|
||||||
|
Criar hashes SHA-256:
|
||||||
|
|
||||||
|
fp_keywords_norm
|
||||||
|
fp_headlines_final_norm
|
||||||
|
fp_descricao_final
|
||||||
|
fp_bucket_map
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REGRAS DE RECONCILIAÇÃO
|
||||||
|
|
||||||
|
R1 — EXISTÊNCIA 7/7
|
||||||
|
Cada keyword_norm deve existir como headline espelho final.
|
||||||
|
|
||||||
|
R2 — INTEGRIDADE DO ESPELHO
|
||||||
|
headline espelho deve permanecer idêntica.
|
||||||
|
|
||||||
|
R3 — CONTAGEM DE ESPELHOS
|
||||||
|
Exatamente 7 espelhos únicos.
|
||||||
|
|
||||||
|
R4 — DKI SAFE MODE
|
||||||
|
|
||||||
|
* exatamente 1 headline DKI
|
||||||
|
* formato {Keyword:PADRAO}
|
||||||
|
* não substitui espelhos
|
||||||
|
* fora do bucket literal
|
||||||
|
|
||||||
|
R5 — BUCKET LAYOUT
|
||||||
|
Bucket 1: 5 espelhos
|
||||||
|
Bucket 2: 3 CTA + 1 espelho + 1 DKI
|
||||||
|
Bucket 3: 4 provas + 1 espelho
|
||||||
|
|
||||||
|
R6 — DESCRIÇÃO ANTI-GHOST
|
||||||
|
Fingerprint da descrição final deve existir nas descrições geradas.
|
||||||
|
|
||||||
|
R7 — INTEGRIDADE FINAL DA DESCRIÇÃO
|
||||||
|
80 ≤ caracteres ≤ 92
|
||||||
|
Termina com . ou !
|
||||||
|
|
||||||
|
R8 — CALLOUTS
|
||||||
|
7 obrigatórios, ≤25 caracteres.
|
||||||
|
|
||||||
|
R9 — SITELINKS
|
||||||
|
Cada sitelink com description1 e description2.
|
||||||
|
|
||||||
|
R10 — HEADLINES (V12.6)
|
||||||
|
* todas as 15 headlines devem ter **≤30 caracteres**
|
||||||
|
* proibido truncar automaticamente (se estourar, regenerar)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SEVERIDADE
|
||||||
|
|
||||||
|
CRÍTICO: bloquear saída
|
||||||
|
ALTA: reexecutar etapa
|
||||||
|
MÉDIA: warning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REEXECUÇÃO AUTOMÁTICA
|
||||||
|
|
||||||
|
DESYNC_MIRROR_* → Passo 4.1/5
|
||||||
|
DESYNC_DKI_VIOLATION → Passo 4.2/5
|
||||||
|
DESYNC_BUCKET_LAYOUT → Passo 5
|
||||||
|
DESYNC_DESCRIPTION_* → Passo 6
|
||||||
|
DESYNC_CALLOUT_TRUNC → Passo 7
|
||||||
|
DESYNC_SITELINK_INCOMPLETE → Passo 7
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## STATUS FINAL
|
||||||
|
|
||||||
|
PASS_RECONCILIATION
|
||||||
|
FAIL_RECONCILIATION
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# RESULTADO
|
||||||
|
|
||||||
|
A Reconciliação elimina falsos negativos estruturais garantindo execução determinística do HARD MATCH + DKI SAFE MODE.
|
||||||
84
regras_prompt_old-1.md
Normal file
84
regras_prompt_old-1.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
```md
|
||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V4 — Multi-Grupo)
|
||||||
|
|
||||||
|
## 7. ESTRUTURA DA CAMPANHA (3 Grupos de Anúncio)
|
||||||
|
A campanha DEVE ser dividida em exatamente 3 Grupos de Anúncio (Ad Groups) temáticos.
|
||||||
|
Analise o conteúdo da Landing Page e identifique 3 ângulos/temas distintos para segmentar a campanha (ex: por serviço, por dor do cliente, por diferencial competitivo).
|
||||||
|
Cada grupo deve ter um nome descritivo e conter seus próprios Keywords, Headlines e Descriptions (Regras 1, 3 e 4 aplicam-se POR GRUPO).
|
||||||
|
As Negativas, Sitelinks e Callouts são compartilhados no nível da campanha (Regras 2, 5).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. PALAVRAS-CHAVE (Fundo de Funil) — POR GRUPO
|
||||||
|
Gere 7 termos por grupo (total ~21) com alta intenção de contratação. Use [Exata] e "Frase". Limite cada termo a no máximo 25 caracteres para viabilizar o uso nos títulos.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. NEGATIVAS (Nível de Campanha)
|
||||||
|
Gere 20 termos para filtrar curiosos e buscas por gratuidade (ex: pdf, curso, modelo, jurisprudência, tcc, concurso, como fazer, gratuito).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. TÍTULOS (Headlines) - Regra Mista e Local — POR GRUPO
|
||||||
|
Gere 15 títulos por grupo (máx. 30 caracteres cada) seguindo esta distribuição:
|
||||||
|
|
||||||
|
### 🆕 ALTERAÇÃO — REGRA DE INTEGRIDADE (NÃO TRUNCAR)
|
||||||
|
- Os títulos NÃO podem ser truncados, cortados ou exceder o limite.
|
||||||
|
- Cada título deve ser gerado já dentro do limite de caracteres.
|
||||||
|
- É proibido gerar textos maiores esperando corte automático do Google Ads.
|
||||||
|
|
||||||
|
### 🆕 ALTERAÇÃO — REGRA LITERAL DE PALAVRA-CHAVE (OBRIGATÓRIA)
|
||||||
|
- Devem existir exatamente 05 Títulos de Palavra-chave.
|
||||||
|
- Cada título deve conter a palavra-chave da Regra 1 de forma INTEGRAL, IDÊNTICA e LITERAL.
|
||||||
|
- O título deve ser EXATAMENTE igual à palavra-chave.
|
||||||
|
- Proibido adicionar qualquer palavra antes ou depois.
|
||||||
|
- Proibido hífen, pontuação extra, variações, pluralização ou mudança de capitalização.
|
||||||
|
- Exemplo normativo:
|
||||||
|
Se a keyword for **“Advogado Divórcio”**, então o título deve ser **“Advogado Divórcio”**.
|
||||||
|
|
||||||
|
### 🆕 ALTERAÇÃO — REGRA DE LOCALIZAÇÃO (SP / SÃO PAULO)
|
||||||
|
- Sempre que possível, incluir referência geográfica “SP” ou “São Paulo” nos títulos de CTA/Local e nos títulos de Prova Social/Diferencial.
|
||||||
|
- A inclusão da localização NÃO deve violar limites de caracteres.
|
||||||
|
- A localização NÃO pode ser adicionada nos 05 Títulos de Palavra-chave literais.
|
||||||
|
|
||||||
|
### Distribuição obrigatória:
|
||||||
|
- 05 Títulos de Palavra-chave (regra literal acima).
|
||||||
|
- 05 Títulos de CTA/Local: Focados em Chamadas para Ação e Relevância Local (ex: "Fale Conosco em SP", "Agende sua Consulta Hoje").
|
||||||
|
- 05 Títulos de Prova Social/Diferencial: Utilize dados da LP (ex: "+10 anos de experiência", "Avaliação 5.0 no Google", "Atendimento 100% Online").
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. DESCRIÇÕES (Maximização de Espaço) — POR GRUPO
|
||||||
|
Gere 4 descrições por grupo.
|
||||||
|
|
||||||
|
### 🆕 ALTERAÇÃO — REGRA DE INTEGRIDADE (NÃO TRUNCAR)
|
||||||
|
- As descrições NÃO podem ser truncadas ou exceder o limite.
|
||||||
|
- Cada descrição deve ser criada já dentro do tamanho permitido.
|
||||||
|
|
||||||
|
### 🆕 ALTERAÇÃO — REGRA DE LOCALIZAÇÃO (SP / SÃO PAULO)
|
||||||
|
- Sempre que possível, incluir “SP” ou “São Paulo” nas descrições para reforçar relevância local.
|
||||||
|
- A localização não pode comprometer a clareza, o limite mínimo (80) e máximo (90) de caracteres.
|
||||||
|
|
||||||
|
Regra de Ouro:
|
||||||
|
- Tente utilizar o mais próximo possível dos 90 caracteres (mínimo 80) para ocupar mais espaço visual.
|
||||||
|
- Devem terminar obrigatoriamente com um ponto final ou exclamação.
|
||||||
|
- Devem incluir a Proposta Única de Valor (UVP).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. EXTENSÕES (Ativos — Nível de Campanha)
|
||||||
|
- 4 Sitelinks: Título (até 25ch) e 2 linhas de descrição (até 35ch cada).
|
||||||
|
- Sempre que possível, incluir “SP” ou “São Paulo” respeitando limites.
|
||||||
|
- 6 Callouts: Frases de destaque (máx. 25ch) focadas em autoridade e agilidade, podendo incluir localização quando couber.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. POLÍTICAS EDITORIAIS E PROIBIÇÕES
|
||||||
|
- Inviolável: NUNCA utilize "Grátis", "Gratuito" ou sinônimos.
|
||||||
|
- Formatação: Proibido CAIXA ALTA (exceto siglas como SP, OAB). Proibido pontuação excessiva (ex: !!!).
|
||||||
|
- Estilo: Use tom profissional, empático e focado em solução.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
RETORNO: APENAS o JSON estruturado, sem texto adicional.
|
||||||
|
```
|
||||||
137
regras_prompt_old-2-boa.md
Normal file
137
regras_prompt_old-2-boa.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V8 — HARD MODE IA / AUDITORIA BLINDADA + MATCH STRICT)
|
||||||
|
|
||||||
|
## 7. ESTRUTURA DA CAMPANHA (3 Grupos de Anúncio)
|
||||||
|
A campanha DEVE ser dividida em exatamente 3 Grupos de Anúncio (Ad Groups) temáticos.
|
||||||
|
Analise o conteúdo da Landing Page e identifique 3 ângulos/temas distintos para segmentar a campanha.
|
||||||
|
Cada grupo deve ter nome descritivo e conter seus próprios Keywords, Headlines e Descriptions (Regras 1, 3 e 4 aplicam-se POR GRUPO).
|
||||||
|
Negativas, Sitelinks e Callouts são compartilhados no nível da campanha (Regras 2 e 5).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. PALAVRAS-CHAVE (Fundo de Funil) — POR GRUPO
|
||||||
|
Gere exatamente 7 termos por grupo (total ~21).
|
||||||
|
Use [Exata] e "Frase".
|
||||||
|
Máximo: 25 caracteres por keyword.
|
||||||
|
|
||||||
|
### 🔒 HARD RULE — BASE PARA MATCH LITERAL
|
||||||
|
- As keywords devem permitir gerar obrigatoriamente 05 títulos literais.
|
||||||
|
- Evitar keywords longas que impeçam títulos ≤30 caracteres.
|
||||||
|
- A lista deve conter pelo menos 5 keywords compatíveis com títulos literais.
|
||||||
|
|
||||||
|
### 🆕 MATCH STRICT — NORMALIZAÇÃO (AUDITORIA)
|
||||||
|
- Keywords podem conter sinais como [ ], " ".
|
||||||
|
- Para gerar Títulos Literais, remover apenas os delimitadores [ ] ou " ".
|
||||||
|
- O texto interno deve permanecer IDÊNTICO.
|
||||||
|
Exemplo:
|
||||||
|
Keyword: "[advogado divórcio]"
|
||||||
|
Título literal permitido: "Advogado Divórcio"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. NEGATIVAS (Nível de Campanha)
|
||||||
|
Gere 20 termos para filtrar curiosos e buscas por gratuidade (ex: pdf, curso, modelo, jurisprudência, tcc, concurso, como fazer, gratuito).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. TÍTULOS (Headlines) - Regra Mista e Local — POR GRUPO
|
||||||
|
|
||||||
|
### 🚫 HARD LIMIT — TAMANHO
|
||||||
|
- Máximo absoluto: 30 caracteres.
|
||||||
|
- Não gerar textos maiores esperando corte automático.
|
||||||
|
- Títulos truncados são proibidos.
|
||||||
|
|
||||||
|
### 🔒 HARD MATCH — TÍTULOS LITERAIS (REGRA DE FERRO)
|
||||||
|
- Devem existir exatamente 05 títulos literais.
|
||||||
|
- Cada título deve ser IDÊNTICO ao TEXTO INTERNO da keyword da Regra 1.
|
||||||
|
- Ignorar apenas delimitadores [ ] ou " ".
|
||||||
|
- Copiar literal após normalização.
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
- adicionar palavras
|
||||||
|
- remover palavras
|
||||||
|
- mudar ordem
|
||||||
|
- alterar acentuação
|
||||||
|
- alterar significado
|
||||||
|
- acrescentar SP se não estiver na keyword
|
||||||
|
|
||||||
|
Exemplo:
|
||||||
|
Keyword: "[advogado inventário]"
|
||||||
|
Título literal obrigatório: "Advogado Inventário"
|
||||||
|
|
||||||
|
Keyword: "\"advogado guarda\""
|
||||||
|
Título literal obrigatório: "Advogado Guarda"
|
||||||
|
|
||||||
|
### 📍 REGRA DE LOCALIZAÇÃO (SP / SÃO PAULO)
|
||||||
|
- Inserir “SP” ou “São Paulo” sempre que possível nos títulos de CTA/Local e Prova Social.
|
||||||
|
- NÃO alterar títulos literais.
|
||||||
|
|
||||||
|
### 📊 DISTRIBUIÇÃO OBRIGATÓRIA
|
||||||
|
1. 05 Títulos Literais (match exato normalizado)
|
||||||
|
2. 05 Títulos CTA/Local
|
||||||
|
3. 05 Títulos Prova Social/Diferencial
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. DESCRIÇÕES (Maximização de Espaço) — POR GRUPO
|
||||||
|
|
||||||
|
### 📏 HARD RANGE — AUDITORIA
|
||||||
|
- Cada descrição deve ter entre 80 e 90 caracteres.
|
||||||
|
- Menos de 80 = erro.
|
||||||
|
- Mais de 90 = erro.
|
||||||
|
|
||||||
|
### 🚫 HARD INTEGRIDADE
|
||||||
|
- Não gerar textos maiores esperando truncamento.
|
||||||
|
- A descrição já deve nascer dentro do limite.
|
||||||
|
|
||||||
|
### 🆕 REGRA ANTI-SUBAPROVEITAMENTO (AUDITORIA)
|
||||||
|
- Descrições abaixo de 80 caracteres devem ser automaticamente expandidas antes da entrega final.
|
||||||
|
|
||||||
|
### 📍 LOCALIZAÇÃO
|
||||||
|
- Sempre que possível incluir “SP” ou “São Paulo”.
|
||||||
|
- Não comprometer clareza.
|
||||||
|
|
||||||
|
### 🎯 CONTEÚDO
|
||||||
|
- Incluir UVP.
|
||||||
|
- Incluir CTA claro.
|
||||||
|
- Final obrigatório com "." ou "!".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. EXTENSÕES (Ativos — Nível de Campanha)
|
||||||
|
|
||||||
|
### SITELINKS
|
||||||
|
- 4 Sitelinks obrigatórios.
|
||||||
|
- Título até 25 caracteres.
|
||||||
|
- 2 linhas de descrição até 35 caracteres cada.
|
||||||
|
- Priorizar CTAs fortes.
|
||||||
|
- Sempre que possível incluir SP/São Paulo.
|
||||||
|
|
||||||
|
### CALLOUTS
|
||||||
|
- 6 Callouts (máx. 25 caracteres).
|
||||||
|
- Incluir obrigatoriamente 01 callout com diferencial competitivo único.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. POLÍTICAS EDITORIAIS E PROIBIÇÕES
|
||||||
|
- Inviolável: NUNCA utilizar "Grátis", "Gratuito" ou sinônimos.
|
||||||
|
- Proibido CAIXA ALTA (exceto SP, OAB).
|
||||||
|
- Proibido pontuação excessiva.
|
||||||
|
- Linguagem profissional e orientada à solução.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 CHECKLIST AUTOMÁTICO (HARD MODE — EXECUÇÃO OBRIGATÓRIA)
|
||||||
|
Antes de gerar o JSON final, validar:
|
||||||
|
|
||||||
|
- Existem exatamente 15 títulos por grupo?
|
||||||
|
- Existem exatamente 05 títulos literais idênticos às keywords (normalizadas)?
|
||||||
|
- Nenhum título excede 30 caracteres?
|
||||||
|
- Todas as descrições possuem entre 80 e 90 caracteres?
|
||||||
|
- Pelo menos uma menção a SP/São Paulo nos textos não literais?
|
||||||
|
- Existe 01 callout diferencial?
|
||||||
|
|
||||||
|
Se qualquer item falhar → regenerar automaticamente antes da entrega.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
RETORNO: APENAS o JSON estruturado, sem texto adicional.
|
||||||
233
regras_prompt_old-3-boa.md
Normal file
233
regras_prompt_old-3-boa.md
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V11.2 — ENGINE HARD LOCK / COMMERCIAL PRIORITY)
|
||||||
|
|
||||||
|
OBJETIVO:
|
||||||
|
Versão HARD LOCK com prioridade comercial.
|
||||||
|
Blindar geração contra variações semânticas e erros de auditoria.
|
||||||
|
Capitalização inicial permitida.
|
||||||
|
Auditoria aceita igualdade semântica após normalização.
|
||||||
|
|
||||||
|
Nenhuma regra anterior foi removida — apenas reforçada.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 0. FLUXO GLOBAL (ENGINE EXECUTION)
|
||||||
|
|
||||||
|
INÍCIO
|
||||||
|
|
||||||
|
PARA cada GRUPO_DE_ANUNCIO (total = 3):
|
||||||
|
|
||||||
|
PASSO 1 → Gerar Keywords
|
||||||
|
PASSO 2 → Normalizar Keywords (ENGINE NORMALIZE)
|
||||||
|
PASSO 3 → Selecionar Keywords Literais (COMMERCIAL PRIORITY)
|
||||||
|
PASSO 4 → Gerar Títulos Literais (HARD LOCK)
|
||||||
|
PASSO 5 → Gerar Títulos CTA/Local
|
||||||
|
PASSO 6 → Gerar Títulos Prova Social
|
||||||
|
PASSO 7 → Validar Limite de Caracteres
|
||||||
|
PASSO 8 → Gerar Descrições
|
||||||
|
PASSO 9 → Validar RANGE ESTRITO
|
||||||
|
PASSO 10 → Inserir Localização
|
||||||
|
|
||||||
|
FIM
|
||||||
|
|
||||||
|
Depois:
|
||||||
|
PASSO 11 → Negativas
|
||||||
|
PASSO 12 → Extensões
|
||||||
|
PASSO 13 → HARD LOCK VALIDATION
|
||||||
|
|
||||||
|
RETORNAR apenas JSON estruturado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 1. PALAVRAS-CHAVE (ENGINE STEP)
|
||||||
|
|
||||||
|
GERAR exatamente 7 keywords por grupo.
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
- usar [Exata] e "Frase"
|
||||||
|
- máximo 25 caracteres
|
||||||
|
- garantir mínimo 5 keywords utilizáveis como título literal
|
||||||
|
|
||||||
|
### ENGINE NORMALIZE (MATCH BASE)
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(keyword):
|
||||||
|
|
||||||
|
remover "[" "]" e '"'
|
||||||
|
aplicar Capitalização Inicial
|
||||||
|
manter acentuação
|
||||||
|
manter ordem original
|
||||||
|
NÃO alterar semântica
|
||||||
|
|
||||||
|
RETORNAR keyword_normalizada
|
||||||
|
|
||||||
|
HARD LOCK TOKEN:
|
||||||
|
|
||||||
|
literal_token = hash(keyword_normalizada)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 3. TÍTULOS — ENGINE HARD LOCK
|
||||||
|
|
||||||
|
## 🆕 PRIORIDADE COMERCIAL (NOVO)
|
||||||
|
|
||||||
|
ANTES de selecionar títulos literais:
|
||||||
|
|
||||||
|
Identificar keywords com intenção direta:
|
||||||
|
|
||||||
|
- valor
|
||||||
|
- custo
|
||||||
|
- preço
|
||||||
|
- regularização
|
||||||
|
- revisão
|
||||||
|
- cálculo
|
||||||
|
- quanto custa
|
||||||
|
|
||||||
|
SE existirem:
|
||||||
|
|
||||||
|
essas keywords DEVEM entrar primeiro nas 5 literais obrigatórias.
|
||||||
|
|
||||||
|
Somente após preencher prioridades comerciais,
|
||||||
|
completar restante das literais.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASSO A — TÍTULOS LITERAIS (MATCH BLOQUEADO)
|
||||||
|
|
||||||
|
VAR lista_literais = selecionar_keywords_prioritarias()
|
||||||
|
|
||||||
|
PARA cada keyword:
|
||||||
|
|
||||||
|
titulo = normalizar(keyword)
|
||||||
|
token_titulo = hash(titulo)
|
||||||
|
|
||||||
|
SE token_titulo != literal_token:
|
||||||
|
BLOQUEAR alteração
|
||||||
|
REGERAR
|
||||||
|
|
||||||
|
SE tamanho(titulo) > 30:
|
||||||
|
substituir keyword
|
||||||
|
|
||||||
|
adicionar titulo
|
||||||
|
|
||||||
|
PERMITIDO:
|
||||||
|
- Capitalização inicial
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
- adicionar SP
|
||||||
|
- alterar palavras
|
||||||
|
- alterar ordem
|
||||||
|
- reescrever semântica
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PASSO B — TÍTULOS CTA/LOCAL
|
||||||
|
- incluir SP ou São Paulo quando possível
|
||||||
|
- máximo 30 caracteres
|
||||||
|
|
||||||
|
## PASSO C — TÍTULOS PROVA SOCIAL
|
||||||
|
- usar autoridade da LP
|
||||||
|
- máximo 30 caracteres
|
||||||
|
|
||||||
|
TOTAL:
|
||||||
|
15 títulos por grupo.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 4. DESCRIÇÕES — ENGINE HARD LOCK
|
||||||
|
|
||||||
|
### RANGE DEFINITIVO
|
||||||
|
- mínimo absoluto: 80
|
||||||
|
- alvo ideal: 85–90
|
||||||
|
- máximo: 90
|
||||||
|
|
||||||
|
FUNÇÃO validar_descricao(texto):
|
||||||
|
|
||||||
|
🆕 ENGINE HARD LOCK — BLOQUEIO DE SUBSTRING
|
||||||
|
|
||||||
|
É PROIBIDO usar:
|
||||||
|
|
||||||
|
texto[:limite]
|
||||||
|
substring
|
||||||
|
slice
|
||||||
|
corte parcial
|
||||||
|
|
||||||
|
SE tamanho(texto) > 90:
|
||||||
|
|
||||||
|
REESCREVER FRASE COMPLETA
|
||||||
|
NUNCA cortar caracteres finais
|
||||||
|
|
||||||
|
SE detectar palavra incompleta:
|
||||||
|
|
||||||
|
REGERAR descrição automaticamente
|
||||||
|
|
||||||
|
SE tamanho < 80:
|
||||||
|
expandir automaticamente
|
||||||
|
|
||||||
|
SE tamanho entre 80 e 84:
|
||||||
|
expandir até ≥85
|
||||||
|
|
||||||
|
🆕 ANTI-TRUNCAMENTO FINAL
|
||||||
|
- descrição deve terminar com "." ou "!"
|
||||||
|
- nunca finalizar com palavra incompleta
|
||||||
|
- nunca terminar com espaço vazio
|
||||||
|
|
||||||
|
CONTEÚDO:
|
||||||
|
- incluir UVP
|
||||||
|
- incluir CTA
|
||||||
|
- incluir SP ou São Paulo quando possível
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. EXTENSÕES (ATIVOS — NÍVEL CAMPANHA)
|
||||||
|
|
||||||
|
SITELINKS:
|
||||||
|
- 4 obrigatórios
|
||||||
|
- título ≤25 caracteres
|
||||||
|
- 2 descrições ≤35 caracteres
|
||||||
|
|
||||||
|
CALLOUTS:
|
||||||
|
- 7 obrigatórios
|
||||||
|
- ≤25 caracteres
|
||||||
|
- incluir 01 diferencial competitivo único
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 6. POLÍTICAS EDITORIAIS
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
- "Grátis"
|
||||||
|
- "Gratuito"
|
||||||
|
- caixa alta total (exceto SP, OAB)
|
||||||
|
- pontuação excessiva
|
||||||
|
|
||||||
|
PERMITIDO:
|
||||||
|
- Capitalização Inicial em títulos literais.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# 7. HARD LOCK VALIDATION
|
||||||
|
|
||||||
|
ANTES DE ENTREGAR:
|
||||||
|
|
||||||
|
VALIDAR:
|
||||||
|
|
||||||
|
- existem 3 grupos?
|
||||||
|
- existem 15 títulos por grupo?
|
||||||
|
- existem 05 títulos literais?
|
||||||
|
- keywords comerciais estão dentro das literais?
|
||||||
|
- hash(titulo_literal) == literal_token ?
|
||||||
|
- nenhum título > 30 caracteres?
|
||||||
|
- descrições entre 80 e 90 caracteres?
|
||||||
|
- nenhuma substring detectada?
|
||||||
|
- existe SP/São Paulo fora das literais?
|
||||||
|
- existem 7 callouts?
|
||||||
|
|
||||||
|
SE falhar:
|
||||||
|
|
||||||
|
REEXECUTAR bloco inválido
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
RETORNO FINAL:
|
||||||
|
APENAS JSON estruturado.
|
||||||
|
SEM explicações.
|
||||||
|
SEM texto adicional.
|
||||||
35
regras_prompt_old.md
Normal file
35
regras_prompt_old.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# REGRAS CONSOLIDADAS PARA GOOGLE ADS (V4 — Multi-Grupo)
|
||||||
|
|
||||||
|
## 7. ESTRUTURA DA CAMPANHA (3 Grupos de Anúncio)
|
||||||
|
A campanha DEVE ser dividida em exatamente 3 Grupos de Anúncio (Ad Groups) temáticos.
|
||||||
|
Analise o conteúdo da Landing Page e identifique 3 ângulos/temas distintos para segmentar a campanha (ex: por serviço, por dor do cliente, por diferencial competitivo).
|
||||||
|
Cada grupo deve ter um nome descritivo e conter seus próprios Keywords, Headlines e Descriptions (Regras 1, 3 e 4 aplicam-se POR GRUPO).
|
||||||
|
As Negativas, Sitelinks e Callouts são compartilhados no nível da campanha (Regras 2, 5).
|
||||||
|
|
||||||
|
## 1. PALAVRAS-CHAVE (Fundo de Funil) — POR GRUPO
|
||||||
|
Gere 7 termos por grupo (total ~21) com alta intenção de contratação. Use [Exata] e "Frase". Limite cada termo a no máximo 25 caracteres para viabilizar o uso nos títulos.
|
||||||
|
|
||||||
|
## 2. NEGATIVAS (Nível de Campanha)
|
||||||
|
Gere 20 termos para filtrar curiosos e buscas por gratuidade (ex: pdf, curso, modelo, jurisprudência, tcc, concurso, como fazer, gratuito).
|
||||||
|
|
||||||
|
Gere 15 títulos por grupo (máx. 30 caracteres). Atenção à distribuição:
|
||||||
|
|
||||||
|
## 3. TÍTULOS (Headlines) - Regra Mista e Local — POR GRUPO
|
||||||
|
Gere 15 títulos por grupo (máx. 30 caracteres cada) seguindo esta distribuição:
|
||||||
|
- 05 Títulos de Palavra-chave: Devem conter obrigatoriamente a palavra-chave da Regra 1 de forma INTEGRAL e IDÊNTICA.
|
||||||
|
- 05 Títulos de CTA/Local: Focados em Chamadas para Ação e Relevância Local (ex: "Fale Conosco em SP", "Agende sua Consulta Hoje").
|
||||||
|
- 05 Títulos de Prova Social/Diferencial: Utilize dados da LP (ex: "+10 anos de experiência", "Avaliação 5.0 no Google", "Atendimento 100% Online").
|
||||||
|
|
||||||
|
## 4. DESCRIÇÕES (Maximização de Espaço) — POR GRUPO
|
||||||
|
Gere 4 descrições por grupo. Regra de Ouro: Tente utilizar o mais próximo possível dos 90 caracteres (mínimo 80) para ocupar mais espaço visual. Devem terminar obrigatoriamente com um ponto final ou exclamação e incluir a Proposta Única de Valor (UVP).
|
||||||
|
|
||||||
|
## 5. EXTENSÕES (Ativos — Nível de Campanha)
|
||||||
|
- 4 Sitelinks: Título (até 25ch) e 2 linhas de descrição (até 35ch cada).
|
||||||
|
- 6 Callouts: Frases de destaque (máx. 25ch) focadas em autoridade e agilidade.
|
||||||
|
|
||||||
|
## 6. POLÍTICAS EDITORIAIS E PROIBIÇÕES
|
||||||
|
- Inviolável: NUNCA utilize "Grátis", "Gratuito" ou sinônimos.
|
||||||
|
- Formatação: Proibido CAIXA ALTA (exceto siglas como SP, OAB). Proibido pontuação excessiva (ex: !!!).
|
||||||
|
- Estilo: Use tom profissional, empático e focado em solução.
|
||||||
|
|
||||||
|
RETORNO: APENAS o JSON estruturado, sem texto adicional.
|
||||||
215
regras_prompt_v12.4-v12_5.md
Normal file
215
regras_prompt_v12.4-v12_5.md
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# GOOGLE ADS --- ENGINE HARD MATCH V12.5 (VERSÃO CONSOLIDADA)
|
||||||
|
|
||||||
|
Atualizado em: 14/02/2026
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 🎯 OBJETIVO
|
||||||
|
|
||||||
|
Estrutura definitiva seguindo:
|
||||||
|
|
||||||
|
- HARD MATCH 7/7
|
||||||
|
- DKI SAFE MODE
|
||||||
|
- RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
- GAS v6 --- ENGINE AUDITOR HARD MATCH
|
||||||
|
|
||||||
|
Sem truncamentos, sem mutações de espelho e com validação estrutural
|
||||||
|
final.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 📦 CAMPANHA GOOGLE ADS --- ESTRUTURA FINAL
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 🔵 GRUPO 1 --- DIVÓRCIO E SEPARAÇÃO
|
||||||
|
|
||||||
|
### Keywords
|
||||||
|
|
||||||
|
- \[divórcio online\]
|
||||||
|
- "advogado divórcio"
|
||||||
|
- \[separação judicial\]
|
||||||
|
- "divórcio consensual"
|
||||||
|
- \[divórcio litigioso\]
|
||||||
|
- "fim do casamento"
|
||||||
|
- \[custo divórcio\]
|
||||||
|
|
||||||
|
### Headlines Espelho (HARD MATCH)
|
||||||
|
|
||||||
|
Divórcio Online\
|
||||||
|
Advogado Divórcio\
|
||||||
|
Separação Judicial\
|
||||||
|
Divórcio Consensual\
|
||||||
|
Divórcio Litigioso\
|
||||||
|
Fim do Casamento\
|
||||||
|
Custo Divórcio
|
||||||
|
|
||||||
|
### Headlines Finais (15)
|
||||||
|
|
||||||
|
Divórcio Online\
|
||||||
|
Advogado Divórcio\
|
||||||
|
Separação Judicial\
|
||||||
|
Divórcio Consensual\
|
||||||
|
Divórcio Litigioso\
|
||||||
|
Fale com um Especialista\
|
||||||
|
Atendimento Online em SP\
|
||||||
|
Solução Rápida e Segura\
|
||||||
|
Fim do Casamento\
|
||||||
|
{Keyword:Divórcio em SP}\
|
||||||
|
Sigilo e Discrição Total\
|
||||||
|
Estratégia Clara e Direta\
|
||||||
|
Acompanhamento do Início\
|
||||||
|
Menos Conflito, Mais Paz\
|
||||||
|
Custo Divórcio
|
||||||
|
|
||||||
|
### Descrições
|
||||||
|
|
||||||
|
Divórcio online com orientação clara e sigilo. Resolva com agilidade e
|
||||||
|
segurança. Fale agora!\
|
||||||
|
Advogado de família para divórcio consensual ou litigioso. Atendimento
|
||||||
|
online e discreto. Consulte!\
|
||||||
|
Separação judicial com estratégia e rapidez. Proteja seus direitos e
|
||||||
|
evite desgaste. Agende uma análise!
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 🟢 GRUPO 2 --- PENSÃO ALIMENTÍCIA E GUARDA
|
||||||
|
|
||||||
|
### Keywords
|
||||||
|
|
||||||
|
- \[pensão alimentícia\]
|
||||||
|
- "advogado guarda filhos"
|
||||||
|
- \[revisão pensão\]
|
||||||
|
- "guarda compartilhada"
|
||||||
|
- \[ação de alimentos\]
|
||||||
|
- "fim da pensão"
|
||||||
|
- \[valor pensão\]
|
||||||
|
|
||||||
|
### Headlines Espelho (HARD MATCH)
|
||||||
|
|
||||||
|
Pensão Alimentícia\
|
||||||
|
Advogado Guarda Filhos\
|
||||||
|
Revisão Pensão\
|
||||||
|
Guarda Compartilhada\
|
||||||
|
Ação de Alimentos\
|
||||||
|
Fim da Pensão\
|
||||||
|
Valor Pensão
|
||||||
|
|
||||||
|
### Headlines Finais (15)
|
||||||
|
|
||||||
|
Pensão Alimentícia\
|
||||||
|
Advogado Guarda Filhos\
|
||||||
|
Revisão Pensão\
|
||||||
|
Guarda Compartilhada\
|
||||||
|
Ação de Alimentos\
|
||||||
|
Defina a Guarda com Calma\
|
||||||
|
Atendimento Online em SP\
|
||||||
|
Fale com um Especialista\
|
||||||
|
Fim da Pensão\
|
||||||
|
{Keyword:Pensão em SP}\
|
||||||
|
Soluções Justas e Claras\
|
||||||
|
Foco no Bem-Estar\
|
||||||
|
Sigilo e Orientação Total\
|
||||||
|
Acompanhamento Completo\
|
||||||
|
Valor Pensão
|
||||||
|
|
||||||
|
### Descrições
|
||||||
|
|
||||||
|
Defina guarda e pensão com equilíbrio e segurança. Atendimento online
|
||||||
|
com foco no bem-estar. Consulte!\
|
||||||
|
Revisão de pensão e ação de alimentos com orientação objetiva. Evite
|
||||||
|
conflitos e ganhe clareza. Fale!\
|
||||||
|
Guarda compartilhada com apoio jurídico completo. Proteja seus filhos e
|
||||||
|
seus direitos. Agende avaliação!
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## 🟣 GRUPO 3 --- INVENTÁRIO E UNIÃO ESTÁVEL
|
||||||
|
|
||||||
|
### Keywords
|
||||||
|
|
||||||
|
- \[inventário judicial\]
|
||||||
|
- "advogado inventário"
|
||||||
|
- \[união estável\]
|
||||||
|
- "dissolução união estável"
|
||||||
|
- \[inventário extrajudicial\]
|
||||||
|
- "herança e bens"
|
||||||
|
- \[custo inventário\]
|
||||||
|
|
||||||
|
### Headlines Espelho (HARD MATCH)
|
||||||
|
|
||||||
|
Inventário Judicial\
|
||||||
|
Advogado Inventário\
|
||||||
|
União Estável\
|
||||||
|
Dissolução União Estável\
|
||||||
|
Inventário Extrajudicial\
|
||||||
|
Herança e Bens\
|
||||||
|
Custo Inventário
|
||||||
|
|
||||||
|
### Headlines Finais (15)
|
||||||
|
|
||||||
|
Inventário Judicial\
|
||||||
|
Advogado Inventário\
|
||||||
|
União Estável\
|
||||||
|
Dissolução União Estável\
|
||||||
|
Inventário Extrajudicial\
|
||||||
|
Regularize com Segurança\
|
||||||
|
Atendimento Online em SP\
|
||||||
|
Agilidade na Partilha\
|
||||||
|
Herança e Bens\
|
||||||
|
{Keyword:Inventário em SP}\
|
||||||
|
Partilha Justa e Rápida\
|
||||||
|
Documentação Sem Estresse\
|
||||||
|
Orientação do Início ao Fim\
|
||||||
|
Segurança Jurídica Total\
|
||||||
|
Custo Inventário
|
||||||
|
|
||||||
|
### Descrições
|
||||||
|
|
||||||
|
Inventário judicial ou extrajudicial com orientação clara. Partilha
|
||||||
|
segura e ágil. Fale com especialista!\
|
||||||
|
União estável e dissolução com segurança jurídica. Atendimento online e
|
||||||
|
acompanhamento completo. Consulte!\
|
||||||
|
Herança e bens com estratégia e transparência. Resolva o inventário sem
|
||||||
|
desgaste. Agende uma análise!
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 🔗 SITELINKS
|
||||||
|
|
||||||
|
Divórcio e Separação\
|
||||||
|
- Soluções rápidas e seguras.\
|
||||||
|
- Atendimento online e sigiloso.
|
||||||
|
|
||||||
|
Pensão e Guarda\
|
||||||
|
- Foco no bem-estar dos filhos.\
|
||||||
|
- Defina guarda e pensão com clareza.
|
||||||
|
|
||||||
|
Inventário e União\
|
||||||
|
- Partilha justa e ágil.\
|
||||||
|
- Regularize sua união com segurança.
|
||||||
|
|
||||||
|
Fale com Especialista\
|
||||||
|
- Análise inicial e orientação clara.\
|
||||||
|
- Converse online com um advogado.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 📢 CALLOUTS
|
||||||
|
|
||||||
|
Atendimento 100% Online\
|
||||||
|
Sigilo Garantido\
|
||||||
|
Resposta Rápida\
|
||||||
|
Especialistas em Família\
|
||||||
|
Estratégia Clara\
|
||||||
|
Acompanhamento Completo\
|
||||||
|
Foco em Solução Justa
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ✅ STATUS
|
||||||
|
|
||||||
|
PASS_RECONCILIATION\
|
||||||
|
HARD MATCH 7/7 ATIVO\
|
||||||
|
ANTI TRUNCAMENTO VALIDADO\
|
||||||
|
COMPATÍVEL COM GAS v6
|
||||||
185
regras_prompt_v12_5.md
Normal file
185
regras_prompt_v12_5.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# REGRAS CONSOLIDADAS --- Google Ads
|
||||||
|
|
||||||
|
## V12.5 --- RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
|
||||||
|
OBJETIVO: Adicionar uma camada determinística de Reconciliação ao
|
||||||
|
pipeline V12.4 (HARD MATCH + DKI SAFE MODE), garantindo integridade
|
||||||
|
total entre o estado criado pelo ENGINE e o estado final entregue.
|
||||||
|
|
||||||
|
Compatível com: - HARD MATCH - DKI SAFE MODE - GAS v6 --- ENGINE AUDITOR
|
||||||
|
HARD MATCH
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 0. PRINCÍPIO
|
||||||
|
|
||||||
|
A Reconciliação NÃO substitui auditoria. Ela valida consistência entre
|
||||||
|
fases.
|
||||||
|
|
||||||
|
ENGINE → cria HARD LOCK → valida regras RECONCILIATION → valida
|
||||||
|
integridade estrutural AUDITORIA → valida qualidade final
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 1. FLUXO GLOBAL ATUALIZADO
|
||||||
|
|
||||||
|
PASSO 1 → Keywords PASSO 2 → Canonização PASSO 3 → Normalização PASSO 4
|
||||||
|
→ Headlines Espelho PASSO 4.2 → DKI SAFE MODE PASSO 5 → Montagem 15
|
||||||
|
Títulos PASSO 6 → Descrições PASSO 7 → Extensões PASSO 8 → HARD LOCK
|
||||||
|
VALIDATION PASSO 12 → RECONCILIATION ENGINE (NOVO) PASSO 13 → AUDITORIA
|
||||||
|
GAS v6 (Opcional)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 2. SNAPSHOTS OBRIGATÓRIOS (ENGINE_STATE)
|
||||||
|
|
||||||
|
keywords_raw\[7\] keywords_canon\[7\] keywords_norm\[7\]
|
||||||
|
headlines_espelho_criados\[7\] headline_dki_criado\[1\]
|
||||||
|
headlines_final\[15\]
|
||||||
|
|
||||||
|
bucket_map_final: - bucket1_literal\[5\] - bucket2_cta_local\[5\] -
|
||||||
|
bucket3_prova\[5\]
|
||||||
|
|
||||||
|
descricoes_geradas\[\] descricao_final callouts_final\[7\]
|
||||||
|
sitelinks_final\[4\]
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 3. FINGERPRINTS (OBRIGATÓRIO)
|
||||||
|
|
||||||
|
Criar hashes SHA-256:
|
||||||
|
|
||||||
|
fp_keywords_norm fp_headlines_final_norm fp_descricao_final
|
||||||
|
fp_bucket_map
|
||||||
|
|
||||||
|
Objetivo: Detectar alterações invisíveis após montagem.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 4. REGRAS DE RECONCILIAÇÃO
|
||||||
|
|
||||||
|
## R1 --- EXISTÊNCIA 7/7 (CRÍTICO)
|
||||||
|
|
||||||
|
Cada keyword_norm deve existir como headline espelho final.
|
||||||
|
|
||||||
|
Se faltar: DESYNC_MIRROR_MISSING
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R2 --- INTEGRIDADE DO ESPELHO (CRÍTICO)
|
||||||
|
|
||||||
|
headline espelho deve ser idêntica após normalização.
|
||||||
|
|
||||||
|
Proibido: - adicionar palavras - trocar ordem - criar variações
|
||||||
|
|
||||||
|
Erro: DESYNC_MIRROR_MUTATED
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R3 --- CONTAGEM DE ESPELHOS (CRÍTICO)
|
||||||
|
|
||||||
|
Deve haver exatamente 7 espelhos únicos.
|
||||||
|
|
||||||
|
Erro: DESYNC_MIRROR_COUNT
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R4 --- DKI SAFE MODE (CRÍTICO)
|
||||||
|
|
||||||
|
Regras obrigatórias:
|
||||||
|
|
||||||
|
- existe exatamente 1 headline DKI
|
||||||
|
- formato {Keyword:PADRAO}
|
||||||
|
- DKI não substitui espelhos
|
||||||
|
- DKI não está no Bucket Literal
|
||||||
|
|
||||||
|
Erro: DESYNC_DKI_VIOLATION
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R5 --- BUCKET LAYOUT (ALTA)
|
||||||
|
|
||||||
|
Distribuição obrigatória:
|
||||||
|
|
||||||
|
Bucket 1: 5 espelhos
|
||||||
|
|
||||||
|
Bucket 2: 3 CTA/Local 1 espelho (6º) 1 DKI
|
||||||
|
|
||||||
|
Bucket 3: 4 provas 1 espelho (7º)
|
||||||
|
|
||||||
|
Erro: DESYNC_BUCKET_LAYOUT
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R6 --- DESCRIÇÃO ANTI-GHOST (CRÍTICO)
|
||||||
|
|
||||||
|
Fingerprint da descrição final deve existir dentro das descrições
|
||||||
|
geradas.
|
||||||
|
|
||||||
|
Proibido: - editar manualmente - cortar string
|
||||||
|
|
||||||
|
Erro: DESYNC_DESCRIPTION_GHOST_EDIT
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R7 --- INTEGRIDADE FINAL DA DESCRIÇÃO (CRÍTICO)
|
||||||
|
|
||||||
|
Revalidar:
|
||||||
|
|
||||||
|
80 ≤ caracteres ≤ 92 termina com "." ou "!" não termina com espaço não
|
||||||
|
termina com palavra incompleta
|
||||||
|
|
||||||
|
Erro: DESYNC_DESCRIPTION_INVALID_FINAL
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R8 --- CALLOUTS (ALTA)
|
||||||
|
|
||||||
|
- 7 obrigatórios
|
||||||
|
- ≤25 caracteres
|
||||||
|
- sem palavra incompleta
|
||||||
|
- sem substring
|
||||||
|
|
||||||
|
Erro: DESYNC_CALLOUT_TRUNC
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
## R9 --- SITELINKS (MÉDIA)
|
||||||
|
|
||||||
|
Cada sitelink deve possuir:
|
||||||
|
|
||||||
|
description1 description2
|
||||||
|
|
||||||
|
Erro: DESYNC_SITELINK_INCOMPLETE
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 5. SEVERIDADE
|
||||||
|
|
||||||
|
CRÍTICO: bloquear saída do grupo reexecutar bloco inválido
|
||||||
|
|
||||||
|
ALTA: reexecutar etapa específica
|
||||||
|
|
||||||
|
MÉDIA: warning
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 6. REEXECUÇÃO AUTOMÁTICA
|
||||||
|
|
||||||
|
DESYNC_MIRROR\_\* → Passo 4.1 / 5 DESYNC_DKI_VIOLATION → Passo 4.2 / 5
|
||||||
|
DESYNC_BUCKET_LAYOUT → Passo 5 DESYNC_DESCRIPTION\_\* → Passo 6
|
||||||
|
DESYNC_CALLOUT_TRUNC → Passo 7 DESYNC_SITELINK_INCOMPLETE → Passo 7
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 7. STATUS FINAL
|
||||||
|
|
||||||
|
PASS_RECONCILIATION FAIL_RECONCILIATION
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# RESULTADO
|
||||||
|
|
||||||
|
A Reconciliação elimina falsos negativos estruturais, garantindo que o
|
||||||
|
pipeline HARD MATCH + DKI SAFE MODE permaneça determinístico do início
|
||||||
|
ao fim.
|
||||||
224
regras_prompt_v12_6_1.md
Normal file
224
regras_prompt_v12_6_1.md
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
# REGRAS CONSOLIDADAS --- Google Ads
|
||||||
|
|
||||||
|
## V12.6.1 --- UNIFIED ENGINE CORE COMPACT
|
||||||
|
|
||||||
|
(HARD MATCH + DKI SAFE MODE + RECONCILIATION ENGINE)
|
||||||
|
|
||||||
|
OBJETIVO: Versão compacta e determinística do ENGINE CORE. Unifica
|
||||||
|
criação, montagem, validação estrutural e reconciliação
|
||||||
|
anti-falso-negativo em um único fluxo otimizado.
|
||||||
|
|
||||||
|
Compatível com: - HARD MATCH 7/7 - DKI SAFE MODE - Anti-Truncamento -
|
||||||
|
Reconciliação Estrutural
|
||||||
|
|
||||||
|
Auditoria (GAS v6) permanece EXTERNA.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 0. PRINCÍPIOS DO ENGINE
|
||||||
|
|
||||||
|
- Execução determinística
|
||||||
|
- Espelhamento obrigatório
|
||||||
|
- DKI controlado
|
||||||
|
- Zero substring
|
||||||
|
- Reconciliação integrada
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 1. FLUXO GLOBAL
|
||||||
|
|
||||||
|
PASSO 1 → Gerar Keywords (7 por grupo) PASSO 2 → Canonizar Keywords
|
||||||
|
PASSO 3 → Normalizar Keywords PASSO 4 → Classificar Prioridade PASSO 5 →
|
||||||
|
Gerar Headlines Espelho (7/7) PASSO 6 → Gerar 01 DKI SAFE MODE PASSO 7 →
|
||||||
|
Montar 15 Títulos (05/05/05) PASSO 8 → Gerar Descrições (NO TRUNC
|
||||||
|
ENGINE) PASSO 9 → Gerar Extensões PASSO 10 → HARD LOCK VALIDATION PASSO
|
||||||
|
11 → RECONCILIATION ENGINE
|
||||||
|
|
||||||
|
RETORNAR apenas JSON estruturado.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 2. CANONIZAÇÃO
|
||||||
|
|
||||||
|
FUNÇÃO canonizar(keyword):
|
||||||
|
|
||||||
|
- remover duplicação de delimitadores
|
||||||
|
- transformar em forma jurídica titulável
|
||||||
|
- manter acentuação
|
||||||
|
- manter ordem semântica
|
||||||
|
|
||||||
|
Keyword inválida → DESCARTAR.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 3. NORMALIZAÇÃO
|
||||||
|
|
||||||
|
FUNÇÃO normalizar(texto):
|
||||||
|
|
||||||
|
- remover "\[" "\]" e '"'
|
||||||
|
- remover espaços duplicados
|
||||||
|
- permitir Capitalização Inicial
|
||||||
|
- manter acentuação
|
||||||
|
- manter ordem
|
||||||
|
|
||||||
|
Uso exclusivo para comparação.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 4. PRIORIDADE
|
||||||
|
|
||||||
|
PRIORIDADE 1: valor, custo, preço, revisão, cálculo
|
||||||
|
|
||||||
|
PRIORIDADE 2: advogado, ação, processo, consulta
|
||||||
|
|
||||||
|
PRIORIDADE 3: demais termos
|
||||||
|
|
||||||
|
Ordenação:
|
||||||
|
|
||||||
|
prioridade ASC tamanho ASC ordem original
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 5. HARD MATCH --- ESPELHOS
|
||||||
|
|
||||||
|
- 7 keywords → 7 headlines espelho obrigatórios
|
||||||
|
- headline = normalizar(canonizar(keyword))
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
|
||||||
|
- adicionar palavras
|
||||||
|
- alterar ordem
|
||||||
|
- sinônimos
|
||||||
|
- pluralização
|
||||||
|
|
||||||
|
Se keyword não contém SP → espelho não pode conter SP.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 6. DKI SAFE MODE
|
||||||
|
|
||||||
|
Formato obrigatório:
|
||||||
|
|
||||||
|
{Keyword:PADRAO_DO_GRUPO}
|
||||||
|
|
||||||
|
REGRAS:
|
||||||
|
|
||||||
|
- exatamente 1 DKI
|
||||||
|
- ≤30 caracteres
|
||||||
|
- não substitui espelhos
|
||||||
|
- não entra no Bucket Literal
|
||||||
|
- sem termos proibidos
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 7. TÍTULOS --- MONTAGEM 15 FIXOS
|
||||||
|
|
||||||
|
Bucket 1 --- Literal (5) → 5 primeiros espelhos
|
||||||
|
|
||||||
|
Bucket 2 --- CTA/Local (5) → 3 CTAs → 1 espelho (6º) → 1 DKI
|
||||||
|
|
||||||
|
Bucket 3 --- Prova (5) → 4 provas → 1 espelho (7º)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 8. DESCRIÇÕES --- NO TRUNC ENGINE
|
||||||
|
|
||||||
|
RANGE:
|
||||||
|
|
||||||
|
80 ≤ caracteres ≤ 92 Ideal: 85--90
|
||||||
|
|
||||||
|
PROCESSO:
|
||||||
|
|
||||||
|
1) gerar descrição completa
|
||||||
|
2) validar
|
||||||
|
3) se inválida → REGERAR DO ZERO
|
||||||
|
|
||||||
|
PROIBIDO:
|
||||||
|
|
||||||
|
- substring
|
||||||
|
- slice
|
||||||
|
- corte parcial
|
||||||
|
|
||||||
|
Descrição válida se:
|
||||||
|
|
||||||
|
- termina com "." ou "!"
|
||||||
|
- não termina com espaço
|
||||||
|
- não termina com palavra incompleta
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 9. EXTENSÕES
|
||||||
|
|
||||||
|
SITELINKS:
|
||||||
|
|
||||||
|
- 4 obrigatórios
|
||||||
|
- título ≤25
|
||||||
|
- description1 obrigatório
|
||||||
|
- description2 obrigatório
|
||||||
|
|
||||||
|
CALLOUTS:
|
||||||
|
|
||||||
|
- 7 obrigatórios
|
||||||
|
- ≤25 caracteres
|
||||||
|
- sem palavra incompleta
|
||||||
|
- reescrever se exceder (nunca cortar)
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 10. HARD LOCK VALIDATION
|
||||||
|
|
||||||
|
VALIDAR:
|
||||||
|
|
||||||
|
- 3 grupos
|
||||||
|
- 7 keywords por grupo
|
||||||
|
- 7 espelhos presentes
|
||||||
|
- 15 títulos totais
|
||||||
|
- DKI único
|
||||||
|
- buckets 05/05/05
|
||||||
|
- descrições válidas
|
||||||
|
- callouts completos
|
||||||
|
|
||||||
|
Falha → REEXECUTAR BLOCO.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 11. RECONCILIATION ENGINE (ANTI FALSE NEGATIVE)
|
||||||
|
|
||||||
|
R1 --- EXISTÊNCIA 7/7 (CRÍTICO) keyword_norm deve existir nos títulos
|
||||||
|
finais. Erro: DESYNC_MIRROR_MISSING
|
||||||
|
|
||||||
|
R2 --- INTEGRIDADE DO ESPELHO (CRÍTICO) headline espelho idêntica após
|
||||||
|
normalização. Erro: DESYNC_MIRROR_MUTATED
|
||||||
|
|
||||||
|
R3 --- CONTAGEM (CRÍTICO) exatamente 7 espelhos únicos. Erro:
|
||||||
|
DESYNC_MIRROR_COUNT
|
||||||
|
|
||||||
|
R4 --- DKI SAFE MODE (CRÍTICO) 1 DKI, fora do Bucket Literal. Erro:
|
||||||
|
DESYNC_DKI_VIOLATION
|
||||||
|
|
||||||
|
R5 --- BUCKET LAYOUT (ALTA) 05/05/05 obrigatório. Erro:
|
||||||
|
DESYNC_BUCKET_LAYOUT
|
||||||
|
|
||||||
|
R6 --- DESCRIÇÃO ANTI-GHOST (CRÍTICO) descrição final deve ter sido
|
||||||
|
gerada pelo engine. Erro: DESYNC_DESCRIPTION_GHOST_EDIT
|
||||||
|
|
||||||
|
R7 --- INTEGRIDADE FINAL (CRÍTICO) validar novamente range e
|
||||||
|
finalização. Erro: DESYNC_DESCRIPTION_INVALID_FINAL
|
||||||
|
|
||||||
|
R8 --- CALLOUTS (ALTA) 7 callouts completos. Erro: DESYNC_CALLOUT_TRUNC
|
||||||
|
|
||||||
|
R9 --- SITELINKS (MÉDIA) description1 e description2 obrigatórios. Erro:
|
||||||
|
DESYNC_SITELINK_INCOMPLETE
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# 12. SEVERIDADE
|
||||||
|
|
||||||
|
CRÍTICO → bloquear saída ALTA → reexecutar etapa MÉDIA → warning
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# RESULTADO
|
||||||
|
|
||||||
|
ENGINE CORE compacto, sem duplicação de regras, com HARD MATCH + DKI
|
||||||
|
SAFE + RECONCILIATION totalmente integrados.
|
||||||
20
src/Regras_old.md
Normal file
20
src/Regras_old.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
REGRAS CONSOLIDADAS PARA GOOGLE ADS (V3)
|
||||||
|
1. PALAVRAS-CHAVE (Fundo de Funil):
|
||||||
|
Gere 20 termos com alta intenção de contratação. Use [Exata] e "Frase". Limite cada termo a no máximo 25 caracteres para viabilizar o uso nos títulos.
|
||||||
|
2. NEGATIVAS:
|
||||||
|
Gere 20 termos para filtrar curiosos e buscas por gratuidade (ex: pdf, curso, modelo, jurisprudência, tcc, concurso, como fazer, gratuito).
|
||||||
|
3. TÍTULOS (Headlines) - Regra Mista e Local:
|
||||||
|
Gere 15 títulos (máx. 30 caracteres cada) seguindo esta distribuição:
|
||||||
|
05 Títulos de Palavra-chave: Devem conter obrigatoriamente a palavra-chave da Regra 1 de forma INTEGRAL e IDÊNTICA.
|
||||||
|
05 Títulos de CTA/Local: Focados em Chamadas para Ação e Relevância Local (ex: "Fale Conosco em SP", "Agende sua Consulta Hoje").
|
||||||
|
05 Títulos de Prova Social/Diferencial: Utilize dados da LP (ex: "+10 anos de experiência", "Avaliação 5.0 no Google", "Atendimento 100% Online").
|
||||||
|
4. DESCRIÇÕES (Maximização de Espaço):
|
||||||
|
Gere 4 descrições. Regra de Ouro: Tente utilizar o mais próximo possível dos 90 caracteres (mínimo 80) para ocupar mais espaço visual. Devem terminar obrigatoriamente com um ponto final ou exclamação e incluir a Proposta Única de Valor (UVP).
|
||||||
|
5. EXTENSÕES (Ativos):
|
||||||
|
4 Sitelinks: Título (até 25ch) e 2 linhas de descrição (até 35ch cada).
|
||||||
|
6 Callouts: Frases de destaque (máx. 25ch) focadas em autoridade e agilidade.
|
||||||
|
6. POLÍTICAS EDITORIAIS E PROIBIÇÕES:
|
||||||
|
Inviolável: NUNCA utilize "Grátis", "Gratuito" ou sinônimos.
|
||||||
|
Formatação: Proibido CAIXA ALTA (exceto siglas como SP, OAB). Proibido pontuação excessiva (ex: !!!).
|
||||||
|
Estilo: Use tom profissional, empático e focado em solução.
|
||||||
|
RETORNO: APENAS o JSON estruturado, sem texto adicional."""
|
||||||
Binary file not shown.
BIN
src/__pycache__/auditor.cpython-310.pyc
Normal file
BIN
src/__pycache__/auditor.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
@@ -10,9 +10,13 @@ import json
|
|||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
from pathlib import Path
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
from google import genai
|
from google import genai
|
||||||
|
|
||||||
|
# ─── Caminho do arquivo de regras ─────────────────────────────────
|
||||||
|
_RULES_FILE = Path(__file__).resolve().parent.parent / "regras_prompt.md"
|
||||||
|
|
||||||
|
|
||||||
# ─── Modelos disponíveis por provider ─────────────────────────────
|
# ─── Modelos disponíveis por provider ─────────────────────────────
|
||||||
MODELS = {
|
MODELS = {
|
||||||
@@ -78,7 +82,7 @@ def _call_openai(system_prompt: str, user_prompt: str, model: str) -> str:
|
|||||||
{"role": "user", "content": user_prompt},
|
{"role": "user", "content": user_prompt},
|
||||||
],
|
],
|
||||||
temperature=0.7,
|
temperature=0.7,
|
||||||
max_tokens=4000,
|
max_tokens=8000,
|
||||||
)
|
)
|
||||||
|
|
||||||
return response.choices[0].message.content.strip()
|
return response.choices[0].message.content.strip()
|
||||||
@@ -103,7 +107,7 @@ def _call_gemini(system_prompt: str, user_prompt: str, model: str, max_retries:
|
|||||||
config=genai.types.GenerateContentConfig(
|
config=genai.types.GenerateContentConfig(
|
||||||
system_instruction=system_prompt,
|
system_instruction=system_prompt,
|
||||||
temperature=0.7,
|
temperature=0.7,
|
||||||
max_output_tokens=4000,
|
max_output_tokens=8000,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
return response.text.strip()
|
return response.text.strip()
|
||||||
@@ -150,9 +154,21 @@ def _build_system_prompt() -> str:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_rules() -> str:
|
||||||
|
"""Carrega as regras do arquivo externo regras_prompt.md."""
|
||||||
|
if _RULES_FILE.exists():
|
||||||
|
return _RULES_FILE.read_text(encoding="utf-8")
|
||||||
|
raise FileNotFoundError(f"Arquivo de regras não encontrado: {_RULES_FILE}")
|
||||||
|
|
||||||
|
|
||||||
def _build_user_prompt(lp_content: str) -> str:
|
def _build_user_prompt(lp_content: str) -> str:
|
||||||
"""Constrói o prompt do usuário para geração de ativos."""
|
"""Constrói o prompt do usuário para geração de ativos."""
|
||||||
|
rules = _load_rules()
|
||||||
|
|
||||||
return f"""Com base no conteúdo desta Landing Page, gere ativos completos para uma campanha de Google Ads.
|
return f"""Com base no conteúdo desta Landing Page, gere ativos completos para uma campanha de Google Ads.
|
||||||
|
A campanha DEVE conter exatamente 3 Grupos de Anúncio (Ad Groups) temáticos.
|
||||||
|
Analise a LP e identifique 3 ângulos/temas distintos. Cada grupo deve ter seus próprios keywords, headlines e descriptions.
|
||||||
|
As negativas, sitelinks e callouts são compartilhados no nível da campanha.
|
||||||
|
|
||||||
=== CONTEÚDO DA LANDING PAGE ===
|
=== CONTEÚDO DA LANDING PAGE ===
|
||||||
{lp_content}
|
{lp_content}
|
||||||
@@ -161,19 +177,48 @@ def _build_user_prompt(lp_content: str) -> str:
|
|||||||
Gere o seguinte em formato JSON:
|
Gere o seguinte em formato JSON:
|
||||||
|
|
||||||
{{
|
{{
|
||||||
|
"ad_groups": [
|
||||||
|
{{
|
||||||
|
"name": "Nome descritivo do tema do grupo 1",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
{{"keyword": "exemplo de palavra-chave", "match_type": "Exact"}},
|
{{"keyword": "exemplo de palavra-chave", "match_type": "Exact"}},
|
||||||
{{"keyword": "outro exemplo", "match_type": "Phrase"}}
|
{{"keyword": "outro exemplo", "match_type": "Phrase"}}
|
||||||
],
|
],
|
||||||
"negative_keywords": [
|
|
||||||
{{"keyword": "grátis", "reason": "Atrai tráfego que não converte"}},
|
|
||||||
{{"keyword": "como fazer", "reason": "Topo de funil, não converte"}}
|
|
||||||
],
|
|
||||||
"headlines": [
|
"headlines": [
|
||||||
"Título com até 30 caracteres"
|
"Título com até 30 caracteres"
|
||||||
],
|
],
|
||||||
"descriptions": [
|
"descriptions": [
|
||||||
"Descrição com até 90 caracteres que destaca benefícios e inclui CTA"
|
"Descrição com até 90 caracteres que destaca benefícios e inclui CTA"
|
||||||
|
]
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
"name": "Nome descritivo do tema do grupo 2",
|
||||||
|
"keywords": [
|
||||||
|
{{"keyword": "palavra-chave do grupo 2", "match_type": "Exact"}}
|
||||||
|
],
|
||||||
|
"headlines": [
|
||||||
|
"Título do grupo 2"
|
||||||
|
],
|
||||||
|
"descriptions": [
|
||||||
|
"Descrição do grupo 2 com CTA e benefícios"
|
||||||
|
]
|
||||||
|
}},
|
||||||
|
{{
|
||||||
|
"name": "Nome descritivo do tema do grupo 3",
|
||||||
|
"keywords": [
|
||||||
|
{{"keyword": "palavra-chave do grupo 3", "match_type": "Phrase"}}
|
||||||
|
],
|
||||||
|
"headlines": [
|
||||||
|
"Título do grupo 3"
|
||||||
|
],
|
||||||
|
"descriptions": [
|
||||||
|
"Descrição do grupo 3 com CTA e benefícios"
|
||||||
|
]
|
||||||
|
}}
|
||||||
|
],
|
||||||
|
"negative_keywords": [
|
||||||
|
{{"keyword": "grátis", "reason": "Atrai tráfego que não converte"}},
|
||||||
|
{{"keyword": "como fazer", "reason": "Topo de funil, não converte"}}
|
||||||
],
|
],
|
||||||
"sitelinks": [
|
"sitelinks": [
|
||||||
{{"title": "Título do Sitelink", "description1": "Linha 1", "description2": "Linha 2"}}
|
{{"title": "Título do Sitelink", "description1": "Linha 1", "description2": "Linha 2"}}
|
||||||
@@ -183,44 +228,56 @@ Gere o seguinte em formato JSON:
|
|||||||
]
|
]
|
||||||
}}
|
}}
|
||||||
|
|
||||||
REGRAS OTIMIZADAS PARA GOOGLE ADS:
|
{rules}"""
|
||||||
|
|
||||||
1. PALAVRAS-CHAVE (Fundo de Funil): Gere 20 termos com alta intenção de contratação. Use [Exata] e "Frase". Limite os termos a no máximo 25 caracteres para viabilizar o uso nos títulos.
|
|
||||||
2. NEGATIVAS: Gere 20 termos que filtrem estudantes, curiosos e buscas gratuitas (ex: pdf, curso, modelo, jurisprudência, tcc).
|
|
||||||
3. TÍTULOS (Headlines): Gere 15 títulos (máx. 30 caracteres). Regra de Ouro: A palavra-chave da Regra 1 deve aparecer de forma INTEGRAL e IDENTICA no título. Se a keyword for longa, o título será apenas ela.
|
|
||||||
4. DESCRIÇÕES: Gere 4 descrições (máx. 90 caracteres). Devem terminar obrigatoriamente com um ponto final ou exclamação. Inclua uma Proposta Única de Valor (UVP).
|
|
||||||
5. EXTENSÕES (Sitelinks & Callouts):
|
|
||||||
- 4 Sitelinks (Título 25ch / Desc 35ch).
|
|
||||||
- 6 Callouts (Frases de destaque, máx 25ch) focadas em autoridade e agilidade.
|
|
||||||
6. POLÍTICAS EDITORIAIS:
|
|
||||||
- Proibido: "Grátis" e sinônimos.
|
|
||||||
- Proibido: CAIXA ALTA em palavras inteiras (exceto siglas como SP, OAB).
|
|
||||||
- Proibido: Uso excessivo de pontuação (ex: !!!).
|
|
||||||
|
|
||||||
RETORNO: Apenas o JSON estruturado."""
|
|
||||||
|
|
||||||
def _validate_and_normalize(data: dict) -> dict:
|
def _validate_and_normalize(data: dict) -> dict:
|
||||||
"""Valida e normaliza os dados retornados pela IA."""
|
"""Valida e normaliza os dados retornados pela IA (estrutura multi-grupo)."""
|
||||||
result = {
|
result = {
|
||||||
"keywords": [],
|
"ad_groups": [],
|
||||||
"negative_keywords": [],
|
"negative_keywords": [],
|
||||||
"headlines": [],
|
|
||||||
"descriptions": [],
|
|
||||||
"sitelinks": [],
|
"sitelinks": [],
|
||||||
"callouts": [],
|
"callouts": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Keywords
|
# Ad Groups — cada grupo contém keywords, headlines e descriptions
|
||||||
for kw in data.get("keywords", []):
|
for group in data.get("ad_groups", []):
|
||||||
|
if not isinstance(group, dict):
|
||||||
|
continue
|
||||||
|
|
||||||
|
normalized_group = {
|
||||||
|
"name": group.get("name", "Grupo sem nome"),
|
||||||
|
"keywords": [],
|
||||||
|
"headlines": [],
|
||||||
|
"descriptions": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Keywords do grupo
|
||||||
|
for kw in group.get("keywords", []):
|
||||||
if isinstance(kw, dict) and "keyword" in kw:
|
if isinstance(kw, dict) and "keyword" in kw:
|
||||||
result["keywords"].append({
|
normalized_group["keywords"].append({
|
||||||
"keyword": kw["keyword"],
|
"keyword": kw["keyword"],
|
||||||
"match_type": kw.get("match_type", "Phrase"),
|
"match_type": kw.get("match_type", "Phrase"),
|
||||||
})
|
})
|
||||||
elif isinstance(kw, str):
|
elif isinstance(kw, str):
|
||||||
result["keywords"].append({"keyword": kw, "match_type": "Phrase"})
|
normalized_group["keywords"].append({"keyword": kw, "match_type": "Phrase"})
|
||||||
|
|
||||||
# Negative Keywords
|
# Headlines do grupo - garantir limite de 30 chars
|
||||||
|
for h in group.get("headlines", []):
|
||||||
|
if isinstance(h, str) and len(h) <= 30:
|
||||||
|
normalized_group["headlines"].append(h)
|
||||||
|
elif isinstance(h, str):
|
||||||
|
normalized_group["headlines"].append(h[:30])
|
||||||
|
|
||||||
|
# Descriptions do grupo - garantir limite de 90 chars
|
||||||
|
for d in group.get("descriptions", []):
|
||||||
|
if isinstance(d, str) and len(d) <= 90:
|
||||||
|
normalized_group["descriptions"].append(d)
|
||||||
|
elif isinstance(d, str):
|
||||||
|
normalized_group["descriptions"].append(d[:90])
|
||||||
|
|
||||||
|
result["ad_groups"].append(normalized_group)
|
||||||
|
|
||||||
|
# Negative Keywords (nível de campanha)
|
||||||
for nkw in data.get("negative_keywords", []):
|
for nkw in data.get("negative_keywords", []):
|
||||||
if isinstance(nkw, dict) and "keyword" in nkw:
|
if isinstance(nkw, dict) and "keyword" in nkw:
|
||||||
result["negative_keywords"].append({
|
result["negative_keywords"].append({
|
||||||
@@ -230,21 +287,7 @@ def _validate_and_normalize(data: dict) -> dict:
|
|||||||
elif isinstance(nkw, str):
|
elif isinstance(nkw, str):
|
||||||
result["negative_keywords"].append({"keyword": nkw, "reason": ""})
|
result["negative_keywords"].append({"keyword": nkw, "reason": ""})
|
||||||
|
|
||||||
# Headlines - garantir limite de 30 chars
|
# Sitelinks (nível de campanha)
|
||||||
for h in data.get("headlines", []):
|
|
||||||
if isinstance(h, str) and len(h) <= 30:
|
|
||||||
result["headlines"].append(h)
|
|
||||||
elif isinstance(h, str):
|
|
||||||
result["headlines"].append(h[:30])
|
|
||||||
|
|
||||||
# Descriptions - garantir limite de 90 chars
|
|
||||||
for d in data.get("descriptions", []):
|
|
||||||
if isinstance(d, str) and len(d) <= 90:
|
|
||||||
result["descriptions"].append(d)
|
|
||||||
elif isinstance(d, str):
|
|
||||||
result["descriptions"].append(d[:90])
|
|
||||||
|
|
||||||
# Sitelinks
|
|
||||||
for sl in data.get("sitelinks", []):
|
for sl in data.get("sitelinks", []):
|
||||||
if isinstance(sl, dict) and "title" in sl:
|
if isinstance(sl, dict) and "title" in sl:
|
||||||
result["sitelinks"].append({
|
result["sitelinks"].append({
|
||||||
@@ -253,7 +296,7 @@ def _validate_and_normalize(data: dict) -> dict:
|
|||||||
"description2": sl.get("description2", "")[:35],
|
"description2": sl.get("description2", "")[:35],
|
||||||
})
|
})
|
||||||
|
|
||||||
# Callouts
|
# Callouts (nível de campanha)
|
||||||
for c in data.get("callouts", []):
|
for c in data.get("callouts", []):
|
||||||
if isinstance(c, str) and len(c) <= 25:
|
if isinstance(c, str) and len(c) <= 25:
|
||||||
result["callouts"].append(c)
|
result["callouts"].append(c)
|
||||||
|
|||||||
134
src/auditor.py
Normal file
134
src/auditor.py
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
"""
|
||||||
|
Módulo de Auditoria - Google Ads Scorecard (GAS).
|
||||||
|
|
||||||
|
Recebe o JSON de uma campanha gerada e avalia a qualidade dos ativos
|
||||||
|
com base em critérios ponderados (Keyword Match, Compliance, Espaço,
|
||||||
|
Gatilhos e Ativos). Também gera uma simulação visual do anúncio (CTR).
|
||||||
|
Suporta múltiplos providers de IA (OpenAI e Google Gemini).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from src.ai_generator import _call_openai, _call_gemini, _clean_json_response, MODELS
|
||||||
|
|
||||||
|
# ─── Caminho do arquivo de regras de auditoria ────────────────────
|
||||||
|
_RULES_FILE = Path(__file__).resolve().parent.parent / "regras_auditoria.md"
|
||||||
|
|
||||||
|
|
||||||
|
# ─── Prompt de auditoria ──────────────────────────────────────────
|
||||||
|
|
||||||
|
_AUDIT_SYSTEM_PROMPT = (
|
||||||
|
"Você é um Auditor Sênior de Google Ads com mais de 15 anos de experiência. "
|
||||||
|
"Seu papel é analisar JSONs de campanhas e atribuir uma nota rigorosa de 0 a 10, "
|
||||||
|
"avaliando cada critério individualmente. Seja específico nas falhas e nas "
|
||||||
|
"sugestões de correção. Responda SEMPRE em formato JSON válido, "
|
||||||
|
"sem markdown, sem blocos de código. Apenas o JSON puro."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _load_rules() -> str:
|
||||||
|
"""Carrega as regras de auditoria do arquivo externo regras_auditoria.md."""
|
||||||
|
if _RULES_FILE.exists():
|
||||||
|
return _RULES_FILE.read_text(encoding="utf-8")
|
||||||
|
raise FileNotFoundError(f"Arquivo de regras de auditoria não encontrado: {_RULES_FILE}")
|
||||||
|
|
||||||
|
|
||||||
|
def _build_audit_user_prompt(campaign_json: str) -> str:
|
||||||
|
"""Constrói o prompt do usuário para auditoria da campanha (multi-grupo)."""
|
||||||
|
rules = _load_rules()
|
||||||
|
|
||||||
|
return f"""Aja como Auditor de Google Ads. Analise o JSON de campanha abaixo e gere uma avaliação completa.
|
||||||
|
A campanha possui estrutura multi-grupo (ad_groups). Avalie TODOS os grupos e gere uma nota consolidada.
|
||||||
|
|
||||||
|
=== JSON DA CAMPANHA ===
|
||||||
|
{campaign_json}
|
||||||
|
=== FIM DO JSON ===
|
||||||
|
|
||||||
|
{rules}"""
|
||||||
|
|
||||||
|
|
||||||
|
def audit_campaign(
|
||||||
|
campaign_json: str,
|
||||||
|
provider: str = "OpenAI",
|
||||||
|
model: str = "gpt-4o-mini",
|
||||||
|
) -> tuple[dict, dict]:
|
||||||
|
"""
|
||||||
|
Audita uma campanha Google Ads a partir do JSON dos ativos.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
campaign_json: JSON string da campanha a ser auditada.
|
||||||
|
provider: Provider de IA ("OpenAI" ou "Gemini").
|
||||||
|
model: Modelo a ser usado (deve ser compatível com o provider).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Tupla com:
|
||||||
|
- audit_result: Dicionário com nota_final, criterios, resumo, simulacao.
|
||||||
|
- prompts: Dicionário com system_prompt e user_prompt usados.
|
||||||
|
"""
|
||||||
|
user_prompt = _build_audit_user_prompt(campaign_json)
|
||||||
|
|
||||||
|
prompts = {
|
||||||
|
"system_prompt": _AUDIT_SYSTEM_PROMPT,
|
||||||
|
"user_prompt": user_prompt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if provider == "OpenAI":
|
||||||
|
raw_response = _call_openai(_AUDIT_SYSTEM_PROMPT, user_prompt, model)
|
||||||
|
elif provider == "Gemini":
|
||||||
|
raw_response = _call_gemini(_AUDIT_SYSTEM_PROMPT, user_prompt, model)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Provider não suportado: {provider}")
|
||||||
|
|
||||||
|
raw_response = _clean_json_response(raw_response)
|
||||||
|
result = json.loads(raw_response)
|
||||||
|
audit_result = _validate_audit_result(result)
|
||||||
|
|
||||||
|
return audit_result, prompts
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_audit_result(data: dict) -> dict:
|
||||||
|
"""Valida e normaliza o resultado da auditoria retornado pela IA (multi-grupo)."""
|
||||||
|
result = {
|
||||||
|
"nota_final": float(data.get("nota_final", 0)),
|
||||||
|
"criterios": [],
|
||||||
|
"resumo": data.get("resumo", "Sem resumo disponível."),
|
||||||
|
"simulacoes_anuncio": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
for criterio in data.get("criterios", []):
|
||||||
|
if isinstance(criterio, dict) and "nome" in criterio:
|
||||||
|
result["criterios"].append({
|
||||||
|
"nome": criterio.get("nome", ""),
|
||||||
|
"peso": float(criterio.get("peso", 0)),
|
||||||
|
"nota": float(criterio.get("nota", 0)),
|
||||||
|
"nota_maxima": float(criterio.get("nota_maxima", 0)),
|
||||||
|
"falhas": criterio.get("falhas", []),
|
||||||
|
"sugestoes": criterio.get("sugestoes", []),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Simulações de anúncio — uma por grupo
|
||||||
|
for sim in data.get("simulacoes_anuncio", []):
|
||||||
|
if isinstance(sim, dict):
|
||||||
|
result["simulacoes_anuncio"].append({
|
||||||
|
"grupo": sim.get("grupo", ""),
|
||||||
|
"titulo_linha_1": sim.get("titulo_linha_1", ""),
|
||||||
|
"titulo_linha_2": sim.get("titulo_linha_2", ""),
|
||||||
|
"titulo_linha_3": sim.get("titulo_linha_3", ""),
|
||||||
|
"url_display": sim.get("url_display", "www.exemplo.com.br"),
|
||||||
|
"descricao": sim.get("descricao", ""),
|
||||||
|
})
|
||||||
|
|
||||||
|
# Fallback: compatibilidade com formato antigo (simulacao_anuncio singular)
|
||||||
|
if not result["simulacoes_anuncio"] and "simulacao_anuncio" in data:
|
||||||
|
sim = data["simulacao_anuncio"]
|
||||||
|
if isinstance(sim, dict):
|
||||||
|
result["simulacoes_anuncio"].append({
|
||||||
|
"grupo": sim.get("grupo", "Grupo único"),
|
||||||
|
"titulo_linha_1": sim.get("titulo_linha_1", ""),
|
||||||
|
"titulo_linha_2": sim.get("titulo_linha_2", ""),
|
||||||
|
"titulo_linha_3": sim.get("titulo_linha_3", ""),
|
||||||
|
"url_display": sim.get("url_display", "www.exemplo.com.br"),
|
||||||
|
"descricao": sim.get("descricao", ""),
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
"""
|
"""
|
||||||
Módulo de Exportação - Gera arquivos CSV compatíveis com Google Ads Editor.
|
Módulo de Exportação - Gera arquivos CSV compatíveis com Google Ads Editor.
|
||||||
|
|
||||||
Recebe os ativos gerados pela IA e os formata em DataFrames do Pandas,
|
Recebe os ativos gerados pela IA (estrutura multi-grupo) e os formata
|
||||||
prontos para importação no Google Ads Editor.
|
em DataFrames do Pandas, prontos para importação no Google Ads Editor.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
|
|
||||||
def create_keywords_df(assets: dict, campaign_name: str = "Campanha LP", ad_group: str = "Grupo 1") -> pd.DataFrame:
|
def create_keywords_df(assets: dict, campaign_name: str = "Campanha LP") -> pd.DataFrame:
|
||||||
"""Cria DataFrame de palavras-chave no formato Google Ads Editor."""
|
"""Cria DataFrame de palavras-chave no formato Google Ads Editor (multi-grupo)."""
|
||||||
rows = []
|
rows = []
|
||||||
for kw in assets.get("keywords", []):
|
for group in assets.get("ad_groups", []):
|
||||||
|
group_name = group.get("name", "Grupo")
|
||||||
|
for kw in group.get("keywords", []):
|
||||||
match_type = kw.get("match_type", "Phrase")
|
match_type = kw.get("match_type", "Phrase")
|
||||||
keyword = kw.get("keyword", "")
|
keyword = kw.get("keyword", "")
|
||||||
|
|
||||||
@@ -26,7 +28,7 @@ def create_keywords_df(assets: dict, campaign_name: str = "Campanha LP", ad_grou
|
|||||||
|
|
||||||
rows.append({
|
rows.append({
|
||||||
"Campaign": campaign_name,
|
"Campaign": campaign_name,
|
||||||
"Ad Group": ad_group,
|
"Ad Group": group_name,
|
||||||
"Keyword": formatted_kw,
|
"Keyword": formatted_kw,
|
||||||
"Match Type": match_type,
|
"Match Type": match_type,
|
||||||
"Status": "Enabled",
|
"Status": "Enabled",
|
||||||
@@ -49,14 +51,17 @@ def create_negative_keywords_df(assets: dict, campaign_name: str = "Campanha LP"
|
|||||||
return pd.DataFrame(rows)
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
def create_ads_df(assets: dict, campaign_name: str = "Campanha LP", ad_group: str = "Grupo 1") -> pd.DataFrame:
|
def create_ads_df(assets: dict, campaign_name: str = "Campanha LP") -> pd.DataFrame:
|
||||||
"""Cria DataFrame do anúncio responsivo (RSA) no formato Google Ads Editor."""
|
"""Cria DataFrame dos anúncios responsivos (RSA) no formato Google Ads Editor (multi-grupo)."""
|
||||||
headlines = assets.get("headlines", [])
|
rows = []
|
||||||
descriptions = assets.get("descriptions", [])
|
for group in assets.get("ad_groups", []):
|
||||||
|
group_name = group.get("name", "Grupo")
|
||||||
|
headlines = group.get("headlines", [])
|
||||||
|
descriptions = group.get("descriptions", [])
|
||||||
|
|
||||||
row = {
|
row = {
|
||||||
"Campaign": campaign_name,
|
"Campaign": campaign_name,
|
||||||
"Ad Group": ad_group,
|
"Ad Group": group_name,
|
||||||
"Ad Type": "Responsive Search Ad",
|
"Ad Type": "Responsive Search Ad",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +73,9 @@ def create_ads_df(assets: dict, campaign_name: str = "Campanha LP", ad_group: st
|
|||||||
for i, d in enumerate(descriptions[:4], start=1):
|
for i, d in enumerate(descriptions[:4], start=1):
|
||||||
row[f"Description {i}"] = d
|
row[f"Description {i}"] = d
|
||||||
|
|
||||||
return pd.DataFrame([row])
|
rows.append(row)
|
||||||
|
|
||||||
|
return pd.DataFrame(rows) if rows else pd.DataFrame()
|
||||||
|
|
||||||
|
|
||||||
def create_sitelinks_df(assets: dict, campaign_name: str = "Campanha LP") -> pd.DataFrame:
|
def create_sitelinks_df(assets: dict, campaign_name: str = "Campanha LP") -> pd.DataFrame:
|
||||||
@@ -97,7 +104,7 @@ def create_callouts_df(assets: dict, campaign_name: str = "Campanha LP") -> pd.D
|
|||||||
return pd.DataFrame(rows)
|
return pd.DataFrame(rows)
|
||||||
|
|
||||||
|
|
||||||
def export_all_to_excel(assets: dict, campaign_name: str = "Campanha LP", ad_group: str = "Grupo 1") -> BytesIO:
|
def export_all_to_excel(assets: dict, campaign_name: str = "Campanha LP") -> BytesIO:
|
||||||
"""
|
"""
|
||||||
Exporta todos os ativos em um único arquivo Excel com múltiplas abas.
|
Exporta todos os ativos em um único arquivo Excel com múltiplas abas.
|
||||||
|
|
||||||
@@ -107,7 +114,7 @@ def export_all_to_excel(assets: dict, campaign_name: str = "Campanha LP", ad_gro
|
|||||||
output = BytesIO()
|
output = BytesIO()
|
||||||
|
|
||||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||||
kw_df = create_keywords_df(assets, campaign_name, ad_group)
|
kw_df = create_keywords_df(assets, campaign_name)
|
||||||
if not kw_df.empty:
|
if not kw_df.empty:
|
||||||
kw_df.to_excel(writer, sheet_name="Keywords", index=False)
|
kw_df.to_excel(writer, sheet_name="Keywords", index=False)
|
||||||
|
|
||||||
@@ -115,7 +122,7 @@ def export_all_to_excel(assets: dict, campaign_name: str = "Campanha LP", ad_gro
|
|||||||
if not nkw_df.empty:
|
if not nkw_df.empty:
|
||||||
nkw_df.to_excel(writer, sheet_name="Negative Keywords", index=False)
|
nkw_df.to_excel(writer, sheet_name="Negative Keywords", index=False)
|
||||||
|
|
||||||
ads_df = create_ads_df(assets, campaign_name, ad_group)
|
ads_df = create_ads_df(assets, campaign_name)
|
||||||
if not ads_df.empty:
|
if not ads_df.empty:
|
||||||
ads_df.to_excel(writer, sheet_name="Ads RSA", index=False)
|
ads_df.to_excel(writer, sheet_name="Ads RSA", index=False)
|
||||||
|
|
||||||
@@ -131,7 +138,7 @@ def export_all_to_excel(assets: dict, campaign_name: str = "Campanha LP", ad_gro
|
|||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def export_all_to_csv(assets: dict, campaign_name: str = "Campanha LP", ad_group: str = "Grupo 1") -> dict[str, str]:
|
def export_all_to_csv(assets: dict, campaign_name: str = "Campanha LP") -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Exporta todos os ativos como strings CSV separadas.
|
Exporta todos os ativos como strings CSV separadas.
|
||||||
|
|
||||||
@@ -140,7 +147,7 @@ def export_all_to_csv(assets: dict, campaign_name: str = "Campanha LP", ad_group
|
|||||||
"""
|
"""
|
||||||
csvs = {}
|
csvs = {}
|
||||||
|
|
||||||
kw_df = create_keywords_df(assets, campaign_name, ad_group)
|
kw_df = create_keywords_df(assets, campaign_name)
|
||||||
if not kw_df.empty:
|
if not kw_df.empty:
|
||||||
csvs["keywords"] = kw_df.to_csv(index=False)
|
csvs["keywords"] = kw_df.to_csv(index=False)
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ def export_all_to_csv(assets: dict, campaign_name: str = "Campanha LP", ad_group
|
|||||||
if not nkw_df.empty:
|
if not nkw_df.empty:
|
||||||
csvs["negative_keywords"] = nkw_df.to_csv(index=False)
|
csvs["negative_keywords"] = nkw_df.to_csv(index=False)
|
||||||
|
|
||||||
ads_df = create_ads_df(assets, campaign_name, ad_group)
|
ads_df = create_ads_df(assets, campaign_name)
|
||||||
if not ads_df.empty:
|
if not ads_df.empty:
|
||||||
csvs["ads"] = ads_df.to_csv(index=False)
|
csvs["ads"] = ads_df.to_csv(index=False)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user