An AI-powered course recommendation chatbot built for the Journey lifelong learning platform. It runs as a multi-agent pipeline inside a Next.js 14 serverless application and is exposed through a floating chat panel on every page of the platform.
The chatbot handles five intent types:
| Intent | Description |
|---|---|
recommendation |
Personalised course recommendations via hybrid semantic search |
faq |
Platform documentation answers via RAG over Firestore |
general |
Conversational responses using the LLM's parametric knowledge |
comparison |
Side-by-side course comparison across two topics |
learning_path |
Three-tier roadmap (Beginner / Intermediate / Advanced) |
Responses are streamed to the client over Server-Sent Events. User interest profiles are updated asynchronously after each turn via Vercel's waitUntil.
The backend is a 6-node LangGraph StateGraph:
START → InputGuardNode → ControllerNode → RecommenderNode ──┐
→ ComparerNode ──────┤→ OutputGuardNode → END
→ LearningPathNode ──┘
→ OutputGuardNode (faq/general)
Each request goes through:
- Auth & rate limiting — Firebase ID token verification, Redis-backed fixed-window limiter (20 req/min authenticated, 5 req/min anonymous)
- Speculative pre-execution — profile fetch, input validation and intent classification run concurrently via
Promise.allbefore the graph starts - Multi-agent pipeline — input guard → controller/specialist → output guard
- SSE egress — streamed response with typed events (
token,course_card,thought,follow_up,done, etc.) - Async profiling — learning interest signals extracted and written to Firestore off the critical path
For full architecture documentation see notes/architecture.md.
| Layer | Technology |
|---|---|
| Runtime | Next.js 14 (App Router serverless) |
| Orchestration | LangGraph StateGraph |
| LLM / Embeddings | OpenAI gpt-4o-mini, text-embedding-3-small |
| Moderation | OpenAI Moderation API |
| Auth | Firebase Auth (client JWT + Admin SDK verification) |
| Database | Firestore |
| Cache | Upstash Redis (shared), Vercel Data Cache (edge), in-process module variable (46 MB embedding corpus) |
| UI | React 19, Tailwind CSS v4, Radix UI, Framer Motion |
| Observability | LangSmith (LLM tracing), Vercel structured logs |
- Node.js 18+
- A
.env.localfile in the project root (see below)
npm install
npm run devOpen http://localhost:3000. Sign in with a learner account to access the chatbot.
The predev hook runs scripts/write-firebase-key.cjs before Next.js starts. This reconstructs lib/config/firebase-service-account.json from your environment variables — you do not need to create that file manually.
Create .env.local in the project root.
# Firebase client SDK (find in Firebase console → Project Settings)
NEXT_PUBLIC_FIREBASE_API_KEY=
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=
NEXT_PUBLIC_FIREBASE_PROJECT_ID=
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
NEXT_PUBLIC_FIREBASE_APP_ID=
# Firebase Admin SDK — either the full JSON or the three separate fields
FIREBASE_SERVICE_ACCOUNT= # full service account JSON as a single-line string
# OR:
FIREBASE_PROJECT_ID=
FIREBASE_CLIENT_EMAIL=
FIREBASE_PRIVATE_KEY= # include the -----BEGIN PRIVATE KEY----- header
# OpenAI
OPENAI_API_KEY=
# Application URL
NEXT_PUBLIC_APP_URL=http://localhost:3000# Upstash Redis — shared recommendation cache, rate limiting, circuit breaker
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
# LangSmith tracing — all four must be set together
LANGSMITH_TRACING=true
LANGSMITH_ENDPOINT=https://api.smith.langchain.com
LANGSMITH_API_KEY=
LANGSMITH_PROJECT=
# Google Analytics
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=These are one-time setup scripts. Run them if you are bootstrapping a fresh Firestore database.
# Compute and upload 1,536-dim embeddings for every course
npm run generate-embeddings
# Migrate FAQ / platform docs to the platformDocs Firestore collection
npm run migrate-faq
# Precompute collaborative filtering co-enrolment scores
npm run precompute-coenrollmentAll three scripts are idempotent and can be re-run after bulk course updates. You do not need to run them if the Firestore database already contains course and platform doc data.
| File | Description |
|---|---|
app/api/chat/route.ts |
SSE stream handler — auth, rate limiting, circuit breaker, pipeline entry |
agents/chatGraph.ts |
LangGraph StateGraph definition, specialist nodes, retrieval tools |
agents/controllerAgent.ts |
handleMessage() orchestrator, SSE state machine, speculative pre-execution |
agents/inputGuardAgent.ts |
4-stage input validation (length limit, rule engine, moderation API, LLM) |
agents/outputGuardAgent.ts |
4-stage output validation (rule engine, moderation API, grounding heuristic, LLM) |
lib/courseEmbeddingSearch.ts |
Hybrid retrieval — dense cosine, BM25, RRF, 5-signal reranking, MMR |
lib/userProfileService.ts |
Builds EffectiveUserProfile; applies 30-day half-life interest decay |
lib/redis.ts |
Upstash Redis singleton — recommendation cache, platform docs, rate limiter, circuit breaker |
lib/rateLimiter.ts |
Fixed-window rate limiter |
lib/circuitBreaker.ts |
Redis-backed circuit breaker for OpenAI outages |
components/chatbot_components/Chatbot.tsx |
Main chat UI — SSE ingestion, message rendering, session persistence |
agents/agentConfig.json |
Per-agent model IDs, temperatures, max tokens and system prompts |
agents/pipelineConfig.json |
Guard thresholds, cache TTLs, rate-limit windows, circuit breaker settings |
agents/rankingConfig.json |
Reranking signal weights, MMR lambda, candidate pool size |
Detailed notes live in notes/:
notes/architecture.md— system design, API, requirements, use cases, feature traceability matrixnotes/backend.md— backend features F-20 to F-44 (agent pipeline, retrieval, caching, resilience)notes/frontend.md— frontend features F-01 to F-19 (chat UI, SSE ingestion, accessibility)notes/getting-started.md— local setup, environment variables, auth model, serverless cold-start behaviour

