✏️Content Generation Pipeline
Automate blog posts, social media, emails, and marketing content at scale
Content Generation Pipeline
Building a reliable content generation pipeline means more than calling an API once and hoping for the best. A production-grade system breaks the creative process into discrete, repeatable stages — each with its own prompt, quality gate, and feedback loop. This lesson walks through every layer of that architecture, from blog posts and social media to email campaigns and batch operations.
1. Pipeline Architecture Overview
A content pipeline is a directed graph of stages. Each stage receives structured input, applies a specialised prompt, and produces structured output that feeds the next stage.
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ BRIEF │ ──▶ │ OUTLINE │ ──▶ │ DRAFT │ ──▶ │ EDIT │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
│
▼
┌────────────┐
│ SEO / QA │
└────────────┘
│
▼
┌────────────┐
│ PUBLISH │
└────────────┘Key principles:
- Single responsibility — each stage does one thing well.
- Structured I/O — every stage reads and writes JSON, never free-form text between stages.
- Idempotency — re-running a stage with the same input produces a consistent result.
- Traceability — every output carries the prompt version and model parameters that produced it.
interface PipelineStage<TIn, TOut> {
name: string;
promptTemplate: string;
run(input: TIn): Promise<TOut>;
validate(output: TOut): boolean;
}
interface ContentBrief {
topic: string;
audience: string;
tone: string;
keywords: string[];
wordCountTarget: number;
platform: "blog" | "social" | "email";
}2. Blog Post Generation — Outline to Published Article
Stage 1: Outline Generation
The outline stage transforms a brief into a hierarchical structure with headings, sub-points, and estimated word counts per section.
const outlinePrompt = `
You are a content strategist. Given the brief below, produce a JSON outline.
Each section must include: heading, subPoints (array), estimatedWords (number).
Brief:
{{brief}}
Rules:
- Include an introduction and conclusion.
- Target total word count: {{wordCount}}.
- Optimise heading hierarchy for the keywords: {{keywords}}.
`;Stage 2: Draft Generation
Feed the outline to a drafting prompt. This prompt receives the full outline plus the brand voice guidelines and produces the raw article.
const draftPrompt = `
You are a professional writer. Expand the outline below into a full article.
Follow the brand voice: {{brandVoice}}.
Use the keywords naturally: {{keywords}}.
Write exactly one section per outline entry. Do not skip sections.
Outline:
{{outline}}
`;Stage 3: Editing Pass
A separate editing prompt reviews the draft for clarity, grammar, redundancy, and adherence to the brand voice. This prompt never sees the original brief — it judges the draft on its own merits.
const editPrompt = `
You are a senior editor. Review the draft below.
Fix grammar, remove redundancy, improve transitions.
Return the edited article plus a JSON array of changes you made with reasons.
Draft:
{{draft}}
`;Stage 4: SEO Optimisation
The SEO stage adds meta descriptions, checks keyword density, suggests internal links, and generates alt text for images.
interface SEOResult {
metaTitle: string;
metaDescription: string;
keywordDensity: Record<string, number>;
suggestedInternalLinks: string[];
readabilityScore: number;
finalArticle: string;
}3. Social Media Content — Platform-Specific Formatting
Each social platform has unique constraints. A pipeline stage maps a single core message to multiple platform-specific outputs.
interface SocialPost {
platform: "twitter" | "linkedin" | "instagram" | "facebook";
text: string;
hashtags: string[];
characterCount: number;
mediaPrompt?: string; // description for image generation
}
const socialPrompt = `
You are a social media manager. Given the core message below,
create one post for each platform. Respect character limits:
- Twitter/X: 280 characters
- LinkedIn: 3000 characters (professional tone)
- Instagram: 2200 characters (casual, emoji-friendly, hashtag-rich)
- Facebook: 500 characters (conversational)
Core message:
{{message}}
Brand voice: {{brandVoice}}
Hashtag pool: {{hashtags}}
Return a JSON array of SocialPost objects.
`;Platform-specific tips encoded in the prompt:
- Twitter/X — lead with a hook, use threads for longer content, limit to 3 hashtags.
- LinkedIn — open with a bold statement or statistic, use line breaks, end with a question.
- Instagram — front-load the caption before the fold, use 20-30 hashtags in a comment.
- Facebook — keep it conversational, ask for engagement, use 1-2 hashtags at most.
4. Email Campaign Generation
Email pipelines add personalisation and A/B testing as first-class concepts.
Personalisation with Variables
interface EmailTemplate {
subject: string;
preheader: string;
bodyHtml: string;
variables: string[]; // e.g. ["firstName", "companyName", "lastPurchase"]
}
const emailPrompt = `
You are an email marketing expert. Write an email for the campaign below.
Use the variables in double curly braces: {{variables}}.
Campaign goal: {{goal}}
Audience segment: {{segment}}
Tone: {{tone}}
Return JSON with: subject, preheader, bodyHtml.
Include at least 2 personalisation variables in the body.
`;A/B Testing Variants
Generate multiple subject lines and opening paragraphs to test:
const abTestPrompt = `
Generate 4 subject-line variants for the email below.
Vary along these axes:
- Variant A: curiosity-driven
- Variant B: benefit-driven
- Variant C: urgency-driven
- Variant D: personalisation-driven
Email body:
{{emailBody}}
Return JSON array of { variant: string, subjectLine: string, openingLine: string }.
`;5. Content Templates with Variables
Templates are reusable prompt skeletons with typed placeholders.
interface ContentTemplate {
id: string;
name: string;
description: string;
promptTemplate: string;
variables: TemplateVariable[];
outputSchema: Record<string, unknown>;
}
interface TemplateVariable {
name: string;
type: "string" | "number" | "enum" | "array";
required: boolean;
default?: unknown;
enumValues?: string[];
description: string;
}
// Example template registry
const templates: ContentTemplate[] = [
{
id: "product-description",
name: "Product Description",
description: "Generate product copy for e-commerce listings",
promptTemplate: "Write a {{tone}} product description for {{productName}}...",
variables: [
{ name: "productName", type: "string", required: true, description: "Product name" },
{ name: "tone", type: "enum", required: true, enumValues: ["professional", "casual", "luxury"], description: "Writing tone" },
{ name: "features", type: "array", required: true, description: "Key features list" },
],
outputSchema: { title: "string", shortDesc: "string", longDesc: "string", bulletPoints: "string[]" },
},
];6. Tone and Brand Voice Control
Brand voice is not a single adjective — it is a multi-dimensional profile.
interface BrandVoice {
name: string;
personality: string[]; // e.g. ["confident", "approachable", "witty"]
vocabulary: {
preferred: string[]; // words to use
avoided: string[]; // words to never use
};
sentenceStyle: string; // e.g. "Short and punchy. Fragments allowed."
formality: "casual" | "neutral" | "formal";
exampleParagraph: string; // gold-standard sample for few-shot
}
const brandVoicePrompt = `
You must write in the following brand voice:
Personality: {{personality}}
Preferred words: {{preferred}}
Avoided words: {{avoided}}
Sentence style: {{sentenceStyle}}
Formality: {{formality}}
Example of the desired voice:
"{{exampleParagraph}}"
Now write the following content matching this voice exactly:
{{contentRequest}}
`;A post-generation validation step checks that none of the avoided words appear and that the tone matches the personality descriptors.
7. Batch Content Generation
When you need 50 social posts or 20 email variants, batch processing keeps costs predictable and throughput high.
interface BatchJob {
id: string;
template: ContentTemplate;
inputs: Record<string, unknown>[];
concurrency: number;
status: "pending" | "running" | "completed" | "failed";
results: BatchResult[];
}
async function runBatch(job: BatchJob): Promise<BatchResult[]> {
const results: BatchResult[] = [];
const queue = [...job.inputs];
// Process in parallel with concurrency limit
const workers = Array.from({ length: job.concurrency }, async () => {
while (queue.length > 0) {
const input = queue.shift()!;
try {
const output = await generateContent(job.template, input);
const qualityScore = await reviewContent(output);
results.push({ input, output, qualityScore, status: "success" });
} catch (error) {
results.push({ input, output: null, qualityScore: 0, status: "error", error });
}
}
});
await Promise.all(workers);
return results;
}Batch best practices:
- Set concurrency to 3-5 to avoid rate limits.
- Log every request/response pair for audit.
- Implement exponential back-off on failures.
- Store intermediate results so a partial failure does not lose completed work.
8. Quality Control with Review Prompts
Every piece of generated content should pass through an automated review before it reaches a human approver.
const reviewPrompt = `
You are a content quality reviewer. Score the content below on a 1-10 scale
for each criterion. Return JSON.
Criteria:
- accuracy: Are all facts correct and verifiable?
- relevance: Does it address the target audience's needs?
- tone: Does it match the brand voice description?
- grammar: Is it free of grammatical errors?
- engagement: Would the target audience find this compelling?
- seo: Are the keywords used naturally and sufficiently?
Content:
{{content}}
Brand voice description:
{{brandVoice}}
Target audience:
{{audience}}
`;
interface QualityScore {
accuracy: number;
relevance: number;
tone: number;
grammar: number;
engagement: number;
seo: number;
overall: number;
suggestions: string[];
approved: boolean; // true if overall >= 7
}If the overall score falls below the threshold, the content re-enters the editing stage with the reviewer's suggestions appended to the prompt. This creates a self-correcting feedback loop.
9. Scheduling and Publishing Workflow
The final pipeline stage handles timing, approvals, and distribution.
interface PublishSchedule {
contentId: string;
platform: string;
scheduledAt: Date;
status: "draft" | "review" | "approved" | "scheduled" | "published";
approvedBy?: string;
publishedUrl?: string;
}
interface ContentCalendar {
weekStart: Date;
entries: PublishSchedule[];
generateWeekPlan(topics: string[], platforms: string[]): Promise<PublishSchedule[]>;
}
// Automated scheduling based on platform best-practice posting times
const optimalPostTimes: Record<string, string[]> = {
twitter: ["09:00", "12:00", "17:00"],
linkedin: ["08:00", "10:30", "17:30"],
instagram: ["11:00", "14:00", "19:00"],
facebook: ["09:00", "13:00", "16:00"],
};10. Practical Example — Generate a Week of Social Posts
Let us put everything together. This example generates seven days of social media content across three platforms.
async function generateWeekOfPosts(
topics: string[],
brandVoice: BrandVoice,
platforms: string[]
): Promise<PublishSchedule[]> {
const calendar: PublishSchedule[] = [];
for (let day = 0; day < 7; day++) {
const topic = topics[day % topics.length];
// Step 1: Generate core message for the day
const coreMessage = await generateContent(coreMessageTemplate, {
topic,
brandVoice: brandVoice.name,
dayOfWeek: getDayName(day),
});
// Step 2: Adapt to each platform
for (const platform of platforms) {
const post = await generateContent(socialTemplate, {
message: coreMessage,
platform,
brandVoice,
hashtags: getHashtagPool(topic),
});
// Step 3: Quality review
const review = await reviewContent(post);
if (!review.approved) {
const revised = await reviseContent(post, review.suggestions);
calendar.push(createScheduleEntry(revised, platform, day));
} else {
calendar.push(createScheduleEntry(post, platform, day));
}
}
}
return calendar;
}
// Usage
const weekPlan = await generateWeekOfPosts(
["AI productivity tips", "Customer success stories", "Product updates",
"Industry trends", "Behind the scenes", "User tips", "Weekend inspiration"],
myBrandVoice,
["twitter", "linkedin", "instagram"]
);
console.log("Generated", weekPlan.length, "posts for the week");
// Output: Generated 21 posts for the weekKey Takeaways
- Break content creation into stages — outline, draft, edit, SEO, publish.
- Use structured I/O between stages — JSON objects, not loose text.
- Encode brand voice as a multi-dimensional profile, not a single adjective.
- Automate quality control — review prompts catch issues before humans see them.
- Batch intelligently — respect rate limits, log everything, handle partial failures.
- Platform-specific formatting is not optional — each channel has its own rules.
- A/B test systematically — generate variants along defined axes.
- Schedule with data — use optimal posting times per platform.
- Build feedback loops — low-scoring content goes back through editing automatically.
- Keep templates versioned — prompt changes affect output quality; track them.