AI A/B Testing Engine
Automatically A/B test lesson content — including AI-generated interactive game variants — using a single script tag
Overview
The AI A/B Testing Engine is a Next.js + Prisma service that plugs into this site via a single <script> tag. It lets an admin select any paragraph or heading on a lesson page, generate an AI variant (text rewrite, Mermaid diagram, or interactive GameEngine level), and launch a 50/50 split test — all without touching the page source.
Engine repo: magic005/ai_ab_test_engine
Deployed at: https://ai-ab-test-engine.vercel.app
How It Works
┌─────────────────────────────────────────────────────────┐
│ OCS Jekyll Site │
│ │
│ <script src=".../sdk.js" │
│ data-project-id="..." │
│ data-site-baseurl="/"> │
│ │
│ SDK runs on every page load: │
│ 1. Assigns user to A or B group (deterministic hash) │
│ 2. Fetches active tests for this page │
│ 3. Applies variant (text / diagram / game) to DOM │
│ 4. Tracks view + click conversions │
└─────────────────────────┬───────────────────────────────┘
│ fetch /api/tests + /api/events
▼
┌─────────────────────────────────────────────────────────┐
│ AI A/B Engine (Vercel / Next.js) │
│ │
│ /api/tests → returns active tests for project │
│ /api/generate → LLM generates variant content │
│ /api/admin/tests → admin creates / reads tests │
│ /api/events → records view/click events │
│ │
│ DB: Neon PostgreSQL (Prisma ORM) │
└─────────────────────────────────────────────────────────┘
Variant Types
1. Text Variant
The most basic variant. The LLM (llama-3.1-8b-instant) rewrites the selected element with a punchier or simplified version.
Stored as plain text in the database.
2. Mermaid Diagram Variant
The selected paragraph is converted into a Mermaid flowchart by llama-3.3-70b-versatile. The SDK swaps the paragraph for a rendered <div class="mermaid"> block.
Stored with MERMAID: prefix.
3. Game Variant (featured)
The LLM extracts 2–4 key concepts from the selected text and outputs structured JSON. The server assembles a valid GameEnginev1.1 ES module — with NPC characters that each teach one concept via dialogue — and stores it with a GAME: prefix.
When a user lands in the B group, the SDK:
- Hides the original element
- Imports
GameExecutorfrom/assets/js/pages/runners/index.js(this site) - Injects a Play/Pause/Stop/Fullscreen runner UI
- Passes the generated level code to
GameExecutor.run()
Players walk around with WASD and press E near NPCs to learn the concepts.
Generated Game Level Structure
The API generates a complete GameEnginev1.1 module. Here is a simplified example from a data structures lesson:
import GameControl from '/assets/js/GameEnginev1.1/essentials/GameControl.js';
import GameEnvBackground from '/assets/js/GameEnginev1.1/essentials/GameEnvBackground.js';
import Player from '/assets/js/GameEnginev1.1/essentials/Player.js';
import Npc from '/assets/js/GameEnginev1.1/essentials/Npc.js';
class DataStructureExplorer {
constructor(gameEnv) {
const { path, innerWidth: width, innerHeight: height } = gameEnv;
this.classes = [
{ class: GameEnvBackground, data: { src: path + '/images/gamebuilder/bg/clouds.jpg' } },
{ class: Player, data: { src: path + '/images/gamify/chillguy.png', /* ... */ } },
{ class: Npc, data: {
id: 'ArrayList',
src: path + '/images/gamify/r2_idle.png',
INIT_POSITION: { x: width * 0.25, y: height * 0.5 },
dialogues: [
'Dynamic resizing is supported.',
'Use ArrayList for indexed access.',
],
reaction: function() { if (this.dialogueSystem) this.showReactionDialogue(); },
}},
// ... more NPCs for each concept
];
}
}
export const gameLevelClasses = [DataStructureExplorer];
export { GameControl };
Key design decision: the LLM only fills in content (NPC names, dialogue lines, positions). The structural boilerplate is assembled server-side from a verified template, so the output is always a valid ES module.
Admin Overlay
Append ?ab_admin=true to any page URL to activate the admin panel.
Steps:
- Click Select Element → hover over any paragraph/heading
- Click the element to lock it in
- Click Generate AI Variants, Generate as Diagram, or Generate as Game
- Review the generated content in the panel
- Click Launch 50/50 Test — the test is live immediately
The overlay is served from the engine at /overlay.js and injected by the SDK dynamically — no changes to individual pages required.
Jekyll Integration
The only change needed in this repo is one line in _includes/head-custom.html:
<script src="https://ai-ab-test-engine.vercel.app/sdk.js"
data-site-baseurl=""
data-project-id="cmnfour780000f63gu69kg2v8"></script>
The data-site-baseurl attribute (rendered by Liquid at build time) lets the SDK resolve asset paths correctly on GitHub Pages — critical for loading GameEngine sprites like /images/gamify/chillguy.png.
%%html
<!-- Live Demo: visit any lesson page with ?ab_admin=true to try the overlay -->
<div style="background:#0f172a;border:1px solid #1e293b;border-radius:10px;padding:20px;font-family:monospace;color:#94a3b8;">
<div style="color:#38bdf8;font-size:13px;margin-bottom:12px;">▶ Try it on any lesson page</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<a href="/LessonMethodology?ab_admin=true"
style="background:#1e40af;color:#fff;padding:8px 16px;border-radius:6px;text-decoration:none;font-size:13px;">
Lesson Methodology
</a>
<a href="/csp?ab_admin=true"
style="background:#1e40af;color:#fff;padding:8px 16px;border-radius:6px;text-decoration:none;font-size:13px;">
CSP Lessons
</a>
</div>
<div style="margin-top:16px;font-size:12px;color:#475569;">
Select any paragraph → Generate as Game → Launch 50/50 Test
</div>
</div>