ML Integration Guide
Best practices for integrating ML-powered toxicity detection into your application
This guide covers best practices, architecture patterns, and optimization strategies for ML-powered content moderation.
When to Use ML Detection
ML detection excels at catching contextual toxicity that word lists miss — insults, threats, and harmful content expressed without explicit profanity.
ML Catches What Rules Miss
| Example | Rules | ML |
|---|---|---|
| "you fucking idiot" | ✅ | ✅ |
| "you are worthless" | ❌ | ✅ |
| "go kys loser" | ❌ | ✅ |
| "nobody wants you here" | ❌ | ✅ |
| "I hope bad things happen to you" | ❌ | ✅ |
When to Enable ML
Enable ML when:
- Moderating user-generated content (comments, chat, forums)
- Users actively try to evade filters
- Context matters (gaming, social, professional)
- You need to catch threats and harassment
Skip ML when:
- Simple word filtering is sufficient
- Latency is critical (under 10ms requirement)
- Running on edge/serverless with strict limits
- Budget constraints (ML uses more compute)
Architecture Patterns
Pattern 1: ML as Primary (High Accuracy)
import { HybridFilter } from 'glin-profanity/ml';
const filter = new HybridFilter({
enableML: true,
combinationMode: 'ml-override',
mlThreshold: 0.85,
});
// ML result takes precedence
const result = await filter.checkProfanityAsync(text);Pros: Best accuracy, handles context well Cons: Slower (~50-100ms), higher resource usage
Pattern 2: Rules First, ML Fallback (Balanced)
import { HybridFilter } from 'glin-profanity/ml';
const filter = new HybridFilter({
enableML: true,
combinationMode: 'rules-first',
borderlineThreshold: 0.5,
detectLeetspeak: true,
});
// Fast rules catch obvious cases, ML handles edge cases
const result = await filter.checkProfanityAsync(text);Pros: Fast for common cases, ML safety net Cons: Slightly more complex logic
Pattern 3: Tiered Moderation (High Volume)
import { Filter } from 'glin-profanity';
import { ToxicityDetector } from 'glin-profanity/ml';
class TieredModerator {
private ruleFilter = new Filter({ detectLeetspeak: true });
private mlDetector = new ToxicityDetector({ threshold: 0.85 });
private mlReady = false;
async initialize() {
await this.mlDetector.loadModel();
this.mlReady = true;
}
async moderate(text: string) {
// Tier 1: Fast rule-based check
const ruleResult = this.ruleFilter.checkProfanity(text);
if (ruleResult.containsProfanity) {
return { action: 'block', reason: 'profanity', fast: true };
}
// Tier 2: ML check for borderline content
if (this.mlReady) {
const mlResult = await this.mlDetector.analyze(text);
if (mlResult.isToxic) {
return { action: 'flag', reason: mlResult.matchedCategories, fast: false };
}
}
return { action: 'allow', fast: true };
}
}Pros: Maximum throughput, graceful degradation Cons: More code to maintain
Optimization Strategies
1. Preload Model on Startup
// app.ts or server.ts
import { HybridFilter } from 'glin-profanity/ml';
let moderator: HybridFilter;
async function initializeApp() {
moderator = new HybridFilter({ enableML: true });
console.log('Loading ML model...');
await moderator.initialize();
console.log('ML model ready');
// Start server after model is ready
app.listen(3000);
}
initializeApp();2. Use Batch Processing
// Process multiple texts in one ML call
const comments = ['comment1', 'comment2', 'comment3'];
// Bad: N separate ML calls
for (const comment of comments) {
await filter.checkProfanityAsync(comment); // Slow!
}
// Good: Single batched ML call
const results = await filter.checkProfanityBatchAsync(comments); // Fast!3. Cache Results
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, boolean>({ max: 1000 });
async function checkWithCache(text: string) {
const cached = cache.get(text);
if (cached !== undefined) return cached;
const result = await filter.isToxicAsync(text);
cache.set(text, result);
return result;
}4. Use Rules for Quick Reject
async function smartModerate(text: string) {
// Quick sync check first
if (filter.isProfane(text)) {
return { blocked: true, source: 'rules' };
}
// ML only for content that passed rules
const mlResult = await filter.analyzeWithML(text);
if (mlResult?.isToxic) {
return { blocked: true, source: 'ml' };
}
return { blocked: false };
}Memory Management
Serverless Considerations
// Lambda / Vercel Functions
let filter: HybridFilter | null = null;
export async function handler(event) {
// Reuse filter across warm invocations
if (!filter) {
filter = new HybridFilter({ enableML: true });
await filter.initialize();
}
const result = await filter.checkProfanityAsync(event.body.text);
return { statusCode: 200, body: JSON.stringify(result) };
}Long-Running Services
class ModerationService {
private filter: HybridFilter;
constructor() {
this.filter = new HybridFilter({ enableML: true });
}
async start() {
await this.filter.initialize();
}
async stop() {
// Free memory on shutdown
this.filter.dispose();
}
}Error Handling
import { HybridFilter } from 'glin-profanity/ml';
async function safeModeerate(text: string) {
try {
const result = await filter.checkProfanityAsync(text);
return result;
} catch (error) {
console.error('ML moderation failed:', error);
// Fallback to rule-based only
const ruleResult = filter.checkProfanity(text);
return {
isToxic: ruleResult.containsProfanity,
reason: 'Rule-based fallback (ML unavailable)',
confidence: 0.7,
};
}
}Testing ML Integration
// test/moderation.test.ts
import { ToxicityDetector } from 'glin-profanity/ml';
describe('ML Moderation', () => {
let detector: ToxicityDetector;
beforeAll(async () => {
detector = new ToxicityDetector({ threshold: 0.85 });
await detector.loadModel();
});
afterAll(() => {
detector.dispose();
});
test('detects insults', async () => {
const result = await detector.analyze('you are an idiot');
expect(result.isToxic).toBe(true);
expect(result.matchedCategories).toContain('insult');
});
test('allows clean content', async () => {
const result = await detector.analyze('great work on this project');
expect(result.isToxic).toBe(false);
});
test('handles batch processing', async () => {
const results = await detector.analyzeBatch([
'hello friend',
'you are terrible',
]);
expect(results[0].isToxic).toBe(false);
expect(results[1].isToxic).toBe(true);
});
});Monitoring & Logging
import { HybridFilter } from 'glin-profanity/ml';
async function moderateWithMetrics(text: string) {
const startTime = Date.now();
const result = await filter.checkProfanityAsync(text);
// Log metrics
const metrics = {
latency: Date.now() - startTime,
isToxic: result.isToxic,
confidence: result.confidence,
mlUsed: result.mlResult !== null,
categories: result.mlResult?.matchedCategories ?? [],
};
// Send to monitoring (Datadog, etc.)
logger.info('moderation_check', metrics);
return result;
}Cross-References
- ToxicityDetector API — Standalone ML detection
- HybridFilter API — Combined detection
- Combination Strategies — Choosing the right mode
- TensorFlow Installation — Setup guide