ABB site scraping → Chunking → FAISS retrieval → Guardrails → GPT answers → Web UI + stats
A focused RAG system that answers questions only from ABB Bank’s public content. It scrapes ABB pages, chunks and embeds them, builds a FAISS index, and serves a multilingual chat UI with strict guardrails (Azerbaijani, English, Russian). Out-of-scope questions return "Bunu bilmirəm."
- Context-only replies with injection + distance gates and language-aware prompt.
- Pre-scraped data: deterministic, no live crawl dependency at question time.
- Two-process split:
appgateway +qaretrieval/LLM service. - Upload-to-ingest: UI can upload JSON chunks, store in browser, send to backend ingest.
- Observability: questions/answers logged to SQLite, visualized via Chart.js in UI.
- Negative + positive black-box suites to prove guardrails.
| Decision | Why |
|---|---|
| FAISS L2 (IndexFlatL2) | Simple, fast vector search; distance gate (RETRIEVAL_MAX_DISTANCE) blocks weak matches. |
| Multilingual prompt + injection list (AZ/EN/RU) | Prevents prompt override and keeps answers in user language. |
| Separated QA service | Lets retrieval/LLM scale or swap independently from API/DB/UI. |
| SQLite logs | Zero-config persistence for demo; ready to swap to Postgres. |
| LocalStorage upload | Meets “user-provided data upload” requirement without extra backend state. |
Architecture diagrams were made with Eraser!
| Service | Port | Purpose |
|---|---|---|
qa |
8001 | Guarded retrieval + LLM answers (FastAPI qa_service/qa.py). |
app |
8000 | Public API + UI hosting + logging + cache + ingest proxy (FastAPI backer/app.py). |
Shared volume: ./backer/data (FAISS + meta) and ./scraper/output (chunks JSON).
docker compose up -d --buildEnv: set OPENAI_API_KEY (in shell or .env).
Open UI: http://localhost:8000/ui/
Prereqs: Python 3.11, OPENAI_API_KEY in env.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
1) Scrape ABB
python -m scraper.run # writes scraper/output/abb_chunks.json
2) Start QA service (port 8001)
uvicorn qa_service.qa:app --host 0.0.0.0 --port 8001
3) Start APP gateway/UI (port 8000)
uvicorn backer.app:app --host 0.0.0.0 --port 8000
4) Ingest chunks (once services are up)
curl -s -X POST http://127.0.0.1:8000/ingest \
-H "Content-Type: application/json" \
--data-binary @scraper/output/abb_chunks.json- Scrape:
python -m scraper.run→scraper/output/abb_chunks.json. - Ingest:
POST /ingestor UI upload → embeds, builds FAISS, savesbacker/data/abb.index,abb_meta.json, clears cache. - Ask: UI
Ask→appcache/log →qaguardrails → retrieve (FAISS) → distance gate → GPT response → return answer. - Stats:
GET /statsfeeds Chart.js questions-per-day graph.
GET /health→{ "sagligdi": true }POST /ask{ "question": "..." }→{ answer, sources[] }POST /ingestbody: abb_chunks JSON array →{ "status":"ok" }(rebuilds index)GET /stats→ aggregated counts by day- Static UI at
/ui
qa service: POST /answer (internal) same payload; applies guards + retrieval.
- Prompt injection blocklist (AZ/EN/RU) checked pre-retrieval.
- Distance gate: reject answers if best FAISS distance >
RETRIEVAL_MAX_DISTANCE(default 1.35) → "Bunu bilmirəm." - Context-only prompt: translate context to question language, forbid speculation, multilingual compliance.
- Out-of-scope suites: 20 negative cURL tests ensure OOS → "Bunu bilmirəm."
- Python 3.11, FastAPI, httpx, pydantic
- OpenAI
gpt-4o-minifor answers,text-embedding-3-largefor vectors - FAISS
IndexFlatL2 - Chart.js for frontend stats
- SQLite for Q/A logs
- Docker Compose for app + qa services
- Env vars:
OPENAI_API_KEY(required);QA_SERVICE_URL(optional, defaults to http://127.0.0.1:8001/answer locally / set in compose);RETRIEVAL_MAX_DISTANCE(default 1.35);TOP_K(default 10). - Data files (post-ingest):
backer/data/abb.index,backer/data/abb_meta.json,scraper/output/abb_chunks.json. - Known limits: distance gate 1.35; cache is in-memory only; Q/A logs in SQLite.
scraper/— crawl + parse + chunk ABB content →output/abb_chunks.jsonbacker/— gateway API, ingest, logging, cache, configroutes/—ask.py,ingest_route.py,stats.py,health.pycore/—guards.py,llm.py,cache.pydata/— FAISS index + meta (post-ingest)
qa_service/qa.py— retrieval + LLM servicefronter/— static UI (Azerbaijani) with Chart.jsdocker/—Dockerfile.backerdocker-compose.yml— runsappandqabacker/BLACK-BOX-TEST-RESULTS/— saved negative test outputs
- Set
OPENAI_API_KEYin shell or.env. docker compose up -d --build- Open UI at http://localhost:8000/ui/


