gerador-prompts-hoteis/lib/prompt-builder.ts

131 lines
3.8 KiB
TypeScript

import { CATEGORIES } from "./categories";
export type PromptAnswers = Record<string, string>;
type PromptSection = {
id: string;
title: string;
render: (answers: PromptAnswers) => string;
};
export type PromptTemplate = {
name: string;
intro?: string;
sections: PromptSection[];
};
const safeValue = (value?: string) => {
const cleaned = value?.trim();
return cleaned ? cleaned : "Não informado.";
};
const renderContextItem = (label: string, value?: string) =>
`- ${label}: ${safeValue(value)}`;
const buildExampleHeadline = (answers: PromptAnswers) => {
const perfil = answers.perfil?.trim();
const local = answers.localizacao?.trim();
if (perfil && local) {
return `Experimente ${perfil} em ${local}.`;
}
if (perfil) {
return `Descubra ${perfil} feito para surpreender.`;
}
if (local) {
return `Sua próxima estadia em ${local} começa aqui.`;
}
return "Uma experiência de hotelaria pensada para encantar.";
};
const buildExampleCTA = (answers: PromptAnswers) => {
const cta = answers.cta?.trim();
return cta ? cta : "Reserve agora e viva essa experiência.";
};
export const DEFAULT_PROMPT_TEMPLATE: PromptTemplate = {
name: "hotelaria-core",
intro:
"Você é um redator sênior em hotelaria. Use as informações abaixo para criar uma peça de comunicação clara, persuasiva e fiel ao briefing.",
sections: [
{
id: "objetivo",
title: "Objetivo",
render: (answers) => safeValue(answers.objetivo)
},
{
id: "contexto",
title: "Contexto",
render: (answers) => {
const lines = [
renderContextItem("Perfil do hotel", answers.perfil),
renderContextItem("Localização", answers.localizacao),
renderContextItem("Diferenciais", answers.diferenciais),
renderContextItem("Serviços e amenidades", answers.servicos),
renderContextItem("Experiência desejada", answers.experiencia),
renderContextItem("Gastronomia", answers.gastronomia),
renderContextItem("Bem-estar", answers["bem-estar"]),
renderContextItem("Sustentabilidade", answers.sustentabilidade),
renderContextItem("Sazonalidade", answers.sazonalidade)
];
return lines.join("\n");
}
},
{
id: "publico",
title: "Público",
render: (answers) => safeValue(answers["publico-alvo"])
},
{
id: "tom",
title: "Tom",
render: (answers) => safeValue(answers["tom-de-voz"])
},
{
id: "formato",
title: "Formato",
render: (answers) => [
"Idioma: português (pt-BR).",
"Estrutura: 1 headline, 1 parágrafo curto, 1 lista de até 3 diferenciais, CTA final.",
"Extensão sugerida: 90 a 130 palavras.",
`CTA preferencial: ${safeValue(answers.cta)}`
].join("\n")
},
{
id: "restricoes",
title: "Restrições",
render: (answers) => safeValue(answers.restricoes)
},
{
id: "exemplos",
title: "Exemplos",
render: (answers) =>
[
`Headline: ${buildExampleHeadline(answers)}`,
`CTA: ${buildExampleCTA(answers)}`
].join("\n")
}
]
};
export const montarPromptFinal = (
answers: PromptAnswers,
template: PromptTemplate = DEFAULT_PROMPT_TEMPLATE
) => {
const intro = template.intro ? `${template.intro}\n\n` : "";
const body = template.sections
.map((section) => {
const content = section.render(answers);
return `### ${section.title}\n${content}`;
})
.join("\n\n");
const missing = CATEGORIES.filter(
(category) => !answers[category.id]?.trim()
).map((category) => category.title);
const missingLine = missing.length
? `\n\nAtenção: faltam respostas em ${missing.join(", ")}.`
: "";
return `${intro}${body}${missingLine}`.trim();
};