Multi-brand AI customer support drafting tool for D2C brands. Each brand signs up, onboards a brain file, and gets a private dashboard for drafting + reviewing replies.
Python + Flask, Flask-Login, SQLite, vanilla HTML/JS, Groq SDK (llama-3.3-70b-versatile). Deployable on Railway.
cd drafttwin
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # macOS/Linux
pip install -r requirements.txt
cp .env.example .env # then edit .env and paste GROQ_API_KEY
python app.pyOpen http://localhost:5000.
/signup— email + password. Creates the brand account./onboarding— fill the brand form. Generatesbrain.mdv1.0, saves to SQLite +brains/<id>-<slug>.md./dashboard— paste a customer DM → structured{ classification, reply, reasoning }response. Last 20 drafts appear below with[Flag this reply]buttons./brain— edit brain.md section-by-section. Saving bumps the minor version (v1.0 → v1.1 → v1.2). Current version + last-updated shown on dashboard./flagged— review all flagged drafts with customer message, twin's original reply, and your correction. Use this as your gap list when editing the brain.
- One account per brand (
brands.emailis UNIQUE). - Every
/dashboard,/brain,/flagged, and/api/*route is@login_required. - All DB queries are scoped to
current_user.id— brands cannot see each other's drafts, flags, or brain files. - Passwords hashed with
werkzeug.security.generate_password_hash(PBKDF2).
- System prompt = full brand
brain.md+ strict operating rules. - Model:
llama-3.3-70b-versatileon Groq (fast inference, OpenAI-compatible function calling). - Structured output is enforced by a forced
draft_replytool call. Classification is constrained toAUTO | DRAFT+APPROVE | ESCALATE.
Stored as a plain major.minor string in brands.version. The editor always bumps the minor on save. The parser in brain_editor.py splits the brain into 5 fixed sections + internal notes; the assembler regenerates the header/footer with the new version, so the on-disk file stays well-formed regardless of what the founder edits.
Push this directory to a GitHub repo. In Railway, "New Project" → "Deploy from GitHub repo" → pick the repo. railway.toml + Procfile tell Railway to run gunicorn app:app --bind 0.0.0.0:$PORT.
Required env vars (set in Railway → Variables):
GROQ_API_KEY— from https://console.groq.com/keysFLASK_SECRET_KEY— any long random stringDRAFTTWIN_DB— optional. Defaults todrafttwin.dbin the working directory. For persistence across redeploys, attach a Railway Volume at/dataand setDRAFTTWIN_DB=/data/drafttwin.db.
Without a volume, the SQLite file is wiped on every redeploy — fine for testing, not for production users.
| File | Purpose |
|---|---|
app.py |
Flask routes (index, onboarding, dashboard, brain, flagged, API) |
auth.py |
Flask-Login wiring, User model, login/signup/logout blueprint |
brain_generator.py |
Onboarding form → initial brain.md |
brain_editor.py |
Parse/assemble brain.md sections; version bump |
llm.py |
Groq SDK call, forced tool use |
db.py |
SQLite schema, migrations, query helpers |
templates/ |
Base, login, signup, onboarding, dashboard, brain, flagged |
static/style.css |
Styling |
brains/ |
Generated brain.md files per brand |
railway.toml, Procfile |
Railway deploy |
- Brain editing: diff view between versions, rollback, import of an external brain.md
- Slack/Telegram notifications for DRAFT+APPROVE / ESCALATE
- "Retrain from flags" — auto-suggest brain edits from flagged examples