AI Services
RankFlow AI — Medical Compliance & Safety Guardrails
Version: 2.0.0
docs/specs/ai/ai-services-06-compliance.mdOn this page
- 1. Compliance Philosophy
- 1.1 Non-Negotiable Rules
- 1.2 Compliance Layers
- 2. Medical Compliance Framework
- 2.1 Medical Practice Categories
- 2.2 Medical Practice Detection
- 3. Banned Phrase Detection
- 3.1 Banned Phrase Registry
- 3.2 Banned Phrase Detection Algorithm
- 4. Content Approval Gates
- 4.1 Approval Gate Architecture
- 4.2 Approval State Machine
- 4.3 Approval SLA Configuration
- 5. Medical Disclaimer System
- 5.1 Disclaimer Templates
- 5.2 Disclaimer Placement Rules
- 6. HIPAA & Data Privacy
- 6.1 HIPAA Considerations
- 6.2 Data Privacy in AI Generation
- 7. Compliance Evaluation Pipeline
- 7.1 Compliance Pipeline Steps
- 7.2 Compliance Judge Prompt
- 8. Alert & Escalation System
- 8.1 Alert Thresholds
- 8.2 Escalation Matrix
- 8.3 Alert Messages
- 9. Compliance Audit Trail
- 9.1 Audit Trail Requirements
- 9.2 Audit Trail Queries
- 9.3 Monthly Compliance Report
- 10. Database Schema
Version: 2.0.0 Date: 2026-06-16 Scope: Medical compliance filters, safety guardrails, banned phrase detection, content approval gates, HIPAA considerations, disclaimer system, and audit trails Target Audience: AI Engineers, Compliance Officers, Content Strategists, Legal Team Service Path:
src/server/services/ai/compliance/,src/server/services/ai/safety/
1. Compliance Philosophy#
1.1 Non-Negotiable Rules#
| # | Rule | Enforcement |
|---|---|---|
| 1 | No medical claims without evidence | Auto-reject + flag |
| 2 | No guaranteed cure promises | Auto-reject + alert |
| 3 | Medical content requires disclaimer | Auto-append if missing |
| 4 | Medical content requires human review | 24h approval queue |
| 5 | All banned phrases are blocked | Regex + keyword scan |
| 6 | All compliance failures are logged | Permanent audit trail |
| 7 | No PHI in AI prompts | PII detection + sanitization |
| 8 | AI-generated medical content is advisory only | Disclaimer system |
1.2 Compliance Layers#
┌─────────────────────────────────────────────────────────────┐
│ COMPLIANCE DEFENSE IN DEPTH │
│ │
│ Layer 1: Prompt-Level Prevention │
│ ├─ System prompt includes compliance rules │
│ ├─ Negative constraints ("Do NOT use...") │
│ └─ Example: "Never claim guaranteed cures" │
│ │
│ Layer 2: Generation-Time Filtering │
│ ├─ Banned phrase regex scan (fast, < 1ms) │
│ ├─ Medical keyword detection │
│ └─ Real-time blocking of problematic content │
│ │
│ Layer 3: Post-Generation Compliance Check │
│ ├─ LLM-as-judge compliance evaluation │
│ ├─ Medical claim verification │
│ └─ False claim detection │
│ │
│ Layer 4: Human Review Gate │
│ ├─ All medical content queued for 24h review │
│ ├─ Admin approval required before publish │
│ └─ Rejection triggers regeneration │
│ │
│ Layer 5: Publication Safeguards │
│ ├─ Disclaimer auto-appended to medical content │
│ ├─ No-edit protection (content can't be modified post-approval)│
│ └─ Version control (all changes tracked) │
│ │
│ Layer 6: Monitoring & Audit │
│ ├─ Real-time compliance metrics dashboard │
│ ├─ Alert on any compliance failure │
│ └─ Monthly compliance audit report │
└─────────────────────────────────────────────────────────────┘
2. Medical Compliance Framework#
2.1 Medical Practice Categories#
All practices are categorized by medical sensitivity:
| Category | Examples | Compliance Level | Human Review Required |
|---|---|---|---|
| High-Risk | Hospitals, Surgical Centers, Oncology | Strict | Always (24h queue) |
| Medium-Risk | Dental Clinics, Dermatology, Physiotherapy | Standard | Yes (if medical claims) |
| Low-Risk | General Clinics, Wellness Centers, Ayurveda | Standard | Yes (if medical claims) |
| Non-Medical | Restaurants, Retail, Schools, IT Services | Minimal | No (unless edge case) |
2.2 Medical Practice Detection#
// src/server/services/ai/compliance/detection.ts
// Medical practice detection based on category and keywords:
const MEDICAL_CATEGORIES = [
"CLINIC", "HOSPITAL", "DOCTOR", "DENTAL", "DENTIST",
"PHYSIOTHERAPY", "PHYSIOTHERAPIST", "DERMATOLOGY", "DERMATOLOGIST",
"ONCOLOGY", "ONCOLOGIST", "CARDIOLOGY", "CARDIOLOGIST",
"ORTHOPEDICS", "ORTHOPEDIST", "PEDIATRICS", "PEDIATRICIAN",
"GYNECOLOGY", "GYNECOLOGIST", "OPHTHALMOLOGY", "OPHTHALMOLOGIST",
"ENT", "ENT_SPECIALIST", "NEUROLOGY", "NEUROLOGIST",
"PSYCHIATRY", "PSYCHIATRIST", "PSYCHOLOGY", "PSYCHOLOGIST",
"AYURVEDA", "AYURVEDIC", "HOMEOPATHY", "HOMEOPATH",
"NATUROPATHY", "WELLNESS", "REHABILITATION", "DIAGNOSTIC_CENTER",
"PATHOLOGY", "RADIOLOGY", "PHARMACY", "MEDICAL_STORE",
"NURSING_HOME", "CARE_CENTER", "MATERNITY",
];
const MEDICAL_KEYWORDS = [
"treatment", "cure", "medicine", "medication", "diagnosis",
"surgery", "operation", "procedure", "therapy", "therapeutic",
"healing", "patient", "doctor", "physician", "specialist",
"prescription", "drug", "dose", "dosage", "side effect",
"symptom", "disease", "condition", "disorder", "syndrome",
"chronic", "acute", "severe", "mild", "moderate",
"recovery", "rehabilitation", "prognosis", "diagnosis",
];
function isMedicalPractice(practice: Practice): boolean {
// Check category
if (MEDICAL_CATEGORIES.includes(practice.category.toUpperCase())) {
return true;
}
// Check service names
if (practice.services.some(s =>
MEDICAL_KEYWORDS.some(kw => s.name.toLowerCase().includes(kw))
)) {
return true;
}
// Check brand keywords
if (practice.brandKeywords.some(kw =>
MEDICAL_KEYWORDS.some(mk => kw.toLowerCase().includes(mk))
)) {
return true;
}
return false;
}
// Content-level medical detection:
function containsMedicalClaims(content: string): boolean {
const medicalPatterns = [
/\b(treat|treatment|treating)\b.*\b(cure|curing|heal|healing)\b/,
/\b(diagnose|diagnosis|diagnosing)\b/,
/\b(surgery|surgical|operation|operative)\b/,
/\b(medicine|medication|drug|prescription)\b/,
/\b(side effect|side effects|adverse effect)\b/,
/\b(symptom|symptoms|sign|signs)\b/,
/\b(disease|diseases|condition|conditions|disorder)\b/,
];
return medicalPatterns.some(pattern => pattern.test(content.toLowerCase()));
}
3. Banned Phrase Detection#
3.1 Banned Phrase Registry#
// src/server/services/ai/compliance/banned-phrases.ts
interface BannedPhrase {
phrase: string;
severity: "CRITICAL" | "HIGH" | "MEDIUM" | "LOW";
category: "medical_claim" | "guarantee" | "false_promise" | "superlative" | "sensitive";
action: "REJECT" | "FLAG" | "WARN";
reason: string;
alternatives?: string[]; // Suggested alternatives
}
const BANNED_PHRASES: BannedPhrase[] = [
// CRITICAL: Medical claims without evidence
{ phrase: "guaranteed cure", severity: "CRITICAL", category: "medical_claim", action: "REJECT", reason: "Cannot guarantee medical outcomes" },
{ phrase: "100% success", severity: "CRITICAL", category: "guarantee", action: "REJECT", reason: "No medical procedure has 100% success rate" },
{ phrase: "permanent fix", severity: "CRITICAL", category: "guarantee", action: "REJECT", reason: "Medical outcomes are not permanent guarantees" },
{ phrase: "no side effects", severity: "CRITICAL", category: "false_promise", action: "REJECT", reason: "All medical procedures have potential side effects" },
{ phrase: "miracle treatment", severity: "CRITICAL", category: "medical_claim", action: "REJECT", reason: "Miracle claims are unsubstantiated" },
{ phrase: "instant results", severity: "CRITICAL", category: "false_promise", action: "REJECT", reason: "Medical results take time" },
{ phrase: "never fails", severity: "CRITICAL", category: "guarantee", action: "REJECT", reason: "No treatment never fails" },
{ phrase: "completely safe", severity: "CRITICAL", category: "false_promise", action: "REJECT", reason: "No procedure is completely risk-free" },
{ phrase: "risk-free", severity: "CRITICAL", category: "false_promise", action: "REJECT", reason: "Medical procedures carry risks" },
{ phrase: "doctor recommended", severity: "CRITICAL", category: "medical_claim", action: "REJECT", reason: "Must attribute to specific doctor if used" },
// HIGH: Strong superlatives without evidence
{ phrase: "best in", severity: "HIGH", category: "superlative", action: "FLAG", reason: "Superlative requires evidence", alternatives: ["highly rated", "well-regarded", "trusted by many"] },
{ phrase: "most advanced", severity: "HIGH", category: "superlative", action: "FLAG", reason: "Requires evidence of advancement", alternatives: ["modern", "up-to-date", "latest technology"] },
{ phrase: "most experienced", severity: "HIGH", category: "superlative", action: "FLAG", reason: "Requires evidence of experience", alternatives: ["experienced", "skilled", "well-trained"] },
{ phrase: "number one", severity: "HIGH", category: "superlative", action: "FLAG", reason: "Requires ranking evidence", alternatives: ["leading", "top-rated", "highly recommended"] },
{ phrase: "top-rated", severity: "HIGH", category: "superlative", action: "FLAG", reason: "Requires rating evidence", alternatives: ["well-rated", "positively reviewed", "appreciated by patients"] },
// MEDIUM: Potentially misleading
{ phrase: "cheap", severity: "MEDIUM", category: "sensitive", action: "WARN", reason: "May imply low quality", alternatives: ["affordable", "cost-effective", "budget-friendly"] },
{ phrase: "discount", severity: "MEDIUM", category: "sensitive", action: "WARN", reason: "May devalue services", alternatives: ["special offer", "promotion", "limited-time savings"] },
{ phrase: "free consultation", severity: "MEDIUM", category: "sensitive", action: "WARN", reason: "Ensure consultation is truly free", alternatives: ["complimentary consultation", "no-obligation consultation"] },
{ phrase: "quick fix", severity: "MEDIUM", category: "false_promise", action: "WARN", reason: "Medical issues require proper treatment", alternatives: ["effective treatment", "efficient care", "timely solution"] },
// LOW: Style preferences
{ phrase: "in today's world", severity: "LOW", category: "sensitive", action: "WARN", reason: "Generic opening phrase", alternatives: ["nowadays", "currently", "in modern practice"] },
{ phrase: "we are a", severity: "LOW", category: "sensitive", action: "WARN", reason: "Generic AI-sounding opening", alternatives: ["At [Practice Name], we", "[Practice Name] offers"] },
];
// Regex compilation for fast matching:
const BANNED_PHRASE_REGEX = new RegExp(
BANNED_PHRASES.map(bp => `\\b${bp.phrase.replace(/\s+/g, '\\s+')}\\b`).join('|'),
'gi'
);
3.2 Banned Phrase Detection Algorithm#
// Fast detection (< 1ms for typical content):
function detectBannedPhrases(content: string): DetectionResult {
const matches: BannedPhraseMatch[] = [];
for (const phrase of BANNED_PHRASES) {
const regex = new RegExp(`\\b${phrase.phrase.replace(/\s+/g, '\\s+')}\\b`, 'gi');
const match = regex.exec(content);
if (match) {
matches.push({
phrase: phrase.phrase,
severity: phrase.severity,
category: phrase.category,
action: phrase.action,
reason: phrase.reason,
position: match.index,
alternatives: phrase.alternatives,
});
}
}
const hasCritical = matches.some(m => m.severity === "CRITICAL");
const hasReject = matches.some(m => m.action === "REJECT");
return {
matches,
passed: !hasReject,
severity: hasCritical ? "CRITICAL" : matches.some(m => m.severity === "HIGH") ? "HIGH" : "MEDIUM",
requiresAction: hasReject || matches.some(m => m.action === "FLAG"),
};
}
// Auto-correction (for non-critical matches):
function autoCorrectBannedPhrases(content: string): string {
let corrected = content;
for (const phrase of BANNED_PHRASES) {
if (phrase.action === "WARN" && phrase.alternatives) {
const regex = new RegExp(`\\b${phrase.phrase.replace(/\s+/g, '\\s+')}\\b`, 'gi');
corrected = corrected.replace(regex, phrase.alternatives[0]);
}
}
return corrected;
}
4. Content Approval Gates#
4.1 Approval Gate Architecture#
┌─────────────────────────────────────────────────────────────┐
│ CONTENT APPROVAL GATES │
│ │
│ Gate 1: Auto-Generation (AI) │
│ ├─ Content generated by AI │
│ ├─ Initial quality score calculated │
│ └─ Compliance check performed │
│ │
│ Gate 2: Auto-Evaluation (Automated) │
│ ├─ Banned phrase scan → PASS / FAIL │
│ ├─ Medical claim detection → PASS / FLAG │
│ ├─ Quality score → ≥ 70 PASS, < 70 QUEUE │
│ └─ Compliance status → PASS / WARN / FAIL │
│ │
│ Gate 3: Auto-Approval (Non-Medical) │
│ ├─ If non-medical + score ≥ 70 → AUTO-PUBLISH │
│ ├─ If non-medical + score 60-69 → AUTO-PUBLISH + FLAG │
│ └─ If non-medical + score < 60 → QUEUE for review │
│ │
│ Gate 4: Medical Review Queue (Medical) │
│ ├─ If medical content → ALWAYS queue for 24h review │
│ ├─ Admin receives notification (email + Slack) │
│ ├─ Admin reviews in dashboard │
│ └─ Admin approves / rejects / edits │
│ │
│ Gate 5: SLA Auto-Resolution │
│ ├─ If 24h passes and no admin action: │
│ │ ├─ Score ≥ 80 → AUTO-PUBLISH │
│ │ └─ Score < 80 → AUTO-REJECT + ALERT │
│ └─ Admin can override auto-resolution │
│ │
│ Gate 6: Publication │
│ ├─ Content published to platform (GBP, social, website) │
│ ├─ Disclaimer auto-appended (if medical) │
│ └─ Audit log created │
└─────────────────────────────────────────────────────────────┘
4.2 Approval State Machine#
// Content approval states:
enum ContentApprovalStatus {
DRAFT = "draft", // AI generated, not yet evaluated
PENDING_EVALUATION = "pending_evaluation", // Automated evaluation in progress
PENDING_REVIEW = "pending_review", // Queued for human review
APPROVED = "approved", // Human approved
REJECTED = "rejected", // Human rejected or auto-rejected
AUTO_PUBLISHED = "auto_published", // Auto-published (non-medical, score ≥ 70)
SLA_AUTO_PUBLISHED = "sla_auto_published", // Auto-published after 24h SLA
SLA_AUTO_REJECTED = "sla_auto_rejected", // Auto-rejected after 24h SLA (score < 80)
PUBLISHED = "published", // Live on platform
EDITED = "edited", // Edited after approval, needs re-review
ARCHIVED = "archived", // No longer active
}
// State transitions:
// DRAFT → PENDING_EVALUATION (auto)
// PENDING_EVALUATION → PENDING_REVIEW (if medical or score < 70)
// PENDING_EVALUATION → AUTO_PUBLISHED (if non-medical and score ≥ 70)
// PENDING_REVIEW → APPROVED (admin action)
// PENDING_REVIEW → REJECTED (admin action)
// PENDING_REVIEW → SLA_AUTO_PUBLISHED (24h passed, score ≥ 80)
// PENDING_REVIEW → SLA_AUTO_REJECTED (24h passed, score < 80)
// APPROVED → PUBLISHED (auto, after approval)
// AUTO_PUBLISHED → PUBLISHED (auto)
// PUBLISHED → EDITED (if content modified)
// EDITED → PENDING_REVIEW (re-queue for review)
4.3 Approval SLA Configuration#
const APPROVAL_SLA = {
medical: {
reviewWindow: 24 * 60 * 60 * 1000, // 24 hours
autoPublishThreshold: 80, // Score ≥ 80 auto-publishes after SLA
autoRejectThreshold: 60, // Score < 60 auto-rejects after SLA
notifyBefore: 4 * 60 * 60 * 1000, // Notify 4 hours before SLA
escalationAfter: 12 * 60 * 60 * 1000, // Escalate to manager after 12h
},
nonMedical: {
reviewWindow: 24 * 60 * 60 * 1000, // 24 hours (optional review)
autoPublishThreshold: 70, // Score ≥ 70 auto-publishes immediately
autoPublishWithFlag: 60, // Score 60-69 auto-publishes with flag
queueBelow: 60, // Score < 60 queues for review
},
};
// Notification schedule for medical content:
// - T+0: Content queued, notification sent (email + Slack)
// - T+4h: Reminder notification (if not reviewed)
// - T+12h: Escalation notification (to manager + admin)
// - T+20h: Final reminder (before SLA auto-resolution)
// - T+24h: Auto-publish (if score ≥ 80) or auto-reject (if score < 80)
5. Medical Disclaimer System#
5.1 Disclaimer Templates#
// src/server/services/ai/compliance/disclaimers.ts
interface DisclaimerTemplate {
id: string;
name: string;
content: string;
applicableTo: string[]; // Categories or task types
placement: "beginning" | "end" | "inline";
priority: number;
}
const DISCLAIMER_TEMPLATES: DisclaimerTemplate[] = [
{
id: "medical-general",
name: "General Medical Disclaimer",
content: "This information is for educational purposes only and does not constitute medical advice. Consult a qualified healthcare professional for personalized diagnosis and treatment.",
applicableTo: ["CLINIC", "HOSPITAL", "DOCTOR", "DENTAL", "DENTIST"],
placement: "end",
priority: 1,
},
{
id: "medical-treatment",
name: "Treatment Disclaimer",
content: "Individual results may vary. The effectiveness of treatment depends on various factors including individual health conditions. Consult your doctor for personalized advice.",
applicableTo: ["CLINIC", "HOSPITAL", "DOCTOR"],
placement: "end",
priority: 2,
},
{
id: "dental-general",
name: "Dental Disclaimer",
content: "Dental procedures and outcomes vary by individual. This information is for general awareness only. Please consult a licensed dentist for a proper evaluation and treatment plan.",
applicableTo: ["DENTAL", "DENTIST"],
placement: "end",
priority: 1,
},
{
id: "ayurveda-general",
name: "Ayurveda Disclaimer",
content: "Ayurvedic treatments are complementary wellness practices. They are not a substitute for modern medical care. Consult a qualified physician for serious health conditions.",
applicableTo: ["AYURVEDA", "AYURVEDIC"],
placement: "end",
priority: 1,
},
{
id: "wellness-general",
name: "Wellness Disclaimer",
content: "Wellness programs and advice are for general health improvement. They are not medical treatments. Consult a healthcare provider for medical conditions.",
applicableTo: ["WELLNESS", "NATUROPATHY", "REHABILITATION"],
placement: "end",
priority: 1,
},
{
id: "pharmacy-general",
name: "Pharmacy Disclaimer",
content: "Medication information is for educational purposes. Always consult a pharmacist or doctor before taking any medication. Do not self-medicate.",
applicableTo: ["PHARMACY", "MEDICAL_STORE"],
placement: "end",
priority: 1,
},
];
// Disclaimer auto-appending logic:
function appendDisclaimer(content: string, practice: Practice): string {
const template = DISCLAIMER_TEMPLATES.find(t =>
t.applicableTo.includes(practice.category.toUpperCase())
);
if (!template) return content;
// Check if content already has disclaimer
if (content.includes(template.content.substring(0, 50))) {
return content; // Already has disclaimer
}
// Append disclaimer
if (template.placement === "end") {
return `${content}\n\n---\n\n**Disclaimer:** ${template.content}`;
}
if (template.placement === "beginning") {
return `**Disclaimer:** ${template.content}\n\n---\n\n${content}`;
}
return content;
}
5.2 Disclaimer Placement Rules#
| Content Type | Disclaimer Placement | Required? |
|---|---|---|
| Landing Page Hero | End of page | Yes (if medical) |
| Landing Page About | End of section | Yes (if medical) |
| Landing Page Services | End of each service description | Yes (if treatment-related) |
| Landing Page FAQ | End of each medical answer | Yes (if medical) |
| Blog Post | End of article | Yes (if medical) |
| GBP Post | End of post | Yes (if medical) |
| Social Post | Last sentence or comment | Yes (if medical) |
| Review Reply | Not needed (reply is not medical advice) | No |
| Citation Description | Not needed (directory listing) | No |
| End of email | Yes (if medical) |
6. HIPAA & Data Privacy#
6.1 HIPAA Considerations#
// HIPAA compliance for AI content generation:
// 1. NO PHI in AI prompts
// - Never include patient names, addresses, phone numbers, emails
// - Never include medical record numbers, insurance IDs
// - Never include specific diagnosis or treatment details of real patients
// - Use synthetic/fictional examples only
// 2. Prompt Sanitization
function sanitizePromptForHIPAA(prompt: string): string {
// Remove potential PHI patterns:
const phiPatterns = [
/\b\d{3}-\d{2}-\d{4}\b/g, // SSN
/\b\d{10}\b/g, // Phone numbers (10 digit)
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Emails
/\b[A-Z]{2}\d{6}\b/g, // Indian PIN codes (might be part of address)
/\bpatient\s+(?:name|id|number|record)\s*:?\s*\w+/gi, // Patient references
];
let sanitized = prompt;
for (const pattern of phiPatterns) {
sanitized = sanitized.replace(pattern, "[REDACTED]");
}
return sanitized;
}
// 3. Data Handling
// - AI provider APIs (OpenAI, Anthropic, Google) are HIPAA-compliant?
// → No, they are NOT HIPAA-compliant by default
// → Solution: Use Business Associate Agreements (BAAs) if required
// → Alternative: Self-hosted models (if HIPAA compliance is mandatory)
// → Current approach: No PHI in prompts, so HIPAA compliance is not required
// for AI generation. PHI is handled in the application layer (database),
// which is HIPAA-compliant.
// 4. Audit Trail
// - All AI interactions are logged (without PHI)
// - Logs include: task type, model, cost, timestamp, practice ID (not patient ID)
// - Logs are retained for 6 years (HIPAA requirement)
6.2 Data Privacy in AI Generation#
| Data Type | In AI Prompt? | Risk Level | Mitigation |
|---|---|---|---|
| Practice name | Yes | Low | Public information |
| Practice address | Yes | Low | Public information |
| Practice phone | Yes | Low | Public information |
| Practice services | Yes | Low | Public information |
| Patient names | No | High | Never include |
| Patient contact info | No | High | Never include |
| Patient medical history | No | High | Never include |
| Reviewer names (from reviews) | Yes | Medium | Public, but anonymize if sensitive |
| Review content | Yes | Medium | Public, but sanitize if contains PHI |
| Appointment details | No | High | Never include |
| Payment information | No | High | Never include |
7. Compliance Evaluation Pipeline#
7.1 Compliance Pipeline Steps#
┌─────────────────────────────────────────────────────────────┐
│ COMPLIANCE EVALUATION PIPELINE │
│ │
│ Step 1: Fast Heuristic Checks (local, no LLM) │
│ ├─ Banned phrase scan (regex, < 1ms) │
│ ├─ Medical keyword detection (< 1ms) │
│ ├─ Length and format validation (< 1ms) │
│ ├─ Disclaimer presence check (if medical, < 1ms) │
│ └─ Superlative detection (< 1ms) │
│ If any CRITICAL fail → REJECT immediately │
│ If any HIGH/MEDIUM fail → FLAG for review │
│ │
│ Step 2: LLM-as-Judge Compliance Check │
│ ├─ Model: Claude Haiku (~$0.002) │
│ ├─ Evaluates: │
│ │ ├─ Medical claim accuracy │
│ │ ├─ False promise detection │
│ │ ├─ Superlative substantiation │
│ │ ├─ Cultural sensitivity │
│ │ └─ Overall compliance score │
│ └─ Output: Compliance score (0-100) + issues list │
│ │
│ Step 3: Auto-Correction (for non-critical issues) │
│ ├─ Replace banned phrases with alternatives │
│ ├─ Add disclaimer if missing (medical content) │
│ ├─ Adjust superlatives ("best" → "highly rated") │
│ └─ Re-evaluate after correction │
│ │
│ Step 4: Decision │
│ ├─ Compliance score ≥ 90 + no CRITICAL → PASS │
│ ├─ Compliance score 70-89 + no CRITICAL → PASS with WARN │
│ ├─ Compliance score 60-69 → QUEUE for human review │
│ └─ Compliance score < 60 or CRITICAL → REJECT │
│ │
│ Step 5: Audit & Logging │
│ ├─ Log compliance score and issues │
│ ├─ Log any auto-corrections made │
│ └─ Update compliance metrics dashboard │
└─────────────────────────────────────────────────────────────┘
7.2 Compliance Judge Prompt#
## TASK: Compliance Evaluation
Evaluate the following content for legal and medical compliance.
### Content:
"""{{content}}"""
### Context:
- Practice: {{practiceName}} ({{category}})
- Medical Content: {{isMedical}}
- Target Audience: {{targetAudience}}
### Evaluation Criteria:
#### 1. Banned Phrases (CRITICAL)
- Does the content contain any banned phrases?
- Banned phrases include: "guaranteed cure", "100% success", "permanent fix", "no side effects", "miracle treatment", "instant results", "never fails", "completely safe", "risk-free"
- Score: 100 if none, 0 if any present
#### 2. Medical Claims (HIGH)
- Does the content make unsubstantiated medical claims?
- Does it claim to treat, cure, or diagnose conditions without evidence?
- Does it mention specific medications or treatments without context?
- Score: 100 if no claims, 50 if claims are vague, 0 if claims are specific and unsubstantiated
#### 3. Superlatives (MEDIUM)
- Does the content use superlatives ("best", "most advanced", "top-rated") without evidence?
- Are superlatives substantiated with data or attribution?
- Score: 100 if no superlatives or substantiated, 50 if unsubstantiated superlatives, 0 if misleading
#### 4. Disclaimers (HIGH for medical)
- Does medical content include appropriate disclaimers?
- Is the disclaimer visible and clear?
- Score: 100 if disclaimer present, 0 if missing (for medical content)
#### 5. Cultural Sensitivity (MEDIUM)
- Is the content culturally appropriate for the target audience?
- Does it avoid stereotypes or insensitive references?
- Score: 100 if appropriate, 50 if minor issues, 0 if major issues
#### 6. False Promises (CRITICAL)
- Does the content make promises that can't be kept?
- Does it set unrealistic expectations?
- Score: 100 if no false promises, 0 if any present
### Output Format (JSON):
{
"bannedPhrases": { "score": 100, "issues": [] },
"medicalClaims": { "score": 100, "issues": [] },
"superlatives": { "score": 100, "issues": [] },
"disclaimers": { "score": 100, "issues": [] },
"culturalSensitivity": { "score": 100, "issues": [] },
"falsePromises": { "score": 100, "issues": [] },
"overall": 100,
"status": "PASS | WARN | FAIL",
"action": "AUTO_PUBLISH | QUEUE | REJECT",
"issues": []
}
8. Alert & Escalation System#
8.1 Alert Thresholds#
| Alert Type | Threshold | Channel | Recipients | Action |
|---|---|---|---|---|
| CRITICAL: Banned Phrase | Any CRITICAL match | Slack + Email | Admin + Compliance Officer | Auto-reject + investigate |
| HIGH: Compliance Score < 60 | Content score < 60 | Slack | Admin | Queue for review |
| MEDIUM: Compliance Score 60-70 | Content score 60-70 | Admin | Flag for review | |
| MEDIUM: Missing Disclaimer | Medical content without disclaimer | Slack | Admin | Auto-append + notify |
| LOW: Superlative Unsubstantiated | "Best" without evidence | Log only | — | Flag in dashboard |
| SLA: Review Pending > 20h | 20 hours without review | Slack + Email | Admin + Manager | Escalate |
| SLA: Review Pending > 24h | 24 hours without review | Slack + Email + SMS | Admin + Manager + On-call | Auto-resolve + alert |
| SYSTEM: Compliance Rate < 95% | System-wide compliance < 95% | Slack | Admin + Engineering | Investigate |
| SYSTEM: Banned Phrase Rate > 1% | > 1% of content triggers banned phrases | Slack | Admin + Compliance | Review templates |
8.2 Escalation Matrix#
┌─────────────────────────────────────────────────────────────┐
│ ESCALATION MATRIX │
│ │
│ Level 1: Automated Alert │
│ ├─ Channel: Slack (#ai-compliance) │
│ ├─ Recipients: Content Admin │
│ └─ Action: Auto-reject, queue for review, or flag │
│ │
│ Level 2: Admin Notification (4 hours) │
│ ├─ Channel: Email + Slack │
│ ├─ Recipients: Content Admin + Practice Manager │
│ └─ Action: Reminder to review pending content │
│ │
│ Level 3: Manager Escalation (12 hours) │
│ ├─ Channel: Email + Slack + In-app │
│ ├─ Recipients: Content Admin + Department Manager │
│ └─ Action: Escalation, request immediate attention │
│ │
│ Level 4: Auto-Resolution (24 hours) │
│ ├─ Channel: Email + Slack │
│ ├─ Recipients: All above + On-call Engineer │
│ └─ Action: Auto-publish (score ≥ 80) or auto-reject │
│ │
│ Level 5: Critical Incident (> 5 compliance failures/hour) │
│ ├─ Channel: PagerDuty + Slack + Email + SMS │
│ ├─ Recipients: On-call + Engineering Lead + Compliance Officer│
│ └─ Action: Emergency review, potential system pause │
└─────────────────────────────────────────────────────────────┘
8.3 Alert Messages#
// Slack alert for banned phrase detection:
const BANNED_PHRASE_ALERT = {
text: "🚨 CRITICAL: Banned Phrase Detected",
attachments: [
{
color: "danger",
fields: [
{ title: "Practice", value: "{{practiceName}}", short: true },
{ title: "Task", value: "{{taskType}}", short: true },
{ title: "Phrase", value: "{{bannedPhrase}}", short: false },
{ title: "Severity", value: "{{severity}}", short: true },
{ title: "Action", value: "{{action}}", short: true },
{ title: "Content Preview", value: "{{contentPreview}}", short: false },
],
actions: [
{ name: "review", text: "Review Content", type: "button", url: "{{reviewUrl}}" },
{ name: "regenerate", text: "Regenerate", type: "button", url: "{{regenerateUrl}}" },
],
},
],
};
// Slack alert for SLA breach:
const SLA_BREACH_ALERT = {
text: "⚠️ SLA Breach: Content Review Pending > 24h",
attachments: [
{
color: "warning",
fields: [
{ title: "Practice", value: "{{practiceName}}", short: true },
{ title: "Content Type", value: "{{contentType}}", short: true },
{ title: "Queued At", value: "{{queuedAt}}", short: true },
{ title: "Auto-Action", value: "{{autoAction}}", short: true },
],
actions: [
{ name: "review", text: "Review Now", type: "button", url: "{{reviewUrl}}" },
{ name: "override", text: "Override Auto-Action", type: "button", url: "{{overrideUrl}}" },
],
},
],
};
9. Compliance Audit Trail#
9.1 Audit Trail Requirements#
Every compliance-related action is logged with:
| Field | Description | Retention |
|---|---|---|
| Timestamp | When the action occurred | 6 years |
| Action | What was done (reject, approve, flag, auto-correct) | 6 years |
| Content ID | Which content was affected | 6 years |
| Practice ID | Which practice | 6 years |
| User ID | Who performed the action (or "system") | 6 years |
| Reason | Why the action was taken | 6 years |
| Original Content | The content before action (first 500 chars) | 6 years |
| Compliance Score | Score at the time of action | 6 years |
| Issues | List of compliance issues found | 6 years |
| Model Used | Which AI model generated the content | 6 years |
| Prompt Version | Which prompt template was used | 6 years |
9.2 Audit Trail Queries#
// Common audit queries:
// 1. All compliance failures in the last 30 days:
// SELECT * FROM ai_compliance_audit
// WHERE action IN ('REJECT', 'FLAG')
// AND created_at > NOW() - INTERVAL '30 days'
// ORDER BY created_at DESC;
// 2. Compliance rate by practice:
// SELECT
// practice_id,
// COUNT(*) as total,
// SUM(CASE WHEN action = 'REJECT' THEN 1 ELSE 0 END) as rejected,
// (1 - SUM(CASE WHEN action = 'REJECT' THEN 1 ELSE 0 END)::float / COUNT(*)) * 100 as compliance_rate
// FROM ai_compliance_audit
// WHERE created_at > NOW() - INTERVAL '30 days'
// GROUP BY practice_id;
// 3. Most common banned phrases:
// SELECT issue, COUNT(*) as count
// FROM ai_compliance_audit
// WHERE action = 'REJECT'
// AND created_at > NOW() - INTERVAL '30 days'
// GROUP BY issue
// ORDER BY count DESC
// LIMIT 10;
// 4. SLA breach analysis:
// SELECT
// practice_id,
// COUNT(*) as breaches,
// AVG(EXTRACT(EPOCH FROM (resolved_at - created_at))/3600) as avg_resolution_hours
// FROM ai_compliance_audit
// WHERE sla_breached = true
// AND created_at > NOW() - INTERVAL '30 days'
// GROUP BY practice_id;
9.3 Monthly Compliance Report#
┌─────────────────────────────────────────────────────────────┐
│ Monthly Compliance Report (June 2026) │
│ │
│ OVERVIEW │
│ Total Generations: 1,234 │
│ Compliance Pass Rate: 97.8% (▼ 0.5% from May) │
│ Banned Phrase Detections: 12 (1.0%) │
│ Medical Content Reviewed: 156 (100% of medical content) │
│ SLA Breaches: 3 (1.9% of queued content) │
│ │
│ BANNED PHRASE BREAKDOWN │
│ Phrase │ Count │ Action │
│ ──────────────────────────┼───────┼───────────────────────│
│ "best in" │ 4 │ FLAG → Alternative │
│ "guaranteed cure" │ 1 │ REJECT → Regenerate │
│ "100% success" │ 2 │ REJECT → Regenerate │
│ "no side effects" │ 1 │ REJECT → Regenerate │
│ "quick fix" │ 4 │ WARN → Alternative │
│ │
│ COMPLIANCE BY PRACTICE TYPE │
│ Type │ Generations │ Pass Rate │ Avg Score │
│ ─────────────┼─────────────┼───────────┼─────────────────│
│ Dental │ 456 │ 98.2% │ 85.3 │
│ Hospital │ 123 │ 96.8% │ 82.1 │
│ Clinic │ 234 │ 97.4% │ 84.5 │
│ Ayurveda │ 89 │ 95.5% │ 81.2 │
│ Non-Medical │ 332 │ 99.1% │ 88.7 │
│ │
│ IMPROVEMENTS MADE │
│ 1. Updated banned phrase list (added 3 new phrases) │
│ 2. Improved dental disclaimer template │
│ 3. Recalibrated compliance judge for medical claims │
│ 4. Added cultural sensitivity check for vernacular content │
│ │
│ RECOMMENDATIONS │
│ 1. Review "best in" template usage (4 flags this month) │
│ 2. Add "quick fix" to auto-correction list │
│ 3. Improve Ayurveda compliance (lowest pass rate) │
│ 4. Reduce SLA breaches (target: < 1%) │
└─────────────────────────────────────────────────────────────┘
10. Database Schema#
-- Compliance Audit Log
CREATE TABLE ai_compliance_audit (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-- Content reference
content_id UUID NOT NULL,
content_type VARCHAR(50) NOT NULL,
practice_id UUID NOT NULL REFERENCES practices(id) ON DELETE CASCADE,
task_type VARCHAR(50) NOT NULL,
-- Action details
action VARCHAR(20) NOT NULL, -- REJECT, FLAG, WARN, APPROVE, AUTO_CORRECT, AUTO_PUBLISH, AUTO_REJECT
reason TEXT NOT NULL,
-- Compliance details
compliance_score INTEGER,
banned_phrases JSONB, -- Array of {phrase, severity, position}
medical_claims_detected BOOLEAN DEFAULT FALSE,
superlatives_detected JSONB, -- Array of {phrase, position}
disclaimer_present BOOLEAN DEFAULT FALSE,
-- Content snapshot
content_preview TEXT NOT NULL, -- First 500 chars
original_content TEXT, -- Full content if needed
corrected_content TEXT, -- If auto-corrected
-- AI metadata
model VARCHAR(50) NOT NULL,
prompt_version VARCHAR(20) NOT NULL,
-- User/System
performed_by VARCHAR(50) NOT NULL, -- user_id or "system"
-- SLA
sla_breached BOOLEAN DEFAULT FALSE,
sla_deadline TIMESTAMP,
resolved_at TIMESTAMP,
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
INDEX idx_practice_created (practice_id, created_at),
INDEX idx_action (action),
INDEX idx_compliance_score (compliance_score)
);
-- Banned Phrase Detection Log
CREATE TABLE banned_phrase_detections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
audit_id UUID NOT NULL REFERENCES ai_compliance_audit(id) ON DELETE CASCADE,
phrase VARCHAR(200) NOT NULL,
severity VARCHAR(20) NOT NULL,
category VARCHAR(50) NOT NULL,
action VARCHAR(20) NOT NULL,
position INTEGER,
suggested_alternative VARCHAR(200),
auto_corrected BOOLEAN DEFAULT FALSE,
corrected_to VARCHAR(200),
created_at TIMESTAMP DEFAULT NOW()
);
-- Content Review Queue (medical and flagged content)
CREATE TABLE content_review_queue (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content_id UUID NOT NULL,
content_type VARCHAR(50) NOT NULL,
practice_id UUID NOT NULL REFERENCES practices(id) ON DELETE CASCADE,
-- Content snapshot
content_preview TEXT NOT NULL,
full_content TEXT,
-- Generation metadata
generated_at TIMESTAMP NOT NULL,
model VARCHAR(50) NOT NULL,
quality_score INTEGER,
compliance_score INTEGER,
-- Compliance details
compliance_issues JSONB,
banned_phrases JSONB,
medical_claims_detected BOOLEAN DEFAULT FALSE,
-- Review status
status VARCHAR(20) NOT NULL DEFAULT "pending", -- pending, approved, rejected, edited, auto_published, auto_rejected
reviewed_by UUID REFERENCES users(id),
reviewed_at TIMESTAMP,
-- Human feedback
human_compliance_score INTEGER,
human_feedback TEXT,
edited_content TEXT,
-- SLA
sla_deadline TIMESTAMP NOT NULL,
sla_breached BOOLEAN DEFAULT FALSE,
auto_publish_on_breach BOOLEAN DEFAULT FALSE,
auto_reject_on_breach BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
-- Disclaimer Usage Log
CREATE TABLE disclaimer_usage (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
content_id UUID NOT NULL,
content_type VARCHAR(50) NOT NULL,
practice_id UUID NOT NULL REFERENCES practices(id) ON DELETE CASCADE,
disclaimer_template_id VARCHAR(50) NOT NULL,
disclaimer_content TEXT NOT NULL,
placement VARCHAR(20) NOT NULL, -- beginning, end, inline
auto_appended BOOLEAN DEFAULT FALSE,
manually_added BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT NOW()
);
-- Compliance Metrics (aggregated)
CREATE TABLE compliance_metrics (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
date DATE NOT NULL,
practice_id UUID REFERENCES practices(id) ON DELETE CASCADE, -- NULL = system-wide
total_generations INTEGER NOT NULL DEFAULT 0,
pass_count INTEGER NOT NULL DEFAULT 0,
reject_count INTEGER NOT NULL DEFAULT 0,
flag_count INTEGER NOT NULL DEFAULT 0,
warn_count INTEGER NOT NULL DEFAULT 0,
auto_correct_count INTEGER NOT NULL DEFAULT 0,
banned_phrase_count INTEGER NOT NULL DEFAULT 0,
medical_claim_count INTEGER NOT NULL DEFAULT 0,
superlative_count INTEGER NOT NULL DEFAULT 0,
disclaimer_missing_count INTEGER NOT NULL DEFAULT 0,
sla_breach_count INTEGER NOT NULL DEFAULT 0,
avg_resolution_hours DECIMAL(5, 2),
avg_compliance_score DECIMAL(5, 2),
UNIQUE(date, practice_id)
);
End of Medical Compliance & Safety Guardrails Documentation — RankFlow AI v2.0.0