Browse documentation

AI Services

RankFlow AI — Prompt Engineering: Meta Prompts, Brand Voice & Template System

Version: 2.0.0

docs/specs/ai/ai-services-02-prompts.md
On this page

Version: 2.0.0 Date: 2026-06-16 Scope: Meta Prompt System, Brand Voice Framework, Template System, Variable Schema, Prompt Versioning, A/B Testing, and Prompt Registry Target Audience: Prompt Engineers, AI Engineers, Content Strategists, Product Team Service Path: src/server/services/ai/prompts/


1. Core Philosophy#

1.1 Every Client is Unique#

The #1 rule of RankFlow AI's prompt engineering: No generic AI output reaches a customer. Every piece of content generated must be personalized through the Meta Prompt System to reflect the client's unique brand voice, business context, and audience.

1.2 Principles#

# Principle Implementation
1 Voice over Volume 10 high-quality personalized posts beat 100 generic ones
2 Context is King Every prompt includes full business context (services, city, audience, tone)
3 Templates are Data Prompts live in the database, not code. Versioned, editable, testable
4 A/B Test Everything Every prompt improvement is validated through A/B testing
5 Fallback is Safe If personalization fails, the system falls back to a generic but safe prompt
6 Medical is Separate Medical prompts have stricter rules, compliance checks, and human review gates

1.3 Content Quality Spectrum#

Generic AI Output (BAD) ──────────────────> Personalized Brand Voice (GOOD)

❌ "We are a dental clinic offering quality services."
↓
⚠️ "Dr. Smith's Dental Clinic offers comprehensive dental care."
↓
✅ "At Dr. Smith's Dental Clinic in Kochi, we blend advanced technology 
    with compassionate care — because every smile tells a story."

The difference is not just "better writing" — it's context injection via the Meta Prompt System.


2. Meta Prompt System#

2.1 What is a Meta Prompt?#

A Meta Prompt is the outer layer that wraps around every specific task prompt. It injects:

  • Client identity (name, city, category, services)
  • Brand voice characteristics (tone, formality, warmth, technicality)
  • Audience context (patients, corporate, general public, local community)
  • Content constraints (length, format, keywords, compliance rules)
  • Platform-specific rules (GBP, Instagram, LinkedIn, etc.)

2.2 Meta Prompt Structure#

┌─────────────────────────────────────────────────────────────┐
│                     META PROMPT (Outer Layer)              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  You are the content strategist for [Practice Name]. │   │
│  │  You write with [Tone] and [Formality] voice.       │   │
│  │  Your audience is [Target Audience].                 │   │
│  │  You are based in [City, State].                    │   │
│  │  Your services: [Service 1], [Service 2], ...         │   │
│  │  Your brand keywords: [Keyword 1], [Keyword 2], ...   │   │
│  │  Your content rules: [Rule 1], [Rule 2], ...          │   │
│  │  Compliance: [Medical rules if applicable]           │   │
│  └─────────────────────────────────────────────────────┘   │
│                              │                               │
│                              ▼                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │              TASK PROMPT (Inner Layer)               │   │
│  │  Generate a [Content Type] about [Topic]...          │   │
│  │  Format: [Format Rules]                              │   │
│  │  Length: [Length Rules]                                │   │
│  │  Keywords: [Target Keywords]                           │   │
│  │  Call to action: [CTA]                               │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

2.3 Meta Prompt Template (Full)#

# META PROMPT TEMPLATE

## Identity
You are the lead SEO strategist for {{practiceName}}.
{{practiceName}} is a {{category}} located in {{city}}, {{state}}.

## Brand Voice
{{voiceDescription}}

Specifically, your writing style is:
- **Tone**: {{tone}} (e.g., warm, professional, casual, authoritative)
- **Formality**: {{formalityLevel}} (e.g., formal, semi-formal, casual)
- **Warmth**: {{warmthLevel}} (e.g., warm, neutral, clinical)
- **Technicality**: {{technicalityLevel}} (e.g., technical, simplified, mixed)
- **Humor**: {{humorLevel}} (e.g., none, subtle, playful)
- **Urgency**: {{urgencyLevel}} (e.g., calm, moderate, urgent)

## Audience
Your primary audience is {{targetAudience}}.
They are {{audienceDescription}}.
Common pain points: {{audiencePainPoints}}.
What they care about: {{audienceValues}}.

