Five real messages from the demo run, each with a safe outcome: own data only, refund prepared, injection refused, other customers hidden, human hand-off.
The challenge
Most AI chatbots are one clever message away from leaking customer data or promising a refund they shouldn't. Online stores need a support assistant that is genuinely useful to customers and safe for the business.
The solution
I built an AI customer support assistant that answers from the store's help center, looks up each customer's own orders, prepares refunds and cancellations for approval and hands hard cases to a human agent in the same chat.
Real conversations, real outcomes
The five messages above are copied word for word from the project's demo run (fictional store data, built-in offline model):
Own data only: order status comes from the database through a tool, not from the model's imagination.
Refund prepared, not made: nothing changes until the customer confirms, and then a manager approves it.
Injection refused: a prompt-injection attempt gets a polite refusal, with no data, no system prompt and no tools.
Other customers hidden: asking about someone else's order returns "not found", so nothing leaks.
Human hand-off: "I'd like to talk to a real person" creates a ticket, and a support agent replies in the same chat.
Seven checkpoints between the customer and every answer. The AI only suggests; tested code decides what is shown or changed.
How it works
The language model only suggests; tested code decides. Every customer message passes seven checkpoints before a reply is shown:
Screen: the message is checked for prompt injection on the way in.
Redact: personal data is masked before the model sees anything.
Classify: one of 11 intents is detected, with priority and sentiment.
Policy: fixed, tested rules choose to answer, clarify, refuse or escalate.
Retrieve: relevant help-center articles are found in a Qdrant vector database.
Tools: only the tools allowed for that request can run (13 in total).
Guard: every reply is checked before the customer sees it.
Refunds and cancellations always follow the same path: AI prepares → customer confirms → manager approves → Stripe refund.
Six real risks for an AI support desk, and the defense built in for each one.
Security by design
An AI support desk is a target, so each common attack has a defense built in:
Prompt injection: input screening, customer text fenced as data, write tools removed on suspicious turns, and an output guard on every reply.
Someone else's order: ownership is checked in the database, and the answer is a plain "not found", so nothing leaks.
Talking the bot into a refund: refunds are only proposed; the customer confirms and a manager approves. Stripe calls use idempotency keys, and webhooks are signed.
Personal data reaching the model: it is redacted before the model sees any text, sensitive fields are encrypted, and customers can export or erase their data.
Stolen passwords: Argon2id, account lockout, two-factor sign-in (required for staff) and short-lived tokens with reuse-detecting refresh tokens.
A poisoned help-center article: injection screening at upload and at retrieval, plus quarantine and an approval step before it is used.
Also built in: rate limits per IP, user and endpoint, daily token and cost budgets, an append-only audit trail, and SSRF-safe outbound requests.
484 automated tests at 90% coverage, plus fuzzing, OWASP ZAP and security scans on every change.
Quality you can verify
484 automated tests at 90% branch coverage run on every change in GitHub Actions, on Python 3.12 to 3.14, together with:
Static analysis with ruff, mypy (strict) and Bandit
An end-to-end run of the whole stack: PostgreSQL, Redis, Qdrant, the API and the worker
API fuzzing of every operation and an OWASP ZAP scan
CodeQL, container scanning, secret scanning and a dependency audit
Signed releases with an SBOM
Tech stack: Python, FastAPI, PostgreSQL, Qdrant, Redis, Docker and Stripe. It works with a cloud AI model or a fully offline one, so the demo runs without an API key.