AI Services
RankFlow AI — Prompt Engineering: Meta Prompts, Brand Voice & Template System
Version: 2.0.0
docs/specs/ai/ai-services-02-prompts.mdOn this page
- 1. Core Philosophy
- 1.1 Every Client is Unique
- 1.2 Principles
- 1.3 Content Quality Spectrum
- 2. Meta Prompt System
- 2.1 What is a Meta Prompt?
- 2.2 Meta Prompt Structure
- 2.3 Meta Prompt Template (Full)
- 2.4 Meta Prompt Variable Schema
- 2.5 Meta Prompt Generation During Onboarding
- 2.6 Meta Prompt Example (Generated Output)
- 3. Brand Voice Framework
- 3.1 Voice Dimensions
- 3.2 Voice Dimension Mapping
- 3.3 Voice Examples by Practice Type
- 4. Template System
- 4.1 Template Hierarchy
- 4.2 Template Resolution Algorithm
- 4.3 Template Format
- 4.4 Template Variable Substitution
- 5. Variable Schema
- 5.1 Common Variables (All Tasks)
- 5.2 Task-Specific Variables
- 6. Prompt Versioning
- 6.1 Versioning Strategy
- 6.2 Version Lifecycle
- 6.3 Version Comparison
- 7. A/B Testing Framework
- 7.1 A/B Test for Prompts
- 7.2 A/B Test Flow
- 7.3 A/B Test Metrics Dashboard
- 8. Prompt Registry
- 8.1 Registry Structure
- 8.2 Prompt Registry UI (Admin Dashboard)
- 9. Prompt Engineering Best Practices
- 9.1 Prompt Design Rules
- 9.2 Anti-Patterns
- 9.3 Token Optimization
- 10. Database Schema
- 10.1 Prompt Tables
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