Pipeline de Conteúdo Automatizado com IA: do Prompt ao Publish com TypeScript
O problema real: conteúdo que não escala sem virar lixo
Gerar conteúdo com IA é trivial. Abrir o ChatGPT, colar um prompt e copiar o resultado leva dois minutos. O problema começa quando você precisa de volume com qualidade mínima: 20 posts por semana, cada um com metadados corretos, validação de frontmatter, checagem de links, revisão humana antes de publicar e deploy automático depois da aprovação.
Sem um pipeline, o processo é manual, frágil e inconsistente. Com um pipeline mal feito, você publica conteúdo genérico que prejudica o domínio. Este post mostra como montar a infraestrutura de automação: a parte de engenharia que transforma "chamar uma API de LLM" em um sistema previsível.
A arquitetura cobre quatro estágios: geração, validação, revisão e publicação. Cada estágio é um módulo independente conectado por fila.
Arquitetura do pipeline em quatro estágios
[Briefing Queue] → [Geração via LLM] → [Validação Programática] → [Revisão Humana] → [Publicação]
↑ |
└── Rejeição com feedback ───────────────┘
O fluxo é linear com um loop de rejeição. Se a validação falha, o item volta para a fila de geração com o motivo da falha anexado ao prompt. Se a revisão humana rejeita, o item volta com anotações do revisor. Sem esse loop, o pipeline vira um canhão de conteúdo ruim.
Cada estágio roda como um worker independente. Isso permite escalar a geração (que depende de latência da API) separadamente da validação (que é CPU-bound e rápida).
Modelagem do briefing e da fila
O briefing é o input do pipeline. Ele precisa ser estruturado o suficiente para gerar prompts consistentes, mas flexível para cobrir formatos diferentes.
// src/types/briefing.ts
export interface ContentBriefing {
id: string;
topic: string;
targetKeyword: string;
contentType: "tutorial" | "comparison" | "deep-dive" | "opinion";
targetWordCount: number;
categorySlug: string;
// Contexto extra que o LLM recebe como system prompt
additionalContext?: string;
// Controle de retry
attempt: number;
maxAttempts: number;
previousFeedback?: string;
}
export interface PipelineItem {
briefing: ContentBriefing;
generatedContent?: string;
validationResult?: ValidationResult;
status: "queued" | "generating" | "validating" | "reviewing" | "approved" | "rejected" | "published";
createdAt: Date;
updatedAt: Date;
}
Para a fila, BullMQ com Redis resolve. Se o volume é baixo (menos de 50 itens por dia), um array em SQLite com polling já funciona. A escolha depende de quantos workers concorrentes você precisa.
// src/queue/setup.ts
import { Queue, Worker } from "bullmq";
import IORedis from "ioredis";
// Conexão separada para queue e worker evita bloqueio
// no subscriber do Redis quando a queue publica eventos
const connection = new IORedis({
host: process.env.REDIS_HOST ?? "localhost",
port: Number(process.env.REDIS_PORT ?? 6379),
maxRetriesPerRequest: null,
});
export const generationQueue = new Queue("content-generation", {
connection,
defaultJobOptions: {
attempts: 3,
backoff: { type: "exponential", delay: 5000 },
removeOnComplete: { count: 100 },
},
});
export const validationQueue = new Queue("content-validation", {
connection,
});
export const publishQueue = new Queue("content-publish", {
connection,
});
Se você já trabalha com filas em Node.js, o padrão é familiar. Para quem usa Docker no ambiente de desenvolvimento, a configuração do Redis fica simples com docker-compose.
Geração: chamando o LLM com controle
O worker de geração recebe o briefing, monta o prompt e chama a API. A parte que a maioria dos tutoriais ignora: controle de custo, timeout e tratamento de respostas malformadas.
// src/workers/generation.worker.ts
import { Worker, Job } from "bullmq";
import OpenAI from "openai";
import { ContentBriefing, PipelineItem } from "../ty
---
Leia o artigo completo em [https://www.vivodecodigo.com.br/backend/pipeline-conteudo-automatizado-ia-typescript-openai](https://www.vivodecodigo.com.br/backend/pipeline-conteudo-automatizado-ia-typescript-openai)