## Services
{{practiceName}} offers:
{{#each services}}
- {{name}}: {{description}}
{{/each}}

## Unique Selling Points
{{#each usps}}
- {{this}}
{{/each}}

## Brand Keywords
Always incorporate these keywords naturally: {{brandKeywords}}.
Avoid: {{avoidKeywords}}.

## Local Context
- City: {{city}}
- State: {{state}}
- Local landmarks: {{localLandmarks}}
- Local language: {{localLanguage}}
- Local cultural context: {{localContext}}

## Content Rules
{{contentRules}}

## Platform-Specific Rules
{{platformRules}}

## Compliance Rules
{{#if isMedical}}
{{medicalComplianceRules}}
{{/if}}

## Task
{{taskPrompt}}

2.4 Meta Prompt Variable Schema#

// src/server/services/ai/prompts/meta-prompt.ts

interface MetaPromptVariables {
  // Identity
  practiceName: string;
  category: string;           // "Dental Clinic", "Hospital", "Restaurant", etc.
  city: string;
  state: string;
  country: string;            // "India", "UAE", etc.
  
  // Brand Voice (5-point scale, but stored as string descriptions)
  tone: string;                // "warm and approachable"
  formalityLevel: string;      // "semi-formal"
  warmthLevel: string;         // "warm"
  technicalityLevel: string;   // "simplified for general audience"
  humorLevel: string;          // "subtle, occasional"
  urgencyLevel: string;        // "calm and reassuring"
  
  // Voice Description (free text, generated during onboarding)
  voiceDescription: string;    // "You write like a trusted friend who happens to be an expert. You are warm but precise. You never use jargon without explaining it. You make people feel safe and understood."
  
  // Audience
  targetAudience: string;      // "local families seeking dental care"
  audienceDescription: string;   // "Parents aged 30-50, middle-class, value quality and safety over price"
  audiencePainPoints: string[];  // ["fear of dental procedures", "concern about cost", "lack of time"]
  audienceValues: string[];    // ["family safety", "quality care", "transparent pricing"]
  
  // Services
  services: {
    name: string;
    description: string;
    keywords: string[];
  }[];
  
  // USPs
  usps: string[];              // ["24/7 emergency care", "AI-assisted diagnosis", "Home visits available"]
  
  // Keywords
  brandKeywords: string[];     // ["best dental clinic Kochi", "affordable dental care", "family dentist"]
  avoidKeywords: string[];     // ["cheap", "discount", "guarantee", "100%"]
  
  // Local Context
  localLandmarks: string[];     // ["Lulu Mall Kochi", "Marine Drive"]
  localLanguage: string;       // "Malayalam"
  localContext: string;         // "Kochi is a coastal city with a mix of traditional and modern healthcare seekers."
  
  // Content Rules
  contentRules: string;         // "Never use the word 'cheap'. Always include a call to action. Keep sentences under 25 words."
  
  // Platform Rules (dynamic per task)
  platformRules: string;        // "For Instagram: Use emojis, short sentences, engaging hooks. For LinkedIn: Use professional tone, statistics, thought leadership."
  
  // Medical Compliance
  isMedical: boolean;
  medicalComplianceRules: string; // "Never claim guaranteed cures. Always include disclaimers for medical claims. Avoid superlatives like 'best' or 'most advanced' unless substantiated."
  
  // Task-specific (injected from the Task Prompt)
  taskPrompt: string;
}

2.5 Meta Prompt Generation During Onboarding#

The Meta Prompt is generated during the onboarding interview (Admin fills a questionnaire) and stored in the PracticeBrandProfile table.

// Onboarding flow for Meta Prompt generation:
// 1. Admin fills "Brand Voice Questionnaire" (10 questions)
// 2. System generates a draft Meta Prompt using AI
// 3. Admin reviews and edits the draft
// 4. System stores the final Meta Prompt in PracticeBrandProfile
// 5. All future content generation uses this Meta Prompt

// The Brand Voice Questionnaire:
const BRAND_VOICE_QUESTIONS = [
  "How would you describe your practice's personality? (3 adjectives)",
  "How formal should your content be? (1-5 scale)",
  "How warm and personal should your content feel? (1-5 scale)",
  "How technical should your content be? (1-5 scale)",
  "What tone do you want for social media vs. website?",
  "What are your top 3 services you want to highlight?",
  "What makes your practice different from competitors?",
  "What words should NEVER appear in your content?",
  "What are your target audience's main concerns?",
  "How do you want patients to feel after reading your content?",
];

2.6 Meta Prompt Example (Generated Output)#

Input: Dr. Ananya's Dental Clinic, Kochi, Kerala

Output Meta Prompt:

You are the lead content SEO for Dr. Ananya's Dental Clinic.
Dr. Ananya's Dental Clinic is a Dental Clinic located in Kochi, Kerala.

Your writing style is:
- **Tone**: Warm, approachable, and reassuring
- **Formality**: Semi-formal (professional but not stiff)
- **Warmth**: High warmth — you care about patients as people, not just cases
- **Technicality**: Simplified for general audience — explain dental terms in plain language
- **Humor**: Subtle, occasional — a light touch when appropriate
- **Urgency**: Calm and reassuring — never alarming

Your primary audience is local families seeking dental care in Kochi.
They are parents aged 30-50, middle-class, value quality and safety over price.
Common pain points: fear of dental procedures, concern about cost, lack of time for appointments.
What they care about: family safety, quality care, transparent pricing, child-friendly environment.

Dr. Ananya's Dental Clinic offers:
- General Dentistry: Routine checkups, cleanings, and preventive care for all ages
- Cosmetic Dentistry: Teeth whitening, veneers, and smile makeovers
- Pediatric Dentistry: Child-friendly dental care with gentle approach
- Orthodontics: Braces and clear aligners for children and adults
- Emergency Dental Care: 24/7 availability for dental emergencies

Unique Selling Points:
- 24/7 emergency dental care available
- Child-friendly environment with play area
- AI-assisted diagnosis for accurate treatment planning
- Transparent pricing with no hidden costs
- Home visits available for elderly patients

Always incorporate these keywords naturally: "best dental clinic Kochi", "affordable dental care", "family dentist Kochi", "emergency dentist", "pediatric dentist Kochi".
Avoid: "cheap", "discount", "guarantee", "100% success", "permanent fix", "no side effects".

Local Context:
- City: Kochi, Kerala
- Local landmarks: Lulu Mall Kochi, Marine Drive, Fort Kochi
- Local language: Malayalam (content should be culturally sensitive)
- Local context: Kochi is a coastal city with a mix of traditional and modern healthcare seekers. Families value trust and long-term relationships with healthcare providers.

Content Rules:
- Never use the word "cheap" or "discount".
- Always include a call to action (phone number, appointment link, visit us).
- Keep sentences under 25 words for readability.
- Use active voice, not passive voice.
- Include at least one brand keyword per piece of content.
- For medical topics, always include a disclaimer: "Consult a dentist for personalized advice."
- Never make unverified claims (e.g., "best in Kerala" without evidence).
- Use patient-centric language ("we help you" not "we treat patients").
- For social media: Use emojis sparingly (1-2 per post), use engaging hooks.
- For website: Use longer-form content, SEO-optimized, structured with headings.

Compliance Rules:
- Never claim guaranteed cures or 100% success rates.
- Always include medical disclaimers for treatment-related content.
- Avoid superlatives unless substantiated with evidence.
- Use "may help" or "can improve" instead of "will cure".
- Flag any content mentioning specific medications for review.
- Ensure all medical claims are accurate and evidence-based.

3. Brand Voice Framework#

3.1 Voice Dimensions#

Each practice's voice is defined along 6 dimensions. During onboarding, the admin rates the practice on each dimension (1-5), and the system generates the appropriate language.

Dimension 1 2 3 4 5
Tone Clinical / Formal Professional Friendly Warm Conversational
Formality Very Formal Formal Semi-Formal Casual Very Casual
Warmth Cold Neutral Friendly Warm Very Warm
Technicality Highly Technical Technical Mixed Simplified Very Simple
Humor None Subtle Light Playful Humorous
Urgency Calm Gentle Moderate Urgent Very Urgent

3.2 Voice Dimension Mapping#

// src/server/services/ai/prompts/voice-framework.ts

const VOICE_DIMENSION_MAP: Record<string, Record<number, string>> = {
  tone: {
    1: "clinical and precise",
    2: "professional and informative",
    3: "friendly and approachable",
    4: "warm and engaging",
    5: "conversational and casual",
  },
  formality: {
    1: "very formal (use 'we are pleased to' rather than 'we're happy to')",
    2: "formal (use full sentences, avoid contractions)",
    3: "semi-formal (use contractions occasionally, professional but approachable)",
    4: "casual (use contractions, shorter sentences, conversational)",
    5: "very casual (use slang, emojis, short phrases)",
  },
  warmth: {
    1: "neutral and objective",
    2: "slightly friendly",
    3: "friendly and welcoming",
    4: "warm and caring",
    5: "very warm and personal (use 'we care about you', 'your family')",
  },
  technicality: {
    1: "highly technical (use industry jargon, assume expert knowledge)",
    2: "technical (use technical terms but explain briefly)",
    3: "mixed (balance technical and simple language)",
    4: "simplified (explain all technical terms in plain language)",
    5: "very simple (avoid all jargon, use everyday analogies)",
  },
  humor: {
    1: "no humor at all (strictly professional)",
    2: "subtle (occasional light touch)",
    3: "light (use occasional humor when appropriate)",
    4: "playful (use humor regularly, keep it light)",
    5: "humorous (use jokes and puns when appropriate)",
  },
  urgency: {
    1: "calm and reassuring (no urgency, take your time)",
    2: "gentle nudge (softly encourage action)",
    3: "moderate urgency (clear but not pushy)",
    4: "urgent (clear call to action, time-sensitive language)",
    5: "very urgent (act now, limited time, don't miss out)",
  },
};

3.3 Voice Examples by Practice Type#

Practice Type Tone Formality Warmth Technicality Example Opening
Luxury Dental Professional Formal Warm Simplified "At Radiant Smiles, we believe exceptional dental care is a blend of artistry and precision. Your comfort is our priority."
Budget Dental Friendly Semi-Formal Warm Very Simple "Looking for affordable dental care in Kochi? We got you! Quality treatment without breaking the bank."
Corporate Hospital Professional Formal Neutral Mixed "MediCare Hospital offers comprehensive dental services with state-of-the-art technology and experienced specialists."
Pediatric Dental Warm Casual Very Warm Very Simple "Hey little heroes! Dr. Priya's dental clinic is the fun place where smiles get brighter and fears disappear!"
Ayurvedic Clinic Warm Semi-Formal Very Warm Simplified "Welcome to Ayurveda Wellness — where ancient wisdom meets modern care. Your journey to holistic health begins here."
Tech Startup Conversational Casual Friendly Technical "We built RankFlow because SEO shouldn't be a black box. Here's how we're changing the game for local businesses."

4. Template System#

4.1 Template Hierarchy#

┌─────────────────────────────────────────────────────────────┐
│                   TEMPLATE HIERARCHY                         │
│                                                              │
│  Layer 1: GLOBAL DEFAULTS (fallback)                       │
│  ├─ Stored in: `PromptTemplate` WHERE practiceId = NULL    │
│  ├─ Version: `global-v{version}`                            │
│  └─ Fallback: Hardcoded in code (last resort)                 │
│                                                              │
│  Layer 2: PRACTICE-SPECIFIC (default)                        │
│  ├─ Stored in: `PromptTemplate` WHERE practiceId = {id}      │
│  ├─ Version: `practice-v{version}`                           │
│  └─ Generated from: Meta Prompt + Task Template               │
│                                                              │
│  Layer 3: TASK-SPECIFIC OVERRIDES                            │
│  ├─ Stored in: `PromptTemplate` WHERE practiceId = {id}    │
│  ├─ AND taskType = {task}                                    │
│  └─ Version: `task-v{version}`                              │
│                                                              │
│  Layer 4: RUNTIME OVERRIDES (request-level)                  │
│  ├─ Passed in: `GenerateRequest.systemPrompt`              │
│  └─ Priority: HIGHEST (used for A/B testing)                  │
│                                                              │
└─────────────────────────────────────────────────────────────┘

4.2 Template Resolution Algorithm#

// src/server/services/ai/prompts/template-resolver.ts

async function resolveTemplate(request: GenerateRequest): Promise<ResolvedTemplate> {
  const { task, practiceId } = request;
  
  // Priority 1: Runtime override (from request)
  if (request.systemPrompt) {
    return {
      systemPrompt: request.systemPrompt,
      userPrompt: request.prompt || await buildTaskPrompt(task, request.variables),
      source: "runtime_override",
      version: "runtime",
    };
  }
  
  // Priority 2: Task-specific practice template
  const taskTemplate = await db.promptTemplate.findFirst({
    where: { practiceId, taskType: task, isActive: true },
    orderBy: { version: "desc" },
  });
  if (taskTemplate) {
    return {
      systemPrompt: taskTemplate.systemPrompt,
      userPrompt: substituteVariables(taskTemplate.userPrompt, request.variables),
      source: "task_specific",
      version: taskTemplate.version,
    };
  }
  
  // Priority 3: Practice default template (all tasks)
  const practiceTemplate = await db.promptTemplate.findFirst({
    where: { practiceId, taskType: null, isActive: true },
    orderBy: { version: "desc" },
  });
  if (practiceTemplate) {
    const taskPrompt = await buildTaskPrompt(task, request.variables);
    return {
      systemPrompt: practiceTemplate.systemPrompt,
      userPrompt: taskPrompt,
      source: "practice_default",
      version: practiceTemplate.version,
    };
  }
  
  // Priority 4: Global default template
  const globalTemplate = await db.promptTemplate.findFirst({
    where: { practiceId: null, taskType: task, isActive: true },
    orderBy: { version: "desc" },
  });
  if (globalTemplate) {
    return {
      systemPrompt: globalTemplate.systemPrompt,
      userPrompt: substituteVariables(globalTemplate.userPrompt, request.variables),
      source: "global_default",
      version: globalTemplate.version,
    };
  }
  
  // Priority 5: Hardcoded fallback (code-level)
  const fallback = HARDCODED_PROMPTS[task];
  if (fallback) {
    return {
      systemPrompt: fallback.systemPrompt,
      userPrompt: substituteVariables(fallback.userPrompt, request.variables),
      source: "hardcoded",
      version: "fallback",
    };
  }
  
  throw new Error(`No prompt template found for task: ${task}`);
}

4.3 Template Format#

// src/server/services/ai/prompts/types.ts

interface PromptTemplate {
  id: string;
  practiceId: string | null;        // null = global
  taskType: string | null;           // null = practice default (all tasks)
  
  name: string;                      // Human-readable name
  description: string;               // What this template does
  
  // The actual prompt content
  systemPrompt: string;               // Meta Prompt + context (injected as system message)
  userPrompt: string;                // Task-specific instructions (injected as user message)
  
  // Variables used in this template (for validation)
  variables: string[];             // ["practiceName", "city", "services", ...]
  
  // Model configuration
  modelConfig: {
    model: string;                   // e.g., "claude-sonnet"
    temperature: number;             // 0.0 - 1.0
    maxTokens: number;
    jsonMode?: boolean;
  };
  
  // Versioning
  version: string;                   // "v1.0.0", "v2.1.0"
  isActive: boolean;                 // Only one active version per practice/task
  
  // A/B testing
  abTestId?: string;                 // If this is part of an A/B test
  abTestVariant?: "A" | "B";       // Which variant
  
  // Metadata
  createdBy: string;                // User ID
  createdAt: Date;
  updatedAt: Date;
}

4.4 Template Variable Substitution#

// src/server/services/ai/prompts/variables.ts

function substituteVariables(template: string, variables: Record<string, unknown>): string {
  return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
    const value = variables[key];
    if (value === undefined || value === null) {
      logger.warn({ key, template: template.substring(0, 100) }, "Missing variable in prompt template");
      return `[MISSING: ${key}]`;
    }
    if (Array.isArray(value)) {
      return value.join("\n");
    }
    if (typeof value === "object") {
      return JSON.stringify(value, null, 2);
    }
    return String(value);
  });
}

// Example:
// template: "Welcome to {{practiceName}} in {{city}}!"
// variables: { practiceName: "Dr. Smith's Clinic", city: "Kochi" }
// result: "Welcome to Dr. Smith's Clinic in Kochi!"

5. Variable Schema#

5.1 Common Variables (All Tasks)#

These variables are available for ALL tasks. The system populates them automatically from the practice profile.

Variable Type Source Example
practiceName string Practice.name "Dr. Smith's Dental Clinic"
practiceSlug string Practice.slug "dr-smith-dental"
category string Practice.category "Dental Clinic"
city string Practice.city "Kochi"
state string Practice.state "Kerala"
country string Practice.country "India"
address string Practice.address "123 MG Road, Kochi"
phone string Practice.phone "+91-9876543210"
email string Practice.email "contact@drsmithdental.com"
website string Practice.website "https://drsmithdental.com"
services array PracticeService[] [{ name: "Root Canal", description: "..." }]
usps array PracticeBrandProfile.usps ["24/7 Emergency", "AI Diagnosis"]
tone string PracticeBrandProfile.tone "warm and approachable"
targetAudience string PracticeBrandProfile.targetAudience "local families"
brandKeywords array PracticeBrandProfile.brandKeywords ["best dental clinic Kochi"]
avoidKeywords array PracticeBrandProfile.avoidKeywords ["cheap", "discount"]
isMedical boolean Practice.isMedical true
isPremium boolean Practice.isPremium false

5.2 Task-Specific Variables#

Each task has additional variables that are populated from the task context.

Task Additional Variables Source
gbp_post postType, topic, seasonalEvent, promotionDetails Request / Schedule
social_caption platform, imageDescription, postType, hashtagSet Request / Schedule
landing_page_article articleTopic, targetKeywords, wordCount, tone Request
citation_description directoryName, directoryUrl, maxLength, directoryTone Request
review_reply reviewRating, reviewText, reviewerName, platform Review
schema_markup schemaType, pageUrl, pageContent Request
blog_post blogTopic, targetKeywords, wordCount, outline Request / Schedule
content_refresh pageUrl, currentContent, refreshReason, newKeywords Request
translation targetLanguage, sourceContent, culturalNotes Request
image_prompt imageType, subject, style, platform Request
hashtag_generation postTopic, platform, trendingHashtags Request
meta_description pageTitle, pageContent, targetKeywords Request
alt_text imageDescription, imageContext, pageTopic Image Upload
report_summary reportData, timeRange, focusAreas Report
seo_audit siteUrl, auditData, priorityIssues Audit
keyword_research seedKeywords, competitorUrls, location Request
faq faqTopic, commonQuestions, serviceContext Request
aeo_content targetQuestion, currentAnswer, competitorAnswers Request

6. Prompt Versioning#

6.1 Versioning Strategy#

Version Format: v{major}.{minor}.{patch}

- MAJOR: Breaking change (e.g., new variable structure, different output format)
- MINOR: Improvement (e.g., better wording, new constraint added)
- PATCH: Fix (e.g., typo fix, missing variable added)

Example history:
v1.0.0: Initial prompt
v1.1.0: Added "local landmarks" variable
v1.1.1: Fixed typo in system prompt
v1.2.0: Added compliance rules for medical clients
v2.0.0: Changed output format from text to JSON (breaking change)

6.2 Version Lifecycle#

┌─────────────────────────────────────────────────────────────┐
│  DRAFT → ACTIVE → DEPRECATED → ARCHIVED                     │
│                                                              │
│  DRAFT: Created by admin, not yet active. Can be edited.    │
│  ACTIVE: Live prompt used for generation. Only one active    │
│          version per practice/task.                          │
│  DEPRECATED: Old version, kept for historical reference.    │
│              Not used for generation.                          │
│  ARCHIVED: Invisible in UI, kept for audit trail.           │
│                                                              │
│  Transition:                                                  │
│  DRAFT → ACTIVE: Admin clicks "Activate" (requires A/B test)│
│  ACTIVE → DEPRECATED: New version activated (auto)           │
│  DEPRECATED → ARCHIVED: After 90 days (auto)                  │
└─────────────────────────────────────────────────────────────┘

6.3 Version Comparison#

// Admin can compare two versions side-by-side:
interface VersionComparison {
  versionA: PromptTemplate;
  versionB: PromptTemplate;
  
  differences: {
    systemPrompt: DiffResult;   // Text diff (added/removed lines)
    userPrompt: DiffResult;
    variables: {                // Added/removed variables
      added: string[];
      removed: string[];
    };
    modelConfig: {              // Changed model settings
      old: ModelConfig;
      new: ModelConfig;
    };
  };
  
  // Performance metrics (if A/B test was run)
  metrics: {
    versionA: {
      generations: number;
      avgQualityScore: number;
      avgCost: number;
      complianceRate: number;
    };
    versionB: {
      generations: number;
      avgQualityScore: number;
      avgCost: number;
      complianceRate: number;
    };
  };
}

7. A/B Testing Framework#

7.1 A/B Test for Prompts#

Every prompt change goes through an A/B test before being promoted to the default.

// src/server/services/ai/ab-testing.ts

interface PromptABTest {
  id: string;
  name: string;                    // e.g., "GBP Post v2.1 vs v2.2"
  taskType: TaskType;
  practiceId: string;
  
  // Variants
  variantA: {
    promptTemplateId: string;
    weight: number;               // Traffic split (e.g., 0.5 = 50%)
  };
  variantB: {
    promptTemplateId: string;
    weight: number;
  };
  
  // Success criteria
  successCriteria: {
    metric: "quality_score" | "cost" | "compliance_rate" | "engagement";
    minImprovement: number;       // e.g., 5% improvement required
    minSampleSize: number;          // e.g., 100 generations minimum
    duration: number;               // e.g., 7 days
  };
  
  // Status
  status: "draft" | "running" | "completed" | "cancelled";
  startedAt: Date;
  endedAt: Date;
  
  // Results
  winner: "A" | "B" | "tie" | null;
  results: {
    variantA: ABMetrics;
    variantB: ABMetrics;
  };
}

interface ABMetrics {
  generations: number;
  avgQualityScore: number;
  avgCost: number;
  complianceRate: number;
  avgLatency: number;
  userFeedback: {
    positive: number;
    negative: number;
    neutral: number;
  };
}

7.2 A/B Test Flow#

┌─────────────────────────────────────────────────────────────┐
│  1. Admin creates new prompt version (DRAFT)                │
│  2. Admin clicks "Start A/B Test"                           │
│  3. System assigns traffic: 50% Variant A, 50% Variant B     │
│  4. System runs for minimum duration (7 days)               │
│  5. System collects metrics: quality, cost, compliance       │
│  6. System evaluates winner based on success criteria       │
│  7. If winner = B: Auto-promote B to ACTIVE, deprecate A   │
│  8. If winner = A: Keep A as ACTIVE, archive B             │
│  9. If tie: Extend test or keep A (default)                │
└─────────────────────────────────────────────────────────────┘

7.3 A/B Test Metrics Dashboard#

The admin dashboard shows A/B test results:

Metric Variant A Variant B Winner
Generations 150 148
Avg Quality Score 78.5 82.3 B (+4.8%)
Avg Cost/Gen $0.005 $0.005 Tie
Compliance Rate 99.2% 99.5% Tie
Avg Latency 1200ms 1100ms B
User Feedback (+) 45 52 B
User Feedback (-) 12 8 B
Overall B

8. Prompt Registry#

8.1 Registry Structure#

All prompts are stored in a centralized registry for discoverability and management.

// src/server/services/ai/prompts/registry.ts

interface PromptRegistry {
  // Global defaults (fallback)
  globals: Record<TaskType, PromptTemplate[]>;
  
  // Practice-specific prompts
  practices: Record<string, {
    default: PromptTemplate;           // Default for all tasks
    tasks: Record<TaskType, PromptTemplate[]>;
  }>;
  
  // Active A/B tests
  abTests: PromptABTest[];
  
  // Search & Discovery
  search(query: string): PromptTemplate[];
  filter(criteria: FilterCriteria): PromptTemplate[];
  
  // Management
  create(template: PromptTemplate): Promise<PromptTemplate>;
  update(id: string, changes: Partial<PromptTemplate>): Promise<PromptTemplate>;
  activate(id: string): Promise<PromptTemplate>;
  deprecate(id: string): Promise<PromptTemplate>;
  
  // Bulk operations
  clone(fromPracticeId: string, toPracticeId: string): Promise<void>;
  export(practiceId: string): Promise<string>; // Returns JSON
  import(data: string): Promise<PromptTemplate[]>;
}

8.2 Prompt Registry UI (Admin Dashboard)#

┌─────────────────────────────────────────────────────────────┐
│  Admin Dashboard > AI Prompts                              │
│                                                              │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Search: [____________________] [Filter ▼]         │   │
│  │                                                      │   │
│  │  Global Templates (7)                                │   │
│  │  ├─ gbp_post (v2.1) [ACTIVE]                        │   │
│  │  ├─ social_caption (v1.3) [ACTIVE]                   │   │
│  │  ├─ landing_page_article (v3.0) [ACTIVE]             │   │
│  │  ├─ review_reply (v1.5) [ACTIVE]                   │   │
│  │  ├─ schema_markup (v2.0) [ACTIVE]                   │   │
│  │  ├─ citation_description (v1.1) [ACTIVE]            │   │
│  │  └─ blog_post (v2.2) [ACTIVE] [A/B Testing]        │   │
│  │                                                      │   │
│  │  Practice-Specific Templates                         │   │
│  │  ├─ Dr. Smith's Dental (15 templates)              │   │
│  │  ├─ MediCare Hospital (12 templates)                │   │
│  │  ├─ Radiant Smiles (18 templates)                  │   │
│  │  └─ ...                                              │   │
│  │                                                      │   │
│  │  [+ Create New Template] [Import] [Export All]    │   │
│  └─────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────┘

9. Prompt Engineering Best Practices#

9.1 Prompt Design Rules#

# Rule Example
1 Be explicit about format "Return a JSON object with keys: title, content, keywords, cta"
2 Set length constraints "Keep the post under 300 characters. Use 2-3 sentences."
3 Provide examples "Example: '🦷✨ New smile, new you! Book your consultation today. 📞 9876543210'"
4 Use negative constraints "Do NOT use: 'cheap', 'discount', 'guarantee'."
5 Include context about audience "Your audience is middle-class parents. They care about safety and quality."
6 Specify output structure "Return as: Hook → Body → CTA"
7 Add compliance reminders "Remember: This is medical content. Include a disclaimer."
8 Use delimiters "### CONTENT START ### ... ### CONTENT END ###"
9 Test edge cases "What if the practice has no services? Handle gracefully."
10 Version and document Every prompt has a version number, changelog, and owner

9.2 Anti-Patterns#

# Anti-Pattern Why Bad Fix
1 "Write a good post" Vague, no context Use Meta Prompt + specific task instructions
2 No length constraints AI generates 500 words for a tweet Always specify max length
3 No format specification AI returns unstructured text Always specify JSON/text/markdown
4 Generic system prompt "You are a helpful assistant" Use Meta Prompt with full context
5 No compliance check Medical claims go unfiltered Always add compliance layer
6 Hardcoded prompts Can't be updated without deploy Store in database, versioned
7 No A/B testing Can't measure improvements Every change needs A/B validation
8 Ignoring token cost $0.50 per generation Set maxTokens, use cheaper models for simple tasks
9 No fallback If model fails, system breaks Always have fallback chain
10 Not tracking versions Can't debug or rollback Version every prompt, keep history

9.3 Token Optimization#

// Strategies to reduce token count (and cost):

// 1. Use concise system prompts (not verbose)
// BAD: 500-word system prompt with redundant instructions
// GOOD: 150-word system prompt with precise instructions

// 2. Use bullet points instead of prose
// BAD: "You should write in a warm tone and be friendly and use simple language..."
// GOOD: "Tone: warm. Formality: casual. Technicality: simplified."

// 3. Use examples sparingly (1-2 per prompt)
// BAD: 5 examples in the prompt (wastes tokens)
// GOOD: 1 example + "Follow this style"

// 4. Use JSON mode instead of parsing text
// BAD: "Return the title as 'Title: ...' and content as 'Content: ...'"
// GOOD: "Return JSON: { title, content, keywords }"

// 5. Cache system prompts (Claude supports prompt caching at 50% discount)
// Use the same system prompt across multiple requests to the same practice

10. Database Schema#

10.1 Prompt Tables#

-- Brand Voice Profile (per practice)
CREATE TABLE practice_brand_profiles (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  practice_id UUID NOT NULL REFERENCES practices(id) ON DELETE CASCADE,
  
  -- Voice dimensions (1-5 scale)
  tone VARCHAR(50) NOT NULL DEFAULT "friendly",
  formality_level VARCHAR(50) NOT NULL DEFAULT "semi-formal",
  warmth_level VARCHAR(50) NOT NULL DEFAULT "warm",
  technicality_level VARCHAR(50) NOT NULL DEFAULT "simplified",
  humor_level VARCHAR(50) NOT NULL DEFAULT "subtle",
  urgency_level VARCHAR(50) NOT NULL DEFAULT "calm",
  
  -- Descriptions
  voice_description TEXT,
  target_audience TEXT,
  audience_description TEXT,
  audience_pain_points TEXT[], -- JSON array
  audience_values TEXT[], -- JSON array
  
  -- USPs and keywords
  usps TEXT[], -- JSON array
  brand_keywords TEXT[], -- JSON array
  avoid_keywords TEXT[], -- JSON array
  
  -- Local context
  local_landmarks TEXT[], -- JSON array
  local_language VARCHAR(50),
  local_context TEXT,
  
  -- Content rules
  content_rules TEXT,
  platform_rules TEXT,
  
  -- Medical compliance
  is_medical BOOLEAN DEFAULT FALSE,
  medical_compliance_rules TEXT,
  
  -- Meta prompt (the fully generated meta prompt)
  meta_prompt TEXT,
  
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  
  UNIQUE(practice_id)
);

-- Prompt Templates
CREATE TABLE prompt_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  practice_id UUID REFERENCES practices(id) ON DELETE CASCADE, -- NULL = global
  task_type VARCHAR(50), -- NULL = practice default (all tasks)
  
  name VARCHAR(200) NOT NULL,
  description TEXT,
  
  system_prompt TEXT NOT NULL,
  user_prompt TEXT NOT NULL,
  variables TEXT[], -- JSON array of variable names
  
  model_config JSONB NOT NULL DEFAULT '{"model": "claude-haiku", "temperature": 0.7, "maxTokens": 1024}',
  
  version VARCHAR(20) NOT NULL,
  is_active BOOLEAN DEFAULT FALSE,
  status VARCHAR(20) DEFAULT "draft", -- draft, active, deprecated, archived
  
  -- A/B testing
  ab_test_id UUID,
  ab_test_variant VARCHAR(1), -- 'A' or 'B'
  
  created_by UUID REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW(),
  
  -- Constraints
  UNIQUE(practice_id, task_type, version)
);

-- A/B Tests
CREATE TABLE prompt_ab_tests (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(200) NOT NULL,
  task_type VARCHAR(50) NOT NULL,
  practice_id UUID NOT NULL REFERENCES practices(id) ON DELETE CASCADE,
  
  variant_a_prompt_id UUID NOT NULL REFERENCES prompt_templates(id),
  variant_a_weight DECIMAL(3, 2) NOT NULL DEFAULT 0.5,
  variant_b_prompt_id UUID NOT NULL REFERENCES prompt_templates(id),
  variant_b_weight DECIMAL(3, 2) NOT NULL DEFAULT 0.5,
  
  success_criteria JSONB NOT NULL,
  status VARCHAR(20) DEFAULT "draft", -- draft, running, completed, cancelled
  
  started_at TIMESTAMP,
  ended_at TIMESTAMP,
  winner VARCHAR(1), -- 'A', 'B', or 'tie'
  results JSONB,
  
  created_by UUID REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW()
);

-- Hardcoded Prompts (code-level, for fallback)
-- Stored as a TypeScript constant in src/server/services/ai/prompts/hardcoded.ts
-- Not in database — these are the last-resort fallback

End of Prompt Engineering Documentation — RankFlow AI v2.0.0